-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.hs
415 lines (339 loc) · 13.2 KB
/
parser.hs
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
-- Example taken from Lee Pike's SmartCheck:
-- https://github.com/leepike/SmartCheck/blob/master/paper/paper.pdf
-- https://github.com/leepike/smartcheck
{-# LANGUAGE CPP #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
-- | Toy "parser"/"serializer" (with a bug) in And parsing.
module Main where
import Prelude hiding (showList, mod)
--import Test.LeanCheck
import Test.Extrapolate
import qualified Test.LeanCheck as Lean
import Test.LeanCheck.Utils ((-:>))
import Data.List
import GHC.Generics
import Data.Typeable
import Control.Applicative
import Control.Monad.Trans.State
import Data.Char
-----------------------------------------------------------------
-- Let's make up a toy language.
data Lang = Lang
{ modules :: [Mod]
, funcs :: [Func]
} deriving (Show, Read, Typeable, Generic, Eq, Ord)
newtype Var = Var String
deriving (Show, Read, Typeable, Generic, Eq, Ord)
data Mod = Mod
{ imports :: [Var]
, exports :: [Var]
} deriving (Show, Read, Typeable, Generic, Eq, Ord)
data Func = Func
{ fnName :: Var
, args :: [Exp]
, stmts :: [Stmt]
} deriving (Show, Read, Typeable, Generic, Eq, Ord)
data Stmt = Assign Var Exp
| Alloc Var Exp
| Return Exp
-- | Ref Exp
-- | Deref Exp
-- | Assert Exp
-- | Loop Exp [Stmt]
-- | IfTE Exp [Stmt] [Stmt]
deriving (Show, Read, Typeable, Generic, Eq, Ord)
data Exp = Int Int
| Bool Bool
| Add Exp Exp
| Sub Exp Exp
| Mul Exp Exp
| Div Exp Exp
| Not Exp
| And Exp Exp
| Or Exp Exp
deriving (Show, Read, Typeable, Generic, Eq, Ord)
instance Listable Var where
tiers = cons1 Var `suchThat` (\(Var s) -> all isAlphaNum s && not (null s))
deriveListable ''Exp
deriveListable ''Stmt
deriveListable ''Func
deriveListable ''Mod
deriveListable ''Lang
--------------------------------------------------------------------------------
{-
instance Listable Lang where
tiers = undefined
instance Listable Mod where
tiers = undefined
instance Listable Func where
tiers = undefined
instance Listable Stmt where
tiers = undefined
instance Listable Exp where
tiers = undefined
-}
{- Alternative:
deriveGeneralizable ''Var
deriveGeneralizable ''Lang
deriveGeneralizable ''Mod
deriveGeneralizable ''Func
deriveGeneralizable ''Exp
deriveGeneralizable ''Stmt
-}
-- {-
instance Name Var where name _ = "v"
instance Name Lang where name _ = "l"
instance Name Mod where name _ = "m"
instance Name Func where name _ = "f"
instance Name Exp where name _ = "e"
instance Name Stmt where name _ = "stmt"
instance Express Var where
expr (Var v) = value "Var" Var :$ expr v
instance Express Lang where
expr (Lang ms fs) = value "Lang" Lang :$ expr ms :$ expr fs
instance Express Mod where
expr (Mod is es) = value "Mod" Mod :$ expr is :$ expr es
instance Express Func where
expr (Func n as ss) = value "Func" Func :$ expr n :$ expr as :$ expr ss
instance Express Exp where
expr (Int i) = value "Int" Int :$ expr i
expr (Bool p) = value "Bool" Bool :$ expr p
expr (Add e1 e2) = value "Add" Add :$ expr e1 :$ expr e2
expr (Sub e1 e2) = value "Sub" Sub :$ expr e1 :$ expr e2
expr (Mul e1 e2) = value "Mul" Mul :$ expr e1 :$ expr e2
expr (Div e1 e2) = value "Div" Div :$ expr e1 :$ expr e2
expr (Not e) = value "Not" Not :$ expr e
expr (And e1 e2) = value "And" And :$ expr e1 :$ expr e2
expr (Or e1 e2) = value "Or " Or :$ expr e1 :$ expr e2
instance Express Stmt where
expr (Assign v e) = value "Assign" Assign :$ expr v :$ expr e
expr (Alloc v e) = value "Alloc" Alloc :$ expr v :$ expr e
expr (Return e) = value "Return" Return :$ expr e
instance Generalizable Var where
subInstances v = instances (undefined :: [String])
instance Generalizable Lang where
subInstances l = instances (undefined :: [Mod])
. instances (undefined :: [Func])
instance Generalizable Mod where
subInstances m = instances (undefined :: Var)
instance Generalizable Func where
subInstances f = instances (undefined :: Var)
. instances (undefined :: [Exp])
. instances (undefined :: [Stmt])
instance Generalizable Exp where
background e = [ value "==" ((==) -:> e)
, value "/=" ((/=) -:> e) ]
subInstances e = instances (undefined :: Var)
. instances (undefined :: Int)
. instances (undefined :: Bool)
instance Generalizable Stmt where
subInstances s = instances (undefined :: Var)
. instances (undefined :: Exp)
-- -}
--------------------------------------------------------------------------------
-- "serializer"
parens :: String -> String
parens a = '(' : a ++ ")"
showList :: Show' a => Char -> [a] -> String
showList sep ls = parens $ concat $ intersperse [sep] $ map show' ls
class Show a => Show' a where
show' :: a -> String
show' = show
instance Show' Char
instance Show' Int
instance Show' Bool
instance Show' Lang where
show' (Lang m f) = unwords
[ "Lang"
, showList ';' m
, showList ';' f
]
instance Show' Mod where
show' (Mod i e) = unwords
[ "Mod"
, showList ':' i
, showList ':' e
]
instance Show' Func where
show' (Func f a s) = unwords
[ "Func"
, show' f
, showList ',' a
, showList ',' s
]
instance Show' Var where
show' (Var v) = v
instance Show' Stmt where
show' stmt = unwords $ case stmt of
Assign v e -> ["Assign", show' v, parens $ show' e]
Alloc v e -> ["Alloc" , show' v, parens $ show' e]
Return e -> ["Return", parens $ show' e]
instance Show' Exp where
show' e = unwords $ case e of
Int i -> ["Int" , show' i]
Bool b -> ["Bool", show' b]
Add e0 e1 -> ["Add" , parens $ show' e0, parens $ show' e1]
Sub e0 e1 -> ["Sub" , parens $ show' e0, parens $ show' e1]
Mul e0 e1 -> ["Mul" , parens $ show' e0, parens $ show' e1]
Div e0 e1 -> ["Div" , parens $ show' e0, parens $ show' e1]
Not e0 -> ["Not" , parens $ show' e0]
And e0 e1 -> ["And" , parens $ show' e0, parens $ show' e1]
Or e0 e1 -> ["Or" , parens $ show' e0, parens $ show' e1]
--------------------------------------------------------------------------------
-- "parser"
class Read a => Read' a where
read' :: String -> a
read' = read
instance Read' Lang where
read' str = run str $ do
modify (strip "Lang")
m <- state unparens
let ms = map read' (fromSeps ';' m)
f <- state unparens
let fs = map read' (fromSeps ';' f)
return (Lang ms fs)
instance Read' Mod where
read' mod = run mod $ do
modify (strip "Mod")
m <- state unparens
let i = fromSeps ':' m
es <- state unparens
let e = fromSeps ':' es
return (Mod (map Var i) (map Var e))
instance Read' Func where
read' f = run f $ do
modify (strip "Func")
n <- state (procWord id)
as <- state unparens
let ars = map read' (fromSeps ',' as)
ss <- state unparens
let sts = map read' (fromSeps ',' ss)
return (Func (Var n) ars sts)
instance Read' Stmt where
read' stmt | isPrefixOf "Assign" stmt = run stmt $ do
modify (strip "Assign")
v <- state (procWord id)
e <- state (procParens read')
return (Assign (Var v) e)
| isPrefixOf "Alloc" stmt = run stmt $ do
modify (strip "Alloc")
v <- state (procWord id)
e <- state (procParens read')
return (Alloc (Var v) e)
| isPrefixOf "Return" stmt = run stmt $ do
modify (strip "Return")
e <- state (procParens read')
return (Return e)
| otherwise = error $ "Couldn't match stmt " ++ stmt
instance Read' Exp where
read' e | isPrefixOf "Int" e = Int (read $ strip "Int" e)
| isPrefixOf "Bool" e = Bool (read $ strip "Bool" e)
| isPrefixOf "Add" e = run e $ do
modify (strip "Add")
e0 <- state (procParens read')
e1 <- state (procParens read')
return (Add e0 e1)
| isPrefixOf "Sub" e = run e $ do
modify (strip "Sub")
e0 <- state (procParens read')
e1 <- state (procParens read')
return (Sub e0 e1)
| isPrefixOf "Mul" e = run e $ do
modify (strip "Mul")
e0 <- state (procParens read')
e1 <- state (procParens read')
return (Mul e0 e1)
| isPrefixOf "Div" e = run e $ do
modify (strip "Div")
e0 <- state (procParens read')
e1 <- state (procParens read')
return (Div e0 e1)
| isPrefixOf "Not" e = run e $ do
modify (strip "Not")
e0 <- state (procParens read')
return (Not e0)
| isPrefixOf "And" e = run e $ do
modify (strip "And")
e0 <- state (procParens read')
e1 <- state (procParens read')
-- XXX Bug!
return (And e1 e0)
| isPrefixOf "Or" e = run e $ do
modify (strip "Or")
e0 <- state (procParens read')
e1 <- state (procParens read')
-- XXX Bug!
return (And e1 e0)
| otherwise = error $ "Couldn't match exp " ++ e
--------------------------------------------------------------------------------
run :: s -> State s a -> a
run e m = (flip evalState) e m
-- strip a prefix and a space from a string. Return the remainder of the
-- string.
strip :: String -> String -> String
strip pre str = case stripPrefix pre str of
Nothing -> error $ "Couldn't strip " ++ pre ++ " from " ++ str
Just rst -> if null rst then rst else tail rst
-- Strip the next word.
stripWord :: String -> (String, String)
stripWord str = let strs = words str in
(head strs, unwords (tail strs))
procWord :: (String -> a) -> String -> (a, String)
procWord = runProc stripWord
-- Return a prefix inside parens and the remainder of a string.
unparens :: String -> (String, String)
unparens ('(':str) = unparens' (1::Integer) [] str
where
unparens' n s ('(':r) = unparens' (n+1) ('(':s) r
unparens' n s (')':r) | n == 1 = (reverse s, strip "" r)
| otherwise = unparens' (n-1) (')':s) r
unparens' _ _ [] = error $ "End of string reached in unparens"
unparens' n s (c:r) = unparens' n (c:s) r
unparens str = error $ "Unparsens couldn't parse " ++ str
procParens :: (String -> a) -> String -> (a, String)
procParens = runProc unparens
-- Parse up to a sep
fromSep :: Char -> String -> (String, String)
fromSep sep str = let pre = takeWhile (/= sep) str in
let post = drop (length pre + 1) str in
(pre, post)
fromSeps :: Char -> String -> [String]
fromSeps _ [] = []
fromSeps sep str = let (a, b) = fromSep sep str in
let as = fromSeps sep b in
a:as
runProc :: (String -> (String, String))
-> (String -> a)
-> String
-> (a, String)
runProc t f s = let (a, b) = t s in (f a, b)
--------------------------------------------------------------------------------
size :: Lang -> Int
size (Lang m f) = sumit sizem m + sumit sizef f
where
sizem (Mod is es) = length is + length es
sizef (Func _ as sts) = sumit sizee as + sumit sizes sts
sizes stmt = case stmt of
Assign _ e -> 1 + sizee e
Alloc _ e -> 1 + sizee e
Return e -> 1 + sizee e
sizee e = case e of
Int _ -> 1
Bool _ -> 1
Add e0 e1 -> 1 + sizee e0 + sizee e1
Sub e0 e1 -> 1 + sizee e0 + sizee e1
Mul e0 e1 -> 1 + sizee e0 + sizee e1
Div e0 e1 -> 1 + sizee e0 + sizee e1
Not e0 -> 1 + sizee e0
And e0 e1 -> 1 + sizee e0 + sizee e1
Or e0 e1 -> 1 + sizee e0 + sizee e1
sumit sz ls = sum (map sz ls)
--------------------------------------------------------------------------------
prop_parse :: Lang -> Bool
prop_parse e = read' (show' e) == e
main :: IO ()
main = do
Lean.check prop_parse
check prop_parse