packages feed

formura-1.0: src/Formura/Interpreter/Eval.hs

{-# LANGUAGE FlexibleContexts, FlexibleInstances,  RankNTypes, TemplateHaskell #-}
module Formura.Interpreter.Eval where

import           Control.Applicative
import           Control.Lens
import           Control.Monad.RWS hiding (fix)
import qualified Data.Map as M
import qualified Data.Vector as V
import           Text.Trifecta (failed, raiseErr)

import           Formura.Interpreter.Value
import           Formura.Compiler
import           Formura.Language.Combinator
import           Formura.Syntax


type Binding = M.Map IdentName TypedValue

data Environment =
  Environment
  { _envDimension :: Int
  , _envExtent :: [Int]
  , _envCS :: CompilerSyntacticState
  }

makeLenses ''Environment

defaultEnvironment :: Environment
defaultEnvironment = Environment 0 [] defaultCompilerSyntacticState{ _compilerStage = "interpretation" }

instance HasCompilerSyntacticState Environment where
  compilerSyntacticState = envCS

type IM = CompilerMonad Binding () Environment
type IAlgebra f a = f a -> IM a

runIM :: IM a -> IO (Either CompilerError a)
runIM m = evalCompiler m M.empty defaultEnvironment

-- | Monadic 'fold' for twin language.
mfold2 :: Traversable f => AlgebraM IM f (Lang g, Lang h) -> Fix f -> IM (Lang g, Lang h)
mfold2 k (In meta x) = do
  r1 <- traverse (mfold2 k) x
  compilerFocus %= (meta <|>)
  (g2, h2) <- k r1
  return $ (propagateMetadata meta g2, propagateMetadata meta h2)


class Evalable a where
  eval :: a -> IM TypedValue

instance Evalable (ImmF x) where
  eval (ImmF r) = return $ (ElemValue $ fromRational r, ElemType "double")

instance Evalable (IdentF x) where
  eval (IdentF nam) = do
    binding <- ask
    case M.lookup nam (binding :: Binding) of
      Just x  -> return x
      Nothing -> raiseErr $ failed $ "undefined variable: " ++ nam

instance Evalable (OperatorF TypedValue) where
  eval (UniopF "+" x) = return x
  eval (UniopF "-" x) = evalUniop negate x
  eval (BinopF "+" x y) = evalBinop (+) x y
  eval (BinopF "-" x y) = evalBinop (-) x y
  eval (BinopF "*" x y) = evalBinop (*) x y
  eval (BinopF "/" x y) = evalBinop (/) x y
  eval (BinopF str _ _) = raiseErr $ failed $ "unimplemented binary operator: (" ++ str ++ ")"
  eval _ = raiseErr $ failed "unimplemented operator in eval"

evalUniop :: (forall a. Num a => a -> a) -> TypedValue -> IM TypedValue
evalUniop f (ElemValue r, t) = return $ (ElemValue (f r), t)

evalBinop :: (forall a. Fractional a => a -> a -> a) -> TypedValue -> TypedValue -> IM TypedValue
evalBinop f (ElemValue x, tx ) (ElemValue y, ty) = return $ (ElemValue (f x y), tx)

instance Evalable (TupleF TypedValue) where
  eval (Tuple xts) = return $ (Tuple $ map fst xts, Tuple $ map snd xts)

instance Evalable (GridF x) where
  eval _ = raiseErr $ failed "eval of grid unimplemented."

instance Evalable (ApplyF x) where
  eval _ = raiseErr $ failed "eval of apply unimplemented."

instance Evalable (LambdaF x) where
  eval _ = raiseErr $ failed "eval of lambda unimplemented."

instance Evalable (LetF x) where
  eval _ = raiseErr $ failed "eval of let unimplemented."

voidEval :: a -> IM TypedValue
voidEval _ = raiseErr $ failed "eval of void unimplemented."

instance Evalable RExpr where
  eval = mfold2 (eval +:: eval +:: eval +:: eval +:: eval +:: eval +:: eval +:: eval +:: voidEval
                  :: RExprF TypedValue -> IM TypedValue)


ret :: Iso' [Int] Int
ret = iso enc dec
  where
    enc = product
    dec = const []

makeGridValueF :: [Rational] -> ([Rational] -> IM x) -> IM (GridValueF x)
makeGridValueF offset fun = do
  exts <- use envExtent
  let idxs = map (zipWith (+) offset . map toRational) $ spanExts exts
      spanExts []  = [[]]
      spanExts (n:ns) = [i:js| i <- [0..n-1], js <- spanExts ns]
  content <- mapM fun idxs
  return $ GridValueF offset $ V.fromList content

accessGridF :: GridValueF x -> [Rational] -> IM x
accessGridF g addr = do
  let iaddr = zipWith (-) addr (g ^.gridOffset)
  return undefined