【问题标题】:returning meaningful error messages from a parser written with Scala Parser Combinators从使用 Scala Parser Combinators 编写的解析器返回有意义的错误消息
【发布时间】:2010-12-11 21:11:01
【问题描述】:

我尝试使用 Parser Combinators 在 scala 中编写解析器。如果我递归匹配,

def body: Parser[Body] =
("begin" ~> statementList  )  ^^ {
     case s => {   new Body(s); }
}

def statementList : Parser[List[Statement]] = 
  ("end" ^^ { _ => List() } )|
  (statement ~ statementList ^^ { case statement ~ statementList => statement :: statementList  })

然后,只要语句中有错误,我就会收到很好的错误消息。 然而,这是丑陋的长代码。所以我想写这个:

def body: Parser[Body] =
("begin" ~> statementList <~ "end"  )  ^^ {
   case s => {   new Body(s); }
}

def statementList : Parser[List[Statement]] = 
    rep(statement)

此代码有效,但仅在 FIRST 语句中出现错误时才打印有意义的消息。如果它在后面的语句中,则消息将变得非常不可用,因为解析器希望看到整个错误语句被“结束”标记替换:

Exception in thread "main" java.lang.RuntimeException: [4.2] error: "end" expected but "let" found

 let b : string = x(3,b,"WHAT???",!ERRORHERE!,7 ) 

 ^ 

我的问题:有没有办法让 reprepsep 与有意义的错误消息结合使用,将插入符号放在正确的位置而不是开头重复片段?

【问题讨论】:

    标签: parsing scala error-handling combinators


    【解决方案1】:

    您可以通过将“自制”rep 方法与非回溯内部语句相结合来实现。例如:

    scala> object X extends RegexParsers {
         |   def myrep[T](p: => Parser[T]): Parser[List[T]] = p ~! myrep(p) ^^ { case x ~ xs => x :: xs } | success(List())
         |   def t1 = "this" ~ "is" ~ "war"
         |   def t2 = "this" ~! "is" ~ "war"
         |   def t3 = "begin" ~ rep(t1) ~ "end"
         |   def t4 = "begin" ~ myrep(t2) ~ "end"
         | }
    defined module X
    
    scala> X.parse(X.t4, "begin this is war this is hell end")
    res13: X.ParseResult[X.~[X.~[String,List[X.~[X.~[String,String],String]]],String]] =
    [1.27] error: `war' expected but ` ' found
    
    begin this is war this is hell end
                              ^
    
    scala> X.parse(X.t3, "begin this is war this is hell end")
    res14: X.ParseResult[X.~[X.~[String,List[X.~[X.~[String,String],String]]],String]] =
    [1.19] failure: `end' expected but ` ' found
    
    begin this is war this is hell end
                      ^
    

    【讨论】:

    • 你好丹尼尔。从 scala 2.11 开始,你的答案还一样吗?还是增强了 PC 以提供更好的原生错误消息?
    【解决方案2】:

    啊,找到了解决方案!事实证明,您需要在主解析器上使用函数短语来返回 不太倾向于回溯的新解析器。 (我想知道它到底是什么意思,也许如果它发现换行符就不会回溯?) 跟踪发生故障的最后位置。

    改变了:

    def parseCode(code: String): Program = {
     program(new lexical.Scanner(code)) match {
          case Success(program, _) => program
          case x: Failure => throw new RuntimeException(x.toString())
          case x: Error => throw new RuntimeException(x.toString())
      }
    
    }
    
    def program : Parser[Program] ...
    

    进入:

    def parseCode(code: String): Program = {
     phrase(program)(new lexical.Scanner(code)) match {
          case Success(program, _) => program
          case x: Failure => throw new RuntimeException(x.toString())
          case x: Error => throw new RuntimeException(x.toString())
      }
    
    }
    
    
    def program : Parser[Program] ...
    

    【讨论】:

    • phrase 返回一个Parser,只有在接受某些内容后没有输入时才会成功。不过,我不知道为什么会以这种方式改变回溯。
    • 事实证明它根本没有改变回溯,但是短语有一点状态来检查匹配结果最长的失败。它在 Odersky 的书中关于解析器的章节中名为“错误报告”的段落中进行了描述。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-11
    • 1970-01-01
    相关资源
    最近更新 更多