module macro

module lib/macro.mu

import "macro"

macro is the compile-time macro surface: define registers a handler that rewrites its call site during expansion, and requires declares which build-machine capabilities that module's handlers are allowed to reach.

Both are meant to be called directly -- docs/MacroTutorial.md opens with macro.define. What is not meant to be called directly is the __macro_define builtin underneath, which skips define's argument checking.

Functions

define#

fn define(name, handler)

define registers a macro handler with the given name.

Source lib/macro.mu:10
fn define(name, handler) {
  // Can't use typing here because it's not available yet.
  //
  // An error value rather than panic: define runs in the macro phase, where
  // syscall is unavailable, and panic writes to stderr through one. The caller
  // sees a compile-time failure either way.
  if type(name) != "STRING" {
    return error("macro.define: name must be a string")
  }
  return __macro_define(name, handler)
}

requires#

fn requires(_)

requires opts this module's macros back into build-machine capabilities that are withheld from macro handlers by default -- the filesystem, the kernel, the environment, and anything else that makes a macro's output depend on where it was compiled rather than on its arguments.

macro.requires(["env", "platform"])

It is a declaration, not a call: the compiler reads the list out of the source before running any macro, because the capability set has to be decided before the code that would use it runs. At runtime it does nothing. The list must be string literals for the same reason.

Declaring a capability is what records that this module's expansions are not reproducible across build machines.

The parameter is spelled _ because nothing ever reads it here: the argument that matters is the one in the SOURCE, which the compiler has already read by the time this body could run.

Source lib/macro.mu:40
fn requires(_) {
  return nil
}