我刚刚开始学习解析,我在 Haskell 中编写了这个简单的解析器(使用 parsec)来读取 JSON 并为其构建一个简单的树。我正在使用RFC 4627中的语法。
但是,当我尝试解析 string 时{"x":1 }
,我得到了输出:
解析错误(第 1 行,第 8 列): 意外的“}” 期待空白字符或“,”
只有当我在右大括号 (]) 或 mustachio (}) 之前有空格时,才会出现这种情况。
我做错了什么?如果我在结束符号之前避免空格,它就可以完美地工作。
Parsec 不会自动进行倒带和回溯。当您编写sepBy member valueSeparator
时,valueSeparator
会消耗空格,因此解析器将解析您的值,如下所示:
{"x":1 }
[------- object
% beginObject
[-] name
% nameSeparator
% jvalue
[- valueSeparator
X In valueSeparator: unexpected "}"
Legend:
[--] full match
% full char match
[-- incomplete match
X incomplete char match
当valueSeparator
失败时,Parsec 不会返回并尝试不同的解析组合,因为一个字符已经在valueSeparator
.
您有两种选择来解决您的问题:
tok
应该只在 char 之后消耗空格,所以它的定义是tok c = char c *> ws
( (*>)
from Control.Applicative
); 将相同的规则应用于所有其他解析器。由于以这种方式输入“错误的解析器”后您将永远不会消耗空白空间,因此您最终不必回溯。try
在解析器前面添加可能消耗多个字符的解析器,如果它们失败,应该回退它们的输入。编辑:更新 ASCII 图形更有意义。
一个通用的解决方案是让所有解析器跳过尾随空格。查看Parsec 文档中的lexeme
(in ParsecToken
) 以获得一种巧妙的方法来执行此操作,或者自己制作一个简单的版本:
lexeme parser = do result <- parser
spaces
return result
然后在所有标记上使用此函数(如数字文字)。这样,您只需要担心表达式开头的空格。
有关和朋友的更多信息,请查看Parsec 文档ParsecToken
的“词法分析”部分。
仅在令牌之后跳过空格是有意义的,除非在开始时您可以手动跳过它。即使您最终不使用该ParsecToken
模块,您也应该采用这种方法。
似乎您已经拥有tok
了类似于我的行为,只是它在两边都lexeme
消耗了空格。将其更改为仅在令牌之后使用空格,并手动忽略输入开头的空格。那应该(理想情况下:))解决问题。