1

我需要将语句列表拆分为多个部分,如下所示:

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 分成多个部分)

4

2 回答 2

4

默认情况下,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最终将获得一个控制引用代码脏度的标志。

于 2015-01-03T22:02:20.463 回答
2

如果您比较 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"

注意到区别了吗?一次“x”是一个标识,另一次是一个符号。这些符号不应该被查找,也许应该在编译器中进行更改。但现在你可以自己动手,再次用 Idents 替换所有 Syms 以强制进行新的查找:

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

于 2015-01-03T01:19:46.513 回答