使用 foldLine 解析多个 block

标签 parsing haskell indentation megaparsec

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

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

这适用于一 block items:

λ» 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"
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)

最佳答案

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

引用the docs :

Create a parser that supports line-folding. The first argument is used to consume white space between components of line fold, thus it must consume newlines in order to work properly. The second argument is a callback that receives custom space-consuming parser as argument. This parser should be used after separate components of line fold that can be put on different lines.

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

Line fold 不能无限期地运行,所以你应该预料到它会因错误而失败 类似于您现在拥有的消息。要想成功,你应该思考 关于它完成的方法。这通常是通过使用“正常”来完成的 行尾的空间消费者折叠:

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"]]

关于使用 foldLine 解析多个 block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37256316/

相关文章:

html - Android中解析HTML : no XSLT? 有什么用?

c++ - 规则定义中的 AST 和运算符优先级

Vim 表达式(和语言)敏感缩进 - 可能吗?

mysql - 我如何使用 SwiftyJSON 在 Swift 中解析这个 JSON 对象?

C# HTML 字体标签解析

exception - Haskell 惰性异常——区分多个异常?

Haskell 随机生成

haskell - 采用函数式语言的 Kernighan & Ritchie 字数统计示例程序

c# - 是否可以在 C# 中的 RTB 中设置每行的悬挂缩进?

c# - 在字符串属性的 xml 序列化中缩进文本?