Еще немного про способы кодогенерации в макросах.
В предыдущей заметке про макросы описывались способы сгенерировать код выражений на языке.
Можно строить выражения с помощью ручной генерации абстрактного синтаксического дерева. Например, код для генерации выражения let a = 40 + 2
:
var expr40 <- new [[ExprConstInt() value=40]] //40
var expr2 <- new [[ExprConstInt() value=2]] //2
var exprPlus <- new [[ExprOp2() op:="+", left := expr40, right := expr2]] //40+2
var exprLet <- new [[ExprLet()]] //let
exprLet_aSize.variables |> emplace_new() <| new [[Variable()
name := "a", //a
_type <- new [[TypeDecl() baseType=Type tInt]],
init <- exprPlus //=40+2
]]
Генерировать большие функции с использованием ExprXXX-кирпичиков утомительно, поэтому можно использовать макрос quote
, который трансформирует переданное в него выражение в синтаксическое дерево этого выражения:
var exprLet <- quote <| |
В случае, если какую-либо часть выражения нужно сделать изменяемой, можно воспользовать макросом apply_template
:
require daslib/templates |
Недавно в язык была добавлена фича по упрощению генерации правил переписываний выражений — expression reification (аналогичная фича из haxe).
Её можно описать как DSL для задания правил переписывания выражений в шаблонах. Теперь генерацию того же самого выражения можно описать так:let variableName = "a"
let op1 = 40
let op2 = 2
var exprLet <-qmacro <|
let $i(variableName) = $v(op1) + $v(op2)
В таком виде строчка шаблона всё ещё остаётся похожим на сам код, который будет сгенерирован этим шаблоном, а не на синтаксическое дерево или таблицу с описанием правил. Пример на все поддерживаемые правила реификации выражений - reification.das.
Переписанная кодо-генерированная функция инициализации структуры с использованием реификации получается где-то вдвое короче и проще:
def generateStructureInitFunction(var st:StructurePtr; ptrsTypeIndexes:array<int>&) |
Макрос qmacro_expr
позволяет вставить сгенерированное выражение в текущий блок, а не генерировать новый блок.reduce
- функция из стандартной библиотеки functional, позволяющая произвольным образом свернуть массив выражений с помощью функтора.reduce_while
— её дописанная версия, позволяющая задать предикат остановки свёртки выражения по условию.qmacro_function
— макрос для генерации сигнатуры функции и её определения
Получившаяся функция инициализации аналогична той, которая генерировалась в предыдущей заметке:struct Vec2
x, y : float
[memblock, dump_fields]
struct Memblock
a: int?
b: float?
c: int?
d: Vec2?
//output:
---gen_text--------------
// [modifyArgument]
[privateFunction]def init`struct`Memblock ( var memblock : Memblock; var a`count : int; var b`count : int; var c`count : int; var d`count : int )
memblock.a`count = a`count
memblock.b`count = b`count
memblock.c`count = c`count
memblock.d`count = d`count
var aSize : int const = (a`count * 4)
var bSize : int const = (b`count * 4)
var cSize : int const = (c`count * 4)
var dSize : int const = (d`count * 8)
__::builtin`resize(memblock.mem,((((aSize + 0) + bSize) + cSize) + dSize))
memblock.a = reinterpret<int?> addr(memblock.mem[0])
memblock.b = reinterpret<float?> addr(memblock.mem[(aSize + 0)])
memblock.c = reinterpret<int?> addr(memblock.mem[((aSize + 0) + bSize)])
memblock.d = reinterpret<Vec2?> addr(memblock.mem[(((aSize + 0) + bSize) + cSize)])