{-# LANGUAGE ScopedTypeVariables #-}
module Graphics.Implicit.ExtOpenScad.Eval.Statement (runStatementI) where
import Prelude(Maybe(Just, Nothing), Bool(True, False), Either(Left, Right), FilePath, IO, (.), ($), show, putStrLn, concatMap, return, (++), fmap, reverse, fst, readFile)
import Graphics.Implicit.ExtOpenScad.Definitions (
Statement(Include, (:=), Echo, For, If, NewModule, ModuleCall, DoNothing),
Pattern(Name),
Expr(LitE),
OVal(OString, OBool, OList, OModule),
VarLookup,
StatementI(StatementI)
)
import Graphics.Implicit.ExtOpenScad.Util.OVal (getErrors)
import Graphics.Implicit.ExtOpenScad.Util.ArgParser (argument, defaultTo, argMap)
import Graphics.Implicit.ExtOpenScad.Util.StateC (StateC, errorC, modifyVarLookup, mapMaybeM, lookupVar, pushVals, getRelPath, withPathShiftedBy, getVals, putVals)
import Graphics.Implicit.ExtOpenScad.Eval.Expr (evalExpr, matchPat)
import Graphics.Implicit.ExtOpenScad.Parser.Statement (parseProgram)
import Data.Maybe(fromMaybe)
import qualified Data.Map as Map
import Control.Monad (forM_, forM, mapM_)
import Control.Monad.State (get, liftIO, mapM, runStateT, (>>))
import qualified System.FilePath as FilePath
runStatementI :: StatementI -> StateC ()
runStatementI (StatementI lineN (pat := expr)) = do
val <- evalExpr expr
let posMatch = matchPat pat val
case (getErrors val, posMatch) of
(Just err, _ ) -> errorC lineN err
(_, Just match) -> modifyVarLookup $ Map.union match
(_, Nothing ) -> errorC lineN "pattern match failed in assignment"
runStatementI (StatementI lineN (Echo exprs)) = do
let
show2 (OString s) = s
show2 x = show x
vals <- mapM evalExpr exprs
case getErrors (OList vals) of
Nothing -> liftIO . putStrLn $ concatMap show2 vals
Just err -> errorC lineN err
runStatementI (StatementI lineN (For pat expr loopContent)) = do
val <- evalExpr expr
case (getErrors val, val) of
(Just err, _) -> errorC lineN err
(_, OList vals) -> forM_ vals $ \v ->
case matchPat pat v of
Just match -> do
modifyVarLookup $ Map.union match
runSuite loopContent
Nothing -> return ()
_ -> return ()
runStatementI (StatementI lineN (If expr a b)) = do
val <- evalExpr expr
case (getErrors val, val) of
(Just err, _ ) -> errorC lineN ("In conditional expression of if statement: " ++ err)
(_, OBool True ) -> runSuite a
(_, OBool False) -> runSuite b
_ -> return ()
runStatementI (StatementI lineN (NewModule name argTemplate suite)) = do
argTemplate' <- forM argTemplate $ \(name', defexpr) -> do
defval <- mapMaybeM evalExpr defexpr
return (name', defval)
(varlookup, _, path, _, _) <- get
runStatementI . StatementI lineN $ (Name name :=) $ LitE $ OModule $ \_ -> do
newNameVals <- forM argTemplate' $ \(name', maybeDef) -> do
val <- case maybeDef of
Just def -> argument name' `defaultTo` def
Nothing -> argument name'
return (name', val)
let
varlookup' = Map.union (Map.fromList newNameVals) varlookup
suiteVals = runSuiteCapture varlookup' path suite
return suiteVals
runStatementI (StatementI lineN (ModuleCall name argsExpr suite)) = do
maybeMod <- lookupVar name
(varlookup, _, path, _, _) <- get
childVals <- fmap reverse . liftIO $ runSuiteCapture varlookup path suite
argsVal <- forM argsExpr $ \(posName, expr) -> do
val <- evalExpr expr
return (posName, val)
newVals <- case maybeMod of
Just (OModule mod') -> liftIO ioNewVals where
argparser = mod' childVals
ioNewVals = fromMaybe (return []) (fst $ argMap argsVal argparser)
Just foo -> do
case getErrors foo of
Just err -> errorC lineN err
Nothing -> errorC lineN "Object called not module!"
return []
Nothing -> do
errorC lineN $ "Module " ++ name ++ " not in scope."
return []
pushVals newVals
runStatementI (StatementI _ (Include name injectVals)) = do
name' <- getRelPath name
content <- liftIO $ readFile name'
case parseProgram content of
Left e -> liftIO $ putStrLn $ "Error parsing " ++ name ++ ":" ++ show e
Right sts -> withPathShiftedBy (FilePath.takeDirectory name) $ do
vals <- getVals
putVals []
runSuite sts
vals' <- getVals
if injectVals then putVals (vals' ++ vals) else putVals vals
runStatementI (StatementI _ DoNothing) = liftIO $ putStrLn "Do Nothing?"
runSuite :: [StatementI] -> StateC ()
runSuite = mapM_ runStatementI
runSuiteCapture :: VarLookup -> FilePath -> [StatementI] -> IO [OVal]
runSuiteCapture varlookup path suite = do
(res, _) <- runStateT
(runSuite suite >> getVals)
(varlookup, [], path, (), () )
return res