-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlc.hs
More file actions
86 lines (75 loc) · 2.89 KB
/
lc.hs
File metadata and controls
86 lines (75 loc) · 2.89 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
import Data.Set
data Node = Abstr Char Node | App Node Node | Var Char
alphabet:: Set Char
alphabet = fromList ['a'..'z']
pretty_print:: Node -> String
pretty_print (Abstr x n) = "(\\" ++ [x] ++ "." ++ pretty_print n ++ ")"
pretty_print (App m n) = "(" ++ pretty_print m ++ "" ++ pretty_print n ++ ")"
pretty_print (Var x) = [x]
-- Parse a lambda-term.
-- Return value: the AST and the rest of the string if it is unfinished.
parse:: String -> Maybe (Node, String)
parse str = do
(term, rest) <- parse_atomic str
parse_app term rest
-- Parse an application (MN), or a single term M if it has no argument.
-- To handle left-associativity, the result is stored in an accumulator.
parse_app:: Node -> String -> Maybe (Node, String)
parse_app res prog = case prog of
"" -> Just (res, "")
(')':_) -> Just (res, prog)
str -> do
(term, rest) <- parse_atomic str
parse_app (App res term) rest
-- Parse an abstraction, a variable or a parenthesized expression.
parse_atomic:: String -> Maybe (Node, String)
parse_atomic [] = Nothing
parse_atomic (')':_) = Nothing
parse_atomic ('(':tail) = do
(term, rest) <- parse tail
case rest of
(')':tail) -> Just (term, tail)
_ -> Nothing
parse_atomic ('\\':x:'.':tail) = do
(body, rest) <- parse tail
return (Abstr x body, rest)
parse_atomic ('\\':_) = Nothing
parse_atomic ('.':_) = Nothing
parse_atomic (x:tail) = Just ((Var x), tail)
-- Free variables of a term.
fv:: Node -> Set Char
fv (Var x) = singleton x
fv (App m n) = fv m `union` fv n
fv (Abstr x m) = delete x (fv m)
-- Substitution:
subst:: Node -> Char -> Node -> Node
-- [M/x]y = y [M/x]x = M
subst m x (Var y) = if y == x then m else (Var y)
-- [M/x](NL) = ([M/x]N)([M/x]L)
subst m x (App n l) = App (subst m x n) (subst m x l)
-- [M/x](\y.N) = \y.N if x == y
subst _ x (Abstr y n) | x == y = Abstr y n
-- = \z.[M/x][z/y]N if x /= y and y is a free variable of M
subst m x (Abstr y n) | elem y (fv m) =
-- Name collision, substitute y for an unused letter (z)
let z = elemAt 0 (alphabet `difference` (fv m `union` fv n)) in
subst m x (Abstr z (subst (Var z) y n))
-- = \y.[M/x]N if x /= y and y is not a free variable of M
subst m x (Abstr y n) = Abstr y (subst m x n)
eval:: Node -> Node
eval (App m n) = case (eval m) of
-- (\x.M)N = [N/x]M
Abstr x m -> eval (subst (eval n) x m)
e -> App e (eval n)
eval (Abstr x e) = Abstr x (eval e)
eval e = e
eval_print_maybe:: Maybe (Node, String) -> String
eval_print_maybe (Just (ast, "")) = pretty_print (eval ast)
eval_print_maybe (Just (_, rest)) = "Trailing character(s): " ++ rest
eval_print_maybe Nothing = "Syntax Error."
main = do
prog <- getLine
if prog == "exit" || prog == "quit" then return ()
else do
putStrLn $ eval_print_maybe $ parse $ prog
main