Skip to content

Commit

Permalink
Merge pull request #69 from quasarbright/main
Browse files Browse the repository at this point in the history
racket-body
  • Loading branch information
michaelballantyne authored Feb 14, 2025
2 parents 5bf75bc + 52ecfe5 commit 96abac4
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 11 deletions.
11 changes: 11 additions & 0 deletions private/syntax/interface.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
(provide syntax-spec
define-dsl-syntax
(for-syntax racket-expr
racket-body
racket-var
racket-macro

Expand Down Expand Up @@ -348,7 +349,17 @@
e:expr
#:binding (host e)))

(syntax-spec
(nonterminal/exporting racket-body
#:description "racket body"
#:allow-extension racket-macro
((~literal define-values) (x:racket-var ...) e:racket-expr)
#:binding [(export x) ...]

((~literal define-syntaxes) (x:racket-macro ...) e:expr)
#:binding (export-syntaxes x ... e)

e:racket-expr))

(define-syntax define-dsl-syntax
(syntax-parser
Expand Down
15 changes: 14 additions & 1 deletion scribblings/reference/specifying.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,20 @@ This defines a macro @racket[conj] that expands to a goal in miniKanren.

@defidform[#:kind "nonterminal" racket-expr]

A nonterminal that allows arbitrary host language expressions. Expressions are wrapped with @racket[#%host-expression] during DSL expansion.
A nonterminal that allows arbitrary host language expressions. Expressions are wrapped with @racket[#%host-expression] during DSL expansion. This nonterminal does not support definitions.

@defidform[#:kind "nonterminal" racket-body]

A nonterminal that allows arbitrary host language expressions and definitions. This is an exporting nonterminal, so it must be explicitly mentioned in a binding spec, usually with @racket[import].

Example:

@racketblock[
(syntax-spec
(nonterminal my-expr
(my-let ([x:racket-var e:racket-expr]) body:racket-body ...+)
#:binding (scope (bind x) (import body) ...)))
]

@defidform[#:kind "binding class" racket-var]

Expand Down
12 changes: 6 additions & 6 deletions scribblings/tutorial/basic-tutorial.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -250,24 +250,24 @@ An action expression can only @racket[displayln] the value of a variable. What i
...

(nonterminal/exporting state-spec
(state name:state-name ((~datum on-enter) body:racket-expr ...+) transition:transition-spec ...)
#:binding (export name)
(state name:state-name ((~datum on-enter) body:racket-body ...+) transition:transition-spec ...)
#:binding [(export name) (scope (import body) ...)]

(state name:state-name transition:transition-spec ...)
#:binding (export name))

(nonterminal transition-spec
(on (event-name:id arg:event-var ...)
body:racket-expr
body:racket-body
...
((~datum goto) next-state-name:state-name))
#:binding (scope (bind arg) ... body))
#:binding (scope (bind arg) ... (import body) ...))

...))

Instead of using @racket[action-spec] and defining our own nonterminal for action expressions, we can just use @racket[racket-expr], which allows arbitrary racket expressions. And our @racket[event-var] identifiers will be in scope in the racket expression! We can control how references to our DSL-bound variables behave in Racket expressions and whether they're allowed at all using reference compilers, which we'll discuss in the @secref["compilation"] section.
Instead of using @racket[action-spec] and defining our own nonterminal for action expressions, we can just use @racket[racket-body], which allows arbitrary racket expressions and definitions. And our @racket[event-var] identifiers will be in scope in the racket expression! We can control how references to our DSL-bound variables behave in Racket expressions and whether they're allowed at all using reference compilers, which we'll discuss in the @secref["compilation"] section.

In addition to @racket[racket-expr], syntax-spec provides @racket[racket-var] for allowing references to Racket-defined variables in DSL expressions, and @racket[racket-macro] for allowing the language to be extended by arbitrary Racket macros. We'll talk more about macros in the @secref["macros"] section.
In addition to @racket[racket-body], syntax-spec provides @racket[racket-expr] for allowing Racket expressions, @racket[racket-var] for allowing references to Racket-defined variables in DSL expressions, and @racket[racket-macro] for allowing the language to be extended by arbitrary Racket macros. We'll talk more about macros in the @secref["macros"] section.

@section[#:tag "compilation"]{Compilation}

Expand Down
8 changes: 4 additions & 4 deletions tests/dsls/state-machine-for-tutorial.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@
(nonterminal/exporting state-spec
#:allow-extension state-macro

(state name:state-name ((~datum on-enter) body:racket-expr ...+) transition:transition-spec ...)
#:binding (export name)
(state name:state-name ((~datum on-enter) body:racket-body ...+) transition:transition-spec ...)
#:binding [(export name) (scope (import body) ...)]

(state name:state-name transition:transition-spec ...)
#:binding (export name))

(nonterminal transition-spec
(on (event-name:id arg:event-var ...)
body:racket-expr
body:racket-body
...
((~datum goto) next-state-name:state-name))
#:binding (scope (bind arg) ... body ...))
#:binding (scope (bind arg) ... (import body) ...))

(host-interface/expression
(machine #:initial initial-state:state-name s:state-spec ...)
Expand Down
23 changes: 23 additions & 0 deletions tests/racket-body.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#lang racket/base

(require "../testing.rkt")

(syntax-spec
(nonterminal my-expr
(my-let ([x:racket-var e:racket-expr]) body:racket-body ...+)
#:binding (scope (bind x) (import body) ...))
(host-interface/expression
(my-dsl e:my-expr)
(syntax-parse #'e
[(_ ([x e]) body ...+)
#'(let ([x e]) body ...)])))

(check-equal?
(my-dsl (my-let ([x 1]) x))
1)
(check-equal?
(my-dsl (my-let ([x 1]) (define y x) y))
1)
(check-equal?
(my-dsl (my-let ([x 1]) (define-syntax-rule (m) x) (m)))
1)

0 comments on commit 96abac4

Please sign in to comment.