如何评估 nim 中的表达式?
How to evaluate an expression in nim?
我正在尝试在 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}"
不同?
我在这里错过了什么?
提前感谢您的澄清!
注意:虽然此答案概述了为什么会发生这种情况,但请记住,您尝试做的事情可能不会产生您想要的结果(我假设是表达式的运行时评估)。
您正在尝试将 NimNode
传递给需要 string
的 parseStmt
。 fmt
宏自动将 {}
中的任何内容字符串化,您可以通过执行 $value
将节点转换为字符串来省略 fmt
。
正如我已经指出的,这个 将不会 像在 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")
这可以防止将运行时已知的值传递给宏。您现在只能将 const
和文字传递给它,这是正确的行为。
不像 Python 是一种解释型语言,Nim 是编译型的。这意味着所有代码都在编译时被解析并转换为机器码,而你最终得到的程序对 Nim 根本一无所知(至少只要你不将 Nim 编译器导入为模块,这是可能的)。所以 Nim 中的 parseStmt
和所有 macro/template 扩展内容都是在编译期间完成的。该错误虽然可能有点难以阅读,但它试图告诉您传递给 $
的内容(这是 Nim 中的转换为字符串的运算符,并由 echo
在其所有arguments) 是编译时的东西,不能在 运行time 上使用。在这种情况下,这是因为 parseStmt
不是 return "3"
,它 return 类似于 NimNode(kind: nnkIntLit, intVal: 3)
,并且 NimNode
类型仅可用在编译期间。然而,Nim 允许您在编译时将 运行 代码转换为 return 其他代码,这就是宏的作用。您在那里编写的 eval
宏采用 value
,这是在 运行 时间内解析为字符串的任何语句,作为 NimNode
传递。这也是 result = parseStmt value
在您的情况下不起作用的原因,因为 value
还不是字符串,但可能类似于从标准输入读取字符串,这会在 [=43 期间产生字符串=]时间。然而,这里使用 strformat
有点混乱和矫枉过正。如果您将代码更改为:
import macros
macro eval(value: static[string]): untyped =
result = parseStmt value
echo eval("1+2")
它将正常工作。这是因为我们现在已经告诉 Nim value
需要是 static
即在编译时已知。在这种情况下,字符串文字 "1+2"
显然在编译时已知,但这也可能是对编译时过程的调用,甚至是 staticRead
的输出,它在编译期间读取文件。
如您所见,Nim 非常强大,但编译时和 运行-时之间的障碍有时会让人有些困惑。另请注意,您的 eval
过程实际上并没有在编译时评估任何东西,它只是 return 的 Nim 代码 1 + 2
所以您的代码最终是 echo 1 + 2
。如果您想在编译时实际 运行 代码,您可能需要查看编译时过程。
希望这有助于阐明您的问题。
我正在尝试在 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}"
不同?
我在这里错过了什么?
提前感谢您的澄清!
注意:虽然此答案概述了为什么会发生这种情况,但请记住,您尝试做的事情可能不会产生您想要的结果(我假设是表达式的运行时评估)。
您正在尝试将 NimNode
传递给需要 string
的 parseStmt
。 fmt
宏自动将 {}
中的任何内容字符串化,您可以通过执行 $value
将节点转换为字符串来省略 fmt
。
正如我已经指出的,这个 将不会 像在 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")
这可以防止将运行时已知的值传递给宏。您现在只能将 const
和文字传递给它,这是正确的行为。
不像 Python 是一种解释型语言,Nim 是编译型的。这意味着所有代码都在编译时被解析并转换为机器码,而你最终得到的程序对 Nim 根本一无所知(至少只要你不将 Nim 编译器导入为模块,这是可能的)。所以 Nim 中的 parseStmt
和所有 macro/template 扩展内容都是在编译期间完成的。该错误虽然可能有点难以阅读,但它试图告诉您传递给 $
的内容(这是 Nim 中的转换为字符串的运算符,并由 echo
在其所有arguments) 是编译时的东西,不能在 运行time 上使用。在这种情况下,这是因为 parseStmt
不是 return "3"
,它 return 类似于 NimNode(kind: nnkIntLit, intVal: 3)
,并且 NimNode
类型仅可用在编译期间。然而,Nim 允许您在编译时将 运行 代码转换为 return 其他代码,这就是宏的作用。您在那里编写的 eval
宏采用 value
,这是在 运行 时间内解析为字符串的任何语句,作为 NimNode
传递。这也是 result = parseStmt value
在您的情况下不起作用的原因,因为 value
还不是字符串,但可能类似于从标准输入读取字符串,这会在 [=43 期间产生字符串=]时间。然而,这里使用 strformat
有点混乱和矫枉过正。如果您将代码更改为:
import macros
macro eval(value: static[string]): untyped =
result = parseStmt value
echo eval("1+2")
它将正常工作。这是因为我们现在已经告诉 Nim value
需要是 static
即在编译时已知。在这种情况下,字符串文字 "1+2"
显然在编译时已知,但这也可能是对编译时过程的调用,甚至是 staticRead
的输出,它在编译期间读取文件。
如您所见,Nim 非常强大,但编译时和 运行-时之间的障碍有时会让人有些困惑。另请注意,您的 eval
过程实际上并没有在编译时评估任何东西,它只是 return 的 Nim 代码 1 + 2
所以您的代码最终是 echo 1 + 2
。如果您想在编译时实际 运行 代码,您可能需要查看编译时过程。
希望这有助于阐明您的问题。