I am using Beautiful Racket to draft a new DSL, bleir
, based on s-expressions. I first install a new racket package using the Master Recipe.
raco pkg new bleir
cd bleir
raco pkg install
I then set up a vestigial reader and expander in the madatory file, main.rkt
:
#lang br/quicklang
(module+ reader
(provide read-syntax)
(require br/quicklang))
(provide (rename-out [bleir-expander #%module-begin]))
(define-macro (bleir-expander PARSE-TREE)
#'(#%module-begin
(display PARSE-TREE)))
I then write a test file, tyst-bleir.rkt
[intentional misspelling, to sidestep potential problems with the string name "test"]:
#lang bleir
42
I get a problem:
Module Language: only a module expression is allowed, either #lang <language-name> or (module <name> <language> ...) in: 42
I can fix it in two ways, "disgusting" and "mysterious". First, the disgusting fix, in tyst-bleir.rkt
:
(module tyst-bleir bleir
42)
This works, invoking both the doubly exported reader from br/quicklang
and my custom expander, displaying 42
.
QUESTION 1: the #lang bleir
syntax is supposed to be identical to the verbose (module ...)
syntax is it not? Why does #lang bleir
not work to import the reader from br/quicklang
?
Now, the mysterious fix:
#lang s-exp bleir
42
This evidently uses an alternative s-exp reader from module (package?) s-exp
and it's ok, I guess. At least I'm not blocked. HOWEVER, I do not understand why I can't use the reader from br/quicklang
in the same way.
#lang br/quicklang bleir
42
bleir: unbound identifier in: bleir
QUESTION 2: Why doesn't this work? Can I fix it? If so, how?
Here is another failed attempt, going the other way, trying to import the reader from s-exp
in main-rkt
:
(module+ reader
(provide read-syntax)
(require s-exp))
main.rkt:11:11: cannot open module file module path: s-exp path: /usr/share/racket/collects/s-exp/main.rkt system error: no such file or directory; rkt_err=3 in: s-exp no package suggestions are available .
QUESTION 3: Why doesn't this work? Can I fix it? If so, how?
You didn't fully follow the master recipe that you reference. If you continue reading, you will see:
read-syntax must return one value: code for a module expression, represented as a syntax object. Typically, the converted S-expressions from the source file are inserted into this syntax object. This syntax object must have no identifier bindings. This module code must include a reference to the expander that will provide the initial set of bindings when the module code is evaluated. In pseudocode:
#lang br
(module reader br
(provide read-syntax)
(define (read-syntax name port)
(define s-exprs (read-code-from port))
(strip-bindings
#`(module dsl-mod-name dsl/expander
#,@s-exprs))))
In other words, your read-syntax
needs to return (strip-bindings #'(module ...))
.
I think you are confusing "languages that users use" and "languages that language designers use" together.
"languages that users use" are something like #lang bleir
, #lang slideshow
, #lang pollen
, etc. These languages provide constructs to do domain-specific tasks. For example, #lang slideshow
is a language for creating presentations. #lang pollen
is a language for creating books.
"languages that language designers use" are a kind of "languages that users use", where the users are language designers. These languages provide constructs to make language creation easier. #lang br/quicklang
is in this category.
So you, as a language designer, would use #lang br/quicklang
internally in your bleir/main.rkt
for constructing a language bleir
. But your users, who are not language designers, should not need to write (or even be aware of) #lang br/quicklang
.
The master recipe talks about two things: the reader and the expander. In a fully developed language, you would provide both the reader and the expander. s-exp
can be seen as a convenient hack language to "run" non-fully developed languages that only have the expander, but don't have the reader yet.
So, the reason why #lang s-exp bleir
works is that you have a functioning expander, so #lang s-exp bleir
uses your expander but completely ignores your reader
submodule. The reason why #lang bleir
doesn't work is that your reader
submodule is buggy (see Question 1). The reason why #lang br/quicklang bleir
doesn't work is that it doesn't make any sense for users to write #lang br/quicklang
.
I suggest that you simply forget about s-exp
and fix your reader. AFAIK, Beautiful Racket didn't really talk about s-exp
, and if you are following the book, using s-exp
will simply make you confused.
If you want a technical answer, https://docs.racket-lang.org/guide/hash-lang_syntax.html (or more generally https://docs.racket-lang.org/reference/reader.html#%28part._parse-reader%29) answers your question:
Unlike racket, s-exp cannot be used as a module path with require. Although the syntax of language for #lang overlaps with the syntax of module paths, a language is not used directly as a module path. Instead, a language obtains a module path by trying two locations: first, it looks for a reader submodule of the main module for language. If this is not a valid module path, then language is suffixed with /lang/reader. (If neither is a valid module path, an error is raised.) The resulting module supplies read and read-syntax functions using a protocol that is similar to the one for #reader.