【问题标题】:How to evaluate an expression in nim?如何评估 nim 中的表达式?
【发布时间】:2021-03-28 22:24:57
【问题描述】:

我正在尝试在 nim 中执行与 Python 中的 eval 方法等效的操作。 我的印象是 macros 包中的 parseStmt 应该可以帮助我解决这个问题,但我遇到了一个我不明白的编译问题。

import macros

echo parseStmt("1 + 2")

我本以为它会在执行时打印3,但编译却抱怨

Error: request to generate code for .compileTime proc: $

我找到了this thread,并且那里的示例可以正常工作,然后,我能够制作出以下程序,可以按我的预期工作:

import macros
import strformat

macro eval(value: string): untyped =
  result = parseStmt fmt"{value}"

echo eval("1+2")

但我完全不明白为什么它需要以这种方式编写。如果我内联语句 let value = "1 + 2"; echo parseStmt fmt"{value}",我会得到与上面相同的编译错误。

另外,在上面的eval 宏的上下文中,为什么parseStmt value 与parseStmt fmt"{value}" 不同?

我在这里错过了什么?

提前感谢您的澄清!

【问题讨论】:

    标签: macros metaprogramming nim-lang


    【解决方案1】:

    与作为解释型语言的 Python 不同,Nim 是经过编译的。这意味着所有代码都在编译时被解析并转换为机器代码,而您最终得到的程序对 Nim 完全不了解(至少只要您不将 Nim 编译器作为模块,这是可能的)。所以parseStmt 和 Nim 中的所有宏/模板扩展内容都是在编译期间完全完成的。该错误虽然可能有点难以阅读,但它试图告诉您传递给 $ 的内容(这是 Nim 中的转换为字符串的运算符,由 echo 在其所有参数上调用)是编译时不能在运行时使用的东西。在这种情况下,这是因为parseStmt 不返回"3",它返回类似NimNode(kind: nnkIntLit, intVal: 3) 的东西,而NimNode 类型仅在编译时可用。然而,Nim 允许您在编译时运行代码以返回其他代码,这就是宏的作用。您在那里编写的eval 宏采用value,这是在运行时解析为字符串的任何语句,作为NimNode 传递。这也是result = parseStmt value 在您的情况下不起作用的原因,因为value 还不是字符串,但可能类似于从标准输入中读取字符串,这会在运行时生成字符串。然而,在这里使用strformat 有点令人困惑和矫枉过正。如果您将代码更改为:

    import macros
    
    macro eval(value: static[string]): untyped =
      result = parseStmt value
    
    echo eval("1+2")
    

    它会工作得很好。这是因为我们现在告诉 Nim value 必须是 static 即在编译时已知。在这种情况下,字符串文字 "1+2" 在编译时显然是已知的,但这也可能是对编译时过程的调用,甚至是在编译期间读取文件的 staticRead 的输出。

    正如您所见,Nim 非常强大,但编译时和运行时之间的界限有时会让人有点困惑。另请注意,您的eval 过程实际上不会在编译时评估任何内容,它只是返回 Nim 代码1 + 2,因此您的代码最终为echo 1 + 2。如果您想在编译时实际运行代码,您可能需要查看编译时过程。

    希望这有助于阐明您的问题。

    【讨论】:

      【解决方案2】:

      注意:虽然此答案概述了为什么会发生这种情况,但请记住,您尝试执行的操作可能不会产生您想要的结果(我认为这是表达式的运行时评估)。

      您正在尝试将NimNode 传递给parseStmt,它需要string。 fmt 宏自动将 {} 中的任何内容字符串化,您可以省略 fmt,通过执行 $value 将节点转换为字符串。

      正如我已经指出的,这个 不会 像在 Python 中那样工作:Nim 没有运行时评估。字符串中的表达式将在编译时进行评估,因此像这样的简单示例将 不 做你想做的事:

      import std/rdstdin
      let x = readLineFromStdin(">")
      echo eval(x)
      

      为什么?

      首先,因为您正在对传递给 eval 的 AST 进行字符串化,所以传递给宏的不是 x 变量后面的 string - 它是符号表示x 变量。如果对符号进行字符串化,您将获得底层标识符,这意味着 parseStmt 将接收 "x" 作为其参数。这将影响存储在x 中的字符串被打印出来,这是错误的。

      您想要的是以下内容:

      import std/rdstdin
      import std/macros
      
      macro eval(value: static string): untyped =
        result = parseStmt(value)
      
      echo eval("1 + 2")
      

      这可以防止将运行时已知的值传递给宏。您现在只能将 consts 和文字传递给它,这是正确的行为。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-11-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多