Skip to content

Commit bc9928a

Browse files
committed
Add calculator to chapter on monadic parsing
1 parent 13f940a commit bc9928a

File tree

1 file changed

+250
-0
lines changed

1 file changed

+250
-0
lines changed
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
import Control.Applicative
2+
import Data.Char
3+
import System.IO
4+
5+
box :: [String]
6+
box = ["+---------------+",
7+
"| |",
8+
"+---+---+---+---+",
9+
"| q | c | d | = |",
10+
"+---+---+---+---+",
11+
"| 1 | 2 | 3 | + |",
12+
"+---+---+---+---+",
13+
"| 4 | 5 | 6 | - |",
14+
"+---+---+---+---+",
15+
"| 7 | 8 | 9 | * |",
16+
"+---+---+---+---+",
17+
"| 0 | ( | ) | / |",
18+
"+---+---+---+---+"]
19+
20+
buttons :: String
21+
buttons = standard ++ extra
22+
where
23+
standard = "qcd=123+456-789*0()/"
24+
extra = "QCD \ESC\BS\DEL\n"
25+
26+
type Pos = (Int, Int)
27+
28+
cls :: IO ()
29+
cls = putStr "\ESC[2J"
30+
31+
writeat :: Pos -> String -> IO ()
32+
writeat p xs = do
33+
goto p
34+
putStr xs
35+
36+
goto :: Pos -> IO ()
37+
goto (x, y) = putStr ("\ESC[" ++ show y ++ ";" ++ show x ++ "H")
38+
39+
getCh :: IO Char
40+
getCh = do hSetEcho stdin False
41+
x <- getChar
42+
hSetEcho stdin True
43+
return x
44+
45+
showbox :: IO ()
46+
showbox = sequence_ [writeat (1, y) b | (y, b) <- zip [1..] box]
47+
48+
display :: [Char] -> IO ()
49+
display xs = do
50+
writeat (3, 2) (replicate 13 ' ')
51+
writeat (3, 2) (reverse (take 13 (reverse xs)))
52+
53+
calc :: String -> IO ()
54+
calc xs = do
55+
display xs
56+
c <- getCh
57+
if elem c buttons
58+
then process c xs
59+
else do
60+
beep
61+
calc xs
62+
63+
process :: Char -> String -> IO ()
64+
process c xs
65+
| elem c "qQ\ESC" = quit
66+
| elem c "= \n" = eval xs
67+
| elem c "cC" = clear
68+
| otherwise = press c xs
69+
70+
quit :: IO ()
71+
quit = goto (1, 14)
72+
73+
delete :: String -> IO ()
74+
delete [] = calc []
75+
delete xs = calc (init xs)
76+
77+
eval :: String -> IO ()
78+
eval xs = case parse expr xs of
79+
[(n, [])] -> calc (show n)
80+
_ -> do
81+
beep
82+
calc xs
83+
84+
beep :: IO ()
85+
beep = putStr "\BEL"
86+
87+
clear :: IO ()
88+
clear = calc []
89+
90+
press :: Char -> String -> IO ()
91+
press c xs = calc (xs ++ [c])
92+
93+
run :: IO ()
94+
run = do
95+
cls
96+
showbox
97+
clear
98+
99+
100+
newtype Parser a = P (String -> [(a, String)])
101+
102+
-----------------------------------------------------------------------------
103+
-- expr ::= term (+ expr | ∊)
104+
-- term ::= factor (* term | ∊)
105+
-- factor ::= ( expr ) | nat
106+
-- nat ::= 0 | 1 | 2 | ...
107+
-----------------------------------------------------------------------------
108+
109+
expr :: Parser Int
110+
expr = do
111+
t <- term
112+
do
113+
symbol "+"
114+
e <- expr
115+
return (t + e) <|> return t
116+
117+
term :: Parser Int
118+
term = do
119+
f <- factor
120+
do
121+
symbol "*"
122+
t <- term
123+
return (f * t) <|> return f
124+
125+
factor :: Parser Int
126+
factor = do
127+
symbol "("
128+
e <- expr
129+
symbol ")"
130+
return e <|> natural
131+
132+
parse :: Parser a -> String -> [(a, String)]
133+
parse (P p) inp = p inp
134+
135+
item :: Parser Char
136+
item = P $ \inp ->
137+
case inp of
138+
[] -> []
139+
(x:xs) -> [(x, xs)]
140+
141+
sat :: (Char -> Bool) -> Parser Char
142+
sat p = do
143+
x <- item
144+
if p x
145+
then return x
146+
else empty
147+
148+
digit :: Parser Char
149+
digit = sat isDigit
150+
151+
lower :: Parser Char
152+
lower = sat isLower
153+
154+
upper :: Parser Char
155+
upper = sat isUpper
156+
157+
letter :: Parser Char
158+
letter = sat isAlpha
159+
160+
alphanum :: Parser Char
161+
alphanum = sat isAlphaNum
162+
163+
char :: Char -> Parser Char
164+
char x = sat (== x)
165+
166+
string :: String -> Parser String
167+
string [] = return []
168+
string (x:xs) = do
169+
char x
170+
string xs
171+
return (x:xs)
172+
173+
ident :: Parser String
174+
ident = do
175+
x <- lower
176+
xs <- many alphanum
177+
return (x:xs)
178+
179+
nat :: Parser Int
180+
nat = do
181+
xs <- some digit
182+
return (read xs)
183+
184+
space :: Parser ()
185+
space = do
186+
many (sat isSpace)
187+
return ()
188+
189+
int :: Parser Int
190+
int = do
191+
char '-'
192+
n <- nat
193+
return (-n) <|> nat
194+
195+
token :: Parser a -> Parser a
196+
token p = do
197+
space
198+
v <- p
199+
space
200+
return v
201+
202+
identifier :: Parser String
203+
identifier = token ident
204+
205+
natural :: Parser Int
206+
natural = token nat
207+
208+
integer :: Parser Int
209+
integer = token int
210+
211+
symbol :: String -> Parser String
212+
symbol xs = token (string xs)
213+
214+
nats :: Parser [Int]
215+
nats = do
216+
symbol "["
217+
n <- natural
218+
ns <- many $ do
219+
symbol ","
220+
natural
221+
symbol "]"
222+
return (n:ns)
223+
224+
instance Functor Parser where
225+
fmap f p = P $ \inp ->
226+
case parse p inp of
227+
[] -> []
228+
[(v, out)] -> [(f v, out)]
229+
230+
instance Applicative Parser where
231+
pure v = P $ \inp -> [(v, inp)]
232+
233+
pf <*> px = P $ \inp ->
234+
case parse pf inp of
235+
[] -> []
236+
[(f, out)] -> parse (fmap f px) out
237+
238+
instance Monad Parser where
239+
p >>= f = P $ \inp ->
240+
case parse p inp of
241+
[] -> []
242+
[(v, out)] -> parse (f v) out
243+
244+
instance Alternative Parser where
245+
empty = P $ \inp -> []
246+
247+
p <|> q = P $ \inp ->
248+
case parse p inp of
249+
[] -> parse q inp
250+
[(v, out)] -> [(v, out)]

0 commit comments

Comments
 (0)