Состав списка выписок
Мне нужно разделить список выписок на несколько частей, например так:
import macros
macro test: stmt =
var first = quote do:
var x = 1
var second = quote do:
echo x
result = newStmtList()
first.copyChildrenTo(result)
second.copyChildrenTo(result)
echo result.repr
test
Но компилятор говорит мне это:
[..]
var x = 1
echo x
minimalist.nim(14, 0) Info: instantiation from here
minimalist.nim(7, 13) Error: undeclared identifier: 'x'
echo x
Это очень запутанно, учитывая, что x
объявлен в списке узлов. Как мне сделать это правильно? (в случае, если это не очевидно, мне нужно разделить AST на несколько частей по другим причинам)
2 ответа
По умолчанию Nim будет применять правила гигиены макросов для кода внутри блоков в кавычках. Это означает, что имена символов, введенные в одном блоке, не будут видны в другом. Вы можете обойти это, введя переменную для общего символа следующим образом:
import macros
macro test: stmt =
var x = genSym()
var first = quote do:
var `x` = 1
var second = quote do:
echo `x`
result = newStmtList()
first.copyChildrenTo(result)
second.copyChildrenTo(result)
test
Под капотом цитата питается от getAst
операция применяется к незапятнанному шаблону. Если вы используете механизм более низкого уровня самостоятельно, также возможно отключить гигиеническое поведение цитируемых шаблонов, например так:
import macros
macro test: stmt =
template first {.dirty.} =
var x = 1
template second {.dirty.} =
echo x
result = newStmtList()
getAst(first()).copyChildrenTo(result)
getAst(second()).copyChildrenTo(result)
test
Это даст те же результаты. Имейте в виду, что quote
в конечном итоге получит флаг, который контролирует грязность цитируемого кода.
Если вы сравните AST, то увидите, что происходит не так:
dumpTree:
var x = 1
echo x
Это печатает:
StmtList
VarSection
IdentDefs
Ident !"x"
Empty
IntLit 1
Command
Ident !"echo"
Ident !"x"
Тогда в вашем примере:
macro test: stmt =
var first = quote do:
var x = 1
var second = quote do:
echo x
result = newStmtList()
first.copyChildrenTo(result)
second.copyChildrenTo(result)
echo result.treeRepr
test
Это печатает:
StmtList
VarSection
IdentDefs
Sym "x"
Empty
IntLit 1
Command
Sym "echo"
Ident !"x"
Заметили разницу? Однажды "х" является Ident, другой раз Sym. Символы не должны были быть найдены, возможно, это следует изменить в компиляторе. Но сейчас вы можете помочь себе и заменить все Syms на Ident снова, чтобы вызвать новый поиск:
import macros
proc symsToIdents(n): PNimrodNode {.compiletime.} =
if n.kind == nnkSym:
return newIdentNode($n)
result = n
for i in 0 .. <result.len:
result[i] = symsToIdents(n[i])
macro test: stmt =
var first = quote do:
var x = 1
var second = quote do:
echo x
result = newStmtList()
first.copyChildrenTo(result)
second.copyChildrenTo(result)
result = symsToIdents(result)
test
Изменить: сейчас сообщается как возможная ошибка: https://github.com/nim-lang/Nim/issues/1843