私は最近FParsecを使用していますが、汎用パーサーの欠如が私にとって大きな停止点であることがわかりました。この小さなライブラリの私の目標は、単純さと一般的な入力のサポートです。これを改善する追加、または特に悪いものを思いつくことができますか?
LazyListを開く
タイプState<'a、' b>(input:LazyList <'a>、data:' b)=
メンバーthis.Input=input
メンバーthis.Data=data
タイプResult<'a、' b、'c> =
| 'c * State <'a、'b>の成功
| 文字列の失敗*State<'a、' b>
タイプParser<'a、' b、'c> = State <'a、'b>-> Result <'a、'b、' c>
(>> =)左右の状態=
左の状態と一致する
| 成功(結果、状態)->(正しい結果)状態
| 失敗(メッセージ、_)->結果<'a、' b、'd>。失敗(メッセージ、状態)
(<|>)左右の状態=
左の状態と一致する
| 結果としての成功(_、_)->結果
| 失敗(_、_)->正しい状態
let(| >>)パーサー変換状態=
パーサーの状態を
| 成功(結果、状態)->成功(変換結果、状態)
| 失敗(メッセージ、_)->失敗(メッセージ、状態)
let(<?>)パーサーerrorMessage状態=
パーサーの状態を
| 結果としての成功(_、_)->結果
| 失敗(_、_)->失敗(errorMessage、state)
タイプParseMonad()=
メンバーthis.Bind(f、g)= f >> = g
メンバーthis.Returnxs= Success(x、s)
メンバーthis.Zero()s = Failure( ""、s)
メンバーthis.Delay(f:unit-> Parser <_、_、_>)= f()
解析する=ParseMonad()
バックトラック
驚いたことに、あなたが説明したことを実装するのにそれほど多くのコードは必要ありませんでした。それは少しずさんですが、かなりうまくいくようです。
let (>>=) left right state =
seq {
for res in left state do
match res with
| Success(v, s) ->
let v =
right v s
|> List.tryFind (
fun res ->
match res with
| Success (_, _) -> true
| _ -> false
)
match v with
| Some v -> yield v
| None -> ()
} |> Seq.toList
let (<|>) left right state =
left state @ right state
バックトラックパート2
レイジーリストと末尾呼び出しに最適化された再帰を使用するようにコードを切り替えました。
let (>>=) left right state =
let rec readRight lst =
match lst with
| Cons (x, xs) ->
match x with
| Success (r, s) as q -> LazyList.ofList [q]
| Failure (m, s) -> readRight xs
| Nil -> LazyList.empty<Result<'a, 'b, 'd>>
let rec readLeft lst =
match lst with
| Cons (x, xs) ->
match x with
| Success (r, s) ->
match readRight (right r s) with
| Cons (x, xs) ->
match x with
| Success (r, s) as q -> LazyList.ofList [q]
| Failure (m, s) -> readRight xs
| Nil -> readLeft xs
| Failure (m, s) -> readLeft xs
| Nil -> LazyList.empty<Result<'a, 'b, 'd>>
readLeft (left state)
let (<|>) (left:Parser<'a, 'b, 'c>) (right:Parser<'a, 'b, 'c>) state =
LazyList.delayed (fun () -> left state)
|> LazyList.append
<| LazyList.delayed (fun () -> right state)