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,解析器期望它不被一个指令跟着,但是它找到了一个,因此就是错误。

+0

有道理;我怎样才能最好地解决它做我期望的? –

+0

'txt = many1(notFollowedBy directive *> anyChar)'是最简单的修复方法,尽管它每个指令运行两次'directive'。 –