Function Macros
The final feature that we will be taking a look at is one is arguably one of the most unique: macros. Be aware that a macro in Ryna is not the same as a macro in other languages such as C, C++ or even Rust, even though they are close to Rust's. The main difference between Rust's macros and Ryna's is that there is no restriction on the syntax that you can match. Let's take a look at how they work by diving into the first kind of macros Ryna has: function macros.
Syntax
A function macro can be created in Ryna using this syntax:
syntax macro_name from NDL_Pattern {
[...]
}
// This is also allowed
syntax fn macro_name from NDL_Pattern {
[...]
}
When you define a macro of this kind you can "invoke" it by using the syntax anywhere in your code where an expression is legal. Just be aware that macros are internally compiled to a different representation. In the case of function macros, they are converted to do blocks:
syntax macro_name from "Macro" Arg(n, 1{d}) {
return $n;
}
// This macro ...
print(Macro5);
// ... is compiled to this
print(do { return 5; });
You may have noticed that the syntax inside a macro body is not the same as a function. This is because the body of a macro is composed of
patterns that use Arg
s in order to generate code by concatenation. There are a few different types of patterns with varying complexities,
but they all generate a piece of text that is concatenated to the one generated by the previous pattern.
Macro patterns
These are the patterns that you can use inside a macro's body:
Pattern | Syntax | Description |
---|---|---|
Variable | $Var |
Pastes the value of the Arg named Var into the macro result |
Index | $Var.i |
Pastes the i th value of the Arg named Var into the macro result. i must also be an Arg |
Loop | @Var.i {Pat} |
Pastes the result of the Pat subpattern creating a variable i with its corresponing index for each element in Var |
Code | {| Pat |} |
Gets the result of the Pat subpattern and executes the resulting code. Pastes anything that is passed to emit (more information in its own section) |
Text | Anything else | Pastes the given text into the macro result |
In order to be able to write these patterns and disambiguate where the macro ends you may have to escape braces or dots. This is common when writing code patterns, since loops, lambdas and do blocks use braces.
Example
Let's see an example where we try to create a macro to initialize an Array
statically:
syntax array_initialization from "<" Arg(<type>, type) ">[" [{Arg(<expr>, elems) "," [s]} Arg(<expr>, elems)] "]" {
let res = arr<$type>();
@elems.i {
res.push($elems.i);
}
return move(res);
}
This macro allows you to write code such as this:
let array = <Int>[1, 2, 3, 4, 5];
Which will then be compiled to this:
let array = do {
let res = arr<Int>();
res.push(1);
res.push(2);
res.push(3);
res.push(4);
res.push(5);
return *res;
};
As you can imagine, this allows you to create very complex behaviours transparently. You can see more complex examples in the Learn by example section.