|
|
|
# Procedural Macros (aka syntax-case)
|
|
|
|
|
|
|
|
The new procedural macros let us define our macros using JavaScript.
|
|
|
|
|
|
|
|
macro m {
|
|
|
|
case {_ ($x) } => {
|
|
|
|
console.log("I'm in ur macros!");
|
|
|
|
return #{ $x }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m (42)
|
|
|
|
// ---> expands to
|
|
|
|
42 // logs: I'm in ur macros!
|
|
|
|
|
|
|
|
Changes from old design:
|
|
|
|
|
|
|
|
* The pattern is now wrapped in a curly brace to prevent ambiguity. Previously `case $x => ...` would match `m 42` but now this is an error. Instead do `case {_ $x } => ...`.
|
|
|
|
* Also the macro name is being matched as the first pattern and the wildcard pattern `_` has been added so you can ignore it if you want.
|
|
|
|
* The template form `#{...}` has been added. Any bound pattern variables that are in scope are replaced with their match.
|
|
|
|
* Cases are matched in-order. Previously they were being matched by length first.
|
|
|
|
|
|
|
|
# Primitives
|
|
|
|
|
|
|
|
For constructing new syntax objects we have the following primitive functions that are in scope inside a procedural macro. These loosely correspond to Scheme's `datum->syntax` but because JS has different token types we need the different functions. The first argument to each of these functions is used to construct the token and the context is taken from the optional second argument.
|
|
|
|
|
|
|
|
makeValue :: (Any, Null or Syntax) -> Syntax
|
|
|
|
BooleanLiteral
|
|
|
|
NullLiteral
|
|
|
|
NumericLiteral
|
|
|
|
StringLiteral
|
|
|
|
|
|
|
|
makeRegex :: (Str, Str, Null or Syntax) -> Syntax
|
|
|
|
RegexLiteral
|
|
|
|
// the second argument here is the flags for the regex
|
|
|
|
|
|
|
|
makeIdent :: (Str, Null or Syntax) -> Syntax
|
|
|
|
Identifier
|
|
|
|
|
|
|
|
makeKeyword :: (Str, Null or Syntax) -> Syntax
|
|
|
|
Keyword
|
|
|
|
|
|
|
|
makePunc :: (Str, Null or Syntax) -> Syntax
|
|
|
|
Punctuator
|
|
|
|
|
|
|
|
makeDelim :: (Str, [...Syntax], Null or Syntax) -> Syntax
|
|
|
|
Delimiter
|
|
|
|
|
|
|
|
The `makeDelim` function is a little odd but to make a delimiter syntax object we can't just pass it literally. So we say what type it is with the first param (`"{}"`, `"[]"`, or `"()"`), its children syntax objects with the second, and the context with the third. Eg: `makeDelimiter("{}", [makeValue(42, null)], null)`.
|
|
|
|
|
|
|
|
To unwrap the syntax object we have the wonderfully named `unwrapSyntax` (similar to Scheme's `syntax-e`):
|
|
|
|
|
|
|
|
unwrapSyntax :: (Syntax) -> Any
|
|
|
|
|
|
|
|
This gives either a flat value (like a number) or a delimiter token with unwrapped syntax objects inside. |