如何在Julia中解析多行字符串?

Lis*_*iso 5 julia

我该如何解析更多代码?

这是有效的:

julia> eval(parse("""print("O");print("K")"""))
OK
Run Code Online (Sandbox Code Playgroud)

这不起作用:

julia> eval(parse("""print("N");
print("O")"""))
ERROR: ParseError("extra token after end of expression")
Stacktrace:
 [1] #parse#235(::Bool, ::Function, ::String) at ./parse.jl:237
 [2] parse(::String) at ./parse.jl:232
Run Code Online (Sandbox Code Playgroud)

顺便说一句,如果我逐行尝试,我还有其他问题.例如:

julia> parse("""for i in 1:3""")
:($(Expr(:incomplete, "incomplete: premature end of input")))
Run Code Online (Sandbox Code Playgroud)

虽然:

julia> eval(parse("""for i in 1:2
println(i)
end"""))
1
2
Run Code Online (Sandbox Code Playgroud)

NHD*_*aly 5

有点笨拙,但这可以解决问题!

function parseall(str)
    return Meta.parse("begin $str end").args
end
Run Code Online (Sandbox Code Playgroud)

说明:

正如@Alexander莫利指出上面parse被设计为仅解析单个表达式。因此,如果仅使单个表达式能够包含原始字符串中的所有表达式,则它将对其进行解析!:)

最简单的方法是将字符串包装在一个块中,这使其成为有效的julia表达式。您可以看到这样的表达式可以很好地解析:

julia> Meta.parse("""
       begin
       function f3(x)
         x + 2
       end
       print("N")
       print(f3(5))
       end
       """)
>> quote
    #= none:2 =#
    function f3(x)
        #= none:3 =#
        x + 2
    end
    #= none:5 =#
    print("N")
    #= none:6 =#
    print(f3(5))
end
Run Code Online (Sandbox Code Playgroud)

实际上,这就是@Alexander Morley的代码正在构建的结构。(这就是让我想到这种方式的原因!谢谢亚历山大!)

但是,请注意,如果您尝试解析文件,则实际上您不想返回block像返回那样的单个表达式,因为您不能将其评估为单个块:Julia不允许您嵌套一些任何内容中的“顶级语句”(模块)。相反,您想返回一个顶级表达式数组。这就是为什么我们返回argsBlock表达式之外的原因。

通过上面的内容,您可以使用eval每个顶级表达式,并且它们可以正常工作!:)

julia> for expr in parseall("module M  f() = 1  end");  Core.eval(Main, expr);  end

julia> M.f()
1
Run Code Online (Sandbox Code Playgroud)

这部分与@misakawa(Github上的@thautwarm)一起计算。


Ale*_*ley 4

parse旨在解析单个表达式(至少文档是这么说的:考虑到这一点,我实际上对你的第一个示例的工作原理感到有点惊讶,而没有抛出错误......)。

如果您想解析多个表达式,那么您可以利用以下事实:

  1. parse可以接受第二个参数start来告诉它从哪里开始解析。
  2. 如果您提供此开始参数,那么它将返回一个包含表达式以及表达式结束位置的元组。

自己定义一个parseall函数。基地里曾经有一个,但我不确定现在是否还有。编辑:测试中仍然存在,请参见下文

# modified from the julia source ./test/parse.jl
function parseall(str)
    pos = start(str)
    exs = []
    while !done(str, pos)
        ex, pos = parse(str, pos) # returns next starting point as well as expr
        ex.head == :toplevel ? append!(exs, ex.args) : push!(exs, ex) #see comments for info
    end
    if length(exs) == 0
        throw(ParseError("end of input"))
    elseif length(exs) == 1
        return exs[1]
    else
        return Expr(:block, exs...) # convert the array of expressions
                                    # back to a single expression
    end
end
Run Code Online (Sandbox Code Playgroud)