Skip to content

Commit

Permalink
Refactor nested let bindings in one pass
Browse files Browse the repository at this point in the history
Closes #284.
  • Loading branch information
jackfirth committed Sep 14, 2024
1 parent d6cfc5f commit 8748651
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 11 deletions.
60 changes: 60 additions & 0 deletions default-recommendations/let-binding-suggestions-nesting-test.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#lang resyntax/testing/refactoring-test


require: resyntax/default-recommendations let-binding-suggestions


header:
- #lang racket/base


test: "nested let bindings"
------------------------------
(define (f)
(let ([x 1])
(let ([y 1])
(let ([z 1])
1))))
------------------------------
------------------------------
(define (f)
(define x 1)
(define y 1)
(define z 1)
1)
------------------------------


test: "nested let bindings with interleaved expressions"
------------------------------
(define (f)
(let ([x 1])
(displayln "foo")
(let ([y 1])
(displayln "bar")
(let ([z 1])
1))))
------------------------------
------------------------------
(define (f)
(define x 1)
(displayln "foo")
(define y 1)
(displayln "bar")
(define z 1)
1)
------------------------------


test: "nested conflicting let bindings only partially refactorable"
------------------------------
(define (f)
(let ([x 1])
(let ([x 2])
1)))
------------------------------
------------------------------
(define (f)
(define x 1)
(let ([x 2]) 1))
------------------------------
42 changes: 31 additions & 11 deletions default-recommendations/private/let-binding.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,44 @@


(define-splicing-syntax-class body-with-refactorable-let-expression
#:attributes ([refactored 1])
#:attributes ([refactored 1] [id 1])
(pattern
(~seq leading-body ... let-expression:refactorable-let-expression)
#:with (refactored ...) #'(leading-body ... (~@ let-expression.refactored ...))))
#:with (refactored ...) #'(leading-body ... (~@ let-expression.refactored ...))
#:with (id ...) (attribute let-expression.id)))


(define-syntax-class refactorable-let-expression
#:attributes ([refactored 1])
#:literals (let let-values let* let*-values)
(pattern
(~or ((~or let let-values) ~! bindings:binding-group body ...+)
((~or let* let*-values) ~! (~var bindings (binding-group #:nested? #true)) body ...+))
#:when (for/and ([id (attribute bindings.id)])
#:attributes ([refactored 1] [id 1])
(pattern (header:let-header body:let-body)
#:with (id ...) (append (attribute header.id) (attribute body.id))
#:when (for/and ([id (attribute id)])
(not (identifier-has-exact-binding-in-context? id this-syntax)))
#:when (for/and ([id (attribute bindings.id)])
(identifier-binding-unchanged-in-context? id (first (attribute body))))
#:when (not (check-duplicate-identifier
(for/list ([id (attribute id)])
(identifier-in-context id this-syntax))))
#:when (for/and ([id (attribute header.id)])
(identifier-binding-unchanged-in-context? id (attribute body.first-body)))
#:with (refactored ...)
#`(~splicing-replacement (bindings.definition ... body ...) #:original #,this-syntax)))
#`(~splicing-replacement (header.definition ... body.refactored ...) #:original #,this-syntax)))


(define-splicing-syntax-class let-body
#:attributes (first-body [refactored 1] [id 1])
(pattern :body-with-refactorable-let-expression
#:with first-body (first (attribute refactored)))
(pattern (~seq first-body body ...)
#:with (refactored ...) this-syntax
#:with (id ...) '()))


(define-splicing-syntax-class let-header
#:attributes ([id 1] [definition 1])
#:literals (let let-values let* let*-values)
(pattern (~seq (~or let let-values) ~! :binding-group))
(pattern (~seq (~or let* let*-values) ~! (~var bindings (binding-group #:nested? #true)))
#:attr [id 1] (attribute bindings.id)
#:attr [definition 1] (attribute bindings.definition)))


(define-syntax-class binding-clause
Expand Down

0 comments on commit 8748651

Please sign in to comment.