【问题标题】:Choosing between two parsers with a common prefix在具有共同前缀的两个解析器之间进行选择
【发布时间】:2021-11-06 14:41:53
【问题描述】:

我目前正在尝试在 Parsec 中编写一个简单的解析器,但一直遇到有关空格的问题:作为一个最小的示例,我有一个解析器将解析两个字母,两个小写或一个大写和一个小写。我会这样做

testP :: Parser String
testP = do
    lookAhead lower
    a1 <- lower
    a2 <- lower
    return [a1,a2]
    <|> do
    a1 <- upper
    a2 <- lower
    return [a1,a2]

这对“as”或“Bs”等字符串按预期工作。现在我想在输入字符串的开头处理可能的空格。如果我这样做了

testP :: Parser String
testP = do
    spaces
    lookAhead lower
    a1 <- lower
    a2 <- lower
    return [a1,a2]
    <|> do
    a1 <- upper
    a2 <- lower
    return [a1,a2]

我希望程序现在能够解析“as”和“Bs”,但是对于第二个字符串,我得到一个错误“期望空格或小写字母”。 好的,我认为无论采用哪个选项都会解析空格,但显然不是这样,让我们​​在第二个选项的开头放置另一个 spaces,如下所示:

testP :: Parser String
testP = do
    spaces
    lookAhead lower
    a1 <- lower
    a2 <- lower
    return [a1,a2]
    <|> do
    spaces
    a1 <- upper
    a2 <- lower
    return [a1,a2]

但是,当我尝试解析“Bs”时,这仍然给我同样的错误。我怎么会误解这里的空白处理,我该如何正确地做到这一点?

【问题讨论】:

    标签: haskell parsec


    【解决方案1】:

    &lt;|&gt; 不会尝试第二种选择,如果 任何东西 被第一个解析器使用。这样做是为了防止空间泄漏。这是 parsec 的基础设计之一。

    spaces 消耗一些输入时,一切都已确定,解析器现在必须成功 - 否则,将不会尝试替代方案,整个机器就会失败。这就是您观察此行为的原因。 spaces 消耗一些输入,lookAhead lower 失败,整个解析器失败。

    可以使用try 获得任意前瞻,并确保即使第一个消耗输入,也尝试第二个替代方案,但在这种情况下不应该这样做。在这里,spaces 是一个 非致命 解析器,它是两个操作的初步 - 所以只需使用解析器 before 您的任何一个替代方案。

    testP :: Parser String
    testP = spaces *> (do
        lookAhead lower
        a1 <- lower
        a2 <- lower
        return [a1,a2]
        <|> do
        a1 <- upper
        a2 <- lower
        return [a1,a2])
    

    【讨论】:

    • 更简单的方法是将&lt;|&gt; 推得更深:spaces *&gt; liftA2 pair (try lower &lt;|&gt; upper) lower where pair x y = [x, y]
    • (假设lower是Parsec的那个,你不需要trylookAhead
    • 感谢您的解释,现在这很有意义。不知道我怎么没有意识到一开始就使用空格意味着它当然已经消耗了一些东西。
    猜你喜欢
    • 2013-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-25
    • 2010-09-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多