-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathCubeExpr.hs
402 lines (338 loc) · 10.8 KB
/
CubeExpr.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
module CubeExpr(
Sym, Expr(..), Type,
subst, nf, alphaEq, typeCheck, skipLambda
) where
import Data.Char(isAlphaNum, isAlpha)
import Data.List(union, (\\))
import Control.Monad.Error
import Text.PrettyPrint.HughesPJ(Doc, renderStyle, style, text, (<>), (<+>), parens, ($$),
vcat, punctuate, sep, fsep, nest)
import Text.ParserCombinators.ReadP(ReadP, (+++), char, munch1, many1, string, pfail, sepBy,
optional, many, skipSpaces, readP_to_S, look)
type Sym = String
data Expr
= Var Sym
| App Expr Expr
| Lam Sym Type Expr
| Pi Sym Type Type
| Let Sym Type Expr Expr
| Kind Kind
deriving (Eq)
type Type = Expr
data Kind = Star | Box deriving (Eq)
expandLet :: Sym -> Type -> Expr -> Expr -> Expr
expandLet i t e b = App (Lam i t b) e
freeVars :: Expr -> [Sym]
freeVars (Var s) = [s]
freeVars (App f a) = freeVars f `union` freeVars a
freeVars (Lam i t e) = freeVars t `union` (freeVars e \\ [i])
freeVars (Pi i k t) = freeVars k `union` (freeVars t \\ [i])
freeVars (Let i t e b) = freeVars (expandLet i t e b)
freeVars (Kind _) = []
subst :: Sym -> Expr -> Expr -> Expr
subst v x = sub
where sub e@(Var i) = if i == v then x else e
sub (App f a) = App (sub f) (sub a)
sub (Lam i t e) = abstr Lam i t e
sub (Pi i t e) = abstr Pi i t e
sub (Let i t e b) = let App (Lam i' t' b') e' = sub (expandLet i t e b)
in Let i' t' e' b'
sub (Kind k) = Kind k
fvx = freeVars x
cloneSym e i = loop i
where loop i' = if i' `elem` vars then loop (i ++ "'") else i'
vars = fvx ++ freeVars e
abstr con i t e =
if v == i then
con i (sub t) e
else if i `elem` fvx then
let i' = cloneSym e i
e' = substVar i i' e
in con i' (sub t) (sub e')
else
con i (sub t) (sub e)
whnf :: Expr -> Expr
whnf ee = spine ee []
where spine (App f a) as = spine f (a:as)
spine (Lam s _ e) (a:as) = spine (subst s a e) as
spine (Let i t e b) as = spine (expandLet i t e b) as
spine f as = foldl App f as
nf :: Expr -> Expr
nf ee = spine ee []
where spine (App f a) as = spine f (a:as)
spine (Lam s t e) [] = Lam s (nf t) (nf e)
spine (Lam s _ e) (a:as) = spine (subst s a e) as
spine (Pi s k t) as = app (Pi s (nf k) (nf t)) as
spine (Let i t e b) as = spine (expandLet i t e b) as
spine f as = app f as
app f as = foldl App f (map nf as)
substVar :: Sym -> Sym -> Expr -> Expr
substVar s s' e = subst s (Var s') e
alphaEq :: Expr -> Expr -> Bool
alphaEq (App f a) (App f' a') = alphaEq f f' && alphaEq a a'
alphaEq (Lam s t e) (Lam s' t' e') = alphaEq t t' && alphaEq e (substVar s' s e')
alphaEq (Pi s k t) (Pi s' k' t') = alphaEq k k' && alphaEq t (substVar s' s t')
alphaEq (Let s t e b) (Let s' t' e' b') = alphaEq t t' && alphaEq e e' && alphaEq b (substVar s' s b')
alphaEq (Var s) (Var s') = s == s'
alphaEq (Kind k) (Kind k') = k == k'
alphaEq _ _ = False
betaEq :: Expr -> Expr -> Bool
betaEq e1 e2 = alphaEq (nf e1) (nf e2)
-------------------------------
type ErrorMsg = String
data Env = Env [(Sym, Type)] deriving (Show)
initalEnv :: Env
initalEnv = Env []
extend :: Sym -> Type -> Env -> Env
extend s t (Env r) = Env ((s, t) : r)
findVar :: Env -> Sym -> TC Type
findVar (Env r) s =
case lookup s r of
Just t -> return t
Nothing -> throwError ("Cannot find variable " ++ s)
type TC a = Either ErrorMsg a
tCheck :: Env -> Expr -> TC Type
tCheck r (Var s) =
findVar r s
tCheck r (Let s t a e) = do
--tCheck r (expandLet s t a e)
tCheck r t
ta <- tCheck r a
when (not (betaEq ta t)) $ throwError $ "Bad let def\n" ++ show (ta, t)
te <- tCheck r (subst s a e)
tCheck r (Pi s t te)
return te
tCheck r (App f a) = do
tf <- tCheckRed r f
case tf of
Pi x at rt -> do
ta <- tCheck r a
when (not (betaEq ta at)) $ throwError $ "Bad function argument type:\n" ++
"Function: " ++ show (nf f) ++ "\n" ++
"argument: " ++ show (nf a) ++ "\n" ++
"expected type: " ++ show at ++ "\n" ++
" got type: " ++ show ta
return $ subst x a rt
_ -> throwError $ "Non-function in application: " ++ show tf
tCheck r (Lam s t e) = do
tCheck r t
let r' = extend s t r
te <- tCheck r' e
let lt = Pi s t te
tCheck r lt
return lt
tCheck r (Pi x a b) = do
s <- tCheckRed r a
let r' = extend x a r
t <- tCheckRed r' b
when ((s, t) `notElem` allowedKinds) $ throwError $ "Bad abstraction: " ++ show (Pi x a b)
return t
tCheck _ (Kind Star) = return $ Kind Box
tCheck _ (Kind Box) = throwError "Found a Box"
allowedKinds :: [(Type, Type)]
allowedKinds = [(Kind Star, Kind Star), (Kind Star, Kind Box), (Kind Box, Kind Star), (Kind Box, Kind Box)]
tCheckRed :: Env -> Expr -> TC Type
tCheckRed r e = do
t <- tCheck r e
return $ whnf t
typeCheck :: Expr -> Either ErrorMsg Type
typeCheck e = fmap nf $ tCheck initalEnv e
-- case of
-- Left msg -> error ("Type error:\n" ++ msg)
-- Right t -> nf t
typeCheck' :: Expr -> Type
typeCheck' e =
case tCheck initalEnv e of
Left msg -> error ("Type error:\n" ++ msg)
Right t -> nf t
---------------------------------------------------------------------
ppsExpr :: Expr -> String
ppsExpr e = renderStyle style $ ppExpr 0 e
ppExpr :: Int -> Expr -> Doc
ppExpr p (Pi s t e) | s `notElem` freeVars e = pparens (p > 0) $ ppExpr 1 t <> text "->" <> ppExpr 0 e
--ppExpr p (Pi s t e) = pparens (p > 0) $ (parens $ text s <> text "::" <> ppExpr 0 t) <> text "->" <> ppExpr 0 e
ppExpr p l@(Pi _ _ _) = pparens (p > 0) $ text "forall" <+> (fsep $ args ++ [text ".", ppExpr 0 b])
where (args, b) = collectPi [] l
collectPi vts (Pi v t e) | v `elem` freeVars e = collectPi (ppBound v t : vts) e
collectPi vts e = (reverse vts, e)
ppExpr p l@(Lam _ _ _) = pparens (p > 0) $ text "\\" <+> (fsep $ args ++ [text "->", ppExpr 0 b])
where (args, b) = collectLam [] l
collectLam vts (Lam v t e) = collectLam (ppBound v t : vts) e
collectLam vts e = (reverse vts, e)
ppExpr p ee@(Let _ _ _ _) =
let (stes, body) = collectBinds [] ee
ppBind (s, t, Just e) = sep [text s <+> text "::" <+> ppExpr 0 t <> text " =", nest 4 $ ppExpr 0 e]
ppBind (s, t, Nothing) = text s <+> text "::" <+> ppExpr 0 t
ppBinds xs = vcat $ punctuate (text ";") (map ppBind xs)
collectBinds bs (Let s t e b) = collectBinds (bs ++ [(s, t, Just e)]) b
-- collectBinds bs (Lam s t b) = collectBinds (bs ++ [(s, t, Nothing)]) b
collectBinds bs b = (bs, b)
in pparens (p > 0) $
(text "let " <> ppBinds stes) $$ (text "in " <> ppExpr 0 body)
ppExpr p (App f a) = pparens (p > 9) $ ppExpr 9 f <> text " " <> ppExpr 10 a
ppExpr _ (Var s) = text s
ppExpr _ (Kind Star) = text "*"
ppExpr _ (Kind Box) = text "[]"
ppBound :: Sym -> Expr -> Doc
ppBound v t = parens $ text v <+> text "::" <+> ppExpr 0 t
pparens :: Bool -> Doc -> Doc
pparens True d = parens d
pparens False d = d
instance Show Expr where
show e = ppsExpr e
-------------------------------------------------------
instance Read Expr where
readsPrec _ = readP_to_S pTop . removeComments
removeComments :: String -> String
removeComments "" = ""
removeComments ('-':'-':cs) = skip cs
where skip "" = ""
skip s@('\n':_) = removeComments s
skip (_:s) = skip s
removeComments (c:cs) = c : removeComments cs
pTop :: ReadP Expr
pTop = do
e <- pExpr
skipSpaces
return e
pExpr :: ReadP Expr
pExpr = pAExpr +++ pPi +++ pLam +++ pLet
pAExpr :: ReadP Expr
pAExpr = pAtomExpr +++ pApply
pType :: ReadP Expr
pType = pExpr
pAtomExpr :: ReadP Expr
pAtomExpr = pVar +++ pKind +++ pParen pExpr
pParen :: ReadP a -> ReadP a
pParen p = do
schar '('
e <- p
schar ')'
return e
pApply :: ReadP Expr
pApply = do
f <- pAtomExpr
as <- many1 pAtomExpr
return $ foldl App f as
pLet :: ReadP Expr
pLet = do
skeyword "let"
stes <- sepBy pBind (schar ';')
optional (schar ';')
skeyword "in"
b <- pExpr
return $ eLets' stes b
pBind :: ReadP (Sym, Type, Maybe Expr)
pBind = pBindH +++ pBindR
pBindH :: ReadP (Sym, Type, Maybe Expr)
pBindH = do
sy <- pSym
sstring "::"
ty <- pType
schar ';'
sy' <- pSym
as <- many pSym
schar '='
b <- pExpr
e <- matchH ty as b
if sy /= sy' then
pfail
else
return (sy, ty, Just e)
matchH :: Expr -> [Sym] -> Expr -> ReadP Expr
matchH _ [] e = return e
matchH (Pi v t t') (a:as) e | v == a || v == "_" = do
e' <- matchH t' as e
return (Lam a t e')
matchH _ _ _ = pfail
pBindR :: ReadP (Sym, Type, Maybe Expr)
pBindR = do
let addT (s, t) r = Pi s t r
addE (s, t) e = Lam s t e
sy <- pSym
args <- many pArg
sstring "::"
rt <- pType
(do
schar '='
be <- pExpr
return (sy, foldr addT rt args, Just $ foldr addE be args)
) +++
(return (sy, foldr addT rt args, Nothing))
eLet' :: (Sym, Type, Maybe Expr) -> Expr -> Expr
eLet' (s, t, Nothing) b = Lam s t b
eLet' (s, t, Just e) b = Let s t e b
eLets' :: [(Sym, Type, Maybe Expr)] -> Expr -> Expr
eLets' stes b = foldr eLet' b stes
pPi :: ReadP Expr
pPi = pPiQuant +++ pPiArrow
pPiQuant :: ReadP Expr
pPiQuant = do
sstring "forall" -- +++ sstring "\\/"
sts <- (fmap (:[]) pVarType) +++ many1 (pParen pVarType)
schar '.'
e <- pType
return $ foldr (uncurry Pi) e sts
--Pi s t e
pPiArrow :: ReadP Expr
pPiArrow = do
ts <- many1 (do e <- pPiArg; sstring "->"; return e)
rt <- pAExpr
return $ foldr (\ (s, t) r -> Pi s t r) rt ts
where pPiArg = pPiNoDep +++ pArg
pPiNoDep = do
t <- pAExpr
return ("_", t)
pArg :: ReadP (Sym, Type)
pArg = pParen pVarType
pVarType :: ReadP (Sym, Type)
pVarType = do
s <- pSym
sstring "::"
t <- pType
return (s, t)
pLam :: ReadP Expr
pLam = do
schar '\\' -- +++ sstring "/\\"
sts <- fmap (:[]) pVarType +++ many1 (pParen pVarType)
sstring "->"
e <- pExpr
return $ foldr (uncurry Lam) e sts
pVar :: ReadP Expr
pVar = do
s <- pSym
return $ Var s
pKind :: ReadP Expr
pKind = do
(do schar '*'; return $ Kind Star) +++ (do sstring "[]"; return $ Kind Box)
pSym :: ReadP Sym
pSym = do
skipSpaces
cs <- munch1 isSym
if cs `elem` ["let", "in", "forall", "_"] then
pfail
else
return cs
schar :: Char -> ReadP ()
schar c = do
skipSpaces
char c
return ()
sstring :: String -> ReadP ()
sstring s = do
skipSpaces
string s
return ()
skeyword :: String -> ReadP ()
skeyword s = do
sstring s
cs <- look
case cs of
c:_ | isAlpha c -> pfail
_ -> return ()
isSym :: Char -> Bool
isSym c = isAlphaNum c || c `elem` "_'"
-------
skipLambda :: Expr -> Type -> (Expr, Type)
skipLambda (Lam _ _ e) (Pi _ _ t) = skipLambda e t
skipLambda e t = (e, t)