-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathspreadsheet.hs
More file actions
124 lines (101 loc) · 3.8 KB
/
spreadsheet.hs
File metadata and controls
124 lines (101 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
-- -*- haskell-hugs-program-args: ("+." "-98") -*-
{-# LANGUAGE FlexibleContexts #-}
-- A demo program of the Adaptive library, implementing a simple
-- spreadsheet. Requires a VT100-like terminal to work. Expressions
-- have to be entered according to the Expr datatype.
-- Magnus Carlsson, magnus@cse.ogi.edu
import Control.Monad.Adaptive
import Data.Char
import Control.Monad.Adaptive.Ref
import Control.Monad(ap,when)
import Data.IORef(IORef)
import System.Exit
type InIO m a = m IO IORef a
type IOMod a = InIO Modifiable a
data CellRef = CR String (IOMod Integer) deriving Eq
instance Show CellRef where show (CR s _) = s
data Expr c = Const Integer | Add (Expr c) (Expr c) | Cell c
deriving (Eq,Read,Show)
eval :: Expr CellRef -> InIO Changeable Integer
eval (Const i) = return i
eval (Add e1 e2) = return (+) `ap` eval e1 `ap` eval e2
eval (Cell (CR _ n)) = readMod n
memo ma = readMod =<< newMod ma
instance Eq (a -> b) where a == b = False
ap' mf ma = do
m <- newMod mf
a <- memo ma
f <- readMod m
return (f a)
newCell :: NewMod m IO IORef =>
String -> InIO m (IOMod (Expr CellRef), CellRef)
newCell s = do
c <- newMod (return (Const 0))
v <- newMod $ readMod c >>= eval
return (c,CR s v)
newCell' n = do
let s = "c" ++ show n
inM $ prAt (n+2) 0 3 (s++": ")
a@(c,CR s v) <- newCell s
newMod $ readMod v >>= inM . prAt (n+2) 5 10 . show
newMod $ readMod c >>= inM . prAt (n+2) 15 40 . show
return (s,a)
prAt l c w s = putStr (pos l c ++ replicate w ' ' ++ pos l c++s)
esc = ("\ESC["++)
pos l c = esc (show l++";"++show c++"H")
clear = pos 0 0 ++ esc "J"
cleareol = esc "K"
readPrompt c s = do prAt 20 c 0 (s++"> "++ cleareol)
s <- getLine
when (s == "quit") $ exitWith ExitSuccess
return s
msg s = prAt 19 0 0 (s ++ cleareol)
prompt env = inM p where
p = do s <- readPrompt 0 "Cell"
case lookup s env of
Nothing -> do msg ("Cell " ++ show s ++ " not found")
p
Just (c,v) -> do let r = do s <- readPrompt 10 "Expr"
case reads s of
[(e,"")] -> msg "" >> return (c,e)
_ -> do msg "Syntax error"
r
r
data CellName = CN String
instance Read CellName where readsPrec _ s = [(CN $ takeWhile isAlphaNum s',
dropWhile isAlphaNum s')]
where s' = dropWhile isSpace s
instance Show CellName where show (CN s) = s
subst m env (Const i) = Const i
subst m env (Add e1 e2) = Add (subst m env e1) (subst m env e2)
subst m env (Cell (CN s)) = Cell $ case lookup s env of
Nothing -> m
Just (c,v) -> v
main :: IO ()
main = run $ do
inM $ putStr clear
env <- mapM newCell' [0..9]
m0 <- CR "?" `fmap` newMod (return 0)
let loop = do (c,e) <- prompt env
let e' = subst m0 env e
change c e'
propagate
loop
loop
-- small non-interactive example
newCellPr s = do
a@(c,CR s v) <- newCell s
newMod $ do e <- readMod c
x <- readMod v
inM $ putStrLn (s++" = "++show e ++ " = " ++ show x)
return a
test = run $ do
[(c1,v1),(c2,v2)] <- mapM newCellPr ["c1","c2"]
change c1 (Const 10)
change c2 (Add (Cell v1) (Const 5))
inM (putStrLn "Propagate") >> propagate
change c1 (Add (Cell v2) (Const 4))
change c2 (Const 1)
inM (putStrLn "Propagate") >> propagate
change c2 (Const 2)
inM (putStrLn "Propagate") >> propagate