3

I am trying to achieve below with fparsec and unions (1 + (2 * 3)) //DSL sample input(recursive)

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

in fparsec I have createParserForwardedToRef for Add and Mul as

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

but fparsec doc says for alternatives if parser p1 consumes input and fails it will not try p2.

in my case both Add and Mul has same pattern before operator, so only p1 is working. how can I refactor it so I can parse my input? in fparsec doc solution example, it worked as it was just parsing and not constructing Discriminated union instance. in my case I have to know which pattern matched so that I can create either Add or Mul

Tarmil
  • 11,177
  • 30
  • 35
abhishek
  • 373
  • 1
  • 9
  • I can use prefix operator notion to solve it. but for readability of my DSL input I want infix operator notion. for simple operator prefix notion like +(1,2) works. but as I evolve my grammar with complex operator like string {in} list Or number {between} . Prefix will have less readability. – abhishek Jan 09 '17 at 05:18
  • Wrap the alternatives in `attempt`. – Fyodor Soikin Jan 09 '17 at 05:34
  • thanks Fyodor for reply. I am new to f# and fprasec, can you elaborate that or better give pseudo code. with multiple attempt parser, how will I know which one matched so that I can appropriately instance my discriminator type. I tried creating a string parser for my operands like `pstring "+" <|> pstring "*" ` and then used tuple3 to get (lefthand operand, Operator, righthandoperand). and then used if/else on Operator to decide which operator matched and instance my discriminator type. it worked but I think its not correct functional approach. – abhishek Jan 09 '17 at 18:31

1 Answers1

1

Edit: my original comment was just as flawed, as pointed out by @FyodorSoikin.

You are on the right track in your comment from yesterday by making a common parser for the operators and then having a single parser for operations that uses it. To make this more functional, you can have the operator parser return the union case to apply. This way, when parsing the full operation, you can just call it as a function.

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

Original comment:

One possibility is to use attempt as said in the comments, but that function should generally be used as a last resort. A better solution is to factor out the wrapping:

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

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

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

let parseOp = between (pstring "(") (pstring ")") (parseAdd <|> parseMul)

implementation := parseConstant <|> parseOp
Tarmil
  • 11,177
  • 30
  • 35