我刚刚开始学习解析,我在Haskell中编写了this simple parser(使用parsec)来读取JSON并为它构建一个简单的树。我在RFC 4627中使用语法。
但是,当我尝试解析字符串{"x":1 }
时,我得到了输出:
parse error at (line 1, column 8): unexpected "}" expecting whitespace character or ","
当我在结束括号(])或mustachio(})之前有空格时,似乎只会发生这种情况。
我做错了什么?如果我在结束符号之前避免使用空格,那么它就能完美运行。
答案 0 :(得分:6)
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
(来自(*>)
的{{1}});将相同的规则应用于所有其他解析器。因为在以这种方式进入“错误的解析器”之后你永远不会消耗空白,所以你最终不必回溯。Control.Applicative
,在Parsec中使用反向跟踪,如果失败则应回滚它们的输入。编辑:更新了ASCII图形以使其更有意义。
答案 1 :(得分:1)
一般解决方案是让所有解析器跳过尾随空格。查看Parsec文档中的lexeme
(在ParsecToken
中)以获得一个简洁的方法,或者自己制作一个简单的版本:
lexeme parser = do result <- parser
spaces
return result
然后在所有标记上使用此函数(如数字文字)。这样,您只需要担心表达式开头的空格。
有关ParsecToken
和朋友的详细信息,请查看Parsec docs的“词法分析”部分。
只有在令牌之后才跳过空白才有意义,除非在最开始你可以手动跳过它。即使您最终没有使用ParsecToken
模块,也应该采用这种方法。
您似乎已经tok
的行为与我的lexeme
相似,只是它消耗了两个方面的空白。将其更改为仅在令牌之后消耗空白,并在手动输入的最开头忽略空格。这应该(理想情况:) :)解决问题。