2015-04-16 24 views
0

我想在scala中编写一个简单的解析器,但是当我添加重复的令牌时Scala似乎陷入了无限循环。scala解析器组合器无限循环

我有2个下面的解析方法。一个使用rep()。非重复版本按预期工作(不是我想要的),但使用rep()版本会导致无限循环。

编辑: 这是一个学习的例子,我厌倦了执行'='被空白包围。

如果它是有帮助的,这是我的实际测试文件:

a = 1 
b = 2 
c = 1 2 3 

我能够解析:(与parse1法) ķ= V

但随后就遇到了这个问题,尝试当扩大锻炼出来: K = V1 V2 V3

import scala.util.parsing.combinator._ 
import scala.io.Source.fromFile 

class MyParser extends RegexParsers { 
    override def skipWhitespace(): Boolean = { false } 

    def key: Parser[String] = """[a-zA-Z]+""".r ^^ { _.toString } 
    def eq: Parser[String] = """\s+=\s+""".r ^^ { _.toString.trim } 
    def string: Parser[String] = """[^ \t\n]*""".r ^^ { _.toString.trim } 
    def value: Parser[List[String]] = rep(string) 

    def foo(key: String, value: String): Boolean = { 
    println(key + " = " + value) 
    true 
    } 

    def parse1: Parser[Boolean] = key ~ eq ~ string ^^ { case k ~ eq ~ string => foo(k, string) } 
    def parse2: Parser[Boolean] = key ~ eq ~ value ^^ { case k ~ eq ~ value => foo(k, value.toString) } 

    def parseLine(line: String): Boolean = { 
     parse(parse2, line) match { 
     case Success(matched, _) => true 
     case Failure(msg, _) => false 
     case Error(msg, _) => false 
    } 
    } 
} 

object TestParser { 
    def usage() = { 
    System.out.println("<file>") 
    } 

    def main(args: Array[String]) : Unit = { 
    if (args.length != 1) { 
     usage() 
    } else { 
     val mp = new MyParser() 

     fromFile(args(0)).getLines().foreach { mp.parseLine } 
     println("done") 
    } 
    } 
} 

回答

1

下一次,请提供一些具体的例子,这不是很明显你的我输入应该看起来像。

同时,你可以试试这个,也许你会发现它有用:

import scala.util.parsing.combinator._ 
import scala.io.Source.fromFile 

class MyParser extends JavaTokenParsers { 
    // override def skipWhitespace(): Boolean = { false } 

    def key: Parser[String] = """[a-zA-Z]+""".r ^^ { _.toString } 
    def eq: Parser[String] = "=" 
    def string: Parser[String] = """[^ \t\n]+""".r 
    def value: Parser[List[String]] = rep(string) 

    def foo(key: String, value: String): Boolean = { 
    println(key + " = " + value) 
    true 
    } 

    def parse1: Parser[Boolean] = key ~ eq ~ string ^^ { case k ~ eq ~ string => foo(k, string) } 
    def parse2: Parser[Boolean] = key ~ eq ~ value ^^ { case k ~ eq ~ value => foo(k, value.toString) } 

    def parseLine(line: String): Boolean = { 
     parseAll(parse2, line) match { 
     case Success(matched, _) => true 
     case Failure(msg, _) => false 
     case Error(msg, _) => false 
    } 
    } 
} 

val mp = new MyParser() 
for (line <- List("hey = hou", "hello = world ppl", "foo = bar baz blup")) { 
    println(mp.parseLine(line)) 
} 

说明:

JavaTokenParsers和RegexParsers区别对待空白。 JavaTokenParsers为您处理空白空间,它不是特定于Java,它适用于大多数非深奥语言。只要你不试图解析空白,JavaTokenParsers是一个很好的起点。

你的字符串定义包括一个*,它导致了无限递归。 您的eq定义包含与空白处理混淆的东西(除非真的有必要,否则不要这样做)。 此外,如果你想解析整行,你必须调用parseAll, ,否则它只以非贪婪的方式解析字符串的开头。

最后的备注:对于逐行解析键值对,一些String.split和 String.trim将是完全足够的。 Scala Parser Combinator对此有点矫枉过正。

PS:嗯......你想允许= -signs在你的密钥名中?然后我的版本在这里不起作用,因为它没有在key-name之后强制执行一个空的空间。

+0

呃,我不应该迟疑。这是一个可怕的问题.... 我会更新,但是是的目标是写一个解析器(用于学习),强制K = V与强制性空白。它看起来像一个简单的初学者解析器。 – user2466803

+0

我不确定在这里做什么。看起来这个问题已经得到解答,但它没有。这在很大程度上是我错误地提出这个问题的错。是否有可能把Andrey Tyukin折腾出一些业力或者做出伟大的尝试,但这仍然表明我被困在这里? – user2466803

+0

再次。对不起,我没有意识到你关心空白。我认为这是关于'''''Parser'中的''''''''''''''''''''''的错误。我添加了明确照顾空白区域的另一个版本。 –

1

这是不是一个重复的,这是一个不同的版本RegexParsers这需要空白的照顾明确

如果出于某种原因真的关心空格,那么你可以坚持RegexParsers,做如下(注意skipWhitespace = false,对空白ws明确的解析器,两个ws与各地等号squiglies和repsep和显式指定ws):

import scala.util.parsing.combinator._ 
import scala.io.Source.fromFile 

class MyParser extends RegexParsers { 
    override def skipWhitespace(): Boolean = false 

    def ws: Parser[String] = "[ \t]+".r 
    def key: Parser[String] = """[a-zA-Z]+""".r ^^ { _.toString } 
    def eq: Parser[String] = ws ~> """=""" <~ ws 
    def string: Parser[String] = """[^ \t\n]+""".r 
    def value: Parser[List[String]] = repsep(string, ws) 

    def foo(key: String, value: String): Boolean = { 
    print(key + " = " + value) 
    true 
    } 

    def parse1: Parser[Boolean] = (key ~ eq ~ string) ^^ { case k ~ e ~ v => foo(k, v) } 
    def parse2: Parser[Boolean] = (key ~ eq ~ value) ^^ { case k ~ e ~ v => foo(k, v.toString) } 

    def parseLine(line: String): Boolean = { 
     parseAll(parse2, line) match { 
     case Success(matched, _) => true 
     case Failure(msg, _) => false 
     case Error(msg, _) => false 
    } 
    } 
} 

val mp = new MyParser() 
for (line <- List("hey = hou", "hello = world ppl", "foo = bar baz blup", "foo= bar baz", "foo =bar baz")) { 
    println(" (Matches: " + mp.parseLine(line) + ")") 
} 

现在解析器拒绝哪里有等号周围没有空白行:

hey = List(hou) (Matches: true) 
hello = List(world, ppl) (Matches: true) 
foo = List(bar, baz, blup) (Matches: true) 
(Matches: false) 
(Matches: false) 

*代替+string该bug已被删除,就像在以前的版本。