We said that stackerizer would convert an “appropriate” Racket S-expression into a stacker program. To keep things simple, we’ll support only a limited vocabulary of S-expressions:
The + and * operators. Of course.
Any numbers as arguments to these operators, in any quantity. Let’s remember that stacker can only perform operations on two arguments at a time, so we’ll need to make some kind of translation there.
Any degree of nesting. stacker only handles a flat list of arguments, so we’ll have to flatten the nesting somehow.
A stacker program represents a single expression. Therefore, the stackerizer language only needs to handle a single expression as input.
Open DrRacket. Start a new file called "stackerizer.rkt" and save it in a convenient location. All we need for now is the #lang line:
1 | #lang br/quicklang |
In the same directory, create a second file called "stackerizer-test.rkt", also blank except for the #lang line:
1 | #lang s-exp "stackerizer.rkt" |
Instead of #lang reader path, we’re using the new #lang s-exp path notation. Recall that #lang reader path was convenient for a prototype language, because it allows us to bootstrap into a certain reader (which in turn points to an expander).
By contrast, #lang s-exp path does two things.
Instead of making our own reader, #lang s-exp invokes Racket’s default reader (which reads S-expressions, thus it’s abbreviated s-exp). In other words, because our new language is already made of S-expressions, we can skip the fandango of making our own read-syntax function.
We cannot, however, skip making an expander. So the other job of #lang s-exp is to identify the expander we want to use, in this case "stackerizer.rkt".
Everything else works the same way. Just like the read-syntax function we made before, the s-exp reader returns the code for a module, packaged as a syntax object. And as before, our expander will need to start with a #%module-begin macro.
In Racket lingo, a language that uses the standard S-expression reader is known as a module language. Like macro vs. function, this terminology is counterintuitive. All Racket code lives in some module. Therefore, a language that uses a custom reader can only get it from a module. Anyhow. If we see “module language” in the Racket docs, we’ll know that it refers more precisely to a language that has no custom reader.