push
This commit is contained in:
commit
7e72c415ed
1 changed files with 163 additions and 0 deletions
163
main.hs
Normal file
163
main.hs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
{-# LANGUAGE LambdaCase #-}
|
||||
|
||||
import qualified Data.Map as Map
|
||||
import Data.Map (Map)
|
||||
import Control.Monad
|
||||
import System.IO
|
||||
data Expr
|
||||
= Number Integer
|
||||
| Symbol String
|
||||
| List [Expr]
|
||||
| Lambda [String] Expr Env
|
||||
| Macro [String] Expr Env
|
||||
| Builtin ([Expr] -> Eval Expr)
|
||||
| Thunk Expr Env
|
||||
|
||||
instance Show Expr where
|
||||
show (Number n) = show n
|
||||
show (Symbol s) = s
|
||||
show (List xs) = "(" ++ unwords (map show xs) ++ ")"
|
||||
show (Lambda _ _ _) = "<lambda>"
|
||||
show (Macro _ _ _) = "<macro>"
|
||||
show (Builtin _) = "<builtin>"
|
||||
show (Thunk _ _) = "<thunk>"
|
||||
|
||||
type Env = Map String Expr
|
||||
type Eval a = Either String a
|
||||
|
||||
eval :: Env -> Expr -> Eval Expr
|
||||
eval env = \case
|
||||
Number n -> return (Number n)
|
||||
|
||||
Symbol s ->
|
||||
case Map.lookup s env of
|
||||
Just (Thunk e eEnv) -> eval eEnv e
|
||||
Just v -> return v
|
||||
Nothing -> Left ("Unbound symbol: " ++ s)
|
||||
|
||||
List [] -> return (List [])
|
||||
|
||||
List (f:args) -> do
|
||||
func <- eval env f
|
||||
apply env func args
|
||||
|
||||
lam@(Lambda {}) -> return lam
|
||||
mac@(Macro {}) -> return mac
|
||||
bi@(Builtin {}) -> return bi
|
||||
Thunk e eEnv -> eval eEnv e
|
||||
|
||||
apply :: Env -> Expr -> [Expr] -> Eval Expr
|
||||
|
||||
apply env (Builtin f) args = do
|
||||
argVals <- mapM (eval env) args
|
||||
f argVals
|
||||
|
||||
-- Lazy lambdas
|
||||
apply env (Lambda params body closure) args =
|
||||
if length params /= length args
|
||||
then Left "Argument mismatch"
|
||||
else
|
||||
let thunked = zip params (map (\a -> Thunk a env) args)
|
||||
newEnv = Map.union (Map.fromList thunked) closure
|
||||
in eval newEnv body
|
||||
|
||||
-- Macros (no evaluation of args!)
|
||||
apply env (Macro params body closure) args =
|
||||
if length params /= length args
|
||||
then Left "Macro arg mismatch"
|
||||
else do
|
||||
let newEnv = Map.union (Map.fromList (zip params args)) closure
|
||||
expanded <- eval newEnv body
|
||||
eval env expanded
|
||||
|
||||
apply _ _ _ = Left "this aint a function!"
|
||||
|
||||
|
||||
numOp f [Number a, Number b] = return $ Number (f a b)
|
||||
numOp _ _ = Left "i said give me 2 numbers!"
|
||||
|
||||
builtins :: Env
|
||||
builtins = Map.fromList
|
||||
[ ("+", Builtin (numOp (+)))
|
||||
, ("-", Builtin (numOp (-)))
|
||||
, ("*", Builtin (numOp (*)))
|
||||
, ("/", Builtin (numOp div))
|
||||
, ("list", Builtin (\xs -> return $ List xs))
|
||||
, ("car", Builtin $ \case
|
||||
[List (x:_)] -> return x
|
||||
_ -> Left "car error")
|
||||
, ("cdr", Builtin $ \case
|
||||
[List (_:xs)] -> return (List xs)
|
||||
_ -> Left "cdr error")
|
||||
]
|
||||
|
||||
special :: Env
|
||||
special = Map.fromList
|
||||
[ ("lambda", Builtin lambdaForm)
|
||||
, ("macro", Builtin macroForm)
|
||||
, ("if", Builtin ifForm)
|
||||
, ("quote", Builtin quoteForm)
|
||||
]
|
||||
|
||||
lambdaForm [List params, body] =
|
||||
return $ Lambda (map getSym params) body Map.empty
|
||||
lambdaForm _ = Left "lambda error"
|
||||
|
||||
macroForm [List params, body] =
|
||||
return $ Macro (map getSym params) body Map.empty
|
||||
macroForm _ = Left "macro error"
|
||||
|
||||
ifForm [cond, t, f] = do
|
||||
v <- eval globalEnv cond
|
||||
case v of
|
||||
Number 0 -> eval globalEnv f
|
||||
_ -> eval globalEnv t
|
||||
ifForm _ = Left "if error"
|
||||
|
||||
quoteForm [x] = return x
|
||||
quoteForm _ = Left "quote error"
|
||||
|
||||
getSym (Symbol s) = s
|
||||
getSym _ = error "unexpected"
|
||||
|
||||
tokenize :: String -> [String]
|
||||
tokenize [] = []
|
||||
tokenize (c:cs)
|
||||
| c `elem` "()" = [c] : tokenize cs
|
||||
| c == ' ' = tokenize cs
|
||||
| otherwise =
|
||||
let (tok, rest) = span (`notElem` " ()") (c:cs)
|
||||
in tok : tokenize rest
|
||||
|
||||
parse :: [String] -> (Expr, [String])
|
||||
parse ("(":ts) = go [] ts
|
||||
where
|
||||
go acc (")":rest) = (List (reverse acc), rest)
|
||||
go acc ts =
|
||||
let (e, ts') = parse ts
|
||||
in go (e:acc) ts'
|
||||
parse (t:ts)
|
||||
| all (`elem` "-0123456789") t = (Number (read t), ts)
|
||||
| otherwise = (Symbol t, ts)
|
||||
parse [] = error "parse error"
|
||||
|
||||
readExpr :: String -> Expr
|
||||
readExpr s = fst $ parse (tokenize s)
|
||||
|
||||
globalEnv :: Env
|
||||
globalEnv = Map.union builtins special
|
||||
|
||||
repl :: IO ()
|
||||
repl = do
|
||||
putStr "haslisp> "
|
||||
hFlush stdout
|
||||
line <- getLine
|
||||
unless (line == ":q") $ do
|
||||
let expr = readExpr line
|
||||
case eval globalEnv expr of
|
||||
Left err -> putStrLn ("Error: " ++ err)
|
||||
Right val -> print val
|
||||
repl
|
||||
|
||||
main :: IO ()
|
||||
main = repl
|
||||
Loading…
Add table
Add a link
Reference in a new issue