Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ellipses #37

Merged
merged 25 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
993c818
ellipses in binding specs, existing examples work
quasarbright Aug 4, 2024
6ab6fa9
ellipses work in tests/dsls/match.rkt
quasarbright Aug 4, 2024
b1b76e0
ellipsize a bunch of tests
quasarbright Aug 4, 2024
54a7dd5
disallow multiple ellipses in import
quasarbright Aug 23, 2024
5bd56e2
runtime support for nest-one in nest
quasarbright Aug 29, 2024
81345e5
allow multiple imports, combine them, and ellipses outside of import …
quasarbright Aug 29, 2024
d2d935b
mandatory ellipses, nest-one -> nest
quasarbright Sep 17, 2024
87e926b
inner ellipses for bind-syntaxes and export-syntaxes
quasarbright Sep 17, 2024
4eeb661
update docs for ellipses
quasarbright Sep 18, 2024
847ee8a
static check for nest with > 1 ellipses
quasarbright Sep 18, 2024
b8f7009
no depth in runtime nest
quasarbright Sep 21, 2024
fa3570f
comments
quasarbright Sep 21, 2024
e632660
no depth in runtime bind-syntaxes
quasarbright Sep 21, 2024
44712db
clearer ellipsis expansion
quasarbright Sep 21, 2024
b5e5f1a
rec ~> import, imports absorb ellipses
quasarbright Sep 25, 2024
47917ef
ellipsis homogeneity check
quasarbright Sep 28, 2024
39239db
document ellipsis homogeneity and other constraints
quasarbright Sep 28, 2024
5a201ba
compile complex import groups
quasarbright Sep 29, 2024
3253a6e
handle groups in order checks
quasarbright Sep 29, 2024
2faaed5
no more recursive flatten for order, test groups and ellipses
quasarbright Sep 29, 2024
f47e205
test that nested has ellipsis depth 0
quasarbright Sep 29, 2024
b53946f
all elaborated spec structs have stx
quasarbright Oct 9, 2024
4441362
re-introduce elaborated binding spec flattening
quasarbright Oct 9, 2024
676020d
add test case that depends on import order
quasarbright Oct 10, 2024
5bafa7f
export ... ...+
michaelballantyne Oct 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions demos/minimal-state-machine/state-machine.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

