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(rawStdlibis used for importing typedefs,customis macros). If__kind__isn't set, it's considered a normal table.- For JSON type definitions, a special value
recof__kind__is allowed to specify the fact__pathStdlib__recursively refers to another entry in the JSON. In Nix,recisn'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 = rhsstatement). Additionally, expressions with__kind__ = "rawStdlib"are always considered validvars since they currently can't be results of function calls, only property and index accesses.__prefixExp__- the value is aprefixexpas per Lua reference (validvars also count as validprefixexps)__wrapSafe__- the expr doesn't need parens when used with binary operators (prefixexps 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 namedxwhich 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.