{-# LANGUAGE PackageImports #-}
module Graphics.Implicit.ExtOpenScad.Eval.Expr (evalExpr, rawRunExpr, matchPat, StateE, ExprState(ExprState), messages, addMessage) where
import Prelude (String, Maybe(Just, Nothing), ($), fmap, pure, zip, (!!), const, (<>), foldr, foldMap, (.), (<$>), traverse)
import Graphics.Implicit.ExtOpenScad.Definitions (
Pattern(Name, ListP, Wild),
OVal(OList, OError, OFunc, OUndefined),
Expr(LitE, ListE, LamE, Var, (:$)),
Symbol(Symbol),
VarLookup(VarLookup),
SourcePosition,
Message(Message),
MessageType(Error),
StateC
)
import Graphics.Implicit.ExtOpenScad.Util.OVal (oTypeStr, getErrors)
import Graphics.Implicit.ExtOpenScad.Util.StateC (getVarLookup)
import qualified Graphics.Implicit.ExtOpenScad.Util.StateC as GIEUS (addMessage)
import Data.List (elemIndex)
import Data.Map (fromList, lookup)
import Data.Foldable (fold, traverse_)
import Data.Functor.Identity (Identity)
import Data.Traversable (for)
import Control.Monad (zipWithM)
import "monads-tf" Control.Monad.State (StateT, get, modify, runState)
data ExprState = ExprState
{ _scadVars :: VarLookup
, patterns :: [String]
, messages :: [Message]
, _sourcePos :: SourcePosition
}
type StateE = StateT ExprState Identity
addMessage :: MessageType -> SourcePosition -> String -> StateE ()
addMessage mtype pos text = addMesg $ Message mtype pos text
where
addMesg :: Message -> StateE ()
addMesg m = modify $ \s -> s { messages = messages s <> pure m }
errorE :: SourcePosition -> String -> StateE ()
errorE = addMessage Error
patVars :: Pattern -> [String]
patVars (Name (Symbol name)) = [name]
patVars (ListP pats) = foldMap patVars pats
patVars Wild = []
patMatch :: Pattern -> OVal -> Maybe [OVal]
patMatch (Name _) val = Just [val]
patMatch (ListP pats) (OList vals) = fold <$> zipWithM patMatch pats vals
patMatch Wild _ = Just []
patMatch _ _ = Nothing
matchPat :: Pattern -> OVal -> Maybe VarLookup
matchPat pat val = VarLookup . fromList . zip (Symbol <$> patVars pat) <$> patMatch pat val
evalExpr :: SourcePosition -> Expr -> StateC OVal
evalExpr pos expr = do
vars <- getVarLookup
let
(valf, s) = runState (evalExpr' expr) $ ExprState vars [] [] pos
moveMessage (Message mtype mpos text) = GIEUS.addMessage mtype mpos text
traverse_ moveMessage $ messages s
pure $ valf []
rawRunExpr :: SourcePosition -> VarLookup -> Expr -> (OVal, [Message])
rawRunExpr pos vars expr = do
let
(valf, s) = runState (evalExpr' expr) $ ExprState vars [] [] pos
(valf [], messages s)
evalExpr' :: Expr -> StateE ([OVal] -> OVal)
evalExpr' (Var (Symbol name)) = do
(ExprState (VarLookup varlookup) namestack _ spos) <- get
case (lookup (Symbol name) varlookup, elemIndex name namestack) of
(_, Just pos) -> pure (!! pos)
(Just val, _) -> pure $ const val
_ -> do
errorE spos ("Variable " <> name <> " not in scope")
pure $ const OUndefined
evalExpr' (LitE val) = pure $ const val
evalExpr' (ListE exprs) = do
valFuncs <- traverse evalExpr' exprs
pure $ \s -> OList $ ($s) <$> valFuncs
evalExpr' (fexpr :$ argExprs) = do
fValFunc <- evalExpr' fexpr
argValFuncs <- traverse evalExpr' argExprs
pure $ \s -> app (fValFunc s) (fmap ($s) argValFuncs)
where
app f l = case (getErrors f, getErrors $ OList l) of
(Nothing, Nothing) -> app' f l where
app' (OFunc f') (x:xs) = app (f' x) xs
app' a [] = a
app' x _ = OError ["Can't apply arguments to " <> oTypeStr x]
(Just err, _ ) -> OError [err]
(_, Just err) -> OError [err]
evalExpr' (LamE pats fexpr) = do
fparts <- for pats $ \pat -> do
modify $ \s -> s { patterns = patVars pat <> patterns s}
pure $ \f xss -> OFunc $ \val -> case patMatch pat val of
Just xs -> f (xs <> xss)
Nothing -> OError ["Pattern match failed"]
fval <- evalExpr' fexpr
pure $ foldr ($) fval fparts