【问题标题】:Scala Parser and combinators: java.lang.RuntimeException: string matching regex `\z' expectedScala 解析器和组合器:java.lang.RuntimeException:字符串匹配正则表达式 `\z' 预期
【发布时间】:2014-03-06 00:30:51
【问题描述】:

作为我硕士论文的一部分,我正在尝试使用 Scala 的 RegexParser 按照动态认知逻辑的语法解析一些文本。但是我在简单的逻辑连词上总是遇到同样的错误。我知道它在哪里以及为什么会失败,但不知道为什么它首先匹配它。

我的代码(严重归结为隔离问题):

import scala.util.parsing.combinator._

class Formula() {
      def and(q:Formula) = Conjunction(this, q) // ∧
}

abstract class Literal extends Formula
abstract class Constant extends Formula

case class Atom(symbol:String) extends Literal 
case class NotAtom(p:Atom) extends Literal

case class Conjunction(p:Formula, q:Formula) extends Formula

class mapParser extends RegexParsers {
    val conjOp = "&"
    val negOp = "~"

    val listseparator = ","

    val leftparen = "("
    val rightparen = ")"

    def id:Parser[String] = "[a-z_]+".r // fluents are never capitalized. but may have underscore.
    def litargs: Parser[String] = repsep("[a-zA-Z]+".r,listseparator) ^^ {case list => "(" + list.toString.stripPrefix("List") + ")"}

    def atom: Parser[Atom] = id~leftparen~litargs~rightparen ^^ {case head~_~tail~_ => Atom(head+tail)}
    def negAtom: Parser[NotAtom] = negOp~>atom ^^ (NotAtom(_))
    def literal: Parser[Literal] = negAtom | atom 

    def and: Parser[Formula] = formula~conjOp~formula ^^ {case p1~_~p2 => Conjunction(p1,p2)}  

    def formula: Parser[Formula] = literal | and
};

object DomainParser extends mapParser {
  def test() =  {
    val domainDesc ="present(A) & ~present(B)";

    println("input: " + domainDesc)
    println("result: " + apply(domainDesc))
  }

  def apply(domainDesc: String) = parseAll(formula, domainDesc) match {
    case Success(result, _) => result
    case failure : NoSuccess => scala.sys.error(failure.msg)
  }
}

我正在从 java 外部调用 DomainParser.test() 函数。输入是

present(A) & ~present(B)

应该产生:

Conjunction(Atom(present((A))),NotAtom(Atom(present((B)))))

但是却给了我错误:

Exception in thread "main" java.lang.RuntimeException: string matching regex `\z' expected but `&' found
    at scala.sys.package$.error(package.scala:27)
    at mAp.DomainParser$.apply(DEL.scala:48)
    at mAp.DomainParser$.test(DEL.scala:43)
    at mAp.DomainParser.test(DEL.scala)
at ma.MA.main(MA.java:8)

此外,如果我直接调用“and”解析器而不是“formula”解析器,它可以正常工作。因此问题似乎出在这一行:

def formula: Parser[Formula] = literal | and

因为它试图将整行解析为单个文字。然后它会正确解析 present(A),但不会在 '&'(不是文字解析器的一部分)上失败并作为 'and'-term 返回解析,而是失败并出现异常。

我不能看在... 看看为什么它会尝试匹配任何 '\z'。它不包含在我的语法中,即使它是 - 它不应该失败并尝试解析为下一个术语而不是异常退出吗?我在认为有一些我不知道的字符串结尾术语的内置功能与认为有一些非常明显的东西在盯着我之间徘徊。

非常需要任何帮助,非常欢迎,并在此先感谢您。

丹真

