【问题标题】:F# FParsec parsing multiplicationF# FParsec 解析乘法
【发布时间】:2019-03-07 07:45:15
【问题描述】:

我正在尝试解决编程中最可怕的部分,那就是解析和 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 = ()

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

【问题讨论】:

    标签: parsing f# fparsec


    【解决方案1】:

    像 FParsec 这样的解析器组合子并不等同于 BNF 文法。最大的区别在于,当您有替代方案时(FParsec 中的&lt;|&gt;),这些案例将按顺序进行尝试。如果左解析器成功,则返回它并且不尝试右解析器。如果左侧解析器在消耗一些输入后失败,则返回失败并且也不尝试右侧解析器。只有当左解析器失败而没有消耗任何输入时,才会尝试右解析器。 [1]

    在您的pNumber &lt;|&gt; pMultiply 中,pNumber 成功并立即返回,而无需尝试执行pMultiply。你可能会想改写pMultiply &lt;|&gt; pNumber 来解决这个问题,但这也不好:在解析最后一个数字时,pMultiply 在消耗了* 的一些输入后将无法找到*,所以整个解析将被标记为失败。

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

    let pNumber = pfloat .>> spaces |>> Float
    let pTimes = pstring "*" .>> spaces >>% (fun x y -> Multiply (x, y))
    let pMultiply = chainl1 pNumber pTimes
    

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

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

    【讨论】:

    • 感谢您的详细解答!
    猜你喜欢
    • 2015-01-07
    • 2015-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-30
    • 1970-01-01
    • 2015-07-22
    相关资源
    最近更新 更多