Можно строить выражения с помощью ручной генерации абстрактного синтаксического дерева. Например, код для генерации выражения 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 <| let a = 40 + 2 print(describe(exprLet)) //output: let /*unused*/ a:auto const = (40 + 2);
В случае, если какую-либо часть выражения нужно сделать изменяемой, можно воспользовать макросом apply_template:
var exprLet <- quote <|
let a = 40 + 2
print(describe(exprLet))
//output:
let/*unused*/ a:autoconst = (40 + 2);
Недавно в язык была добавлена фича по упрощению генерации правил переписываний выражений — expression reification (аналогичная фича из haxe). Её можно описать как DSL для задания правил переписывания выражений в шаблонах. Теперь генерацию того же самого выражения можно описать так:
require daslib/templates
require daslib/templates_boost
var exprLet <- quote <|
let VARIABLE_NAME = OP1 + OP2 //шаблон выражения
var exprLet_rules : Template //правила переписывания выражения
exprLet_rules |> renameVariable("VARIABLE_NAME", "a") //замена одного имени на другое
exprLet_rules |> replaceVariable("OP1", new [[ExprConstInt() value=40]]) //замена одного выражения на другое
exprLet_rules |> replaceVariable("OP2", new [[ExprConstInt() value=2]])
В таком виде строчка шаблона всё ещё остаётся похожим на сам код, который будет сгенерирован этим шаблоном, а не на синтаксическое дерево или таблицу с описанием правил. Пример на все поддерживаемые правила реификации выражений - reification.das.
Переписанная кодо-генерированная функция инициализации структуры с использованием реификации получается где-то вдвое короче и проще:
let variableName = "a"
let op1 = 40
let op2 = 2
var exprLet <-qmacro <|
let $i(variableName) = $v(op1) + $v(op2)
Макрос qmacro_expr позволяет вставить сгенерированное выражение в текущий блок, а не генерировать новый блок. reduce - функция из стандартной библиотеки functional, позволяющая произвольным образом свернуть массив выражений с помощью функтора. reduce_while — её дописанная версия, позволяющая задать предикат остановки свёртки выражения по условию. qmacro_function — макрос для генерации сигнатуры функции и её определения
Получившаяся функция инициализации аналогична той, которая генерировалась в предыдущей заметке: