我正在为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
有没有办法检测到这个?我不希望修改解析器的所有其余部分以删除跳过空格,因为它非常复杂并且会使其无法读取。解决这个问题的最佳方法是什么?
答案 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之间添加逗号或其他分隔符。
对解析器的修改将是微不足道的,然后您可以转到更有趣的问题: - )