【问题讨论】:

  • 带有多个“&”的输入会是什么样子? present(A) & ~present(B) & present(C)?
  • 是的。哪个(如果我正确指定了 BNF)应该希望匹配类似的内容: Conjunction(Conjunction(Atom(present((A))),NotAtom(Atom(present((B)))), (Atom(present((C )))))
  • 好吧,我不是正则表达式方面的专家,但你可以看看这个帖子:stackoverflow.com/questions/1798738/… 似乎首先尝试了文字并成功,但随后\z 代表预计输入结束,但字符串的其余部分仍然是& ~present(B)
  • Kigyo:是的,我后来发现了同样的问题。所以我需要找到一种方法,让 Literal regex 在后面跟着空格和分隔符时失败而不是成功。谢谢你。我看看能不能搞定。
  • 解决了,但由于左递归导致堆栈溢出。我决定删除中缀符号,取而代之的是 "&(present(A), ~present(B)) 似乎工作正常。欢迎提供有关如何实现中缀运算符符号的任何提示。

标签: java regex scala parsing


【解决方案1】:

我将为我制作的命题公式添加一个类似的解析器。也许这对你有帮助。

'+' = 顶部/真

'-' = 底部/假

'!' = 否定

'&' = 连词

'|' = 析取

'>' = 暗示

'

object FormulaParser extends StandardTokenParsers with PackratParsers {
  //Symbols for all connectives
  private val parseSymbols = List("(", ")", "+", "-", "!", "&", "|", ">", "<")
  lexical.delimiters ++= parseSymbols

  private lazy val formula: PackratParser[Formula] = implication | equivalence | conjunction | disjunction | term
  private lazy val formulaWithoutBrackets: PackratParser[Formula] = implication | equivalence | conjunction | disjunction | termWithoutBrackets

  private lazy val term: PackratParser[Formula] = top | bottom | variable | parens | negation
  private lazy val termWithoutBrackets = top | bottom | variable | negation

  private lazy val top: PackratParser[Formula] = "+" ^^^ { Top() }
  private lazy val bottom: PackratParser[Formula] = "-" ^^^ { Bottom() }
  private lazy val variable: PackratParser[Formula] = ident ^^ { Variable(_) }
  private lazy val parens: PackratParser[Formula] = "(" ~> formulaWithoutBrackets <~ ")"
  private lazy val negation: PackratParser[Formula] = "!" ~> term ^^ { Negation(_) }

  private lazy val conjunction: PackratParser[Formula] = term ~ "&" ~ term ~ rep("&" ~> term) ^^ {
    case p ~ "&" ~ q ~ conj => conj.foldLeft(Conjunction(p,q))((con, elem) => Conjunction(con, elem))
  }

  private lazy val disjunction: PackratParser[Formula] = term ~ "|" ~ term ~ rep("|" ~> term) ^^ {
    case p ~ "|" ~ q ~ disj => disj.foldLeft(Disjunction(p,q))((dis, elem) => Disjunction(dis, elem))
  }

  private lazy val implication: PackratParser[Formula] = (conjunction | disjunction | term) ~ ">" ~ (conjunction | disjunction | term) ^^ { case p ~ ">" ~ q => Implication(p, q) }

  private lazy val equivalence: PackratParser[Formula] = (conjunction | disjunction | term) ~ "<" ~ (conjunction | disjunction | term) ^^ { case p ~ "<" ~ q => Equivalence(p, q) }
}

有了这个,你可以解析输入,如: (p &amp; q) | (!q &gt; (r &amp; s))

这里的合取和析取也比蕴含和等价的结合力更强。

p &amp; q &gt; r | s 将导致Implication(Conjunction(Variable(p), Variable(q)), Disjunction(Variable(r), Variable(s)))

【讨论】:

  • 太棒了,我肯定可以用它来获得灵感:-) 我不确定为什么你的可以处理中缀运算符,而我的却不能——即使是作为 packratparser 实现的。我会研究你的代码的细节,看看我能不能弄明白。
  • 好吧,这个解析器没有左递归。我认为这与解析器类型无关。我只是把它作为一个 packrat-parser 来做,因为它加快了解析速度,但我对此知之甚少(理论上,它有什么不同)
【解决方案2】:

好的。如果只是由于左递归,我有一个类似的解析器,我在那里解决了这个问题。

您必须更改以下内容:

def and: Parser[Formula] = literal~conjOp~literal~rep(conjOp ~> literal) ^^ {
  case p ~ conjOp ~ q ~ conj => conj.foldLeft(Conjunction(p,q))(Conjunction(_, _))
}
def formula: Parser[Formula] = and | literal

由于最后通过Conjunction连接的只有Literals,你可以这样重写and

您的代码稍微复杂一些:

input: p(A,B) & ~p(B) & p(C)
result: Conjunction(Conjunction(Atom(p(A,B)),NotAtom(Atom(p(B)))),Atom(p(C)))

【讨论】:

  • 是的,这将适用于这种简化。但这只是一个简化版本以隔离错误。在实际代码中,我有所有其他命题结构(&、|、->、、、true、false),以及一组 Belief、EveryoneKnows 和 CommonKnowledge 运算符,所有这些都可以递归包括。到目前为止,我决定简单地使用前缀运算符。它就像一个魅力。
  • 我还有一个用于所有命题连接词的解析器,它适用于中缀,但不适用于“Belief、EveryoneKnows 和 CommonKnowledge”。我认为适应它并不难。如果你愿意,我可以给你。
猜你喜欢
  • 2019-05-01
  • 1970-01-01
  • 2017-09-10
  • 1970-01-01
  • 2013-08-30
  • 2012-04-25
  • 2019-01-08
  • 1970-01-01
  • 2021-06-03
相关资源
最近更新 更多