检测RegexParsers中的新行

时间:2014-01-09 15:10:22

标签: scala parser-combinators

我正在为DSL构建此解析器,其中通常会忽略包含换行符的空格。 DSL包含各种LISP样式表达式,每个表达式都有自己的解析器。变量声明如下:

?varname
?varname1 ?varname2 - type1
?varname3 ?varname4 ?varname5 - type2

如果变量后面没有-,则默认为object类型,如果有-,则后跟类型名称。因此,在上述情况下,?varname的类型为object,而?varname1?varname2的类型为type1

我使用了RegexParsers,因为我的解析器可以完美地用于所有DSL的其余部分。但是,我发现解析上面的变量声明列表存在问题。

上面的解析器如下所示:

def typed_list_variables : Parser[List[LiftedTerm]]= typed_variables.+ ^^ { case list => list.flatten.map(variable =>
        LiftedTerm(variable._1, variable._2 match {
          case "object" => ObjectType
          case _ => TermType(variable._2)
        })) }

def typed_variables = ((variable+) ~ (("-" ~> primitive_type)?)) ^^ {
    case variables ~ primitive_type => 
         for (variable <- variables) yield variable -> primitive_type.getOrElse("object")
}

def variable = """\?[a-zA-Z][a-zA-Z0-9_-]*""".r
def primitive_type = """[a-zA-Z][a-zA-Z0-9_-]*""".r

问题在于,因为我想忽略空格,并且我使用RegexParsers来执行此操作,并且使用其开箱即用设施来跳过空格,?varname被错误地解释为也是输入type

有没有办法检测到这个?我不希望修改解析器的所有其余部分以删除跳过空格,因为它非常复杂并且会使其无法读取。解决这个问题的最佳方法是什么?

4 个答案:

答案 0 :(得分:1)

重新定义空白;从中排除换行符。

override val whiteSpace = """[ \t]+""".r

我不确定这是否被视为良好做法。请查看此主题以获得进一步的讨论和灵感: Scala parser combinators and newline-delimited text

编辑:基于OP的输入进一步细化;另见前面的评论。

在这个特定的DSL中,一些语句(声明)由换行符终止,而其他语句将换行符视为空格,只是分隔标记并被解析器忽略。

对于简单的正则表达式,对换行符的这种不一致的解释可能过于复杂。因此,在这种情况下,不要覆盖变量val whiteSpace,而是覆盖方法def handleWhiteSpace;在这里,您可以通过编程方式确定要将哪些内容视为空白。最简单的方法似乎是定义一个全局可修改变量(var foo: Boolean),该变量由令牌化器/解析器根据正在解析的语句类型打开和关闭。然后,您handleWhiteSpace的实现可以使用此变量来相应地调整其行为。

handleWhiteSpace的新实现可以是原始handleWhiteSpace的副本,其中不可修改的whiteSpace被表达式替换,该表达式在两个正则表达式之间动态切换(一个匹配所有空格包括换行符,另一行不包括换行符),具体取决于全局变量的值。如果可能,您可能希望更好地使用继承,并在其中任何一种情况下调用super.handleWhiteSpace

答案 1 :(得分:0)

这是您可以考虑的替代方法。

编写一个简单的预处理器,将一个显式的分隔符标记(你自己编写的东西)附加到以问号开头的每一行。现在你的解析器可以忽略所有换行符;声明由显式标记明确终止。

答案 2 :(得分:0)

由于您尝试解析的格式使用换行符作为 关键字,你必须在解析时考虑换行符。
从好的方面来说,这很容易完成,与原始代码没有什么不同。 尝试通过添加typed_variables_line函数来创建知道这些行的解析器。然后将文档定义为这些行的列表。你也想要允许空行。我也为此添加了一条规则。

override val whiteSpace = """[ \t]+""".r;
def typed_list_variables : Parser[List[LiftedTerm]]= rep(typed_variables_line | empty_line) ^^ { 
    case list => list.flatten
}

def typed_variables_line:Parser[List[LiftedTerm]] = rep1(variable) ~ opt("-"~>primitive_type) <~ opt("\n") ^^ {
    case vars ~ None =>vars.map(varName=>LiftedTerm(varName,ObjectType));
    case vars ~ Some(primTypeName) =>vars.map(varName=>LiftedTerm(varName,TermType(primTypeName)));
}

def empty_line:Parser[List[LiftedTerm]] = "\n" ^^ {
    case nothing => List();
}

答案 3 :(得分:-2)

我强烈建议您保留语言的语法,以便换行符不重要。

根据我的经验,使空白显着,或者导致用户不知道它的混淆,或者更复杂的规范和语法处理。

我觉得这很麻烦。特别是,对于Scala RegexParsers,忽略空白大多数是非常重要的,但在某些地方使它变得很重要(并且确保你正确地做到了这一点)。

语法变体的两个建议:

a)在声明的末尾添加分号或其他终结符,或

b)在单个声明中的多个varnames之间添加逗号或其他分隔符。

对解析器的修改将是微不足道的,然后您可以转到更有趣的问题: - )