calculator-0.2.0.1: src/Calculator/Parser/Expr.hs
module Calculator.Parser.Expr (parseExpr) where
--------------------------------------------------------------------------------
import Calculator.Parser.Base (parseId, parseNumber)
import Calculator.Prim.Definitions (unaryOps, binaryOps)
import Calculator.Prim.Expr (Expr (..), Operator, constEq)
--------------------------------------------------------------------------------
import Control.Applicative ((<$>), (<*))
import Text.ParserCombinators.Parsec
--------------------------------------------------------------------------------
-- expr -> term ( "+-" term )*
parseExpr :: Parser Expr
parseExpr = do
term <- parseTerm
rest <- parseRestExpr
return $ if null rest
then term
else BinOp (term, rest)
parseRestExpr :: Parser [(Operator, Expr)]
parseRestExpr = many $ do
oper <- oneOf "+-"
let (Just op) = lookup oper binaryOps
expr <- parseTerm
return (op, expr)
--------------------------------------------------------------------------------
-- term -> fact ( "*/" fact )*
parseTerm :: Parser Expr
parseTerm = do
fact <- parseFact
rest <- parseRestTerm
return $ if null rest
then fact
else BinOp (fact, rest)
parseRestTerm :: Parser [(Operator, Expr)]
parseRestTerm = many $ do
oper <- oneOf "*/"
let (Just op) = lookup oper binaryOps
expr <- parseFact
return (op, expr)
--------------------------------------------------------------------------------
-- fact -> val ( "^" fact )?
-- Right recursion for right associativity
parseFact :: Parser Expr
parseFact = do
val <- parseVal
pow <- parsePower
return $ if constEq (Constant 1) (snd pow)
then val
else BinOp (val, [pow])
parsePower :: Parser (Operator, Expr)
parsePower = let (Just op) = lookup '^' binaryOps
in option (op, Constant 1) $ do
_ <- char '^'
fact <- parseFact
return (op, fact)
--------------------------------------------------------------------------------
-- Unary operators
-- val -> $? val'
parseVal :: Parser Expr
parseVal = do
ch <- optionMaybe $ oneOf (map fst unaryOps)
v <- parseVal'
return $ if ch == Nothing
then v
else let Just c = ch
Just op = lookup c unaryOps
in UnOp op v
--------------------------------------------------------------------------------
-- val' -> ( expr ) | func ( expr ) | var | number
parseVal' :: Parser Expr
parseVal' = parseBrackets <|> parseCall <|> parseVariable <|> parseConstant
parseBrackets :: Parser Expr
parseBrackets = do
_ <- try (char '(')
e <- parseExpr
_ <- char ')'
return e
parseVariable :: Parser Expr
parseVariable = Variable <$> parseId
parseConstant :: Parser Expr
parseConstant = Constant <$> parseNumber
parseCall :: Parser Expr
parseCall = do
ident <- try (parseId <* char '(')
args <- parseExpr `sepBy` char ',' <* char ')'
return $ Call ident args
--------------------------------------------------------------------------------