I am trying to extend a recursive-descent parser to handle new operators and make them associate correctly. Originally there were only four operators (+ - / *) and they all had the same precedence. The function I am looking at is the parseExpRec function:
parseExpRec :: Exp -> [Token] -> (Exp, [Token])
parseExpRec e [] = (e, [])
parseExpRec e1 (op : ts) =
let (e2, ts') = parsePrimExp ts in
case op of
T_Power -> parseExpRec (BinOpApp Power e1 e2) ts'
T_Plus -> parseExpRec (BinOpApp Plus e1 e2) ts'
T_Minus -> parseExpRec (BinOpApp Minus e1 e2) ts'
T_Times -> parseExpRec (BinOpApp Times e1 e2) ts'
T_Divide -> parseExpRec (BinOpApp Divide e1 e2) ts'
T_GreaterThan -> parseExpRec (BinOpApp GreaterThan e1 e2) ts'
T_LessThan -> parseExpRec (BinOpApp LessThan e1 e2) ts'
T_GreaterOrEqual -> parseExpRec (BinOpApp GreaterOrEqual e1 e2) ts'
T_LessOrEqual -> parseExpRec (BinOpApp LessOrEqual e1 e2) ts'
T_EqualTo -> parseExpRec (BinOpApp EqualTo e1 e2) ts'
_ -> (e1, op : ts)
All of the pattern matching lines except T_Plus, T_Minus, T_Times and T_Divide have been added by me (and so have the associated tokens and extensions to the Exp datatype). However, none of them seem to associate correctly. For example, the string "3^4 + 2^3" evaluates to:
BinOpApp Power (BinOpApp Plus (BinOpApp Power (LitInt 3) (LitInt 4)) (LitInt 2)) (LitInt 3)
Which is equivalent to this in infix notation (with brackets included):
((3^4)+2)^3
How would I fix this?
Operator precedence parsers use precedence functions that map terminal symbols to integers, and the precedence relations between the symbols are implemented by numerical comparison. The parsing table can be encoded by two precedence functions f and g that map terminal symbols to integers.
Precedence is the priority for grouping different types of operators with their operands. Associativity is the left-to-right or right-to-left order for grouping operands to operators that have the same precedence.
Haskell also has a binary operator called $ . f $ x does function application just like f x , but it is right-associative, which means you can write f $ g $ h $ x to mean f(g(h(x))) .
We use associativity when two or more than two operators with the same precedence are present in the same expression. Example, The precedence of Division and Multiplication arithmetic operators is the same. So, let's say we have an expression with us which is 6 * 3 / 20.
I've written a paper on unparsing expressions using operator precedence. The appendix to the paper has an operator-precedence parser written in ML, which you could easily adapt to Haskell. The code is downloadable from the page above.
While Haskell has many fine libraries of parsing combinators, I have never seen one that was both (a) simple enough for me to understand easily and (b) supported operator-precedence parsing with arbitrary levels of precedence.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With