問題
私は自己教育のために非決定論的パーサーを展開しています。次のようになります。
newtype Parser a b = P { runP :: [a] -> [(b, [a])] }
そして、次の形式のサブルーチンをドロップできるようにしたいと考えています : [a] -> [b]
。これは、文字のバッファを取り、それを結果のリストに送信します。ここでの秘訣は、サブルーチン自体がステートフルな計算であり、呼び出しが成功するたびに状態を遷移することです (有限状態マシンと考えてください)。具体的には:
- サブルーチンが空の list を出力する場合
[]
、パーサーはもう 1 つの char をバッファーに挿入し、それをサブルーチンに渡します。サブルーチンは再び実行されます。 - サブルーチンが空でない list を出力する場合
[b]
、バッファは最初にフラッシュされ、パーサーはもう 1 文字をバッファに挿入し、それをサブルーチンに渡します。は[b]
どこかに保管されている - エスケープ条件に達するまで、ステップ 1 と 2 を繰り返し実行します。すべての中間結果は何らかの方法で組み合わせる必要があります。
エスケープ条件に達すると、サブルーチンは結果
bs
をパーサーに戻し、残りのストリームと結合しますas
。rs = fmap (flip (,) as) bs :: [(b,[a])]
したがって、署名を満たすrunP
関数には次の署名がある場合があります。withf :: ([a] -> [b]) -> Parser a b
重要なことは、withf g
がパーサーでなければならないということ<*>
です。関数シグネチャの提案g
は純粋な関数であるため、正しい可能性は低いことに注意してください。
試した解決策
私はさまざまなコルーチン パッケージを使用してこれを実装しようとしましたが、私にとっては、パーサーをコルーチン計算コンテキストに変換する方が理にかなってlift
おり、これもコンテキストに持ち上げられるトランスデューサーで構成されます。つまり、パーサーではなくなります。
withf
また、パーサー値コンストラクターにアクセスできるプリミティブ関数として実装しようとしました。基本的に手順 1..4 をコードに変換します。ここで私が抱えている最大の問題は、誰がどの情報を担当しているかということです。
- バッファの状態
- 中間結果の状態
- 中間結果を組み合わせる方法のロジック。
- エスケープ条件をどのように実装するか、またはより良い方法で構成する必要があります
withf
また、パーサーに焼き付けられたさまざまな自作のコルーチン実装も試しましたが (Parser
上記で定義した型は使用しません)、ほとんど成功しませんでした。
私を正しい方向に向けることができる人は誰でも大歓迎です。