{----------------------------------------------------------------- (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 ) where -- imports import qualified Data.Map as M import Control.Monad -- local imports import CalculatorState import ExtraFunctions import TokenParser -- | main parser entry point calculator :: CharParser CalcState (Maybe Double, CalcState) calculator = parse_calc >>= \val -> getState >>= \state -> return (val, state) -- | grammar description for parser -- NOTE: We have to make sure to provide a default -- catch all parsing rule in case the user -- enters something we don't understand. Otherwise -- parsing fails and we loose our state. parse_calc :: CharParser CalcState (Maybe Double) parse_calc = try define_variable <|> try (add_term >>= \x -> end_of_line >> return (Just x)) <|> return Nothing "math expression, variable definition " ++ "or 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 (Maybe Double) define_variable = (spaces >> 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 (Maybe Double) variable_def varName = ( spaces >> reservedOp "=" >> spaces >> ( 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 (Maybe Double) variable_def_by_value varName = ( add_term >>= \value -> updateState (insert_variable value varName) >> return (Just value) ) "variable from value" -- | define a variable via the value of another variable variable_def_by_var :: String -> CharParser CalcState (Maybe Double) variable_def_by_var varName = parse_variable >>= \value -> updateState (insert_variable value varName) >> return (Just value) -- | look for the value of a given variable if any parse_variable :: CharParser CalcState Double parse_variable = ( variable >>= \val -> spaces >> 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 = (spaces >> factor) `chainl1` exp_action -- | parser for individual factors, i.e, numbers, -- variables or operations factor :: CharParser CalcState Double factor = parens add_term <|> parse_keywords <|> parse_number <|> parse_variable "token or variable" -- | parse all operations we currently know about parse_keywords :: CharParser CalcState Double parse_keywords = msum $ extract_ops keywords 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 = naturalOrFloat >>= \num -> notFollowedBy alphaNum >> case num of Left i -> return $ fromInteger i Right x -> return x -- | 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