Scala解析器组合器用于(几乎)微不足道的语法

3

我一直在尝试编写一个解析器,用于解析一种(非常)简单的语言。该语言如下所示:

block{you are a cow too blkA{ but maybe not} and so is he} hear me moo blockZ{moooooo}

我可以使用正则表达式将其拆分开:
.*?[^ ]*?\\{
.*?\\}

这段代码会一直读取字符直到找到匹配的 [^ ]*?\\{ 或者 \\},也就是一个块的开头或结尾。我的问题是,如果我想使用Scala的解析组合器来实现,该怎么做呢?我目前有如下代码:

   def expr: Parser[Any] = (block | text)+
   def text = ".+?".r
   def block = "[^ ]*?\\{".r ~ expr ~ "}"

但是这个不起作用:
parsed: List(b, l, o, c, k, {, y, o, u, a, r, e, a, c, o, w, t, o, o, b, l, k, A, {, b, u, t, m, a, y, b, e, n, o, t, }, a, n, d, s, o, i, s, h, e, }, h, e, a, r, m, e, m, o, o)

似乎 block 解析器没有触发,导致 text 解析器被重复触发。但是当我移除 text 解析器时:
   def expr: Parser[Any] = (block)+

I get:

failure: string matching regex `[^ ]*?\{' expected but `y' found

block{you are a cow too blkA{ but maybe not} and so is he} hear me moo  
      ^

显然,block解析器是工作的,只是当存在text解析器时不起作用。发生了什么?有没有一种“正确”的方法来处理这个问题,对于如此基础的语法而言?

编辑:更改标题,因为这不再是关于不愿意的问题,而只是解决问题。

编辑:我现在有了这个:

def expr: Parser[Any] = (block | text)+

def text = "[^\\}]".r

def block = "[^ ]*?\\{".r ~ expr ~ "}"

这背后的逻辑是对于每个字符,它会测试该字符是否为一个块的开头。如果不是,它将继续移动到下一个字符。这给了我:

parsed: List(((block{~List(y, o, u, a, r, e, a, c, o, w, t, o, o, ((blkA{~List(b, u, t, m, a, y, b, e, n, o, t))~}), a, n, d, s, o, i, s, h, e))~}), h, e, a, r, m, e, m, o, o)

这种做法在某种程度上是正确的。不过它是逐个解析非块字符,可能存在性能问题(我想?)。有没有一种方法可以一次解析所有这些非块字符,并将它们保留在一个大字符串中?


请不要在现有问题中添加新问题。可以进行改进,但对于新问题,请创建新的问题。它是逐个解析,因为您使用了非贪婪星号。只需取消非贪婪即可。 - Daniel C. Sobral
1个回答

2
问题在于text会吞噬所有右花括号 (}),具体表现如下:
expr -> block -> expr -> text.+ (until all input is consumed)

此时,它退出expr并尝试解析},但实际上该符号不存在,导致解析失败并在第一个expr上回退到text

您可以使用log来查看解析过程中发生了什么。


这就引出了一个问题:我该如何防止 text 吃掉我的闭合 }?在正则表达式中,我可以匹配 .+?[^ ]*?\\{.+?\\} 来获取所有文本,直到下一个开放块或关闭块符号,但是在 PC 库中没有 .+?(据我所知)。有没有其他方法可以实现相同的效果? - Li Haoyi
@LiHaoyi 嗯,..*? 等同于 .+?,但为什么不直接用 [^{}]+ 呢?如果有嵌套的大括号,你需要将 text 转化为一个递归解析器,而不是正则表达式 -- 正则表达式不能处理递归。 - Daniel C. Sobral

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接