{-# OPTIONS -fglasgow-exts #-} module Eg.Calculator where import Test.FIT.Fixture import Test.FIT.ColumnFixture import Data.IORef import Data.Char import Data.Dynamic data CalculatorState = CalculatorState { calcVolts :: Float , calcWatts :: Float , calcFlash :: Bool , calcKey :: String , calcRegs :: [Float] } deriving Typeable setCalcVolts s v = s { calcVolts = v } setCalcWatts s v = s { calcVolts = v } setCalcFlash s v = s { calcFlash = v } setCalcKey s v = s { calcKey = v } setCalcRegs s v = s { calcRegs = v } getState :: ColumnFixture -> IO CalculatorState getState cf = getDynamicState (columnFixtureState cf) setState :: ColumnFixture -> CalculatorState -> IO () setState cf v = setDynamicState (columnFixtureState cf) v isNumeric "" = False isNumeric [c] = isDigit c isNumeric (c1:c2:cs) = (isDigit c1) || (c1 == '-' && isDigit c2) set_xx f ret upd val = do s <- getState f setState f (upd s val) return ret get_xx f sel = getState f >>= return . sel get_volts f = get_xx f calcVolts set_key f p = set_xx f p setCalcKey (bodyToText p) get_key f = get_xx f calcKey set_regs f r = set_xx f () setCalcRegs r get_regs f = get_xx f calcRegs set_flash f v = set_xx f () setCalcFlash v get_flash f = get_xx f calcFlash radians d = (d * pi) / 180 binOp f op = do a <- pop_reg f; b <- pop_reg f; push_reg f (op b a) get_reg n f = do regs <- get_regs f return (head (drop (n-1) regs)) check_reg n f p = get_reg n f >>= check f p shift_reg f = do r <- get_reg 1 f push_reg f r push_reg f r = do regs <- get_regs f set_regs f (r:regs) pop_reg f = do regs <- get_regs f case regs of (r:rs) -> do set_regs f rs return r _ -> return 0 ---------------------- Public fixture methods ------------------ -- -- These are the methods that are dynamically invoked. x f p = check_reg 1 f p y f p = check_reg 2 f p z f p = check_reg 3 f p t f p = check_reg 4 f p volts f p = set_xx f p setCalcVolts (read (bodyToText p)) -- We'll check watts here, event thought they aren't really used. watts f p = get_xx f calcWatts >>= check f p points f p = get_volts f >>= \v -> check f p (v < 3.4) flash f p = get_flash f >>= check f p key f p = do set_flash f False let k = bodyToText p set_key f p if isNumeric k then push_reg f (read k) else do case k of "enter" -> shift_reg f "clx" -> pop_reg f >> push_reg f 0 "clr" -> set_regs f [0,0,0,0] "chs" -> do r <- pop_reg f; push_reg f (-r) "+" -> binOp f (+) "*" -> binOp f (*) "-" -> binOp f (-) "/" -> do a <- pop_reg f if a /= 0 then do b <- pop_reg f; push_reg f (b / a) else do push_reg f 0; set_flash f True "x^y" -> do -- most binary ops take top of stack as RHS argument for op, -- but not ^ (on this machine). a <- pop_reg f; b <- pop_reg f; push_reg f (a ** b) "sin" -> do r <- pop_reg f; push_reg f (sin (radians r)) otherwise -> error ("Can't do key: " ++ k) return p ---------------------------------------------------------------- newCalcFixture bf = do cf <- newColumnFixture bf setState cf (CalculatorState 0 0.5 False "" [0,0,0,0]) return cf processFixture :: ProcessFixture processFixture bf p = do f <- newCalcFixture bf doTable f p