Functions
This commit is contained in:
parent
acc1a198e8
commit
04b5ccb240
1 changed files with 48 additions and 8 deletions
56
scheme.hs
56
scheme.hs
|
@ -20,6 +20,9 @@ data LispVal = Atom String
|
|||
| Number Integer
|
||||
| String String
|
||||
| Bool Bool
|
||||
| PrimitiveFunc ([LispVal] -> ThrowsError LispVal)
|
||||
| Func {params :: [String], vararg :: (Maybe String),
|
||||
body :: [LispVal], closure :: Env}
|
||||
|
||||
parseString :: Parser LispVal
|
||||
parseString = do
|
||||
|
@ -118,6 +121,12 @@ showVal (Bool True) = "#t"
|
|||
showVal (Bool False) = "#f"
|
||||
showVal (List contents) = "(" ++ unwordsList contents ++ ")"
|
||||
showVal (DottedList head tail) = "(" ++ unwordsList head ++ " . " ++ showVal tail ++ ")"
|
||||
showVal (PrimitiveFunc _) = "<primitive>"
|
||||
showVal (Func {params = args, vararg = varargs, body = body, closure = env}) =
|
||||
"(lambda (" ++ unwords (map show args) ++
|
||||
(case varargs of
|
||||
Nothing -> ""
|
||||
Just arg -> " . " ++ arg) ++ ") ...)"
|
||||
|
||||
unwordsList :: [LispVal] -> String
|
||||
unwordsList = unwords . map showVal
|
||||
|
@ -141,15 +150,40 @@ eval env (List [Atom "set!", Atom var, form]) =
|
|||
eval env form >>= setVar env var
|
||||
eval env (List [Atom "define", Atom var, form]) =
|
||||
eval env form >>= defineVar env var
|
||||
eval env (List (Atom func : args)) =
|
||||
mapM (eval env) args >>= liftThrows . apply func
|
||||
|
||||
eval env (List (Atom "define" : List (Atom var : params) : body)) =
|
||||
makeNormalFunc env params body >>= defineVar env var
|
||||
eval env (List (Atom "define" : DottedList (Atom var : params) varargs : body)) =
|
||||
makeVarargs varargs env params body >>= defineVar env var
|
||||
eval env (List (Atom "lambda" : List params : body)) =
|
||||
makeNormalFunc env params body
|
||||
eval env (List (Atom "lambda" : DottedList params varargs : body)) =
|
||||
makeVarargs varargs env params body
|
||||
eval env (List (Atom "lambda" : varargs@(Atom _) : body)) =
|
||||
makeVarargs varargs env [] body
|
||||
eval env (List (function : args)) = do
|
||||
func <- eval env function
|
||||
argVals <- mapM (eval env) args
|
||||
apply func argVals
|
||||
eval env badForm =
|
||||
throwError $ BadSpecialForm "Unrecognized special form" badForm
|
||||
|
||||
apply :: String -> [LispVal] -> ThrowsError LispVal
|
||||
apply func args = maybe (throwError $ NotFunction "Unrecognized primitive function args" func)
|
||||
($ args)
|
||||
(lookup func primitives)
|
||||
apply :: LispVal -> [LispVal] -> IOThrowsError LispVal
|
||||
apply (PrimitiveFunc func) args = liftThrows $ func args
|
||||
apply (Func params varargs body closure) args =
|
||||
if num params /= num args && varargs == Nothing
|
||||
then throwError $ NumArgs (num params) args
|
||||
else (liftIO $ bindVars closure $ zip params args) >>= bindVarArgs varargs >>= evalBody
|
||||
where remainingArgs = drop (length params) args
|
||||
num = toInteger . length
|
||||
evalBody env = liftM last $ mapM (eval env) body
|
||||
bindVarArgs arg env = case arg of
|
||||
Just argName -> liftIO $ bindVars env [(argName, List $ remainingArgs)]
|
||||
Nothing -> return env
|
||||
|
||||
primitiveBindings :: IO Env
|
||||
primitiveBindings = nullEnv >>= (flip bindVars $ map makePrimitiveFunc primitives)
|
||||
where makePrimitiveFunc (var, func) = (var, PrimitiveFunc func)
|
||||
|
||||
primitives :: [(String, [LispVal] -> ThrowsError LispVal)]
|
||||
primitives = [("+", numericBinop (+)),
|
||||
|
@ -333,10 +367,10 @@ until_ pred prompt action = do
|
|||
else action result >> until_ pred prompt action
|
||||
|
||||
runOne :: String -> IO ()
|
||||
runOne expr = nullEnv >>= flip evalAndPrint expr
|
||||
runOne expr = primitiveBindings >>= flip evalAndPrint expr
|
||||
|
||||
runRepl :: IO ()
|
||||
runRepl = nullEnv >>= until_ (== "quit") (readPrompt "Lisp>>> ") . evalAndPrint
|
||||
runRepl = primitiveBindings >>= until_ (== "quit") (readPrompt "Lisp>>> ") . evalAndPrint
|
||||
|
||||
-- variables
|
||||
-- Simulate states in Haskell using monad
|
||||
|
@ -389,6 +423,12 @@ bindVars envRef bindings = readIORef envRef >>= extendEnv bindings >>= newIORef
|
|||
addBindings (var, value) = do ref <- newIORef value
|
||||
return (var, ref)
|
||||
|
||||
-- functions
|
||||
|
||||
makeFunc varargs env params body = return $ Func (map showVal params) varargs body env
|
||||
makeNormalFunc = makeFunc Nothing
|
||||
makeVarargs = makeFunc . Just . showVal
|
||||
|
||||
-- Main
|
||||
readExpr :: String -> ThrowsError LispVal
|
||||
readExpr input = case parse parseExpr "lisp" input of
|
||||
|
|
Loading…
Reference in a new issue