3

对于这个简化的问题,我试图解析一个看起来像

foo bar
 baz quux 
 woo
hoo xyzzy 
  glulx

进入

[["foo", "bar", "baz", "quux", "woo"], ["hoo", "xyzzy", "glulx"]]

我试过的代码如下:

import qualified Text.Megaparsec.Lexer as L
import Text.Megaparsec hiding (space)
import Text.Megaparsec.Char hiding (space)
import Text.Megaparsec.String
import Control.Monad (void)
import Control.Applicative

space :: Parser ()
space = L.space (void spaceChar) empty empty

item :: Parser () -> Parser String
item sp = L.lexeme sp $ some letterChar

items :: Parser () -> Parser [String]
items sp = L.lineFold sp $ \sp' -> some (item sp')

items_ :: Parser [String]
items_ = items space

这适用于一个块items

λ» parseTest items_ "foo bar\n baz quux\n woo"
["foo","bar","baz","quux","woo"]

但是,一旦我尝试 parse many items,它就会在第一条未缩进的行上失败:

λ» parseTest (many items_) "foo bar\n baz quux\n woo\nhoo xyzzy\n  glulx"
4:1:
incorrect indentation (got 1, should be greater than 1)

或者,使用更简单的输入:

λ» parseTest (many items_) "a\nb"
2:1:
incorrect indentation (got 1, should be greater than 1)
4

1 回答 1

4

Megaparsec 的作者在这里 :-) 使用 Megaparsec 时要记住的一件事是,它的词法分析器模块确实是故意的“低级”。它不会做任何你不能自己构建的事情,它不会把你锁在任何特定的“框架”中。因此,基本上在您的情况下,您 sp'为您提供了空间使用者,但您应该小心使用它,因为当您的缩进级别小于或等于整个折叠开始的缩进级别时它肯定会失败,这就是您的折叠结束的方式,由方式。

引用文档

创建一个支持行折叠的解析器。第一个参数用于消耗行折叠组件之间的空白,因此它必须消耗换行符才能正常工作。第二个参数是一个回调,它接收自定义的占用空间的解析器作为参数。这个解析器应该在可以放在不同行上的单独的折行组件之后使用。

sc = L.space (void spaceChar) empty empty

myFold = L.lineFold sc $ \sc' -> do
  L.symbol sc' "foo"
  L.symbol sc' "bar"
  L.symbol sc  "baz" -- for the last symbol we use normal space consumer

折线不能无限期地运行,因此您应该期望它会失败并显示类似于您现在所拥有的错误消息。要想成功,你应该想办法让它完成。这通常通过在行尾使用“普通”空间消费者来完成:

space :: Parser ()
space = L.space (void spaceChar) empty empty

item :: Parser String
item = some letterChar

items :: Parser () -> Parser [String]
items sp = L.lineFold sp $ \sp' ->
  item `sepBy1` try sp' <* sp

items_ :: Parser [String]
items_ = items space

item `sepBy1` try sp'运行直到失败,然后sp抓取其余部分,因此可以解析下一个折叠。

λ> parseTest items_ "foo bar\n baz quux\n woo"
["foo","bar","baz","quux","woo"]
λ> parseTest (many items_) "foo bar\n baz quux\n woo\nhoo xyzzy\n  glulx"
[["foo","bar","baz","quux","woo"],["hoo","xyzzy","glulx"]]
λ> parseTest (many items_) "foo bar\n baz quux\n woo\nhoo\nxyzzy\n  glulx"
[["foo","bar","baz","quux","woo"],["hoo"],["xyzzy","glulx"]]
于 2016-05-16T18:19:26.870 回答