There are regular macros EMACRO
/SMACRO
/MACRO
(expression/statement/expression+statement macros) and vararg macros
(EMACRO'
/SMACRO'
/MACRO'
). Regular macros take a function that
takes an attrset with __state__
(and potentially other values) and
returns compiled code (potentially using compileExpr
and
compileStmt
). Vararg macros additionally take a variable amount of
arguments which get passed in __args__
.
There are also let macros LMACRO
. They take a function that takes
__state__
and __vars__
(and potentially other values). __state__
is explained above, __vars__
has a type [{ name : string, value : expr }]
. The function must return [{ code : string, expr : expr, local? : bool, predef? : bool }]
, where code
is the raw code used to
initialize the variable, expr
is whatever is returned to the user
(could be simply RAW var.name
, could be something else). local
is an
optional flag indicating the value should be local (defaults to true),
predef
means the value should be declared before setting every value
(defaults to false).
Each macro may have arbitrary data added to it by doing
macro // { ... }
. This way you can pass additional parameters to
macros, or specify the type info (see the below section).
For example, Vararg macros are implemented as regular macros with
__functor
set to a function that extends self.__args__
with the
provided argument.
Types flow in one direction. After a value has been defined, its type can't become more specific - that would require complex type inference algorithms which are out of scope for this project.
Each expression (including macros) might have the following attrs:
__kind__
- eitherrawStdlib
,custom
,customStmt
,customExpr
(rawStdlib
is used for importing typedefs,custom
is macros). If__kind__
isn't set, it's considered a normal table.- For JSON type definitions, a special value
rec
of__kind__
is allowed to specify the fact__pathStdlib__
recursively refers to another entry in the JSON. In Nix,rec
isn't allowed.
- For JSON type definitions, a special value
__pathStdlib__
- if__kind__
israwStdlib
, then this contains the code needed to access the expression.__validVar__
- the value is a validvar
(i.e. can be assigned to vialhs = rhs
statement). Additionally, expressions with__kind__ = "rawStdlib"
are always considered validvar
s since they currently can't be results of function calls, only property and index accesses.__prefixExp__
- the value is aprefixexp
as per Lua reference (validvar
s also count as validprefixexp
s)__wrapSafe__
- the expr doesn't need parens when used with binary operators (prefixexp
s also dont get wrapped with parens)__type__
- the Lua type of the result of the expression's evaluation (A string - boolean, number, table, nil, function, etc)__meta__
- metatable- for functions:
__minArity__
- specifies min arity (arg count)__maxArity__
- specifies max arity (if this is set,__minArity__
must be set as well)__retType__
- specifies the return type
- for tables:
__entry__
- specifies the default entry type__pathStdlib__
must be set to an empty string, and subproperties must have__pathStdlib__
set to just the property path in relation to the entry (for example, if a table contains other tables with a subtable namedx
which containsy
, the type definitions could look like this:{..., __pathStdlib__": "", "x": {..., "__pathStdlib__": "x", "y": {..., "__pathStdlib__": "x.y"}}}
)
__list__
- a special attribute indicating values from numeric table keys.- The other attrs are used for table property access. This is the
reason the other attributes are surrounded with
__
- the names could clash otherwise.
An expression's type is considered to be its attributes __retType__
,
__type__
, __minArity__
, __maxArity__
, __entry__
. For Nix
expressions (function, strings), type is autogenerated.
For checking that b
's type equals a
's type, each property of
b
's type must either match that of a
's, or either a
or b
's type
should be missing that property.
If __meta__.__call
is set or __type__
is function
, a __functor
attribute is generated to call CALL
. In case of __call
, function
arities get taken from __call
.
All in all, there are many internal functions involved in creating macros. Reading the compiler code should be enough to get an idea of how to create macros.