When we invoke #lang jsonic, Racket will look for a "main.rkt" module within our jsonic directory. Within that module, it will expect to find a reader submodule that provides our read-syntax function. We’ll use require and provide to fetch this function from a "reader.rkt" module that we’ll create next. For now, let’s put a "main.rkt" in the jsonic directory that looks like this:
One more time—let’s recap the role of the reader:
By convention, Racket expects the name of the main reader function to be read-syntax. This read-syntax function must return one value: code for a module expression, packaged as a . This syntax object must have no .
In bf, we saw that we could make a read-syntax function by relying on a and a to do the heavy lifting.
We’ll do the same here. In fact, we’ll just take the read-syntax from bf and reuse it here, changing the expander name from bf/expander to jsonic/expander. Our new "reader.rkt" ends up like this:
1 2 3 4 5 6 7 8 9
By the way, we’re using parse-tree and module-datum as intermediate values for readability. We could also write this function more compactly like so:
1 2 3 4 5 6 7
Because Racket is an expression-based language, it’s usually possible—and therefore tempting—to just keep nesting lines of code inside each other. But it’s not a virtuous habit.
First, it makes the code harder to read—for others, but especially for the future version of ourselves. What seems like cleverly compact notation today will just be an annoying brain-teaser next week.
Second, it doesn’t make the code faster. Before Racket runs a program, it optimizes the code. Most of these simple notational “improvements” will be done automatically by Racket anyhow. So we’re usually better off maximizing the readability of our code.
Our read-syntax function won’t work yet, because it relies on "tokenizer.rkt" and "parser.rkt". We’ll add those next.