The core metalanguage.



Evaluates a metaprogram.


#include <metalang99/lang.h>

#define F_IMPL(x, y) v(x + y)

ML99_EVAL(v(abc ~ 123), ML99_call(F, v(1, 2)))
ML99_call(op, ...)

Invokes a metafunction with arguments.

ML99_callUneval(ident, ...)

Invokes a metafunction ident with unevaluated arguments.

It is semantically the same as ML99_call(ident, v(...)) but performs one less reduction steps.

ML99_appl(f, ...)

Applies arguments to f.

This function implements partial application: instead of invoking a metafunction with all arguments at once, you specify each argument separately. This concept allows better re-use of metafunctions by specifying some arguments immediately, and the other arguments later, even in different execution contexts (for example, see this SO answer).

f must be either a term reducing to a macro name or a term obtained via another call to ML99_appl. If f is a macro name, then a macro named <f>_ARITY (its arity specifier) must denote how many times f will be applied to its arguments. (In Metalang99, an arity is an intentionally more flexible concept than just a number of parameters, see below.) Each time ML99_appl is invoked, it accumulates provided variadic arguments and decrements the arity of f; when the arity of f is already 1, it eventually calls the initial f with all the accumulated arguments and provided variadic arguments.

Most often, an arity specifier denotes a count of all named parameters plus 1 if a macro is variadic (all the functions in the standard library follow this pattern). However, feel free to specify arities as you wish, with regard to the aforementioned semantics; for example, you can have a macro accepting x, y, z with an arity specifier 2, then you must invoke ML99_appl exactly 2 times (either x + y, z or x, y + z). One common pattern is to match a head and a tail of variadic arguments:

#include <metalang99/lang.h>

#define F_IMPL(x, y, z, head, ...) // ...
#define F_ARITY 4

In this case, x, y, and z can be specified separately but other arguments all at once.


#include <metalang99/lang.h>

#define F_IMPL(x, y) v(x##y)
#define F_ARITY      2

// ab
ML99_appl(ML99_appl(v(F), v(a)), v(b))


Currently, the maximum arity is ML99_NAT_MAX. However, some compilers might not support more than 127 macro parameters.

ML99_appl2(f, a, b)

Applies a and b to f.


#include <metalang99/lang.h>

#define F_IMPL(x, y) v(x##y)
#define F_ARITY      2

// ab
ML99_appl2(v(F), v(a), v(b))
ML99_appl3(f, a, b, c)

Applies a, b, and c to f.

ML99_appl4(f, a, b, c, d)

Applies a, b, c, and d to f.

ML99_compose(f, g)

Functional composition of f and g.


#include <metalang99/lang.h>

#define F_IMPL(x) v((x + 1))
#define G_IMPL(x) v((x * 8))

#define F_ARITY 1
#define G_ARITY 1

// ((3 * 8) + 1)
ML99_appl(ML99_compose(v(F), v(G)), v(3))

A value that is pasted as-is; no evaluation occurs on provided arguments.

ML99_fatal(f, ...)

Emits a fatal error.

f must be a macro name that has caused the error and the rest of arguments comprise the error message.

ML99_fatal interprets its variadic arguments without preprocessor expansion &#8212; i.e., they are pasted as-is. This is intended because otherwise identifiers located in an error message may stand for other macros that will be unintentionally expanded.



#include <metalang99/lang.h>

ML99_EVAL(ML99_fatal(F, the description of your error))


playground.c:3:1: error: static assertion failed: "F: the description of your error"
    3 | ML99_EVAL(ML99_fatal(F, the description of your error))
      | ^~~~~~~~~


Immediately aborts the interpretation with evaluated arguments.


#include <metalang99/lang.h>

#define F_IMPL(x) v(~)

// 123
ML99_call(F, ML99_abort(v(123)))

A convenience macro to emphasise that your metafunction expands to more than one term.

This macro just expands to provided arguments.


#include <metalang99/lang.h>

#define F_IMPL(x) ML99_TERMS(v(1), v(x), v(2))

Delays evaluation for provided terms.

ML99_QUOTE(...) is functionally equivalent to v(...).


#include <metalang99/lang.h>

#define F_IMPL(x) v(~x)

#define PROG ML99_TERMS(v(1), v(2), ML99_call(F, v(7)))

// The same as `PROG` pasted into a source file.