Every language made with Racket has two essential components:
For every language, we’ll specify a reader and an expander. Sometimes we’ll write them from scratch. Sometimes we’ll import them from elsewhere. But they’re always mandatory.
They don’t need to be large, either. For stacker, we’ll write both the reader and expander. But they’ll both fit into 40 lines of code.
Every Racket source file begins with a #lang line. If you’ve explored Racket you might have seen variants like these:
1 2 3 4
#lang racket #lang racket/base #lang scribble/doc #lang datalog
In this book, we’ll primarily be using:
1 2 3
#lang br #lang br/quicklang #lang brag
As we just learned, the first step in interpreting a language is to send the source code to the reader for that language. The #lang line’s job is to tell Racket where to find that reader. The reader, in turn, will tell Racket where to find the expander.
The reader and expander for a language are often stored in different source files—or , as they’re also known in Racket. But that’s optional. For this project, we’ll implement the language with just one source file, called "stacker.rkt".
In the future, we’ll prefer to use the shorthand #lang name notation shown in the above examples. But that requires some extra setup. (We’ll learn how to do that in the next tutorial.)
For now, we’ll use the alternate #lang reader path notation, where path is a local path to the source file that contains our reader:
#lang reader "stacker.rkt"
We need to write some Racket code. So let’s learn some Racket:
Function calls go between parentheses (and all parenthesized S-expressions are treated as function calls). The function name sits in the first position inside the parentheses, followed by its arguments. Thus we write (* edge 4), not (edge * 4). (This style is known as .) Parenthesized expressions like (4) or ("blue") will produce errors, because 4 and "blue" aren’t functions. (For more, see functions.)
S-expressions can be nested to any depth. Thus, these expressions are the same:
Linebreaks and indentation are used for readability. They don’t change the meaning of the code.
Square brackets are sometimes used for readability. They mean the same thing as parentheses.
Open DrRacket. Start a new source file called "stacker.rkt" and save it in a convenient location. We’ll make this file with a special tutorial language called br/quicklang. The br/quicklang language comes from the beautiful-racket package we installed during the main setup. It works the same way as ordinary Racket, but adds some extra conveniences. For testing purposes, let’s add a second line, so our whole file looks like this:
#lang br/quicklang 42
Click Run in the DrRacket toolbar—or learn the key shortcut, because we’ll be using it a lot. In the DrRacket interactions window, we’ll see the result:
In the same directory as "stacker.rkt", create a second source file called "stacker-test.rkt". We’ll use this to test our new language as we go (but it won’t work yet):
#lang reader "stacker.rkt"
If we run this file anyway, we get an error that looks like this:
read-syntax: cannot find reader for `#lang reader "stacker.rkt"`
If so, that’s the right result: we’re telling #lang to get a reader out of "stacker.rkt", but we haven’t made one yet.
So let’s do that.