我最近一直在使用 FParsec,我发现缺乏通用解析器对我来说是一个主要的停止点。我对这个小库的目标是简单性以及对通用输入的支持。你能想到任何可以改善这一点或有什么特别糟糕的补充吗?
打开懒惰列表 type State<'a, 'b> (input:LazyList<'a>, data:'b) = 成员 this.Input = 输入 成员 this.Data = 数据 输入结果<'a, 'b, 'c> = | 'c * State<'a, 'b> 成功 | 字符串失败 * State<'a, 'b> type Parser<'a,'b, 'c> = State<'a, 'b> -> 结果<'a, 'b, 'c> 让 (>>=) 左右状态 = 将左状态与 | Success (result, state) -> (right result) state | 失败(消息,_)-> Result<'a,'b,'d>.Failure(消息,状态) 让 (<|>) 左右状态 = 将左状态与 | 成功 (_, _) 作为结果 -> 结果 | 失败 (_, _) -> 正确状态 让 (|>>) 解析器转换状态 = 匹配解析器状态 | Success(结果,状态)-> Success(转换结果,状态) | 失败(消息,_)-> 失败(消息,状态) 让 (<?>) 解析器错误消息状态 = 匹配解析器状态 | 成功 (_, _) 作为结果 -> 结果 | 失败 (_, _) -> 失败 (errorMessage, state) 类型 ParseMonad() = 成员 this.Bind (f, g) = f >>= g 成员 this.Return xs = 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)