packages feed

calculator-0.2.0.0: src/Calculator/Parser/Expr.hs

module Calculator.Parser.Expr (parseExpr) where

--------------------------------------------------------------------------------

import           Calculator.Parser.Base        (parseId, parseNumber)
import           Calculator.Prim.Definitions   (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
  if null rest
    then return term
    else return $ 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
  if null rest
    then return fact
    else return $ 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
  if constEq (Constant 1) (snd pow)
    then return val
    else return $ 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)

--------------------------------------------------------------------------------
-- 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

--------------------------------------------------------------------------------