获取文件/复杂代码的整个 CAST

Getting the whole AST of the file / complex code

朱莉娅手册states:

Every Julia program starts life as a string:

julia> prog = "1 + 1"
"1 + 1"

quote / code_* 或使用 Meta.parse / Meta.show_sexpr 的帮助下,我可以轻松获得简单表达式的 AST,甚至是函数我在字符串中有表达式。

问题:有没有办法得到代码片段的整个 AST,可能包括几个原子表达式?比如,读取源文件并将其转换为 AST?

Julia 解析器的 FemtoLisp 实现中有 jl-parse-file。您可以从 Lisp REPL (julia --lisp) 调用它,它 returns 是整个文件的 S 表达式。由于 Julia 的 Expr 与 Lisp S 表达式没有太大区别,这可能足以满足您的需要。

我仍然想知道如何从 Julia 中访问此结果。如果我没理解错的话,Lisp函数是not exported from libjulia, so there's no direct way to just use a ccall. But maybe a variant of jl_parse_eval_all可以实现的

如果您想从 Julia 而不是 FemtoLisp 执行此操作,您可以执行

function parse_file(path::AbstractString)
    code = read(path, String)
    Meta.parse("begin $code end")
end

这会接收一个文件路径,读取它并将其解析为一个可以计算的大表达式。

这来自@NHDaly 的回答,在这里:

如果您的文件已经是一个字符串并且不想再次阅读它,您可以改为

parse_all(code::AbstractString) = Meta.parse("begin $code end")

Nathan Daly 和 Taine Zhao 在 Slack 上指出此代码不适用于模块:

julia> eval(parse_all("module M x = 1 end"))
ERROR: syntax: "module" expression not at top level
Stacktrace:
 [1] top-level scope at REPL[50]:1
 [2] eval at ./boot.jl:331 [inlined]
 [3] eval(::Expr) at ./client.jl:449
 [4] |>(::Expr, ::typeof(eval)) at ./operators.jl:823
 [5] top-level scope at REPL[50]:1

这可以按如下方式解决:

julia> eval_all(ex::Expr) = ex.head == :block ? for e in ex eval_all(e) end : eval(e);

julia> eval_all(ex::Expr) = ex.head == :block ? eval.(ex.args) : eval(e);

julia> eval_all(parse_all("module M x = 1 end"));

julia> M.x
1

由于提问者不相信上面的代码产生了一棵树,这里是parse_all输出的图形表示,清楚地显示了树结构。

如果您好奇的话,那些标记为 #= none:1 =# 的叶子是行号节点,指示每个后续表达式发生的行。

正如评论中所建议的那样,也可以将 Meta.show_sexpr 应用于 Expr 对象以获得更多 "lispy" AST 表示,而无需 julia 默认情况下所做的所有漂亮打印:

julia> (Meta.show_sexpr ∘ Meta.parse)("begin x = 1\n y = 2\n z = √(x^2 + y^2)\n end")
(:block,
  :(#= none:1 =#),
  (:(=), :x, 1),
  :(#= none:2 =#),
  (:(=), :y, 2),
  :(#= none:3 =#),
  (:(=), :z, (:call, :√, (:call, :+, (:call, :^, :x, 2), (:call, :^, :y, 2))))
)