fparsec解析DSL的区别联合的替代方案

时间:2017-01-09 05:05:34

标签: f# f#-3.0 fparsec

我正在努力通过fparsec和联盟实现以下目标     (1 +(2 * 3))// DSL样本输入(递归)

type AirthmeticExpression =         
    | Constant of float
    | AddNumber of AirthmeticExpression * AirthmeticExpression
    | Mul of AirthmeticExpression * AirthmeticExpression

在fparsec中我为Add和Mul创建了createParserForwardedToRef

let parseExpression, implementation = createParserForwardedToRef<AirthmeticExpression, unit>();;

let parseAdd = between pstring"(" pstring ")" (tuple2 (parseExpression .>> pstring " + ")  parseExpression) |>> AddNumber

let parseMul = between pstring"(" pstring ")" (tuple2 (parseExpression .>> pstring " * ")  parseExpression) |>> Mul

implementation := parseConstant <|> parseAdd <|> parseMull

但是fparsec doc说如果解析器p1消耗输入并且失败则不会尝试p2。

在我的情况下,Add和Mul在操作符之前具有相同的模式,因此只有p1正在工作。我怎么能重构它所以我可以解析我的输入?在fparsec doc解决方案示例中,它起作用,因为它只是解析而不是构造被区分的联合实例。在我的情况下,我必须知道哪个模式匹配,以便我可以创建添加或Mul

1 个答案:

答案 0 :(得分:1)

编辑:正如@FyodorSoikin指出的那样,我的原始评论同样有缺陷。

你在昨天的评论中走的正确,通过为运算符创建一个公共解析器,然后为使用它的操作提供单个解析器。为了使其更具功能性,您可以让运算符解析器返回要应用的union case。这样,在解析完整操作时,您可以将其称为函数。

count

原始评论:

一种可能性是在评论中使用count.ToString(),但该功能通常应作为最后的手段使用。更好的解决方案是分解包装:

let parseExpression, implementation = createParserForwardedToRef<AirthmeticExpression, unit>();;

let parseOperator = // : Parser<AirthmeticExpression * AirthmeticExpression -> AirthmeticExpression>
        (pstring " + " |>> AddNumber)
    <|> (pstring " * " |>> Mul)

let parseOperation =
    pipe3 parseConstant parseOperator parseConstant
        (fun x op y -> op (x, y)) // Here, op is either AddNumber or Mul
    |> between (pstring "(") (pstring ")") 

implementation := parseConstant <|> parseOperation