packages feed

melody-0.2: src/Language/Melody/Interpret/Compile.hs

module Language.Melody.Interpret.Compile (compile) where
import Language.Melody.Syntax
import Language.Melody.Interpret.Types
import Language.Melody.Interpret.Pop
import Control.Monad.Error
import Control.Monad.Reader
import Control.Lens
import Control.Applicative
import qualified Data.Map as M
import Data.Maybe


resolve :: [([TypeName], Melody)] -> MelodyM (Maybe Melody)
resolve cs = fmap snd . foldr choose Nothing <$> filterM matches cs
  where matches (ts, _) = do
          s <- map typeName <$> use stack
          return $ length s >= length ts && s <<: ts
        choose (t1, body1) (Just (t2, body2)) | t1 <<: t2  = Just (t1, body1)
                                              | t2 <<: t1  = Just (t2, body2)
                                              | otherwise = Nothing
        choose t  Nothing   = Just t
        t1 <<: t2 = all (uncurry (<:)) $ zip t1 t2
        
-- | Bind a list of expressions into a closure
addClosure :: Expr NotCompiled -> MelodyM (Expr NotCompiled)
addClosure body = (Func body . Just) <$> ask

-- | Ask whether a word is in a closure, inlining it
-- if it is.
inlineClosedWord :: String -> MelodyM (Expr NotCompiled)
inlineClosedWord w = maybe (Word w) id . M.lookup w <$> ask

-- | Attach a closure to a function without
-- and inline a closed word running and recurses on all
-- other compound structures
close :: Expr NotCompiled -> MelodyM (Expr NotCompiled)
close (Func body Nothing) = addClosure body
close (Word w)            = inlineClosedWord w
close (Dictionary d)      = Dictionary <$> mapM (both close) d
close (List l)            = List <$> mapM close l
close a                   = return a

-- | Resolve a variable name in the current scope
selectVariable :: String -> Melody
selectVariable w = do
  global <- use (env.at w)        -- Global
  mult   <- use (multi.at w) >>= resolving
  loc    <- M.lookup w <$> ask    -- Local
  assertExists $ pushing loc <|> mult <|> global
  where pushing      = fmap push
        resolving    = maybe (return Nothing) id . fmap resolve
        assertExists = fromMaybe . lift . throwError $ NoSuchName w

-- | Compile a group of expressions in order
compileMany :: [Expr NotCompiled] -> Melody
compileMany = mapM_ compile

-- | Bind the list of names to the top items on the stack
addEnv :: [String] -> Melody -> Melody
addEnv names m = do
  vals <- topN (length names)
  local (M.union . M.fromList $ zip names vals) m

-- | Compile a Melody expression.
-- If the expression is a word, than it is evaluated in the
-- current context, otherwise it is pushed on to the stack
compile :: Expr NotCompiled -> Melody
compile (Word w) = selectVariable w
compile (Binding nms exprs) = addEnv nms $ compileMany exprs
compile (Comp es) = compileMany es
compile (Func body Nothing) = addClosure body >>= push
compile (List es) = mapM close es >>= push . List
compile (Dictionary es) = mapM (both close) es >>= push . Dictionary
compile e = push e