Parsec:处理重叠解析器
我真的很难分析Haskell,但它最有意义。Parsec:处理重叠解析器
我正在建立一个模板化程序主要是为了学习解析更好;模板可以通过{{ value }}
表示法插值。
这是我目前的解析器,
data Template a = Template [Either String a]
data Directive = Directive String
templateFromFile :: FilePath -> IO (Either ParseError (Template Directive))
templateFromFile = parseFromFile templateParser
templateParser :: Parser (Template Directive)
templateParser = do
tmp <- template
eof
return tmp
template :: Parser (Template Directive)
template = Template <$> many (dir <|> txt)
where
dir = Right <$> directive
txt = Left <$> many1 (anyChar <* notFollowedBy directive)
directive :: Parser Directive
directive = do
_ <- string "{{"
txt <- manyTill anyChar (string "}}")
return $ Directive txt
然后我在文件上是这样运行:
{{ value }}
This is normal Text
{{ expression }}
当我运行使用templateFromFile "./template.txt"
这个我得到的错误:
Left "./template.txt" (line 5, column 17):
unexpected Directive " expression "
为什么会发生这种情况,我该如何解决?
我的基本理解是,many1 (anyChar <* notFollowedBy directive)
应抓住所有的字符,直到下一个指令的开始,然后应该失败并返回字符列表直到该点;那么 它应该回落到以前的many
,并应尝试再次解析dir
并应成功;显然还有其他事情正在发生。我是 在解析器大部分重叠时无法解析如何解析之间其他事情。
我很想知道如何更加地道地构建这些技巧,请让我知道我是否以愚蠢的方式做事。干杯!谢谢你的时间!
你有几个问题。首先,在Parsec中,如果解析器消耗任何输入然后失败,那就是错误。所以,当分析器:
anyChar <* notFollowedBy directive
失败(因为性格是后跟一个指令),它失败后anyChar
已经消耗输入,并立即生成一个错误。因此,解析器:
let p1 = many1 (anyChar <* notFollowedBy directive)
如果运行到指令中将永远不会成功。例如:
parse p1 "" "okay" -- works
parse p1 "" "oops {{}}" -- will fail after consuming "oops "
可以通过插入一个try
子句解决这个问题:
let p2 = many1 (try (anyChar <* notFollowedBy directive))
parse p2 "" "okay {{}}"
其产生Right "okay"
和揭示了第二个问题。解析器p2
仅消耗指令后面没有的字符,因此排除紧接在指令之前的空间,并且在解析器中无法消耗为后跟指令的字符,因此它会卡住。
你真正想要的东西,如:
let p3 = many1 (notFollowedBy directive *> anyChar)
它首先检查,在当前位置,我们是不是抓住一个字符之前寻找一个指令。否try
子句是必需的,因为如果失败,它将失败而不消耗输入。 (notFollowedBy
从来没有消耗输入,按文档。)
parse p3 "" "okay" -- returns Right "okay"
parse p3 "" "okay {{}}" -- return Right "okay "
parse p3 "" "{{fails}}" -- correctly fails w/o consuming input
所以,把你原来的例子有:
txt = Left <$> many1 (notFollowedBy directive *> anyChar)
应该正常工作。
many1 (anyChar <* notFollowedBy directive)
这只解析没有跟着指令的字符。
{{ value }}
This is normal Text
{{ expression }}
当解析中间的文本,它会停在最后t
,该指令未消费之前离开换行(因为它的,好了,一个字符后跟一个指令),所以下一次迭代,你尝试解析一个指令,你失败了。然后你在那个新行上重试txt
,解析器期望它不被一个指令跟着,但是它找到了一个,因此就是错误。
有道理;我怎样才能最好地解决它做我期望的? –
'txt = many1(notFollowedBy directive *> anyChar)'是最简单的修复方法,尽管它每个指令运行两次'directive'。 –