Skip to content

Commit 22f30e3

Browse files
committed
Added functionality for loading and evaluating files
1 parent 336c67c commit 22f30e3

File tree

3 files changed

+92
-25
lines changed

3 files changed

+92
-25
lines changed

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,14 @@ car, cdr, and friends are there. As well as _cons_, _if_, _cond_, _case_, a whol
3434

3535
### UPDATES
3636

37+
- 01/11/2013 Added support for loading text files as source files and the following file functions:
38+
-- ```open-input-file```
39+
-- ```open-output-file```
40+
-- ```close-input-port```
41+
-- ```close-output-port```
42+
-- ```read```
43+
-- ```write```
44+
-- ```read-contents```
45+
-- ```read-all```
3746
- 01/11/2013 Added the ability to create and store functions using ```define``` and ```lambda```.
3847
- 31/10/2013 Added persistent variables from scheme, namely: ```define``` and ```set!```.

src/leesptypes.hs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module LeespTypes where
22

33
import Data.IORef
4+
import System.IO (Handle)
45
import Control.Monad.Error
56
import Text.ParserCombinators.Parsec (ParseError)
67

@@ -14,6 +15,8 @@ data LispVal = Atom String
1415
| Keyword String
1516
| PrimitiveFunc ([LispVal] -> ThrowsError LispVal)
1617
| Func {params :: [String], vararg :: (Maybe String), body :: [LispVal], closure :: Env}
18+
| IOFunc ([LispVal] -> IOThrowsError LispVal)
19+
| Port Handle
1720

1821
data LispError = NumArgs Integer [LispVal]
1922
| TypeMismatch String LispVal
@@ -66,7 +69,9 @@ showVal (List contents) = "(" ++ unWordsList contents ++ ")"
6669
showVal (DottedList head tail) = "(" ++ unWordsList head ++ " . " ++ showVal tail ++ ")"
6770
showVal (Keyword kword) = kword
6871
showVal (PrimitiveFunc _) = "<PrimitiveFunc>"
69-
showVal (Func {params = args, vararg = varargs, body = body, closure = env}) =
72+
showVal (Func {params = args, vararg = varargs, body = body, closure = env}) =
7073
"(lambda (" ++ unwords (map show args) ++ (case varargs of
7174
Nothing -> ""
7275
Just arg -> " . " ++ arg) ++ ") ...)"
76+
showVal (Port _) = "<IO port>"
77+
showVal (IOFunc _) = "<IO primitive>"

src/main.hs

+77-24
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ import LeespTypes
1818
import LeespParser
1919

2020
main :: IO ()
21-
main = do args <- getArgs
22-
case length args of
23-
0 -> runRepl
24-
1 -> runOne $ head args
25-
_ -> putStrLn "Program takes only 0 or 1 argument"
21+
main = do
22+
args <- getArgs
23+
if null args
24+
then runRepl
25+
else runOne $ args
2626

2727
-- REPL FUNS
2828
flushStr :: String -> IO ()
@@ -33,7 +33,6 @@ readPrompt prompt = flushStr prompt >> getLine
3333

3434
evalString :: Env -> String -> IO String
3535
evalString env expr = runIOThrows $ liftM show $ (liftThrows $ readExpr expr) >>= eval env
36-
-- evalString expr = return $ extractValue $ trapError (liftM show $ readExpr expr >>= eval)
3736

3837
evalAndPrint :: Env -> String -> IO ()
3938
evalAndPrint env expr = evalString env expr >>= putStrLn
@@ -42,33 +41,39 @@ until_ :: Monad m => (a -> Bool) -> m a -> (a -> m ()) -> m ()
4241
until_ pred prompt action = do
4342
result <- prompt
4443
unless (pred result) $ action result >> until_ pred prompt action
45-
--if pred result
46-
-- then return ()
47-
-- else action result >> until_ pred prompt action
48-
49-
--runOne :: String -> IO ()
50-
--runOne expr = nullEnv >>= flip evalAndPrint expr
51-
52-
--runRepl :: IO ()
53-
--runRepl = nullEnv >>= until_ (== "quit") (readPrompt "Leesp >> ") . evalAndPrint
5444

45+
{- This version runs a single input command from stdin
5546
runOne :: String -> IO ()
5647
runOne expr = primitiveBindings >>= flip evalAndPrint expr
48+
-}
49+
50+
-- This version of runOne expects a file input to read from.
51+
runOne :: [String] -> IO ()
52+
runOne args = do
53+
env <- primitiveBindings >>= flip bindVars [("args", List $ map String $ drop 1 args)]
54+
(runIOThrows $ liftM show
55+
$ eval env (List [Atom "load", String (head args)])) >>= hPutStrLn stderr
5756

5857
runRepl :: IO ()
5958
runRepl = primitiveBindings >>= until_ (== "quit") (readPrompt "Leesp >> ") . evalAndPrint
6059

6160
-- END REPL FUNS
6261

63-
readExpr :: String -> ThrowsError LispVal
64-
readExpr input = case parse parseExpr "lisp" input of
65-
Left err -> throwError $ Parser err
62+
readOrThrow :: Parser a -> String -> ThrowsError a
63+
readOrThrow parser input = case parse parser "leesp" input of
64+
Left err -> throwError $ Parser err
6665
Right val -> return val
6766

