【问题标题】:How to unparse a Julia expression如何解开 Julia 表达式
【发布时间】:2018-01-09 02:42:37
【问题描述】:

我一直试图从元编程的角度来理解 Julia,并且经常发现自己处于希望从 Expr 生成面向用户的 Julia 语法的位置。

在 GitHub 上搜索源代码,我发现了 femtolisp 中定义的“deparse”函数。不过好像完全没有暴露。

我可以通过哪些方法仅使用内部表示生成正确的 Julia 表达式?

P。 S. 生成的 Julia 代码应该有某种美化工具,你知道一些这样的(未注册的)pkg 吗?

~#~#~#~#~

更新

我已将 julia 源文件的所有 Meta.show_sexpr 存储到另一个文件中。

# This function is identical to create_adder implementation above.
function create_adder(x)
    y -> x + y
end

# You can also name the internal function, if you want
function create_adder(x)
    function adder(y)
        x + y
    end
    adder
end

add_10 = create_adder(10)
add_10(3) # => 13

转换为

  (:line, 473, :none),
    (:function, (:call, :create_adder, :x), (:block,
        (:line, 474, :none),
        (:function, (:call, :adder, :y), (:block,
            (:line, 475, :none),
            (:call, :+, :x, :y)
          )),
        (:line, 477, :none),
        :adder
      )),
    (:line, 480, :none),
    (:(=), :add_10, (:call, :create_adder, 10)),
    (:line, 481, :none),
    (:call, :add_10, 3))

现在,希望在 julia 中评估这些。

【问题讨论】:

  • CSTParser.jl,特别是通过 Visual Studio 代码扩展 github.com/JuliaEditorSupport/julia-vscode 提供了一个 Julia Prettyfier。
  • 我很困惑你在问什么。如果你有一个现有的 Expr 类型变量,你可以使用dumpMeta.show_sexpr 来检查它的抽象语法树;您可以在终端上show 表示。或者你是说你有“组件”并且你想从它们构建一个 Expr 实例?
  • @TasosPapastylianou,正是第二种解释。我会更新上面的问题。
  • 我不知道是否已经存在将 s_expressions 转换为 Expr 对象的函数,但是如果您有可以将 parse |> eval 转换为元组序列的那种字符串输出,那么它不应该编写一个使用每个元组作为Expr 构造函数的参数的递归函数太难了。 s_expressions 和 Expr 实例构造之间有明显的等价性:例如,:(1 + 2 * 3) 的 s_expr 是 (:call, :+, 1, (:call, :*, 2, 3))。将每个左括号视为对 Expr 构造函数的调用,我们有:Expr(:call, :+, 1, Expr(:call, :*, 2, 3)) === :(1 + 2 * 3)

标签: parsing metaprogramming julia


【解决方案1】:

下面是一个函数示例,它采用元组形式的“s_expression”,并生成相应的 Expr 对象:

"""rxpe_esrap: parse expr in reverse :p """
function rpxe_esrap(S_expr::Tuple)
  return Expr( Tuple( isa(i, Tuple) ? rpxe_esrap(i) : i for i in S_expr )... );
end

演示

让我们生成一个漂亮的 s_expression 元组来测试我们的函数。
(不幸的是Meta.show_sexpr 不会生成字符串,它只是打印到 IOStream,因此要将其输出作为我们可以解析/评估的字符串,我们需要从文件中获取它,或者直接打印变成IOBuffer)

B     = IOBuffer();              # will use to 'capture' the s_expr in
Expr1 = :(1 + 2 * 3);            # the expr we want to generate an s_expr for
Meta.show_sexpr(B, Expr1);       # push s_expr into buffer B
seek(B, 0);                      # 'rewind' buffer
SExprStr = read(B, String);      # get buffer contents as string
close(B);                        # please to be closink after finished, da?
SExpr = parse(SExprStr) |> eval; # final s_expr in tuple form

导致以下 s_expression:

julia> SExpr
(:call, :+, 1, (:call, :*, 2, 3))

现在让我们测试一下我们的功能:

julia> rpxe_esrap(SExpr)
:(1 + 2 * 3)                     # Success!

注意事项:
1.这只是一个简单的功能来演示这个概念,显然如果要用于严肃的项目,这将需要适当的健全性检查。
2.这个实现只需要一个“s_expr tuple”参数;您的示例显示了一个对应于元组 sequence 的字符串,但大概您可以首先标记这样的字符串以获得各个元组参数,然后分别在每个元组上运行该函数。
3.有关 parse / eval 和 scope 的常见警告适用。此外,如果您想将 s_expr 字符串本身作为函数参数传递,而不是“s_expr 元组”,那么您可以修改此函数以在函数内移动 parse / eval 步骤。这可能是一个更好的选择,因为您可以在评估潜在危险代码等之前检查字符串包含的内容。
4. 我并不是说没有官方功能可以做到这一点。虽然如果有的话,我不知道。不过这写起来很有趣。

【讨论】:

  • 感谢@Tasos,这很有启发性!不过,我确实意识到我需要更加小心你提到的“健全性检查”。不错!
  • 嘿,它不适用于 julia-0.5.2 。上网试试,codebunk.com/b/939152719
  • 啊,抱歉,我没想到向后兼容; readstring(::IOStream) 已被弃用,取而代之的是 read(IOSream, String)。从生成器生成元组还没有实现,所以我不得不改用 splatting 一个列表。 codebunk.com/b/327152757
  • ehh,@Tasos 我已经下载了最新的稳定版,它会产生类似的错误。 gist.github.com/abhi18av/b076e3822b3b6883a06f2f8ac733db36
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-07
相关资源
最近更新 更多