私は最近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)