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:
Expr
Type
Pat
Param
Arg
TypeArg
PatArg
MatchCase
EnumVariant
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;