-
Notifications
You must be signed in to change notification settings - Fork 0
macros
Macros in F♮ are a little unusual. Instead of being templates for substitution, they invoke syntax rules that guide the parser.
The best way to describe them is by example. In listutils.fn is a definition of a macro for list comprehensions called lco.
With lco you can write:
lco [ x + 1 for x in xs where x > 2, x < 20 ]
And that will expand to
list.map(fn (x) { x + 1 },
list.filter(fn (x) { x > 2 },
list,filter(fn (x) { x < 20 }, xs)));
lco is defined as follows:
export macro lco: Expr internalLco;
syntax internalLco ::= "["
exp: Expr
"for" x: Name
"in" xs: Expr
filters: Syntax(whereClauses(x, xs))
"]" quote {
map(fn (unquote x) { unquote exp }, unquote filters)
};
syntax whereClauses(x, xs) ::= empty { xs }
| "where" cond: Expr rest: Syntax(moreFilters(x, xs)) quote {
filter(fn (unquote x) { unquote cond }, unquote rest)
};
syntax moreFilters(x, xs) ::= { xs }
| "," cond: Expr rest: Syntax(moreFilters(x, xs)) quote {
filter(fn (unquote x) { unquote cond }, unquote rest)
};
From the top:
export macro lco: Expr internalLco;
The macro declaration merely registers lco as a trigger token, states that lco returns an expression (: Expr)
and hands control to a
syntax rule that does the heavy lifting. If a macro is intended to expand to a definition, use Def instead of Expr.
Syntax rules are introduced by the syntax keyword and describe a limited BNF-style grammar using quoted strings to
match literal tokens. So you can read:
syntax internalLco ::= "[" // Parse a literal "[".
exp: Expr // Parse an expression and save it in `exp`.
"for" // Parse a literal "for".
x: Name // Parse a name and store it in `x`.
"in" // Literal "in".
xs: Expr // Expression stored in `xs`.
filters: Syntax(whereClauses(x, xs)) // Hand off to another syntax rule, passing it
// `x` and `xs`, store the result in `filters`.
"]" // literal "]" to close
quote {
map(fn (unquote x) { unquote exp }, // Result of this syntax rule is the outer map
unquote filters) // wrapping the inner filters.
};
Continuing
syntax whereClauses(x, xs) ::= empty { xs } // If there is no `where` clause just return the list.
| // BNF-style "otherwise".
"where" // Match `where`.
cond: Expr // Parse an expression and store it in `cond`.
rest: Syntax(moreFilters(x, xs)) // Hand off to another syntax rule, passing `x` and `xs`.
quote {
filter(fn (unquote x) { unquote cond }, // Result is the rest of the filters, wrapped
unquote rest) // in this `cond` filter.
};
And to tie it all up, this is just a repeat of whereClauses but with "," instead of "where", and recursing on itself.
syntax moreFilters(x, xs) ::= { xs } // `empty` token is optional.
|
","
cond: Expr
rest: Syntax(moreFilters(x, xs))
quote {
filter(fn (unquote x) { unquote cond }, unquote rest)
};
Next: Command Line Arguments

CEKF(s) a.k.a. F Natural a.k.a F♮