octopus-0.0.1.0: Octopus.hs
module Octopus where
import Import
import qualified Data.Sequence as Seq
import qualified Data.Map as Map
import Control.Monad.State
import Control.Monad.Reader
import System.IO.Error
import Control.Concurrent.MVar
import Octopus.Data
import Octopus.Parser (parseOctopusFile)
import qualified Octopus.Primitive as Oct
import Octopus.Basis
import Octopus.Shortcut
import Octopus.Libraries
eval :: ImportsCache -> Val -> Val -> IO Val
eval cache env code = evalStateT (runReaderT (reduce code) cache) startState
where
startState = MState { environ = env --Ob Map.empty
, control = [NormK []]
}
reduce :: Val -> Machine Val
reduce x@(Nm _) = done x
reduce x@(By _) = done x
reduce x@(Tx _) = done x
reduce x@(Fp _) = done x
reduce x@(Tg _) = done x
reduce x@(Ab _ _) = done x
reduce x@(Cl _ _ _) = done x
reduce x@(Ce _) = done x
reduce x@(Ar _) = done x
reduce x@(Pr _) = done x
reduce sq@(Sq xs) = case toList xs of
[] -> done sq
(x:xs) -> push (Es [] xs) >> reduce x
reduce ob@(Ob m) = case ensureCombination ob of
Just (f, x) -> push (Op x) >> reduce f
Nothing -> case Map.toList m of
[] -> done (mkOb [])
((k,v):xs) -> push (Eo k [] xs) >> reduce v
reduce (Sy x) = gets environ >>= \env -> case Oct.resolveSymbol x env of
Just val -> done val
Nothing -> do
env <- gets environ
error $ "TODO unbound symbol: " ++ show x ++ "\n" ++ show env
combine :: Val -> Val -> Machine Val
combine (Pr Vau) x = case x of
Sq xs -> case toList xs of
[var, ast] -> do
env <- gets environ
done $ Cl var ast env
_ -> error "raise wrong number of args to primitive vau"
_ -> error "raise invalid args to primitive vau"
combine f@(Pr _) x = push (Ap f) >> reduce x
combine f x = case ensureClosure f of --all of these are operatives
Just (var, ast, env) -> do
caller <- gets environ
let x' = mkSq [caller, x]
case Oct.match var x' of
Right env' -> swapEnv (env' `Oct.extend` env) >> reduce ast
Left err -> error "raise match failure"
_ -> error $ "raise not a combiner:\n" ++ show f
apply :: Val -> Val -> Machine Val
apply (Pr Eval) x = case ensureThunk x of
Just (env, ast) -> swapEnv env >> reduce ast
_ -> error $ "raise invalid args to primitive eval: " ++ show x
apply (Pr Ifz) x = case x of
Sq xs -> case toList xs of
[p, c, a] -> done $ Oct.ifz p c a
_ -> error "raise wrong number of args to primitive Ifz"
_ -> error "raise invalid args to primitive Ifz"
apply (Pr Imp) x = impFile x
apply (Pr Extends) x = case x of
Sq xs -> case toList xs of
[] -> done $ mkOb []
xs -> done $ foldr1 Oct.extend xs
_ -> error "raise invalid args to primitive Extends"
apply (Pr pr) x =
case lookup pr table of
Just f -> case f pr x of
Right val -> done val
Left err -> error $ "raise some error in primitive " ++ show pr
Nothing -> error $ "unknown primitive " ++ show pr
where
table =
[ (Match, binary Oct.match)
, (Delete, binary Oct.delete)
, (Keys, unary Oct.keys)
, (Get, binary Oct.get)
, (Eq, binary Oct.eq)
, (Neq, binary Oct.neq)
, (Lt, binary Oct.lt)
, (Lte, binary Oct.lte)
, (Gt, binary Oct.gt)
, (Gt, binary Oct.gt)
, (Add, binary Oct.add)
, (Sub, binary Oct.sub)
, (Mul, binary Oct.mul)
, (Div, binary Oct.div)
, (Len, unary Oct.len)
, (Cat, binary Oct.cat)
, (Cut, binary Oct.cut)
, (Numer, unary Oct.numer)
, (Denom, unary Oct.denom)
, (NumParts, unary Oct.numParts)
]
binary :: (Val -> Val -> Fallible Val) -> Primitive -> Val -> Fallible Val
binary op pr x = case x of
Sq xs -> case toList xs of
[a, b] -> op a b
_ -> error $ "raise wrong number of args to primitive " ++ show pr
_ -> error $ "raise invalid args to primitive " ++ show pr
unary :: (Val -> Fallible Val) -> Primitive -> Val -> Fallible Val
unary op pr x = op x
apply f x = error "TODO apply"
done :: Val -> Machine Val
done x = do
k <- gets control
case k of
[] -> return x
(NormK []):_ -> pop >> done x
(NormK (Re _:_)):_ -> pop >> done x
(NormK (Es xs []:_)):_ -> pop >> done (Sq . Seq.fromList $ reverse (x:xs))
(NormK (Es xs (x':xs'):_)):_ -> replace (Es (x:xs) xs') >> reduce x'
(NormK (Eo k xs []:_)):_ -> pop >> done (Ob . Map.fromList $ (k,x):xs)
(NormK (Eo k xs ((k',x'):xs'):_)):_ -> replace (Eo k' ((k,x):xs) xs') >> reduce x'
(NormK (Op arg:ks)):kss -> pop >> combine x arg
(NormK (Ap f:ks)):kss -> pop >> apply f x
(ImptK _ slot):ks -> liftIO (slot `putMVar` Right x) >> pop >> done x
impFile :: Val -> Machine Val
impFile (Tx pathstr) = do
let path = pathstr --FIXME normalize path
path' = unpack path
--TODO check against builtin files, or else pre-populate the cache
--TODO check the path against the current imports on stack to avoid circular import
cache_var <- ask
cache <- liftIO $ takeMVar cache_var
case Map.lookup path cache of
Just loaded_var -> do
liftIO $ cache_var `putMVar` cache
val_e <- liftIO $ readMVar loaded_var
case val_e of
Right val -> done val
Left err -> error $ "raise exception " ++ show err
Nothing -> do
loading_var <- liftIO newEmptyMVar
liftIO $ cache_var `putMVar` Map.insert path loading_var cache
contents_e <- liftIO $ tryIOError $ readFile path'
case contents_e of
Right contents -> do
swapEnv startData
pushK (ImptK path loading_var)
case parseOctopusFile path' contents of
Right val -> reduce val --TODO consider which env to start a file off with
Left err -> error "raise SyntaxError" --FIXME also don't forget to fill the loading_var
Left err -> error "raise ImportError" --FIXME also don't forget to fill the loading_var
impFile _ = error "TODO raise TypeError"