Documentation for gololang.macros.Utils

This module contains some functions to help writing macros.

Macros

stopCompilation(args...)

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.

thisFunction(self)

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.

thisModule(self, name)

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")

Functions

enterSymScope(scope)

Enters a scope in the internal SymbolGenerator used by gensym and mangle

exitSymScope()

Exists a scope in the internal SymbolGenerator used by gensym and mangle

extractLastArgument(args)

Utility to extract the last argument of a varargs function.

Useful when creating a macro used as a decorator that can take multiple arguments.

Example

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 NamedArguments, and element will contain the GoloFunction.

See also parseArguments

gensym()

Generates a new unique name using an internal SymbolGenerator.

See also SymbolGenerator

gensym(name)

Generates a new unique name using an internal SymbolGenerator with the given name as prefix.

See also SymbolGenerator

getLiteralValue(node)

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.

mangle(name)

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

namedArgsToMap(args)

Converts a NamedArgument collection into a map whose keys are the names of the named arguments, and values the associated values.

Example

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

parseArguments(args)

Same as parseArguments(args, false)

See parseArguments(args, extractLast)

parseArguments(args, extractLast)

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.

Example

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.

toplevel(type)

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.

wrapToplevel(elt)

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.