我正在编写一个解析器(用于学习 pourpuses)。
我希望它解析像
let myVar be 40 plus 2
和
let myVar be (40 plus 2)
没有问题......但我的解析器不“理解”前者。它看到40
并认为“嗯,它是一个Literal Numeric 40
”。
当我放括号时,我的解析器工作得很好。
我很难理解为什么。
解析器:
type value =
| Boolean of bool
| Numeric of float
| String of string
type arithmetic = Sum | Sub | Mul | Div | Pow
type logic = And | Or | Equal | NotEqual | Greater | Smaller
type identifier =
| Identifier of string
type expression =
| Literal of value
| Arithmetic of expression * arithmetic * expression
| Negative of expression
| Negation of expression
| Logic of expression * logic * expression
| Variable of identifier
type statement =
| Assignment of identifier * expression
| Print of expression
| Read of identifier
let private ws = spaces
let private str s = pstring s .>> ws
let private pnumeric =
pfloat
.>> ws
|>> fun n -> Literal (Numeric n)
let private pboolean =
choice [
(stringReturn "true" (Literal (Boolean true)))
(stringReturn "false" (Literal (Boolean false)))
]
.>> ws
let private pstringliteral =
choice [
between (pstring "\"") (pstring "\"") (manyChars (satisfy (fun c -> c <> '"')))
between (pstring "'") (pstring "'") (manyChars (satisfy (fun c -> c <> ''')))
]
|>> fun s -> Literal (String s)
let private pidentifier =
many1Satisfy2L isLetter (fun c -> isLetter c || isDigit c) "identifier"
|>> fun s -> Identifier s
let private betweenParentheses p =
between (str "(") (str ")") p
let private pvalue =
choice [
pnumeric
pboolean
]
let private prefixOperator (p: OperatorPrecedenceParser<_,_,_>) op prec map =
p.AddOperator(PrefixOperator (op, ws, prec, true, map))
let private infixOperator (p: OperatorPrecedenceParser<_,_,_>) op prec map =
p.AddOperator(InfixOperator (op, ws, prec, Associativity.Left, map))
let private oppNegation = new OperatorPrecedenceParser<_,_,_>()
let private oppLogic = new OperatorPrecedenceParser<_,_,_>()
let private oppArithmetic = new OperatorPrecedenceParser<_,_,_>()
let private oppNegative = new OperatorPrecedenceParser<_,_,_>()
prefixOperator oppNegation "not" 1 (fun x -> Negation x)
infixOperator oppLogic "is" 1 (fun x y -> Logic (x, Equal, y))
infixOperator oppLogic "isnt" 1 (fun x y -> Logic (x, NotEqual, y))
infixOperator oppLogic "and" 2 (fun x y -> Logic (x, And, y))
infixOperator oppLogic "or" 3 (fun x y -> Logic (x, Or, y))
prefixOperator oppNegative "-" 1 (fun x -> Negative x)
infixOperator oppArithmetic ">" 1 (fun x y -> Logic (x, Greater, y))
infixOperator oppArithmetic "<" 1 (fun x y -> Logic (x, Smaller, y))
infixOperator oppArithmetic "is" 2 (fun x y -> Logic (x, Equal, y))
infixOperator oppArithmetic "isnt" 2 (fun x y -> Logic (x, NotEqual, y))
infixOperator oppArithmetic "plus" 3 (fun x y -> Arithmetic (x, Sum, y))
infixOperator oppArithmetic "minus" 3 (fun x y -> Arithmetic (x, Sub, y))
infixOperator oppArithmetic "times" 4 (fun x y -> Arithmetic (x, Mul, y))
infixOperator oppArithmetic "divided by" 4 (fun x y -> Arithmetic (x, Div, y))
infixOperator oppArithmetic "power" 5 (fun x y -> Arithmetic (x, Pow, y))
let private negationExprParser = oppNegation.ExpressionParser
let private logicExprParser = oppLogic.ExpressionParser
let private arithmeticExprParser = oppArithmetic.ExpressionParser
let private negativeExprParser = oppNegative.ExpressionParser
oppNegation.TermParser <- choice [
betweenParentheses negationExprParser
pboolean
]
oppLogic.TermParser <- choice [
betweenParentheses logicExprParser
pboolean
]
oppNegative.TermParser <- choice [
betweenParentheses negativeExprParser
pnumeric
]
oppArithmetic.TermParser <- choice [
betweenParentheses arithmeticExprParser
pnumeric
]
let private pexpression =
choice [
attempt <| pstringliteral
attempt <| negationExprParser
attempt <| logicExprParser
attempt <| negativeExprParser
attempt <| arithmeticExprParser
attempt <| (pidentifier |>> fun id -> Variable id)
]
let private passignment =
pipe2 (str "let" .>> ws >>. pidentifier) (ws >>. str "be" >>. ws >>. pexpression) (fun id exp -> Assignment (id, exp))
let private pprint =
str "print"
>>. pexpression
|>> fun exp -> Print exp
let private pread =
str "read"
>>. pidentifier
|>> fun id -> Read id
let private pstatement =
choice [
passignment
pprint
pread
]
let private pline =
skipMany (satisfy (fun c -> c = '\n' || c = ' '))
>>. pstatement
.>> ws
let private pcode =
many pline
let generateAST code =
match run pcode code with
| Success (ast, _, _) -> sprintf "%A" ast
| Failure (msg, _, _) -> msg
用法:
[<EntryPoint>]
let main argv =
printfn "%s\n" (generateAST "let b be 5 plus 7")
// [Assignment (Identifier "b",Literal (Numeric 5.0))]
printfn "%s\n" (generateAST "let b be (5 plus 7)")
// [Assignment
// (Identifier "b",Arithmetic (Literal (Numeric 5.0),Sum,Literal (Numeric 7.0)))]
0