Parsing F#FParsec解析乘法

Parsing F#FParsec解析乘法,parsing,f#,fparsec,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, _, _) ->

我正在努力解决编程中最可怕的部分,那就是解析和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"

我希望得到一组嵌套的乘法。我觉得我遗漏了一些非常明显的东西。

像FParsec这样的解析器组合器并不等同于BNF语法。最大的区别在于,当您有一个备选方案时(在FParsec中为
),案例将按顺序进行审理。如果左解析器成功,则返回该解析器,而不尝试右解析器。如果左解析器在使用某些输入后失败,则返回失败,右解析器也不会尝试。只有当左解析器失败而不使用任何输入时,才尝试右解析器。[1]

在您的
pNumber pMultiply
中,
pNumber
成功并立即返回,无需尝试执行
pmMultiply
。您可能会想通过编写
pMultiply pNumber
来解决这个问题,但这也不好:当解析最后一个数字时,
pMultiply
在为其
parseExpr
消耗了一些输入后将无法找到
*
,因此整个解析将被标记为失败

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

如果您的目标是学习如何使用BNF语法,那么您可能希望了解FParsec而不是FParsec


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

感谢您提供的详细答案!
> test parseExpr "2.0 * 3.0 * 4.0 * 5.0";;
Success: Float 2.0
val it : unit = ()
let pNumber = pfloat .>> spaces |>> Float
let pTimes = pstring "*" .>> spaces >>% (fun x y -> Multiply (x, y))
let pMultiply = chainl1 pNumber pTimes