zyzhu
zyzhu

Reputation: 266

How to parse prefix function such as Pow() with multiple parameters using FParsec

I tried to parse a prefix function such as Pow(3+2,2) using FParsec. I read the calculator tutorial in the example files as follows. The examples are all unary prefix function. I wonder how can I achieve prefix functions with more than one inputs using FParsec.OperatorPrecedenceParser.

http://www.quanttec.com/fparsec/reference/operatorprecedenceparser.html#members.PrefixOperator

let number = pfloat .>> ws

let opp = new OperatorPrecedenceParser<float,unit,unit>()
let expr = opp.ExpressionParser
opp.TermParser <- number <|> between (str_ws "(") (str_ws ")") expr


opp.AddOperator(InfixOperator("+", ws, 1, Associativity.Left, (+)))
opp.AddOperator(InfixOperator("-", ws, 1, Associativity.Left, (-)))
opp.AddOperator(InfixOperator("*", ws, 2, Associativity.Left, (*)))
opp.AddOperator(InfixOperator("/", ws, 2, Associativity.Left, (/)))
opp.AddOperator(InfixOperator("^", ws, 3, Associativity.Right, fun x y -> System.Math.Pow(x, y)))
opp.AddOperator(PrefixOperator("-", ws, 4, true, fun x -> -x))

let ws1 = nextCharSatisfiesNot isLetter >>. ws
opp.AddOperator(PrefixOperator("log", ws1, 4, true, System.Math.Log))
opp.AddOperator(PrefixOperator("exp", ws1, 4, true, System.Math.Exp))

Update 1

I've written a quick script following after-string parser example as I need after-string parser for the actual application http://www.quanttec.com/fparsec/users-guide/tips-and-tricks.html#parsing-f-infix-operators

abs(pow(1,2)) can be parsed but pow(abs(1),2) cannot be done. I'm puzzled about how to use prefix function as part of the input for identWithArgs.

#I @"..\packages\FParsec.1.0.2\lib\net40-client"
#r "FParsecCS.dll"
#r "FParsec.dll"

open FParsec

type PrefixFunc = POW
type Expr =
  | InfixOpExpr of string * Expr * Expr
  | PrefixOpExpr of string * Expr
  | PrefixFuncExpr of PrefixFunc * Expr list 
  | Number of int

let ws = spaces
let ws1 = spaces1
let str s = pstring s
let str_ws s = ws >>. str s .>> ws
let strci s = pstringCI s
let strci_ws s = ws >>. strci s .>> ws
let strciret_ws s x = ws >>. strci s .>> ws >>% x

let isSymbolicOperatorChar = isAnyOf "!%&*+-./<=>@^|~?"
let remainingOpChars_ws = manySatisfy isSymbolicOperatorChar .>> ws

let primitive = pint32 .>> ws |>> Number
let argList = sepBy primitive (str_ws ",")
let argListInParens = between (str_ws "(") (str_ws ")") argList
let prefixFunc = strciret_ws "pow" POW 
let identWithArgs =
    pipe2 prefixFunc argListInParens (fun funcId args -> PrefixFuncExpr(funcId, args))

let opp = new OperatorPrecedenceParser<Expr, string, unit>()
opp.TermParser <-
  primitive <|>
  identWithArgs <|>
  between (pstring "(") (pstring ")") opp.ExpressionParser

// a helper function for adding infix operators to opp
let addSymbolicInfixOperators prefix precedence associativity =
    let op = InfixOperator(prefix, remainingOpChars_ws,
                           precedence, associativity, (),
                           fun remOpChars expr1 expr2 ->
                               InfixOpExpr(prefix + remOpChars, expr1, expr2))
    opp.AddOperator(op)

// the operator definitions:
addSymbolicInfixOperators "*"  10 Associativity.Left
addSymbolicInfixOperators "**" 20 Associativity.Right

opp.AddOperator(PrefixOperator("abs",remainingOpChars_ws,3,true,(),fun remOpChars expr -> PrefixOpExpr("abs", expr)))
opp.AddOperator(PrefixOperator("log",remainingOpChars_ws,3,true,(),fun remOpChars expr -> PrefixOpExpr("log", expr)))

run opp.ExpressionParser "abs(pow(1,2))"
run opp.ExpressionParser "pow(abs(1),2)"

Upvotes: 1

Views: 271

Answers (1)

zyzhu
zyzhu

Reputation: 266

I started to review the problem after one year and finally realized the problem.

I've changed the following code

let argList = sepBy primitive (str_ws ",")

to the following

let opp = new OperatorPrecedenceParser<Expr, string, unit>()
let argList = sepBy opp.ExpressionParser (str_ws ",")

I bring OperatorPrecedenceParser to the beginning of the code. And then I achieve recursively calling opp.ExpressionParser by putting it directly into argList.

I just realized that OperatorPrecedenceParser is very similar to createParserForwardedToRef. It creates a parser first without writing down implementation until later. FParsec has to achieve recursiveness in this way. Similar to its JSON sample parser.

After this change, both abs(pow(1,2)) and pow(abs(1),2) can be parsed. Hope this helps others who ever got this problem.

Upvotes: 1

Related Questions