F#FParsec解析乘法

时间:2019-03-07 07:45:16

标签: parsing f# fparsec

我正在尝试为我解决编程中最可怕的部分,那就是解析和AST。我正在使用F#和FParsec编写一个简单的示例。我想解析一系列简单的乘法。不过,我只有第一学期回来。这是我到目前为止的内容:

open FParsec

let test p str =
    match run p str with
    | Success(result, _, _) -> printfn  "Success: %A" result
    | Failure(errorMsg, _, _) -> printfn "Failure: %s" errorMsg

type Expr =
| Float of float
| Multiply of Expr * Expr

let parseExpr, impl = createParserForwardedToRef ()

let pNumber = pfloat .>> spaces |>> (Float)
let pMultiply = parseExpr .>> pstring "*" >>. parseExpr
impl := pNumber <|> pMultiply

test parseExpr "2.0 * 3.0 * 4.0 * 5.0"

运行此命令时,我得到以下信息:

> test parseExpr "2.0 * 3.0 * 4.0 * 5.0";;
Success: Float 2.0
val it : unit = ()

我希望我得到一组嵌套的乘法。我感觉好像缺少了非常明显的东西。

1 个答案:

答案 0 :(得分:6)

像FParsec这样的解析器组合器不等同于BNF语法。最大的不同是,当您有备用选项(在FParsec中为<|>)时,将按顺序尝试进行案例处理。如果左解析器成功,则将其返回,并且不尝试右解析器。如果左解析器在消耗了一些输入后失败,则返回失败,并且也不会尝试右解析器。仅当左解析器失败而不消耗任何输入时,才尝试右解析器。 [1]

在您的pNumber <|> pMultiply中,pNumber成功并立即返回而无需尝试执行pMultiply。您可能会想通过写pMultiply <|> pNumber来解决此问题,但这也不是一件好事:解析最后一个数字时,pMultiply在消耗了{ {1}},因此整个分析将被标记为失败。

您通常希望尽可能多地使用FParsec的组合器函数,在这种情况下,最好的解决方案可能是使用chainl1

*

如果您的目标是学习如何使用BNF语法,那么您可能想看看FsLex and FsYacc而不是FParsec。

[1]有一个函数attempt可以将消耗性故障转变为非消耗性故障,但应尽量少用。