【问题标题】:How to parse multiline string in Julia?如何在 Julia 中解析多行字符串?
【发布时间】:2017-09-22 12:32:25
【问题描述】:

如何解析更多行代码?

这是有效的:

julia> eval(parse("""print("O");print("K")"""))
OK

这不起作用:

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

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

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

虽然:

julia> eval(parse("""for i in 1:2
println(i)
end"""))
1
2

【问题讨论】:

    标签: julia


    【解决方案1】:

    这有点笨拙,但这会成功!:

    function parseall(str)
        return Meta.parse("begin $str end").args
    end
    

    解释:

    正如@Alexander Morley 指出的aboveparse 旨在仅解析单个表达式。因此,如果您只是使 单个表达式 能够包含原始字符串中的所有表达式,那么它会很好地解析它! :)

    最简单的方法是将字符串包装在一个块中,使其成为有效的 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
    

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

    但是,请注意,如果您尝试解析 文件,您实际上并不想返回一个类似返回的 block 表达式,因为您不能 eval 它作为一个单独的块:Julia 不允许您在任何东西中嵌套一些“顶级语句”(模块)。相反,您想返回一个顶级表达式的 array。这就是我们从 Block 表达式中返回 args 的原因。

    通过上面的方法,你可以eval每个顶级表达式,它会正常工作! :)

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

    这部分是和@misakawa(Github 上的@thautwarm)一起想出来的。

    【讨论】:

      【解决方案2】:

      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
      

      【讨论】:

      • 谢谢!这是鼓舞人心的! :) 一行中的多个表达式似乎有问题。我编辑您的回复以在代码中显示它。但没想到还需要同行评审……你可以查看eval(parseall("print(1); print(2) \n\nfor i in 3:4\n print(i)\nend\n"))eval(parseall("print(1)\nprint(2) \n\nfor i in 3:4\n print(i)\nend\n"))
      • @Liso 您的示例确实错误,这与解析器在找到分号时生成:toplevel 表达式有关。用ex.head == :toplevel ? append!(exs, ex.args) : push!(exs, ex) 替换push!(exs,ex) 为我解决了这个问题。
      • Alexander_Morley 和@Dan_Getz 我喜欢接受固定版本。我们怎样才能做到这一点?
      • 我已经编辑了我的以包含@DanGetz 的想法。没有电脑可以检查,但应该可以!
      • 看起来parseall 已移至Meta.parseall
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-02
      • 2012-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-04
      • 1970-01-01
      相关资源
      最近更新 更多