上一篇我们介绍了F#FParsec入门,其中给出了具体的示例,来说明如何解析浮点类型的值,解析括号中的值等,如果感兴趣的可以回到这篇博客进行阅读。本次将用讲解F#解析器库FParsec对数学公式的解析,并结构化输出构成AST。
首先,我们定义一个数学公式这个领域的自定义类型:
typeExpr=|CstFoffloat|Varofstring|AddofExpr*Expr// +|SubofExpr*Expr// -|MulofExpr*Expr// *|DivofExpr*Expr// / |PowofExpr*Expr// ^ |SinofExpr|CosofExpr|ExpofExpr|LnofExpr
其中的Expr是定义的数学表达式类型,其中由多个元素构成,比如 CstF of float表示浮点类型的数值,Var of string则表示文本类型的变量,Add of Expr * Expr表示数学的加法操作,其中左边可以是一个Expr类型,右边也可以是一个Expr类型。此程序属于模块:
moduleYd.ExpParser.Ast
如果要使用FParsec,则需要引入此库的命名空间:
openFParsec
然后,需要用内置的浮点类型解析器,文本类型的解析器等构建一些工具方法:
//忽略空白字符letws=CharParsers.spaces//忽略特定字符并忽略末尾的空白字符letchc=CharParsers.skipCharc>>. ws//解析浮点类型的值,忽略末尾的空白字符,并转换成 CstF 类型letnum=CharParsers.pfloat .>>ws|>> (funx->CstFx) //变量标识符规则letidentifier=letisIdentifierFirstCharc=isLetterc||c='_'letisIdentifierCharc=isLetterc||isDigitc||c='_'many1Satisfy2LisIdentifierFirstCharisIdentifierChar"identifier"//忽略空白letidentifierws=spaces>>. identifier .>>spaces//变量解析letid=identifierws|>>(funx->Varx) .>>ws
其次,创建操作符解析器,示例代码如下:
//创建一个新的具有优先级的操作符解析器letopp=newOperatorPrecedenceParser<_,_,_>() //重命名表达式解析器ExpressionParser,方便调用letexpr=opp.ExpressionParser//括号中提取表达式letbra_expr=ch'('>>. expr .>>ch')'// 定义支持的术语terms,即操作符外的类型解析器//id表示变量解析器,num表示浮点类型解析器,bra_expr表示表达式解析器letterms=choice[ id; num; bra_expr] opp.TermParser<-terms
再次,需要定义操作符解析器的优先级类型:
opp.AddOperator(InfixOperator("+", ws,1, Associativity.Left, funxy->Add(x, y))) opp.AddOperator(InfixOperator("-", ws,1, Associativity.Left, funxy->Sub(x, y))) opp.AddOperator(InfixOperator("*", ws,2, Associativity.Left, funxy->Mul(x, y))) opp.AddOperator(InfixOperator("/", ws,2, Associativity.Left, funxy->Div(x, y))) opp.AddOperator(InfixOperator("^", ws,3, Associativity.Left, funxy->Pow(x, y))) opp.AddOperator(PrefixOperator("sin", ws,4, true, funx->Sin(x))) opp.AddOperator(PrefixOperator("cos", ws,4, true, funx->Cos(x))) opp.AddOperator(PrefixOperator("exp", ws,4, true, funx->Exp(x))) opp.AddOperator(PrefixOperator("ln", ws,4, true, funx->Ln(x))) opp.AddOperator(PostfixOperator("!", ws,5, true, funx->Factorial(x)))
其中的Associativity.Left代表左结合性。下面给出最终的解析函数:
//忽略空白letexpr_ws=ws>>. expr .>>ws//调用run方法调用字符解析器letparses=CharParsers.runexpr_wss
最后,给出测试示例:
lettest () =//测试用例printfn"%s => %A""1.0+2.0+a" (parse"1.0+2.0+a") printfn"%s => %A""(x + 1.0) * 2.0" (parse"(x + 1.0) * 2.0") printfn"%s => %A""(x + 2) * y / 3.2" (parse"(x + 2) * y / 3.2") printfn"%s => %A""(x+2)^7" (parse"(x+2)^7") printfn"%s => %A""sin( x + 2) + 3 " (parse"sin( x + 2) + 3 ") printfn"%s => %A""sin( x + 2) + cos(x * 2)" (parse"sin( x + 2) + cos(x * 2)") printfn"%s => %A""2+exp(3*y)" (parse"2+exp(3*y)") printfn"%s => %A""2+ln(7*x)" (parse"2+ln(7*x)") printfn"%s => %A""(7*x)!+2" (parse"(7*x)!+2")
运行后,结果如下:
1.0+2.0+a=>Success: Add (Add (CstF1.0, CstF2.0), Var"a") (x+1.0) *2.0=>Success: Mul (Add (Var"x", CstF1.0), CstF2.0) (x+2) *y/3.2=>Success: Div (Mul (Add (Var"x", CstF2.0), Var"y"), CstF3.2) (x+2)^7=>Success: Pow (Add (Var"x", CstF2.0), CstF7.0) sin( x+2) +3=>Success: Add (Sin (Add (Var"x", CstF2.0)), CstF3.0) sin( x+2) +cos(x*2) =>Success: Add (Sin (Add (Var"x", CstF2.0)), Cos (Mul (Var"x", CstF2.0))) 2+exp(3*y) =>Success: Add (CstF2.0, Exp (Mul (CstF3.0, Var"y"))) 2+ln(7*x) =>Success: Add (CstF2.0, Ln (Mul (CstF7.0, Var"x"))) (7*x)!+2=>Success: Add (Factorial (Mul (CstF7.0, Var"x")), CstF2.0)