{----------------------------------------------------------------- (c) 2008-2009 Markus Dittrich This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License Version 3 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 3 for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. --------------------------------------------------------------------} -- | main archy driver module CalculatorParser ( calculator_parser ) where -- imports import qualified Data.Map as M import Control.Monad -- local imports import CalculatorState import ExtraFunctions import Prelude import TokenParser -- | grammar description for calculator parser calculator_parser :: CharParser CalcState (Double, String) calculator_parser = try ( define_variable >>= \x -> return (x,"") ) <|> (add_term >>= \x -> end_of_line >> return (x,"") ) "math expression, variable definition, " ++ "variable name" -- | if the line starts off with a string we either -- have a variable definition or want to show the value -- stored in a variable define_variable :: CharParser CalcState Double define_variable = (whiteSpace >> variable >>= \varName -> variable_def varName ) "variable definition" -- | check that we are at the end of the line; otherwise -- parsing failed since we always expect to parse the -- full expression end_of_line :: CharParser CalcState () end_of_line = getInput >>= \input -> case length input of 0 -> return () _ -> pzero -- | define a variable variable_def :: String -> CharParser CalcState Double variable_def varName = ( whiteSpace >> reservedOp "=" >> whiteSpace >> ( variable_def_by_value varName <|> variable_def_by_var varName) ) "variable" -- | define a variable via a literal double variable_def_by_value :: String -> CharParser CalcState Double variable_def_by_value varName = ( add_term >>= \value -> updateState (insert_variable value varName) >> return value ) "variable from value" -- | define a variable via the value of another variable variable_def_by_var :: String -> CharParser CalcState Double variable_def_by_var varName = parse_variable >>= \value -> updateState (insert_variable value varName) >> return value -- | look for the value of a given variable if any parse_variable :: CharParser CalcState Double parse_variable = ( variable >>= \val -> whiteSpace >> get_variable_value val >>= \result -> case result of Just a -> return a Nothing -> pzero ) "variable" -- | parser for expressions chained via "+" or "-" add_term :: CharParser CalcState Double add_term = mul_term `chainl1` add_action -- | parser for expressions chained via "*" or "/" mul_term :: CharParser CalcState Double mul_term = exp_term `chainl1` multiply_action -- | parser for potentiation operations "^" exp_term :: CharParser CalcState Double exp_term = (whiteSpace >> factor) `chainl1` exp_action -- | parser for individual factors, i.e, numbers, -- variables or operations factor :: CharParser CalcState Double factor = try signed_parenthesis <|> parse_keywords <|> parse_number <|> parse_variable "token or variable" -- | parse a potentially signed expression enclosed in parenthesis. -- In the case of parenzised expressions we parse -() as (-1.0)*() signed_parenthesis :: CharParser CalcState Double signed_parenthesis = parse_sign >>= \sign -> parens add_term >>= \result -> return (sign * result) -- | parse all operations we currently know about parse_keywords :: CharParser CalcState Double parse_keywords = msum $ extract_ops builtinFunctions where extract_ops = foldr (\(x,y) acc -> ((reserved x >> execute y):acc)) [] -- | execute the requested operator on the term enclosed -- in parentheses execute :: OperatorAction -> CharParser CalcState Double execute op = parens add_term >>= return . op multiply_action :: CharParser CalcState (Double -> Double -> Double) multiply_action = (reservedOp "*" >> return (*)) <|> (reservedOp "/" >> return (/)) add_action :: CharParser CalcState (Double -> Double -> Double) add_action = (reservedOp "+" >> return (+)) <|> (reservedOp "-" >> return (-)) exp_action :: CharParser CalcState (Double -> Double -> Double) exp_action = reservedOp "^" >> return real_exp parse_number :: CharParser CalcState Double parse_number = parse_sign >>= \sign -> naturalOrFloat >>= \num -> notFollowedBy alphaNum >> case num of Left i -> return $ sign * (fromInteger i) Right x -> return (sign * x) parse_sign :: CharParser CalcState Double parse_sign = option 1.0 ( whiteSpace >> char '-' >> return (-1.0) ) -- | function retrieving a variable from the database if -- present get_variable_value :: String -> CharParser CalcState (Maybe Double) get_variable_value name = getState >>= \(CalcState { varMap = myMap }) -> return $ M.lookup name myMap