module Language.Executor where
import Debug.Trace
import qualified Data.Either as E
import qualified Data.Map.Strict as M
import qualified Data.Set as S
import Control.Monad (liftM2)
import Language.Ast
import Language.Desugar
import Language.Error
import Language.Primitives
import Language.Primitives.Map as PM
type FullyEvaluated = Either [Error Expression] Expression
type Env = [(Expression, Expression)]
validatePreExec :: [Definition] -> Either [Error Expression] (Env, Expression)
validatePreExec defs = do
let desugaredDefs = map desugarEachDef defs
checkAgainstRepeatedDefs desugaredDefs
initialEnvironment desugaredDefs
exec :: [Definition] -> FullyEvaluated
exec defs = do
(env, mainExpr) <- validatePreExec defs
eval env mainExpr
eval :: Env -> Expression -> FullyEvaluated
eval env namedValue@(MappyNamedValue name) = do
result <- maybe (singleError $ NameNotDefined name) Right (Prelude.lookup namedValue env)
eval env result
eval env (MappyApp fn params) = apply env fn params
eval env (MappyLambda args body) = Right $ MappyClosure args body env
eval env (MappyClosure args body env') = Right $ MappyClosure args body (env ++ env')
eval env (MappyMap map') = evalMap (eval env) map'
eval _ value = Right value
evalMap :: (Expression -> FullyEvaluated) -> PrimitiveMap Expression -> FullyEvaluated
evalMap evaluator (StandardMap map) = go [] (M.toList map)
where
go pairs [] = Right $ MappyMap $ StandardMap $ M.fromList pairs
go pairs ((key, value):rest) = do
key' <- evaluator key
value' <- evaluator value
go ((key', value'):pairs) rest
evalMap _ map = Right $ MappyMap $ map
apply :: Env -> Expression -> [Expression] -> FullyEvaluated
apply = apply'
apply' :: Env -> Expression -> [Expression] -> FullyEvaluated
apply' env (MappyNamedValue "take") (key:map:[]) = do
key' <- eval env key
maybeMap <- eval env map
(MappyMap map') <- assertMap "take" key' maybeMap
maybe (singleError $ KeyNotFound key') Right $ PM.lookup key' map'
apply' env (MappyNamedValue "take") args =
singleError $ WrongNumberOfArguments "take" 2 $ length args
apply' env (MappyNamedValue "default-take") (key:map:def:[]) = do
key' <- eval env key
def' <- eval env def
maybeMap <- eval env map
(MappyMap map') <- assertMap "default-take" key' maybeMap
return $ PM.findWithDefault def' key' map'
apply' env (MappyNamedValue "default-take") args =
singleError $ WrongNumberOfArguments "default-take" 3 $ length args
apply' env (MappyNamedValue "give") (key:value:map:[]) = do
key' <- eval env key
map' <- eval env map
value' <- eval env value
maybe (singleError $ GiveCalledOnNonMap key value' map') Right (mapInsert key' value' map')
where
mapInsert k v (MappyMap map) = Just $ MappyMap $ PM.insert k v map
mapInsert _ _ _ = Nothing
apply' env (MappyNamedValue "give") args =
singleError $ WrongNumberOfArguments "give" 3 $ length args
apply' env nonPrimitive args = do
val <- eval env nonPrimitive
applyNonPrim args env val
applyNonPrim args _ (MappyClosure argNames body closedEnv) = do
env' <- extendEnvironment argNames args closedEnv
eval env' body
applyNonPrim args env kwd@(MappyKeyword _) =
eval env $ MappyApp (MappyNamedValue "take") (kwd:args)
assertMap _ _ map@(MappyMap _) = Right map
assertMap fn key nonMap = Left [TakeCalledOnNonMap fn key nonMap]
extendEnvironment :: [Expression] -> [Expression] -> Env -> Either [Error Expression] Env
extendEnvironment argNames args env =
let
unEvaluated = zip argNames args
evaluated = map extend unEvaluated
partitioned = E.partitionEithers evaluated
in
(liftM2 (++)) (final partitioned) (pure env)
where
final ([], env) = Right env
final (errors, _) = Left $ concat errors
extend (MappyNamedValue name, value) = do
v' <- eval env value
return (MappyNamedValue name, v')
extend (MappyLazyArgument name, value) = Right (MappyNamedValue name, MappyLambda [] value)
extend _ = error "TODO: Better error for when a fn has a non-namey name"
checkAgainstRepeatedDefs :: [Definition] -> Either [Error Expression] [Definition]
checkAgainstRepeatedDefs defs = go (S.empty, []) defs
where
go (_, []) [] = Right defs
go (_, repeats) [] = Left $ map RepeatedDefinition repeats
go (seen, repeats) ((MappyDef (MappyNamedValue name) _):rest) = go (S.insert name seen, newRepeats seen name repeats) rest
newRepeats seen name = (++) (if S.member name seen then [name] else [])
initialEnvironment :: [Definition] -> Either [Error Expression] (Env, Expression)
initialEnvironment = go ([], Nothing)
where
go (env, Just m) [] = Right (env ++ primitives, m)
go (_, Nothing) [] = singleError MainNotFound
go (env, _) (MappyDef (MappyNamedValue "main") mainBody:rest) = go (env, Just mainBody) rest
go (env, maybeMain) (MappyDef name body:rest) = go ((name, body):env, maybeMain) rest