lamb/Interp.hs

548 lines
18 KiB
Haskell
Raw Normal View History

2013-10-21 00:48:02 +00:00
-- Interpreter for the Lamb programming language
-- Copyright (c) 2013 darkf
-- Licensed under the terms of the zlib license, see LICENSE for details
2013-10-20 22:55:30 +00:00
module Interp where
2017-06-01 20:26:33 +00:00
import Prelude hiding (lookup, (<$))
2014-11-02 08:08:32 +00:00
import qualified Data.Map.Strict as M
2013-10-24 03:50:55 +00:00
import qualified Data.ByteString.Char8 as BSC
2013-10-24 04:29:43 +00:00
import qualified Network.Socket as SO
2014-11-01 12:14:38 +00:00
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
2014-11-02 04:36:09 +00:00
import Data.List (intercalate, foldl1')
2015-02-13 11:12:36 +00:00
import Data.Time.Clock.POSIX (getPOSIXTime)
2017-06-01 20:26:33 +00:00
import Data.Bits
2014-11-01 12:14:38 +00:00
import Control.Applicative ((<$>))
2013-12-17 09:26:42 +00:00
import Control.Exception (try, SomeException)
2015-02-13 12:08:58 +00:00
import Control.Concurrent (ThreadId, forkIO, threadDelay, killThread)
import Control.Concurrent.STM (atomically)
import Control.Concurrent.STM.TVar (TVar, newTVarIO, readTVar, writeTVar)
2013-12-17 09:42:21 +00:00
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.State (StateT, runStateT, evalStateT, get, put)
import System.IO (Handle, hPutStr, hGetLine, hClose, hIsEOF, hSetBuffering,
hSetBinaryMode, openBinaryFile, IOMode(..), BufferMode(NoBuffering), stdout, stdin)
2013-10-29 01:48:57 +00:00
import System.Directory (doesFileExist)
2014-02-12 08:50:09 +00:00
import System.FilePath (FilePath, splitExtension, takeBaseName, takeDirectory, (</>))
import System.Environment (getExecutablePath)
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
2013-10-29 09:09:00 +00:00
instance Ord BIF where compare a b = if a == b then EQ else LT
data Value = IntV Integer
2014-11-01 12:14:38 +00:00
| StrV T.Text
2013-10-23 21:41:44 +00:00
| BoolV Bool
2014-02-12 09:33:50 +00:00
| StreamV Handle
2013-10-22 22:10:34 +00:00
| TupleV [Value]
2013-10-18 20:24:00 +00:00
| ListV [Value]
2013-10-29 01:48:57 +00:00
| DictV (M.Map Value Value)
| RefV (TVar Value)
2015-02-13 12:08:58 +00:00
| Thread ThreadId
| Builtin BIF
| FnV Env [(Pattern, AST)] -- closure pattern->body bindings
2013-10-21 05:37:58 +00:00
deriving (Eq)
2013-10-18 01:58:41 +00:00
2013-10-29 09:09:00 +00:00
instance Ord Value where
compare (IntV a) (IntV b) = compare a b
compare (StrV a) (StrV b) = compare a b
compare (BoolV a) (BoolV b) = compare a b
compare (TupleV a) (TupleV b) = compare a b
compare (ListV a) (ListV b) = compare a b
2014-02-12 09:33:50 +00:00
compare (StreamV a) (StreamV b) = if a == b then EQ else LT
2013-10-29 09:09:00 +00:00
compare (Builtin a) (Builtin b) = compare a b
compare (FnV a b) (FnV x y) = if a == x && b == y then EQ else LT
compare (DictV a) (DictV b) = compare a b
compare _ _ = error "compare: not valid"
2014-11-02 08:08:32 +00:00
type Env = [M.Map T.Text Value] -- lexical environment (linked list)
2014-02-12 09:33:50 +00:00
type InterpState = StateT Env IO -- interpreter state (open handles, global env)
2013-10-18 01:58:41 +00:00
2014-11-02 08:08:32 +00:00
type StrDict = M.Map T.Text Value
type ValueDict = M.Map Value Value
emptyEnv = [M.empty]
unitv = TupleV []
-- look up a binding from the bottom up
2014-11-02 08:08:32 +00:00
lookup :: Env -> T.Text -> Maybe Value
lookup [] _ = Nothing
2014-02-12 08:50:09 +00:00
lookup (env:xs) name = maybe (lookup xs name) Just (M.lookup name env)
2013-10-20 23:30:39 +00:00
-- bind in the local environment
2014-11-02 08:08:32 +00:00
bind :: Env -> T.Text -> Value -> Env
bind (env:xs) name value = (M.insert name value env):xs
2013-10-20 23:32:23 +00:00
2013-10-21 05:37:58 +00:00
instance Show Value where
show (IntV i) = show i
show (StrV s) = show s
show (BoolV b) = show b
2013-11-09 10:06:23 +00:00
show (TupleV []) = "(,)"
2013-10-22 22:10:34 +00:00
show (TupleV v) = "(" ++ intercalate "," (map show v) ++ ")"
2013-10-21 05:37:58 +00:00
show (ListV v) = show v
2013-10-29 01:48:57 +00:00
show (DictV d) = "<dict " ++ show d ++ ">"
show (FnV _ _) = "<fn>"
2013-10-21 05:37:58 +00:00
show (StreamV _) = "<stream>"
show (Builtin _) = "<built-in>"
2014-02-12 10:11:36 +00:00
show (RefV _) = "<ref>"
2015-02-13 12:08:58 +00:00
show (Thread t) = "<thread " ++ show t ++ ">"
2013-10-21 05:37:58 +00:00
2013-10-21 00:24:51 +00:00
-- value operators
2013-10-18 01:58:41 +00:00
(IntV l) +$ (IntV r) = IntV (l + r)
2014-11-01 12:14:38 +00:00
(StrV l) +$ (StrV r) = StrV (l `T.append` r)
2013-10-23 22:06:57 +00:00
(ListV l) +$ (ListV r) = ListV (l ++ r)
2013-10-18 01:58:41 +00:00
l +$ r = error $ "cannot + " ++ show l ++ " and " ++ show r
2013-10-21 00:24:51 +00:00
(IntV l) -$ (IntV r) = IntV (l - r)
l -$ r = error $ "cannot - " ++ show l ++ " and " ++ show r
(IntV l) *$ (IntV r) = IntV (l * r)
l *$ r = error $ "cannot * " ++ show l ++ " and " ++ show r
(IntV l) /$ (IntV r) = IntV (l `div` r)
l /$ r = error $ "cannot / " ++ show l ++ " and " ++ show r
(IntV l) <$ (IntV r) = BoolV (l < r)
l <$ r = error $ "cannot < " ++ show l ++ " and " ++ show r
(IntV l) >$ (IntV r) = BoolV (l > r)
l >$ r = error $ "cannot > " ++ show l ++ " and " ++ show r
2017-06-01 20:26:33 +00:00
(IntV l) &$ (IntV r) = IntV (l .&. r)
l &$ r = error $ "cannot & " ++ show l ++ " and " ++ show r
(IntV l) |$ (IntV r) = IntV (l .|. r)
l |$ r = error $ "cannot | " ++ show l ++ " and " ++ show r
(IntV l) <<$ (IntV r) = IntV (l `shiftL` fromInteger r)
l <<$ r = error $ "cannot << " ++ show l ++ " and " ++ show r
(IntV l) >>$ (IntV r) = IntV (l `shiftR` fromInteger r)
l >>$ r = error $ "cannot >> " ++ show l ++ " and " ++ show r
bitNot (IntV v) = IntV (complement v)
bitNot v = error $ "cannot ~ " ++ show v
l ==$ r = BoolV (l == r)
l !=$ r = BoolV (l /= r)
2014-11-02 08:08:32 +00:00
toDict :: StrDict -> Value
toDict m = DictV (M.mapKeys StrV m) -- wrap keys in StrV
2013-12-17 08:51:48 +00:00
2014-11-02 08:08:32 +00:00
fromDict :: ValueDict -> StrDict
fromDict m = M.mapKeys (\(StrV k) -> k) m -- unwrap keys
2013-12-17 09:26:42 +00:00
-- some built-in functions
2014-11-02 04:06:35 +00:00
_fputstr (TupleV [StreamV handle, StrV str]) =
liftIO $ TIO.hPutStr handle str >> return unitv
2014-02-12 09:33:50 +00:00
_fgetline (StreamV handle) = do
2014-11-01 12:14:38 +00:00
str <- liftIO $ TIO.hGetLine handle
if T.last str == '\r' then -- remove trailing CR
return . StrV $ T.init str
2013-10-24 06:36:24 +00:00
else return $ StrV str
2014-02-12 09:33:50 +00:00
_freadbytes (TupleV [StreamV handle, IntV n]) = do
2014-11-01 12:14:38 +00:00
liftIO $ StrV . T.take (fromIntegral n) <$> TIO.hGetContents handle
2013-10-24 03:50:55 +00:00
2015-01-17 11:14:17 +00:00
_freadcontents (StreamV handle) = do
liftIO $ StrV <$> TIO.hGetContents handle
2013-10-24 03:50:55 +00:00
_fopen (TupleV [StrV path, StrV mode]) = do
2014-11-01 12:14:38 +00:00
let mode' = case T.unpack mode of
2013-10-24 03:50:55 +00:00
"r" -> ReadMode
"w" -> WriteMode
"rw" -> ReadWriteMode
2014-11-02 04:36:09 +00:00
StreamV <$> liftIO (openBinaryFile (T.unpack path) mode')
2013-10-24 04:03:36 +00:00
2014-02-12 09:33:50 +00:00
_feof (StreamV handle) = do
2014-11-02 04:36:09 +00:00
BoolV <$> liftIO (hIsEOF handle)
2013-10-24 03:50:55 +00:00
2014-02-12 09:33:50 +00:00
_fclose (StreamV handle) = do
2014-11-02 04:36:09 +00:00
liftIO (hClose handle) >> return unitv
2013-10-24 03:50:55 +00:00
2013-10-24 04:29:43 +00:00
_sockopen (TupleV [StrV host, IntV port]) = do
2014-02-12 09:33:50 +00:00
liftIO $ SO.withSocketsDo $ do
2014-11-01 12:14:38 +00:00
addr:_ <- SO.getAddrInfo Nothing (Just $ T.unpack host) (Just $ show port)
sock <- SO.socket (SO.addrFamily addr) SO.Stream SO.defaultProtocol
SO.connect sock (SO.addrAddress addr)
handle <- SO.socketToHandle sock ReadWriteMode
hSetBuffering handle NoBuffering
2014-02-12 09:33:50 +00:00
return $ StreamV handle
2013-10-24 04:29:43 +00:00
2014-02-12 09:33:50 +00:00
_putstr str@(StrV _) = _fputstr $ TupleV [StreamV stdout, str]
2014-11-02 04:06:35 +00:00
_putbytes str@(StrV _) = _fputstr $ TupleV [StreamV stdout, str]
2014-02-12 09:33:50 +00:00
_getline (TupleV []) = _fgetline (StreamV stdin)
2014-11-01 12:14:38 +00:00
_print v = _putbytes $ StrV $ T.pack (show v) `T.snoc` '\n'
_repr v = return . StrV $ T.pack $ show v
2014-11-01 12:14:38 +00:00
_itos (IntV i) = return $ StrV $ T.pack $ show i
2013-10-21 00:25:38 +00:00
_itos v = error $ "itos: not an int: " ++ show v
2015-01-17 09:07:13 +00:00
_stoi (StrV s) = return $ IntV $ read $ T.unpack s
_stoi v = error $ "stoi: not a string: " ++ show v
2017-06-01 21:30:44 +00:00
_ord (StrV s) = return $ IntV $ toInteger $ fromEnum $ T.head s
_ord v = error $ "ord: not a string: " ++ show v
_chr (IntV i) = return $ StrV $ T.singleton (toEnum (fromInteger i) :: Char)
_chr v = error $ "chr: not an integer: " ++ show v
_ref v = RefV <$> liftIO (newTVarIO v)
2014-02-12 10:11:36 +00:00
_readRef (RefV r) = liftIO $ atomically $ readTVar r
2014-02-12 10:11:36 +00:00
2014-11-02 04:36:09 +00:00
_setRef (TupleV [RefV r, v]) =
liftIO (atomically $ writeTVar r v) >> return v
2014-02-12 10:11:36 +00:00
2015-02-13 11:12:36 +00:00
_time (TupleV []) = fmap IntV $ liftIO $ round <$> getPOSIXTime
_sleep (IntV milliseconds) = liftIO (threadDelay (fromInteger $ 1000 * milliseconds)) >> return unitv
2013-10-24 02:33:44 +00:00
_loop args@(TupleV [fn@(FnV _ _), arg]) = do
v <- apply fn arg
if v /= BoolV False then
_loop $ TupleV [fn, v]
else return arg
2013-10-24 02:33:44 +00:00
2013-12-17 09:26:42 +00:00
_eval (TupleV [StrV code, DictV env]) = do
let trySome :: IO a -> IO (Either SomeException a)
trySome = try
2014-02-12 09:33:50 +00:00
state = [fromDict env]
ret <- liftIO . trySome $ evalStateT (evalString code) state
2013-12-17 09:26:42 +00:00
case ret of
2014-11-01 12:14:38 +00:00
Left err -> return $ TupleV [StrV (T.pack "err"), StrV $ T.pack (show err)]
2013-12-17 09:26:42 +00:00
Right v -> return v
2014-11-02 04:36:09 +00:00
2013-12-17 09:26:42 +00:00
_eval (TupleV [code@(StrV _), (ListV env)]) =
let env' = map (\(TupleV [k,v]) -> (k,v)) env in
_eval (TupleV [code, DictV $ M.fromList env'])
2014-11-02 04:36:09 +00:00
2013-12-17 09:26:42 +00:00
_eval _ = error "eval: invalid args (want code and environment)"
2015-02-13 10:48:13 +00:00
_thread f@FnV{} = do
state <- get
2015-02-13 12:08:58 +00:00
fmap Thread $ liftIO $ forkIO $ (evalStateT (apply f unitv) state >> return ())
2015-02-13 10:48:13 +00:00
_thread _ = error "thread!: need a function"
2015-02-13 12:08:58 +00:00
_kill (Thread thread) = liftIO (killThread thread) >> return unitv
_kill _ = error "kill!: need a thread"
2013-12-17 08:51:48 +00:00
-- returns a dictionary of a new environment with only the standard
-- default-imported functions
_newStdEnv (TupleV []) = do
2014-02-12 09:33:50 +00:00
let [stdEnv] = initialState
2013-12-17 08:51:48 +00:00
return $ toDict stdEnv
2013-12-17 08:55:02 +00:00
_globals (TupleV []) = do
2014-02-12 09:33:50 +00:00
env <- get
2013-12-17 08:55:02 +00:00
return $ toDict (last env)
_locals (TupleV []) = do
2014-02-12 09:33:50 +00:00
locals:_ <- get
2013-12-17 08:55:02 +00:00
return $ toDict locals
2013-10-29 01:48:57 +00:00
-- import a module name as a module
_Import (StrV modname) = do
2014-02-12 09:33:50 +00:00
env <- get -- save current state
2013-10-29 01:48:57 +00:00
put initialState
2014-11-01 12:14:38 +00:00
(path,modname) <- liftIO $ findModule $ T.unpack modname -- find the module file
2013-10-29 01:48:57 +00:00
evalFile path -- evaluate the module file
2014-02-12 09:33:50 +00:00
[modenv] <- get -- get the module env
let [initialEnv] = initialState
--let modenv' = M.difference modenv initialEnv -- subtract prelude stuff
let mod = toDict modenv
2014-11-02 08:08:32 +00:00
let env' = bind env (T.pack modname) mod -- bind it
2014-02-12 09:33:50 +00:00
put env' -- restore state
2013-10-29 01:48:57 +00:00
return mod -- return module value
where
findModule :: FilePath -> IO (FilePath, String)
findModule modname = do
execPath <- fmap takeDirectory getExecutablePath
findModuleIn [".", execPath </> "mods"] -- search paths for modules
where
findModuleIn [] = error $ "module " ++ modname ++ " couldn't be found"
findModuleIn (dir:xs) = do
let path = dir </> modname ++ ".lamb"
exists <- doesFileExist path
if exists then return (path, takeBaseName path)
else findModuleIn xs
2013-10-29 01:48:57 +00:00
2014-02-12 10:11:36 +00:00
bif = Builtin . BIF
2014-11-02 08:08:32 +00:00
initialState = [M.fromList $ map (\(k,v) -> (T.pack k, v)) $ [
2014-11-02 08:14:23 +00:00
("id", FnV emptyEnv [(VarP (T.pack "x"), Var (T.pack "x"))]),
2014-02-12 10:11:36 +00:00
("loop", bif _loop),
("ref!", bif _ref),
("readRef!", bif _readRef),
("setRef!", bif _setRef),
2015-02-13 11:12:36 +00:00
("time!", bif _time),
("sleep!", bif _sleep),
2014-02-12 10:11:36 +00:00
("repr", bif _repr),
("stdout", StreamV stdout),
("stdin", StreamV stdin),
("print", bif _print),
("putstr", bif _putstr),
2014-11-01 12:14:38 +00:00
("putstrln", bif (\x -> _putstr $ x +$ StrV (T.singleton '\n'))),
2014-02-12 10:11:36 +00:00
("getline", bif _getline),
("fgetline", bif _fgetline),
("putbytes", bif _putbytes),
2014-11-02 04:06:35 +00:00
("fputbytes", bif _fputstr),
2014-02-12 10:11:36 +00:00
("fputstr", bif _fputstr),
("freadbytes", bif _freadbytes),
2015-01-17 11:14:17 +00:00
("freadcontents", bif _freadcontents),
2014-02-12 10:11:36 +00:00
("feof", bif _feof),
("fclose", bif _fclose),
("fopen", bif _fopen),
("sockopen", bif _sockopen),
("itos", bif _itos),
2015-01-17 09:07:13 +00:00
("stoi", bif _stoi),
2017-06-01 21:30:44 +00:00
("ord", bif _ord),
("chr", bif _chr),
2014-02-12 10:11:36 +00:00
("globals", bif _globals),
("locals", bif _locals),
("newStdEnv", bif _newStdEnv),
2015-02-13 10:48:13 +00:00
("thread!", bif _thread),
2015-02-13 12:08:58 +00:00
("kill!", bif _kill),
2014-02-12 10:11:36 +00:00
("eval", bif _eval),
("import", bif _Import)]]
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
2014-11-02 08:14:23 +00:00
eval (StrConst s) = return $ StrV s
2013-10-23 21:41:44 +00:00
eval (BoolConst b) = return $ BoolV b
2013-10-18 01:58:41 +00:00
2013-10-20 23:04:56 +00:00
eval (Block body) = foldr1 (>>) $ map eval body
2013-10-21 05:27:27 +00:00
eval (Cons a b) = do
a' <- eval a
b' <- eval b
case b' of
ListV v' -> return $ ListV $ a':v'
2015-01-17 08:52:12 +00:00
StrV v' ->
case a' of
StrV c | T.length c == 1 -> return $ StrV $ T.cons (T.head c) v'
_ -> error "cons: LHS must be a char"
_ -> error "cons: RHS must be a list or string"
2013-10-21 05:27:27 +00:00
2014-11-02 04:36:09 +00:00
eval (ListConst v) = ListV <$> mapM eval v
eval (TupleConst v) = TupleV <$> mapM eval v
2013-10-22 22:10:34 +00:00
2013-10-23 21:43:30 +00:00
eval (IfExpr c t e) = eval c >>= \cond ->
case cond of
BoolV True -> eval t
BoolV False -> eval e
_ -> error "if: condition must be a boolean"
2014-02-12 09:33:50 +00:00
eval (Var var) = get >>= \env ->
2014-11-02 08:14:23 +00:00
maybe (error $ "unbound variable " ++ T.unpack var) return (lookup env var)
2013-10-18 01:58:41 +00:00
eval (Defun name fn) = do
2014-02-12 09:33:50 +00:00
env <- get
2014-11-02 08:14:23 +00:00
case lookup env name of
Nothing -> -- bind new fn
eval fn >>= \fn' ->
2014-11-02 08:14:23 +00:00
put (bind env name fn') >> return fn'
Just oldfn -> -- add pattern to old fn
let FnV cls oldpats = oldfn
Lambda [(pat, body)] = fn
newfn = FnV cls (oldpats ++ [(pat, body)]) in
2014-11-02 08:14:23 +00:00
put (bind env name newfn) >> return newfn
eval (Def pat v') = do
2013-10-18 01:58:41 +00:00
v <- eval v'
2014-02-12 09:33:50 +00:00
locals:xs <- get
case patternBindings pat v of
Nothing -> error $ "pattern binding doesn't satisfy: " ++ show v ++ " with " ++ show pat
2014-02-12 09:33:50 +00:00
Just bindings -> do
put $ (M.union bindings locals):xs -- update our local bindings
return v
2013-10-18 01:58:41 +00:00
2014-02-12 09:33:50 +00:00
eval (Lambda pats) = do
env <- get
if length env == 1 then -- if in global env just use [], denoting the current global scope
return $ FnV [] pats
else return $ FnV env pats
2013-10-18 08:14:13 +00:00
2013-10-21 00:24:51 +00:00
eval (Add l r) = do { l <- eval l; r <- eval r; return $ l +$ r }
eval (Sub l r) = do { l <- eval l; r <- eval r; return $ l -$ r }
eval (Mul l r) = do { l <- eval l; r <- eval r; return $ l *$ r }
eval (Div l r) = do { l <- eval l; r <- eval r; return $ l /$ r }
2013-10-18 01:58:41 +00:00
eval (Equals l r) = do { l <- eval l; r <- eval r; return $ l ==$ r }
eval (NotEquals l r) = do { l <- eval l; r <- eval r; return $ l !=$ r }
eval (LessThan l r) = do { l <- eval l; r <- eval r; return $ l <$ r }
eval (GreaterThan l r) = do { l <- eval l; r <- eval r; return $ l >$ r }
2017-06-01 20:26:33 +00:00
eval (BitAnd l r) = do { l <- eval l; r <- eval r; return $ l &$ r }
eval (BitOr l r) = do { l <- eval l; r <- eval r; return $ l |$ r }
eval (BitShift l r dir) = do { l <- eval l; r <- eval r; return $ (if dir then (<<$) else (>>$)) l r }
eval (BitNot v) = do { v <- eval v; return $ bitNot v }
2013-10-29 09:09:18 +00:00
eval (Access left (Var right)) = do
lhs <- eval left
case lhs of
DictV dict ->
2014-11-02 08:14:23 +00:00
case M.lookup (StrV right) dict of
Just (FnV [] fn) -> -- use the module's global scope
return $ FnV (mapToEnv dict) fn
2013-10-29 09:09:18 +00:00
Just v -> return v
2014-11-01 12:14:38 +00:00
Nothing -> return $ TupleV [StrV (T.pack "nothing")]
2013-10-29 09:09:18 +00:00
_ -> error $ "op/: need a dict, got " ++ show lhs
where
mapToEnv :: M.Map Value Value -> Env
2014-11-02 08:08:32 +00:00
mapToEnv m = [fromDict m]
2013-10-29 09:09:18 +00:00
eval (Access _ _) = error "op/: RHS must be an identifier"
eval (Call lhs arg) = do
2014-02-12 09:33:50 +00:00
env <- get
v <- eval lhs
case v of
fn@(FnV cls _) -> do
arg' <- eval arg
let cls' = if cls == [] then [last env] else cls -- if [], use current global env
2014-02-12 09:33:50 +00:00
put cls' -- enter closure env
2013-10-23 08:59:15 +00:00
v <- apply fn arg'
2014-02-12 09:33:50 +00:00
put env -- restore env
2013-10-23 08:59:15 +00:00
return v
fn@(Builtin _) -> eval arg >>= apply fn
_ -> error $ "call: " ++ show v ++ " is not a function"
2013-10-18 03:46:34 +00:00
eval x = error $ "eval: unhandled: " ++ show x
2014-11-02 08:08:32 +00:00
patternBindings :: Pattern -> Value -> Maybe (M.Map T.Text Value)
2014-11-02 08:14:23 +00:00
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
2013-11-02 04:43:36 +00:00
patternBindings (BoolP b) (BoolV v)
| v == b = Just M.empty
| otherwise = Nothing
2013-10-23 22:31:37 +00:00
patternBindings (StrP x) (StrV y)
2014-11-02 08:14:23 +00:00
| x == y = Just M.empty
2013-10-23 22:31:37 +00:00
| otherwise = Nothing
patternBindings (StrP _) _ = Nothing
2013-10-23 22:15:34 +00:00
-- cons on strings
2014-11-01 12:14:38 +00:00
-- x:[] matches with y:""
patternBindings (ConsP x (ListP [])) (StrV str) =
case T.uncons str of
Just (y, ys) | T.null ys -> -- str matches y:[]
patternBindings x (StrV $ T.singleton y)
_ -> Nothing
2013-10-27 08:35:32 +00:00
-- "xy":xs pattern
patternBindings (ConsP (StrP xp) xsp) (StrV str) =
2014-11-02 08:14:23 +00:00
let len = T.length xp in
if T.take len str == xp then -- matches
2014-11-01 12:14:38 +00:00
patternBindings xsp $ StrV (T.drop len str) -- match the rest of the string
2013-10-27 08:35:32 +00:00
else Nothing -- no match
2014-11-01 12:14:38 +00:00
patternBindings (ConsP xp xsp) (StrV str) =
case T.uncons str of
Just (x, xs) -> do
xe <- patternBindings xp (StrV $ T.singleton x)
xse <- patternBindings xsp $ StrV xs
Just $ M.union xe xse
_ -> Nothing
2013-10-23 22:15:34 +00:00
-- cons on lists
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 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
-- lists
2013-10-23 22:31:37 +00:00
patternBindings (ListP []) (ListV (x:_)) = Nothing -- not enough patterns
2013-10-18 21:06:27 +00:00
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
-- tuples
patternBindings (TupleP []) (TupleV (x:_)) = Nothing -- not enough patterns
patternBindings (TupleP (_:_)) (TupleV []) = Nothing -- not enough values
patternBindings (TupleP []) (TupleV []) = Just M.empty -- base case
patternBindings (TupleP (x:xs)) (TupleV (y:ys)) =
do
env <- patternBindings x y
env' <- patternBindings (TupleP xs) (TupleV ys)
Just $ M.union env' env
patternBindings (TupleP _) _ = Nothing -- not a tuple
2013-10-18 08:14:13 +00:00
patternBindings p x = error $ "patternBindings failure: matching " ++ show x ++ " with pattern " ++ show p
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 =
2013-10-18 08:14:13 +00:00
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 bindings -> -- satisfies
2013-10-18 03:46:34 +00:00
do
2014-02-12 09:33:50 +00:00
env <- get
let newenv = bindings:env
2014-02-12 09:33:50 +00:00
put newenv
eval body
Nothing -> -- doesn't satisfy this pattern
apply' xs
2013-10-18 03:46:34 +00:00
apply (Builtin (BIF fn)) arg = fn arg
2013-10-29 01:48:57 +00:00
-- some helper programs for evaluation
2013-10-18 01:58:41 +00:00
-- sets up stdin/stdout for binary mode and makes them unbuffered
initIO :: IO ()
initIO = do
hSetBinaryMode stdin True
hSetBinaryMode stdout True
hSetBuffering stdout NoBuffering
-- Takes an interpreter state and evaluates it with the empty initial state.
interpret :: InterpState a -> IO a
interpret state = evalStateT state initialState
2013-10-29 01:48:57 +00:00
evalProgram :: [AST] -> InterpState Value
2014-11-02 04:36:09 +00:00
evalProgram nodes = foldl1' (>>) $ map eval nodes
2013-10-29 01:48:57 +00:00
evalString :: T.Text -> InterpState Value
2013-10-19 09:21:34 +00:00
evalString program =
case parseProgram program of
Left err -> error $ show err
Right prg -> evalProgram prg
2013-10-29 01:48:57 +00:00
isLiterate :: FilePath -> Bool
isLiterate path = snd (splitExtension path) == ".lilamb"
-- Takes the lines of a literate program and returns the lines for a new executable program
-- from lines beginning with four spaces.
parseLiterate :: [T.Text] -> [T.Text]
parseLiterate lns = [T.drop 4 line | line <- lns, T.take 4 line == T.pack " "]
2013-10-29 01:48:57 +00:00
evalFile :: FilePath -> InterpState Value
evalFile path = do
contents <- liftIO $ if path == "-" then TIO.getContents else TIO.readFile path
2013-10-29 01:48:57 +00:00
if isLiterate path then
evalString . T.unlines . parseLiterate . T.lines $ contents
2013-10-29 01:48:57 +00:00
else evalString contents
evalFileV :: FilePath -> IO Value
2017-06-01 20:26:33 +00:00
evalFileV = interpret . evalFile