我正在尝试为我解决编程中最可怕的部分,那就是解析和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 = ()
我希望我得到一组嵌套的乘法。我感觉好像缺少了非常明显的东西。
答案 0 :(得分:6)
像FParsec这样的解析器组合器不等同于BNF语法。最大的不同是,当您有备用选项(在FParsec中为<|>
)时,将按顺序尝试进行案例处理。如果左解析器成功,则将其返回,并且不尝试右解析器。如果左解析器在消耗了一些输入后失败,则返回失败,并且也不会尝试右解析器。仅当左解析器失败而不消耗任何输入时,才尝试右解析器。 [1]
在您的pNumber <|> pMultiply
中,pNumber
成功并立即返回而无需尝试执行pMultiply
。您可能会想通过写pMultiply <|> pNumber
来解决此问题,但这也不是一件好事:解析最后一个数字时,pMultiply
在消耗了{ {1}},因此整个分析将被标记为失败。
您通常希望尽可能多地使用FParsec的组合器函数,在这种情况下,最好的解决方案可能是使用chainl1
。
*
如果您的目标是学习如何使用BNF语法,那么您可能想看看FsLex and FsYacc而不是FParsec。
[1]有一个函数attempt
可以将消耗性故障转变为非消耗性故障,但应尽量少用。