私は attoparsec パーサーをコーディングしており、パーサーを再帰パーサーに変えたいパターンにぶつかっています (それらをモナド bind >>= 演算子と再帰的に結合します)。
そこで、次のようにパーサーを再帰パーサーに変換する関数を作成しました。
recursiveParser :: (a -> A.Parser a) -> a -> A.Parser a
recursiveParser parser a = (parser a >>= recursiveParser parser) <|> return a
次のような再帰的なデータ型がある場合に役立ちます
data Expression = ConsExpr Expression Expression | EmptyExpr
parseRHS :: Expression -> Parser Expression
parseRHS e = ConsExpr e <$> parseFoo
parseExpression :: Parser Expression
parseExpression = parseLHS >>= recursiveParser parseRHS
where parseLHS = parseRHS EmptyExpr
もっと慣用的な解決策はありますか?recursiveParser
ある種の折り畳みのように思えます...sepBy
ドキュメントでも見ましたが、この方法は私のアプリケーションにより適しているようです。
編集:ああ、実際に考えてみると、実際には似たようなものになるはずfix
です...どうやってそれを忘れたのかわかりません。
EDIT2 : Rotsor は私の例に対する彼の代替案で良い点を指摘していますが、残念ながら、私の AST は実際にはそれよりも少し複雑です。実際にはもっと似ています(ただし、これはまだ単純化されています)。
data Segment = Choice1 Expression
| Choice2 Expression
data Expression = ConsExpr Segment Expression
| Token String
| EmptyExpr
ここで、文字列a -> b
は右にc:d
ブラケット、左にブラケットし、:
よりきつく結合します->
。
つまり、次a -> b
のように評価されます
(ConsExpr (Choice1 (Token "a")) (Token "b"))
とc:d
評価されます
(ConsExpr (Choice2 (Token "d")) (Token "c"))
foldl
一方と他方に使用できると思いますがfoldr
、まだ複雑です。少し奇妙な方法で再帰的であるため、"a:b:c -> e:f -> :g:h ->"
実際には有効な文字列ですが、そう"-> a"
では"b:"
ないことに注意してください。結局、私にfix
はもっと簡単に思えました。再帰メソッドの名前を次のように変更しました。
fixParser :: (a -> A.Parser a) -> a -> A.Parser a
fixParser parser a = (parser a >>= fixParser parser) <|> pure a
ありがとう。