melody-0.2: src/Language/Melody/Interpret.hs
{-# LANGUAGE TupleSections #-}
module Language.Melody.Interpret (eval, runMelody, Melody) where
import Control.Lens
import Data.List (nub)
import Control.Monad.Error
import Language.Melody.Interpret.Env
import Language.Melody.Interpret.Types
import Language.Melody.Interpret.Compile
import Language.Melody.Interpret.Pop
import Language.Melody.Syntax
import Control.Monad.Reader
import qualified Data.Map as M
-- | Generate constructors for a given type. This creates
-- a new word for each constructor
generateConstrs :: TypeName -> [(Constructor, Int)] -> [(String, Melody)]
generateConstrs t = map makeConstr
where makeConstr (name, arity) = (name, replicateM arity pop >>= push . Boxed t name)
-- | Run a given expression in an environment with no local variables
emptyEnv :: Melody -> Melody
emptyEnv = local (const M.empty)
-- | Evaluate a @TopLevel@. If it is an expression it runs it,
-- otherwise it generates the appropriate bindings for definitions.
eval :: TopLevel -> Melody
eval (Def nm expr) = env.at nm .= Just (emptyEnv $ compile expr)
eval (Exec expr) = compile expr
eval (Type t cs) = assertUnique (map fst cs) >> env %= M.union (M.fromList $ generateConstrs t cs)
where assertUnique names = when (length names /= length (nub names))
. throwError . Misc $ "Constructors are not unique for " ++ t
eval (MultiDef t) = multi.at t .= Just []
eval (MultiExt t args body) = multi.at t._Just %= (:) (args, emptyEnv $ compile body)
-- | Turn a @Melody@ expression into an @IO@ expression
runMelody :: Melody -> IO (Either EvalError [Expr Compiled])
runMelody = getStack $ unwrapMelody (M.fromList []) defaultEnv
where getStack = mapped.mapped._Right %~ view stack