Julia手册中指出:
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手册中指出:
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来完成这个操作,你可以这样做
function parse_file(path::AbstractString)
code = read(path, String)
Meta.parse("begin $code end")
end
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
对象,以获得AST更加“lispy”的表示形式,而不需要默认情况下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))))
)
Meta.parse(“begin \ nimport LinearAlgebra \ nx = 1 \n f(x)= sin(x)+ x \ nend”)|> Meta.show_sexpr
- Expr
显然是树形结构,只是以漂亮的方式打印出来。 - phipsgabler(jl-parse-all "begin\nimport LinearAlgebra\nx = 1\n f(x) = sin(x) + x\nend" "dummy")
,除了包装器。 - phipsgablerExpr
和字面量组成的树:
“前端AST几乎完全由Expr和原子(例如符号、数字)组成。”
https://docs.julialang.org/en/v1/devdocs/ast/#Surface-syntax-AST-1 - NHDalyjl-parse-file
。你可以从Lisp REPL (julia --lisp
)中调用它,它会返回整个文件的S表达式。由于Julia的Expr
与Lisp S表达式相差不大,这可能已经足够满足您的需求。ccall
的方法。但是可能可以实现jl_parse_eval_all
的变体。
:(1 + 1)
已经是最完整的了。你是想递归地展开+
到子AST吗?(如果是,那并不是很可行)。 - phipsgabler1 + 1
到Core.add_int(1, 1)
,因为AST存在于类型推断之前,只有在类型推断之后,才知道将调用哪个+
方法。 - phipsgablerjl-parse-file
,你可以在julia --lisp
中调用它,但我不知道如何从Julia内部执行它。也许可以在C中编写jl_parse_eval_all
的变体。 - phipsgabler