lamb/interp.hs

185 lines
5.3 KiB
Haskell
Raw Normal View History

2013-10-20 22:55:30 +00:00
module Interp where
2013-10-20 23:30:39 +00:00
import Prelude hiding (lookup)
2013-10-18 01:58:41 +00:00
import qualified Data.Map as M
2013-10-20 22:55:30 +00:00
import Control.Monad.State (State, runState, evalState, get, put)
import System.IO (Handle, hPutStr, hGetLine, hFlush, stdout, stdin)
import System.IO.Unsafe (unsafePerformIO)
import AST
import Parser (parseProgram)
2013-10-18 01:58:41 +00:00
-- for Show
newtype BIF = BIF (Value -> InterpState Value)
instance Show BIF where show _ = "<built-in>"
instance Eq BIF where a == b = False
data Value = IntV Integer
2013-10-18 01:58:41 +00:00
| StrV String
| UnitV
2013-10-20 04:34:30 +00:00
| StreamV Int
2013-10-18 20:24:00 +00:00
| ListV [Value]
| Builtin BIF
2013-10-18 08:14:13 +00:00
| FnV [(Pattern, [AST])] -- pattern->body bindings
2013-10-18 01:58:41 +00:00
deriving (Show, Eq)
type Env = M.Map String Value -- an environment
type InterpState = State ([Handle], Env) -- interpreter state (open handles, global env)
2013-10-18 01:58:41 +00:00
2013-10-20 23:30:39 +00:00
lookup :: Env -> String -> Maybe Value
lookup env name = M.lookup name env
2013-10-20 23:32:23 +00:00
bind :: Env -> String -> Value -> Env
bind env name value = M.insert name value env
2013-10-18 01:58:41 +00:00
(IntV l) +$ (IntV r) = IntV (l + r)
(StrV l) +$ (StrV r) = StrV (l ++ r)
l +$ r = error $ "cannot + " ++ show l ++ " and " ++ show r
-- these are pretty nasty and instead of using unsafePerformIO
-- we could throw eval, etc. into StateT with IO instead, but then
-- everything would be in IO.
_putstr (StrV str) = do
(handles,_) <- get
let stdout_s = head handles
let io = unsafe_putstr stdout_s str
return $ seq io UnitV
where
{-# NOINLINE unsafe_putstr #-}
unsafe_putstr h s = unsafePerformIO $ hPutStr h s >> hFlush h
_getline UnitV = do
(handles,_) <- get
let stdin_s = handles !! 1
let str = unsafe_getline stdin_s
return $ seq () $ StrV str
where
{-# NOINLINE unsafe_getline #-}
unsafe_getline h = unsafePerformIO $ hGetLine h
initialState = ([stdout, stdin],
M.fromList [("id", FnV [(VarP "x", [Var "x"])]),
("stdout", StreamV 0),
("putstr", Builtin $ BIF _putstr),
("putstrln", Builtin $ BIF (\x -> _putstr $ x +$ StrV "\n")),
2013-10-20 23:26:53 +00:00
("itos", Builtin $ BIF (\(IntV i) -> return $ StrV $ show i)),
("getline", Builtin $ BIF _getline)])
2013-10-18 03:46:34 +00:00
2013-10-18 01:58:41 +00:00
eval :: AST -> InterpState Value
eval (IntConst i) = return $ IntV i
eval (StrConst s) = return $ StrV s
eval UnitConst = return UnitV
2013-10-20 23:04:56 +00:00
eval (Block body) = foldr1 (>>) $ map eval body
2013-10-18 20:24:00 +00:00
eval (ListConst v) =
mapM eval v >>= \xs ->
return $ ListV xs
2013-10-20 23:30:39 +00:00
eval (Var var) = get >>= \(_,env) ->
case lookup env var of
2013-10-18 01:58:41 +00:00
Just v -> return v
Nothing -> error $ "unbound variable " ++ var
eval (Defun name fn) = do
(s,env) <- get
case lookup env name of
Nothing -> -- bind new fn
eval fn >>= \fn' ->
put (s, bind env name fn') >> return fn'
Just oldfn -> -- add pattern to old fn
let FnV oldpats = oldfn
Lambda [(pat, body)] = fn
newfn = FnV ((pat, body):oldpats) in
put (s, bind env name newfn) >> return newfn
2013-10-18 01:58:41 +00:00
eval (Def name v') = do
v <- eval v'
2013-10-20 23:32:23 +00:00
(s,env) <- get
put (s, bind env name v)
2013-10-18 01:58:41 +00:00
return v
2013-10-18 08:14:13 +00:00
eval (Lambda pats) =
return $ FnV pats
2013-10-18 01:58:41 +00:00
eval (Add l r) = do
l <- eval l
r <- eval r
return $ l +$ r
2013-10-20 23:30:39 +00:00
eval (Call name args) = get >>= \(_,env) ->
case lookup env name of
2013-10-18 03:46:34 +00:00
Just fn@(FnV _) ->
do
xargs <- mapM eval args
2013-10-18 08:14:13 +00:00
applyMany fn xargs
Just fn@(Builtin _) -> mapM eval args >>= applyMany fn
2013-10-18 03:46:34 +00:00
Nothing -> error $ "call: name " ++ name ++ " doesn't exist or is not a function"
patternBindings :: Pattern -> Value -> Maybe Env
patternBindings (VarP n) v = Just $ M.fromList [(n, v)]
2013-10-18 20:49:33 +00:00
patternBindings (IntP n) (IntV v)
| v == n = Just M.empty
| otherwise = Nothing
patternBindings (IntP n) _ = Nothing
2013-10-18 03:46:34 +00:00
patternBindings UnitP UnitV = Just M.empty
patternBindings UnitP _ = Nothing
2013-10-18 21:06:27 +00:00
patternBindings (ConsP x (ListP [])) (ListV (y:[])) = patternBindings x y
2013-10-18 20:49:33 +00:00
patternBindings (ConsP _ _) (ListV (_:[])) = Nothing
patternBindings (ConsP xp xsp) (ListV (x:xs)) =
do
xe <- patternBindings xp x
xse <- patternBindings xsp $ ListV xs
Just $ M.union xe xse
2013-10-19 09:21:04 +00:00
patternBindings (ConsP _ _) _ = Nothing
2013-10-18 20:49:33 +00:00
2013-10-18 21:06:27 +00:00
patternBindings (ListP []) (ListV (x:xs)) = Nothing -- not enough patterns
patternBindings (ListP (_:_)) (ListV []) = Nothing -- not enough values
patternBindings (ListP []) (ListV []) = Just M.empty -- base case
patternBindings (ListP (x:xs)) (ListV (y:ys)) =
do
env <- patternBindings x y
env' <- patternBindings (ListP xs) (ListV ys)
Just $ M.union env' env
2013-10-21 00:04:12 +00:00
patternBindings (ListP _) _ = Nothing -- not a list
2013-10-18 21:06:27 +00:00
2013-10-18 08:14:13 +00:00
-- applies many arguments to a function
applyMany :: Value -> [Value] -> InterpState Value
applyMany fn@(FnV _) (arg:xs) =
apply fn arg >>= \value ->
applyMany value xs
applyMany (Builtin (BIF fn)) (arg:xs) =
fn arg >>= \value ->
applyMany value xs
2013-10-18 08:14:13 +00:00
applyMany value [] = return value
applyMany _ xs = error "couldn't apply all arguments"
2013-10-18 03:46:34 +00:00
-- applies a function
2013-10-18 08:14:13 +00:00
apply :: Value -> Value -> InterpState Value
apply (FnV pats) arg =
apply' pats
2013-10-18 03:46:34 +00:00
where
apply' [] = error $ "argument " ++ show arg ++ " doesn't satisfy any patterns"
2013-10-18 08:14:13 +00:00
apply' ((pat, body):xs) =
2013-10-18 03:46:34 +00:00
case patternBindings pat arg of
Just env' -> -- satisfies
do
2013-10-20 04:34:30 +00:00
(s,env) <- get
put (s, M.union env env')
2013-10-18 03:46:34 +00:00
foldr1 (>>) $ map eval body
Nothing -> -- doesn't satisfy this pattern
apply' xs
2013-10-18 03:46:34 +00:00
2013-10-18 01:58:41 +00:00
evalProgram :: [AST] -> Value -- fold the state from each node and return the result
2013-10-18 03:46:34 +00:00
evalProgram nodes = evalState (foldr1 (>>) $ map eval nodes) initialState
2013-10-18 01:58:41 +00:00
2013-10-19 09:21:34 +00:00
evalString :: String -> Value
evalString program =
case parseProgram program of
Left err -> error $ show err
Right prg -> evalProgram prg