Crate beck_macro

Crate beck_macro 

Source
Expand description

Macro expansion, hygienic from the first commit.

docs/08-roadmap.md Phase 1: “Macro expander with hygiene — from the start (§2.4); retrofitting hygiene is a rewrite.” docs/02-syntax.md §2.4: “Identifiers introduced inside a quote get a fresh hygiene scope in Node.meta; capture is possible but must be explicit (inject(name)).”

§The algorithm

Flatt’s sets of scopes, which is the model Racket settled on after, as §2.4 puts it, “Scheme’s 20-year history here”. Each expansion step:

  1. mints a fresh [Scope];
  2. adds it to the macro’s input — every identifier the call site supplied;
  3. substitutes the arguments into the template;
  4. flips the scope over the whole result.

The flip is what makes it work. Identifiers that came from the call site had the scope added in step 2 and lose it again in step 4, so they mean what they meant where they were written. Identifiers the template introduced never had it, so they gain it — and a binding carrying a scope the call site does not have is invisible to the call site’s references. Capture becomes impossible in both directions rather than unlikely.

Resolution itself lives in beck-types: a binding is a candidate for a reference exactly when binding.scopes ⊆ reference.scopes, and the most specific candidate wins.

§What a macro body may do

Anything a pure Beck function may do. interp is the compile-time interpreter §2.4 calls for — bindings, if, for, while, lambdas, calls to the module’s own defs and to the pure part of the prelude — in a capability-restricted environment: there is no name for a file, a socket, a clock or a process, and the prelude’s effectful primitives are refused by name so that reaching for one is a diagnostic rather than a spelling mistake.

quote: is the form whose value is syntax, and $e inside one is an ordinary expression whose value is reflected back into the template — so $x where x is a parameter is the caller’s code, and $(n * 2) is a literal. That is the whole difference from the template expander this used to be: a let in a macro body now computes rather than substituting.

And refuse("…") is how a body says it has no rule for what it was given — a code generator that meets something it cannot write for otherwise emits code that fails to check somewhere else, with a message about lines the reader never wrote.

§The two phases

An ordinary macro is expanded here, before anything has been checked. A typed macro is left exactly as written by this pass and expanded by the checker, because its body asks what its arguments were inferred to be; typed is that half, and it is the same interpreter with one more name in scope.

Re-exports§

pub use interp::Val;
pub use interp::BUILTINS;
pub use interp::MAX_STEPS;
pub use interp::RESTRICTED;
pub use typed::DeclInfo;
pub use typed::Fields;
pub use typed::TyKind;
pub use typed::TyRepr;
pub use typed::TypeEnv;
pub use typed::TypedExpander;
pub use typed::Variants;

Modules§

interp
The macro interpreter: Beck, evaluated at compile time.
typed
Typed macros: the half of §2.4 that wants the checker’s answers.
vocabulary
What HTML is: the element, attribute and event names ui: will write.

Structs§

Expander

Constants§

MAX_DEPTH
How deep a macro may expand before the expander decides it is not going to terminate.
MAX_EXPANSION
How many nodes a module’s macros may produce, in total.

Functions§

expand_module
Expand every macro in a module to a fixpoint.
expand_module_measured
Expand a module, and say how much of the interpreter’s step budget is left.
expand_module_with
The same, with the macros of the modules this one imports in scope.
expand_ui
Expand (ui (kw do (quote (do …)))) into an Html-valued expression.