68-
--apply :: String -> [LispVal] -> ThrowsError LispVal
69-
--apply func args = maybe (throwError $ NotFunction "Unrecognised primitive function args" func)
70-
-- ($ args)
71-
-- (lookup func primitives)
67+
readExpr :: String -> ThrowsError LispVal
68+
readExpr = readOrThrow parseExpr
69+
70+
readExprList :: String -> ThrowsError [LispVal]
71+
readExprList = readOrThrow (endBy parseExpr spaces)
72+
73+
--readExpr :: String -> ThrowsError LispVal
74+
--readExpr input = case parse parseExpr "lisp" input of
75+
-- Left err -> throwError $ Parser err
76+
-- Right val -> return val
7277

7378
apply :: LispVal -> [LispVal] -> IOThrowsError LispVal
7479
apply (PrimitiveFunc func) args = liftThrows $ func args
@@ -92,6 +97,9 @@ eval env val@(Bool _) = return val
9297
eval env val@(Character _) = return val
9398
eval env val@(Keyword _) = return val
9499
eval env (Atom id) = getVar env id
100+
-- Load a file and eval the contents
101+
eval env (List [Atom "load", String filename]) =
102+
load filename >>= liftM last . mapM (eval env)
95103
-- Handled Quoting.
96104
eval env (List [Atom "quote", val]) = return val
97105
-- Flow Control Functions.
@@ -188,6 +196,17 @@ primitives = [("+", numericBinop (+)),
188196
("string-insert!", stringinsertFn),
189197
("substring", subStringFn)]
190198

199+
ioPrimitives :: [(String, [LispVal] -> IOThrowsError LispVal)]
200+
ioPrimitives = [("apply", applyProc),
201+
("open-input-file", makePort ReadMode),
202+
("open-output-file", makePort WriteMode),
203+
("close-input-port", closePort),
204+
("close-output-port", closePort),
205+
("read", readProc),
206+
("write", writeProc),
207+
("read-contents", readContents),
208+
("read-all", readAll)]
209+
191210
subStringFn :: [LispVal] -> ThrowsError LispVal
192211
subStringFn [String s, Number start, Number end]
193212
| indicesValid = (return . String) $ genericSubList s
@@ -322,9 +341,13 @@ equal badArgList = throwError $ NumArgs 2 badArgList
322341
nullEnv :: IO Env
323342
nullEnv = newIORef []
324343

344+
makeFunc :: Monad m => Maybe String -> Env -> [LispVal] -> [LispVal] -> m LispVal
325345
makeFunc varargs env params body = return $ Func (map showVal params) varargs body env
326346

347+
makeNormalFunc :: Env -> [LispVal] -> [LispVal] -> ErrorT LispError IO LispVal
327348
makeNormalFunc = makeFunc Nothing
349+
350+
makeVarargs :: LispVal -> Env -> [LispVal] -> [LispVal] -> ErrorT LispError IO LispVal
328351
makeVarargs = makeFunc . Just . showVal
329352

330353
liftThrows :: ThrowsError a -> IOThrowsError a
@@ -373,6 +396,36 @@ bindVars envRef bindings = readIORef envRef >>= extendEnv bindings >>= newIORef
373396
return (var, ref)
374397

375398
primitiveBindings :: IO Env
376-
primitiveBindings = nullEnv >>= (flip bindVars $ map makePrimitiveFunc primitives)
399+
primitiveBindings = nullEnv >>= (flip bindVars $ map (makeFunc IOFunc) ioPrimitives
400+
++ map (makeFunc PrimitiveFunc) primitives)
377401
where
378-
makePrimitiveFunc (var, func) = (var, PrimitiveFunc func)
402+
makeFunc constructor (var, func) = (var, constructor func)
403+
404+
-- START IO PRIMITIVES
405+
applyProc :: [LispVal] -> IOThrowsError LispVal
406+
applyProc [func, List args] = apply func args
407+
applyProc (func : args) = apply func args
408+
409+
makePort :: IOMode -> [LispVal] -> IOThrowsError LispVal
410+
makePort mode [String filename] = liftM Port $ liftIO $ openFile filename mode
411+
412+
closePort :: [LispVal] -> IOThrowsError LispVal
413+
closePort [Port port] = liftIO $ hClose port >> (return $ Bool True)
414+
415+
readProc :: [LispVal] -> IOThrowsError LispVal
416+
readProc [] = readProc [Port stdin]
417+
readProc [Port port] = (liftIO $ hGetLine port) >>= liftThrows . readExpr
418+
419+
writeProc :: [LispVal] -> IOThrowsError LispVal
420+
writeProc [obj] = writeProc [obj, Port stdout]
421+
writeProc [obj, Port port] = liftIO $ hPrint port obj >> (return $ Bool True)
422+
423+
readContents :: [LispVal] -> IOThrowsError LispVal
424+
readContents [String filename] = liftM String $ liftIO $ readFile filename
425+
426+
load :: String -> IOThrowsError [LispVal]
427+
load filename = (liftIO $ readFile filename) >>= liftThrows . readExprList
428+
429+
readAll :: [LispVal] -> IOThrowsError LispVal
430+
readAll [String filename] = liftM List $ load filename
431+
-- END IO PRIMITIVES

0 commit comments

Comments
 (0)