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

これは の 1 つのブロックに対して機能しitemsます。

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

しかし、解析しようとするとすぐに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 を使用するときに覚えておくべきことの 1 つは、Megaparsec のレクサー モジュールが実際には意図的に「低レベル」であるということです。自分で構築できないことは何もしません。特定の「フレームワーク」に縛られることもありません。したがって、基本的にあなたのケースではスペースコンシューマー sp'が提供されていますが、インデントレベルがフォールド全体の開始のインデントレベル以下の場合は確実に失敗するため、慎重に使用する必要があります。仕方。

ドキュメントを引用するには:

行折りをサポートするパーサーを作成します。最初の引数は、改行の構成要素間の空白を消費するために使用されるため、適切に機能するには改行を消費する必要があります。2 番目の引数は、スペースを消費するカスタム パーサーを引数として受け取るコールバックです。このパーサーは、異なる行に配置できる行の折り畳みの個別のコンポーネントの後に使用する必要があります。

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 に答える