-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathCube.hs
117 lines (105 loc) · 3.67 KB
/
Cube.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
-- Simple interface to lambda cube type checker.
import System.Environment(getArgs)
import Data.Char(isSpace)
import Data.List(sortBy)
import Data.Function(on)
import REPL
import CubeExpr
main :: IO ()
main = do
args <- getArgs
case args of
["-?"] -> usage
["-help"] -> usage
["--help"] -> usage
[] -> interactive
_ -> batch args
usage :: IO ()
usage = putStr "\
\Usage: cube -- start in interactive mode\n\
\ cube files -- concatenate files, type check, evaluate\n\
\ cube - files -- insert let&in, concatenate files, type check, evaluate\n\
\"
batch :: [FilePath] -> IO ()
batch ("-":names) = mapM readFile names >>= batch' . addIn . ("let " :)
where addIn ss = init ss ++ [" in "] ++ [last ss]
batch names = mapM readFile names >>= batch'
batch' :: [String] -> IO ()
batch' files = do
let expr = unRight "Parse error: " $ unreads $ concat files
typ = unRight "Type error: " $ typeCheck expr
putStrLn $ "Type:\n" ++ show typ
putStrLn $ "Value:\n" ++ show (nf expr)
unRight :: String -> Either String a -> a
unRight msg (Left msg') = error $ msg ++ msg'
unRight _ (Right a) = a
unreads :: (Show a, Read a) => String -> Either String a
unreads s =
let xss = reads s
in case filter (null . snd) xss of
(x, _) : _ -> Right x -- pick first when ambiguous.
_ ->
-- Find the shortest suffix and report error there.
let rest = head $ sortBy (compare `on` length) $ map snd xss ++ [s]
ls = length (filter (=='\n') s)
lrest = length (filter (=='\n') rest)
in Left $ "line " ++ show (ls - lrest) ++ ": " ++ rest
data State = State { defs :: String, skipLam :: Bool } deriving (Show)
interactive :: IO ()
interactive = repl $ REPL { repl_init = cinit, repl_eval = ceval, repl_exit = cexit }
where cinit = do
putStrLn "Welcome to the Cube."
putStrLn "Use :help to get help."
return ("Cube> ", State "" False)
cexit _ = putStrLn "Bye."
ceval s line = do
let rest = dropWhile isSpace $ dropWhile (not . isSpace) line
load = readFile rest >>= addFile s
quit = return (True, s)
help = do putStrLn helpMsg; return (False, s)
case words line of
[] -> return (False, s)
":h" : _ -> help
":help" : _ -> help
":q" : _ -> quit
":quit" : _ -> quit
":let" : _ -> addFile s (rest ++ ";\n")
":l" : _ -> load
":load" : _ -> load
":defs" : _ -> do putStrLn (defs s); return (False, s)
":skip" : _ -> return (False, s { skipLam = not (skipLam s) })
_ -> do evalPrint s line
return (False, s)
evalPrint s se = do
mt <- readAndCheck s se
case mt of
Nothing -> return ()
Just (e, t) -> do
let v = nf e
(v', t') = if skipLam s then skipLambda v t else (v, t)
putStrLn $ show v' ++ "\n ::\n" ++ show t'
return (False, s)
addFile s n = do
let s' = s { defs = defs s ++ n }
mt <- readAndCheck s' "\\ (a::*) -> a"
case mt of
Nothing -> return (False, s)
Just _ -> return (False, s')
readAndCheck s e =
case unreads $ "let " ++ defs s ++ " in " ++ e of
Left msg -> do putStrLn $ "Syntax error: " ++ msg; return Nothing
Right expr ->
case typeCheck expr of
Left msg -> do putStrLn $ "Type error: " ++ msg; return Nothing
Right typ -> return $ Just (expr, typ)
helpMsg :: String
helpMsg = "\
\Commands:\n\
\ :defs show loaded definitions\n\
\ :let def add definition\n\
\ :load name load file with definitions\n\
\ :help show this message\n\
\ :quit quit\n\
\ :skip toggle skipping initial lambdas\n\
\ expr evaluate expression\n\
\"