Macros
This section describes the syntax for macros in Hash. Macros are a way to write code that writes other code. There are two kind of macro invocations: one macro works on AST items, and the other works on tokens.
AST macros
AST-level macros are written with the syntax #macro_name <subject>
or #[macro_name(macro_arg)] <subject>. The first form is a used as a shorthand
for macros that don’t have any additional arguments to the macro itself.
For example, the #dump_ast macro will accept any AST item as the subject and print the parsed AST to the console.
#![allow(unused)]
fn main() {
#dump_ast main := () => {
println("Hello, world!");
}
}
An example of an AST macro being used to set some attributes on a function:
#![allow(unused)]
fn main() {
#[attr(foreign(c), no_mangle, link_name = "jpeg_read_header")]
jpeg_read_header := (&raw cinfo, bool require_image) -> i32;
}
Token macros
Token macros follow a similar syntax to AST macros, but instead of working on AST items, they work on tokens. The syntax for token macros is @macro_name <subject> or @[macro_name(macro_arg)] <subject>. The first form is a used as a shorthand for token macros that have no arguments. However, one significant difference between token macros and AST macros is that the token macro only accepts a token tree
as the subject. A token tree is a sequence of tokens that are
enclosed in a pair of delimiters. Token trees are either [...], {...} or (...). It is then up to the macro to define various
rules for accepting the token tree:
An example of using min and max macros:
#![allow(unused)]
fn main() {
main := () => {
min := @min {1 + 2, 3 * 4, 7 - 6 + 1 };
max := @max {1 + 2, 3 * 4, 7 - 6 + 1 };
if max - min == 0 {
println("min and max are equal")
} else {
println("min and max are not equal")
}
}
}
Another example of macro with a token tree for HTML:
welcome := () => {
@[xml(variant=html)] {
<html>
<head>
<title>My page</title>
</head>
<body>
<h1>Hello, world!</h1>
</body>
</html>
}
}
Defining a macro 🚧
This section hasn’t been defined yet, and is still a work in progress.
Macro Rules 🚧
Macro invocation locations
Both styles of macro invocations can appear in the following positions:
ExprTypePatParamArgTypeArgPatArgMatchCaseEnumVariant
Here is an example in code of all of the possible positions where a macro invocation can appear:
#![allow(unused)]
#![module_attributes]
fn main() {
#dump_ast
Foo := struct<#dump_ast T>(
#dump_ast x: T,
#dump_ast y: T,
#dump_ast z: T,
);
Bar := enum<T>(
#dump_ast A(T),
#dump_ast B(T),
#dump_ast C(T),
);
bing := (#param x: i32) -> #ty i32 => {
foo := Foo(#arg x = 5, #arg y = 6, #arg z = 7);
match x {
#dump_ast 0 => 0,
#dump_ast 1 => 1,
#dump_ast (#dump_ast _) => bing(x - 1) + bing(x - 2),
}
}
}
Grammar
Formally, the macro syntax invocation can be written as follows:
token_macro_invocation ::= "@" ( macro_name | macro_args ) token_tree;
token_tree ::= "{" any "}"
| "[" any "]"
| "(" any ")";
ast_macro_invocation ::= '#' (macro_name | macro_args ) macro_subject;
module_macro_invocation ::= "#!" macro_args;
macro_subject ::= expr
| type
| pat
| param
| arg
| type_arg
| pat_arg
| match_case
| enum_variant;
macro_args ::= "[" ( ∅ | macro_invocation ("," macro_invocation)* ","? ) "]";
macro_invocation ::= macro_name ( "(" ∅ | expr ("," expr )* ","? ")" )?;
macro_name ::= access_name;