(host-interface/expression
(machine #:initial-state s:state-name d:machine-decl ...)
#:binding (scope (import d) s)
#:binding (scope (import d) ... s)
#'(compile-machine s d ...))

(nonterminal/exporting machine-decl
Expand All @@ -23,7 +23,7 @@
(on (evt:id arg:event-var ...)
e:racket-expr ...
((~datum ->) s:state-name))
#:binding (scope (bind arg) e)))
#:binding (scope (bind arg) ... e ...)))

(require syntax/parse/define (for-syntax syntax/parse racket/list))

Expand Down
4 changes: 2 additions & 2 deletions demos/visser-symposium/state-machine.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

(host-interface/expression
(machine #:initial-state s:state-name d:machine-decl ...)
#:binding (scope (import d) s)
#:binding (scope (import d) ... s)
#'(compile-machine s d ...))

(nonterminal/exporting machine-decl
Expand All @@ -23,4 +23,4 @@
(on (evt:id arg:racket-var ...)
e:racket-expr ...
((~datum ->) s:state-name))
#:binding (scope (bind arg) e)))
#:binding (scope (bind arg) ... e ...)))
6 changes: 3 additions & 3 deletions design/statecharts-full.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@

(state n:state-name
sb:state-body ...)
#:binding [(export n) (scope (import sb))]
#:binding [(export n) (scope (import sb) ...)]

(use scn:statechart-name #:as sn:state-name
e:event ...))

(nonterminal event
(on (evt:id arg:var ...)
ab:action ...+)
#:binding (scope (bind arg) ab)
#:binding (scope (bind arg) ... ab ...)

(on-enter ab:action ...)
(on-exit ab:action ...))
Expand All @@ -40,7 +40,7 @@
(emit (name:id arg:racket-expr ...))

(let* (b:binding-group ...) body:action ...)
#:binding (nest b body))
#:binding (nest b ... [body ...]))

(nonterminal/nesting binding-group (tail)
[v:var e:racket-expr]
Expand Down
6 changes: 3 additions & 3 deletions design/statecharts-smaller.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@

(state n:state-name
sb:state-body ...)
#:binding [(export n) (scope (import sb))]
#:binding [(export n) (scope (import sb) ...)]

(use scn:statechart-name #:as sn:state-name
e:event ...))

(nonterminal event
(on (evt:id arg:var ...)
ab:action ...+)
#:binding (scope (bind arg) ab))
#:binding (scope (bind arg) ... ab))

(nonterminal action
(-> s:state-name)
Expand All @@ -34,7 +34,7 @@
(emit (name:id arg:racket-expr ...))

(let* (b:binding-group ...) body:action ...)
#:binding (nest b body))
#:binding (nest b ... body))

(nonterminal/nesting binding-group (tail)
[v:var e:racket-expr]
Expand Down
2 changes: 2 additions & 0 deletions main.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
(for-syntax
number
id
...
...+

mutable-reference-compiler
immutable-reference-compiler
Expand Down
102 changes: 99 additions & 3 deletions private/runtime/binding-spec.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
(struct-out bind)
(struct-out bind-syntax)
(struct-out bind-syntaxes)
(struct-out scope) ; {}
(struct-out scope)
(struct-out group) ; []
(struct-out nest)
(struct-out nest-one)
(struct-out nested)
(struct-out suspend)
(struct-out fresh-env-expr-ctx)
(struct-out ellipsis) ; ...

; used by interface macros
expand-top
Expand Down Expand Up @@ -68,25 +69,32 @@
(struct nested [] #:transparent)
(struct suspend [pvar] #:transparent)
(struct fresh-env-expr-ctx [spec] #:transparent)
(struct ellipsis [pvars spec] #:transparent)

;;
;; Expansion
;;

; A NestState is one of
;; #f, nest-call, or nest-ret

;; pvar-vals is (hashof symbol? (treeof syntax?))
;; nest-state is #f, nest-call, or nest-ret
(struct exp-state [pvar-vals nest-state])

;; Helpers for accessing and updating parts of the exp-state

; exp-state? symbol? -> (treeof syntax?)
(define (get-pvar st pv)
(hash-ref (exp-state-pvar-vals st) pv))

; exp-state? symbol? (treeof syntax?) -> exp-state?
(define (set-pvar st pv val)
(struct-copy
exp-state st
[pvar-vals (hash-set (exp-state-pvar-vals st) pv val)]))

; exp-state? (listof symbol?) ((treeof syntax?) ... -> (treeof syntax?)) -> exp-state?
; updates the environment by applying f to the values of pvs
(define (update-pvar* st pvs f)
(define env (exp-state-pvar-vals st))
(define vals (for/list ([pv pvs]) (hash-ref env pv)))
Expand All @@ -101,6 +109,7 @@
exp-state st
[pvar-vals env^]))

; exp-state? (NestState -> NestState) -> exp-state?
(define (update-nest-state st f)
(struct-copy
exp-state st
Expand Down Expand Up @@ -281,6 +290,7 @@

(set-pvar st^ pv done-seq)]

; TODO deprecate
[(nest-one pv f inner-spec)
(define init-seq (list (get-pvar st pv)))

Expand All @@ -299,7 +309,15 @@

[(suspend pv)
(for/pv-state-tree ([stx pv])
(make-suspension (add-scopes stx local-scopes) (current-def-ctx)))]))
(make-suspension (add-scopes stx local-scopes) (current-def-ctx)))]
[(ellipsis pvs spec)
; filter and split the environments
(define sts (exp-state-split/ellipsis st pvs))
; expand on each sub-environment
(define sts^
(for/list ([st sts])
(simple-expand-internal spec st local-scopes)))
(st-merge/ellipses st pvs sts^)]))

; f is nonterm-transformer
; seq is (listof (treeof syntax?))
Expand All @@ -325,6 +343,84 @@
(call-reconstruct-function (exp-state-pvar-vals st^) reconstruct-f)
(exp-state-nest-state st^))]))

; ellipsis expansion
; when expanding syntax with an ellipsized binding spec, we expect the syntax to be a list.
; For example:
; (e:my-expr ...)
; #:binding [e ...]
; #'(f x y z)
; We'll start with e mapped to (list #'f #'x #'y #'z)
; But since it's ellipsized, we want to run the expander for each element of that list.
; This means we need to expand under an environment mapping e to #'f,
; then expand under an environment mapping e to #'x, and so on.
; Then we'll have a list of environments, each mapping e to an expanded #'f, #'x, #'y, or #'z.
; Let's call those expanded syntaxes #'f^, #'x^, #'y^, #'z^
; Finally, we need to merge those sub-environments back into the same shape as the original
; so we end up with e mapped to (list #'f^ #'x^ #'y^ #'z^).

; exp-state? (listof symbol?) -> (listof exp-state?)
; split an environment mapping pvars to lists into a list of environments mapping pvars to list elements.
(define (exp-state-split/ellipsis st pvars)
(match st
[(exp-state pvar-vals nest-state)
(for/list ([env (env-split/ellipsis pvar-vals pvars)])
(exp-state env nest-state))]))

; (hash symbol? (treeof syntax?)) (listof symbol?) -> (listof (hash symbol? (treeof syntax?)))
; Spit up the environment into a list of envs. One per element of a pvar's value.
; Filters environment to just pvars.
; Pvars should be mapped to lists of equal lengths. Errors if they aren't.
(define (env-split/ellipsis env pvars)
(define env-filtered
(for/hash ([(pv vs) (in-hash env)]
#:when (member pv pvars))
(values pv vs)))
(define repetition-length (env-repetition-length env-filtered))
(for/list ([i (in-range repetition-length)])
(for/hash ([(pv vs) (in-hash env-filtered)])
(values pv (list-ref vs i)))))

(module+ test
(check-equal? (env-split/ellipsis (hash 'a '(1 2 3) 'b '(4 5 6) 'c '())
'(a b))
(list (hash 'a 1 'b 4)
(hash 'a 2 'b 5)
(hash 'a 3 'b 6))))

; (hash symbol? (treeof syntax?)) -> natural?
; Assuming this environment is getting split for ellipses, computes how many environments it should get split into.
; Errors if not all trees have the same length.
(define (env-repetition-length env)
(define result
(or (for/first ([(_ vs) (in-hash env)])
(unless (list? vs)
; TODO check in compiler
(error "too many ellipses in binding spec"))
(length vs))
0))
(for ([(_ vs) (in-hash env)])
(unless (= (length vs) result)
; TODO Can this be checked in the compiler? Would need to make sure ellipsized bs vars
; come from the same ss ellipsis.
(error "incompatible ellipsis match counts for binding spec")))
result)

(module+ test
(check-equal? (env-repetition-length (hash 'a '(1 2 3) 'b '(4 5 6)))
3))

; exp-state? (listof symbol?) (listof exp-state?) -> exp-state?
; merge expanded sub-environments into the original environment.
; st is original state.
; sts^ is a list of states from sub-expansions of each ellipsis repetition.
; pvs is the pvars referenced in the ellipsized binding spec.
(define (st-merge/ellipses st pvs sts^)
; can maintain st's nest state because nested will never occur inside ellipses,
; so nest state will not have changed in any of the sub-expansions.
(for/fold ([st st])
([pv pvs])
(set-pvar st pv (for/list ([st^ sts^]) (hash-ref (exp-state-pvar-vals st^) pv)))))

;; When entering a `nest-one` or `nest` form, add an extra scope. This means that the
;; expansion within is in a new definition context with a scope distinguishing it from
;; surrounding definition contexts where macros may have been defined. The Racket expander
Expand Down
Loading
Loading