{-# LANGUAGE DerivingVia #-}

module Language.Fortran.Vars.Eval
  ( eval
  , eval'
  , evalWithShortcircuit
  )
where

import           Prelude                 hiding ( fail )
import           Language.Fortran.AST           ( BinaryOp(..)
                                                , Expression(..)
                                                , Value(..)
                                                , AList(..)
                                                , argExtractExpr
                                                )
import           Language.Fortran.Util.Position ( getSpan )

import           Language.Fortran.Vars.Types    ( SymbolTableEntry(..)
                                                , ExpVal(..)
                                                , SymbolTable
                                                )

import qualified Language.Fortran.Vars.Eval.FortranSrc as ViaFS
import qualified Language.Fortran.Repr as FS
import qualified Language.Fortran.Repr.Eval.Common as FS.Eval
import qualified Language.Fortran.Repr.Eval.Value as FS.Eval

import Control.Monad.Reader
import Control.Monad.Except

import qualified Data.Map as Map

-- | Given a 'SymbolTable' and some 'Expression', attempt to evaluate that
--   expression into a value in fortran-src's representation, translate it into
--   an 'ExpVal', and return.
eval' :: SymbolTable -> Expression a -> Either String ExpVal
eval' :: forall a. SymbolTable -> Expression a -> Either String ExpVal
eval' SymbolTable
symt Expression a
expr =
    case forall a. SymbolTable -> Eval a -> Either Error a
ViaFS.runEval SymbolTable
symt (forall (m :: * -> *) a.
MonadFEvalValue m =>
Expression a -> m FValue
FS.Eval.evalExpr Expression a
expr) of
      Left Error
err -> forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> String
show Error
err
      Right FValue
a -> FValue -> Either String ExpVal
ViaFS.translateFValue FValue
a

-- | Given a 'SymbolTable' and some 'Expression', evaluate that expression
-- into a basic type and return it as an 'ExpVal'.
eval :: SymbolTable -> Expression a -> ExpVal
eval :: forall a. SymbolTable -> Expression a -> ExpVal
eval SymbolTable
symTable Expression a
expr = case forall a. SymbolTable -> Expression a -> Either String ExpVal
eval' SymbolTable
symTable Expression a
expr of
  Left  String
err -> forall a. HasCallStack => String -> a
error (String
err forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show (forall a. Spanned a => a -> SrcSpan
getSpan Expression a
expr))
  Right ExpVal
r   -> ExpVal
r

-- | Given a 'SymbolTable' and some 'Expression', evaluate that expression
-- into a basic type and return it as an 'ExpVal' or a 'String' describing
-- the issue that prevented the evaluation. In the case of expressions like
--
-- @
--       foobar .AND. .FALSE.
--       .TRUE. .OR. .foobar
-- @
--
-- the expressions will be shortcircuited to produce
--
-- @
--       .FALSE.
--       .TRUE.
-- @
evalWithShortcircuit :: SymbolTable -> Expression a -> Either String ExpVal
evalWithShortcircuit :: forall a. SymbolTable -> Expression a -> Either String ExpVal
evalWithShortcircuit = forall a. HasCallStack => String -> a
error String
"TODO unimplemented in fortran-src evaluator"