This module contains some functions to help writing macros.
Throws a StopCompilationException
to stop the compilation process.
Note that this exception just stop the process and does not print anything on stderr. You should deal with error messages yourself.
See gololang.Messages
for instance to display console messages.
Expands to the fully qualified name of the function in which this macro is called.
Can be used for instance to define variable scopes in macro.
Generate a name in the current name space.
Useful if a macro generates calls to functions defined in the same module. Instead of relying on the module to be imported, one wants to use a fully qualified function name. This macro generate such a name without hard coding the current module name in the written macro.
For instance, instead of writing:
module MyModule
function myFunction = -> null
macro = -> gololang.ir.DSL.call("MyModule.myFunction")
we can write:
module MyModule
function myFunction = -> null
macro = -> gololang.ir.DSL.call(&gololang.macros.Utils.thisModule("myFunction")
Utility to extract the last argument of a varargs function.
Useful when creating a macro used as a decorator that can take multiple arguments.
Such a macro can be used as
@myMacro(answer=42, ping="pong")
function myFunction = ...
It will be defined as
macro foo = |args...| {
let arguments, element = extractLastArgument(args)
####...
return element
}
In this example, arguments
will be an array of NamedArgument
s, and element
will contain the GoloFunction
.
args
: an array containing the argumentsSee also parseArguments
Generates a new unique name using an internal SymbolGenerator
.
See also SymbolGenerator
Generates a new unique name using an internal SymbolGenerator
with the given name as prefix.
See also SymbolGenerator
Converts a IR node into the corresponding runtime value.
This only works for literal values, classes, enums or arrays of theses values. Otherwise the node itself is returned.
Mangles the given name to ensure hygiene.
If the argument is a LocalReference
or a ReferenceLookup
, a new object with
the same type, but with a mangled name, is returned.
See also SymbolGenerator
Converts a NamedArgument
collection into a map whose keys are the names of the
named arguments, and values the associated values.
Given a macro defined as:
macro foo = |args...| {
let arguments = namedArgsToMap(args)
#### ...
}
When called as &foo(a=42, b="hello")
, the arguments
variable will contains
map[["a", constant(42)], ["b", constant("hello")]]
The elements of the collection that are not NamedArgument
are ignored.
See also parseArguments
Same as parseArguments(args, false)
Converts a collection of expressions and named arguments into a triple
containing a list of the expressions and a map extracted from named arguments
like by namedArgsToMap
.
If extractLast
is true
or null
, also extract the last argument as by
extractLastArgument
. If extractLast
is a type, the last argument is extracted only if it
has the corresponding type.
macro myMacro = |args...| {
let positional, named, last = parseArguments(args, true)
#### ...
}
@myMacro(answer=42, "foo", foo="bar")
function plop = -> "daplop"
In the myMacro
macro, positional
will be array[constant("foo")]
, named
will be map[["answer", constant(42)], ["foo", constant("bar")]]
and last
will
be the plop
function IR node.
Decorator to help define macros on top-level elements.
Macros applied on top-level elements may often return a ToplevelElements
to
inject several top-level elements into the module, without using side effects.
When stacking such macros, for instance with the decorator notation, each macro
must be prepared to receive a ToplevelElements
containing various kinds of
elements, instead of the decorated one.
For instance, a macro can work on a struct
and inject several tooling
functions and augmentations, such as:
macro myMacro = |structure| -> toplevels(
structure,
`function("workOn" + structure: name())
: withParameters("s"): body(...),
`augment(structure): with(...)
)
Suppose that a macro otherMacro
has a similar behavior. It is thus not possible to
stack these two macro, as in:
@otherMacro
@myMacro
struct Foo = {x}
since here, the otherMacro
will not receive a Struct
, but the
ToplevelElements
returned by myMacro
.
This decorator adapt the macro to deal with ToplevelElements
, applying it to
each contained element if its type match the given one, and returning the
element unchanged otherwise.
In the previous example, we can use it on the two macros as:
@!toplevel(Struct.class)
macro myMacro = |structure| -> toplevels(
structure,
`function("workOn" + structure: name())
: withParameters(s): body(...),
`augment(structure: packageAndClass()): with(...)
)
The myMacro
will be applied to its argument if its a struct
, don't change
any other type, and if its argument is a ToplevelElements
, it will be applied
to any contained element. The macro can therefore be stacked on the top of other
macros whatever their returned type.
To apply the macro on any type, just use GoloElement.class
as a filter.
Moreover, the decorator makes the macro varargs. It can therefore be called on several structures at once, like:
&myMacro {
struct Foo = {x}
struct Bar = {a, b}
}
This decorator should be banged.
type
: the type of the nodes on which the macro must be applied.Wrap the given object in a ToplevelElements
.
If the argument is an array or a collection of more than 1 element, it is wrapped in a ToplevelElements
.
Otherwise, the element is returned unchanged.