At the end of the reader phase, the source code has been converted into a module expression that contains S-expressions, but has no bindings, e.g.—
Any module can be used as the expander for a language. It’s invoked by the first line of the module code returned by the reader. For instance, this module expression will rely on dsl/expander as its expander:
Question: why don’t we use the #lang notation in a module expression?
In essence, the expander name is like an implied require at the beginning of the module:
Question: why can’t require actually replace the expander?
Every module used as an expander must provide a #%module-begin , which will be the first thing invoked in the expander. #%module-begin must accept as input all the expressions that appear in the body of the module expression returned by read-syntax.
To start the expansion, Racket imports the #%module-begin from the expander specified in the module expression. It replaces the module expression with a call to this #%module-begin, passing it all the parsed expressions that are in the body of the module expression. So this:
Often, the #%module-begin for a language will perform some language-specific processing on the parse tree, and then call the #%module-begin in the implementation language. To prevent namespace collisions between the two #%module-begin macros, use rename-out:
1 2 3 4 5
The define-macro form introduces a macro rather than an ordinary function. The first line contains a that binds , which must be in UPPERCASE, but their names are otherwise arbitrary. If the input to the macro doesn’t match the pattern, the macro raises an error.
Here, THING is a pattern variable used in two syntax templates:
But unlike a variable, THING cannot be used on its own:
The initial syntax pattern can contain literal values (which must match literally, of course) and sublists:
Question: why does the input (mac2 (list "foo" 42 "bar")) raise an error?
The ellipsis matches a series of input arguments, and then denotes a “step and repeat” operation in the syntax template:
Question: what if you want to handle the first element separately?
Write a language called #lang s-exp expand-only that:
Converts every number into 42.
Converts every string into "whee".
Converts every other expression into 'kaboom.
And otherwise preserves the list structure of the program.
Test input (slightly different from before—note the x instead of 3):
"whee" '(kaboom 42 (kaboom 42 (kaboom kaboom)))
#lang s-exp is a variant of #lang that handles reading the source file, and just feeds a list of S-expressions to our expander (like we did manually in the previous exercise).
Hint: You can start with this template:
Hint: Write a helper function called convert-expr that is applied to each expression in EXPR ....
Hint: If you write 'EXPR in the syntax template, it means “substitute the value of EXPR, and then quote it.”
Hint: In DrRacket, your test file will also produce an error like Interactions disabled: expand-only does not support a REPL. Ignore it.
The hand that rocks the cradle.