packages feed

fortran-vars-0.4.0: src/Language/Fortran/Vars/SymbolTable/Arrays.hs

module Language.Fortran.Vars.SymbolTable.Arrays where

import Language.Fortran.AST

import Language.Fortran.Vars.Eval ( eval' )
import Language.Fortran.Vars.Types ( ExpVal(..), SymbolTable, Dim(..), Dims(..), Dimensions )

import Control.Monad.Except
--import Data.List.NonEmpty ( NonEmpty( (:|) ) )

resolveDims
    :: SymbolTable -> [DimensionDeclarator a] -> Maybe Dimensions
resolveDims symt dds
  -- We assume array type from a quick look at the dimension declarators.
  | any dimDeclLooksLikeAssumedSize  dds =
      case resolveDimsAssumedSize symt dds of
        Left _err -> Nothing -- discard errors/warnings :(
        Right (Nothing, x) -> Just $ DimsAssumedSize Nothing x
        -- resolveDimsAssumedSize can't return an empty list. Clumsy code means
        -- we don't prove this in types.
        Right (Just (a:as), x) -> Just $ DimsAssumedSize (Just (a :| as)) x
        Right (Just [], _x) -> error "impossible"
  | any dimDeclLooksLikeAssumedShape dds =
      case resolveDimsAssumedShape symt dds of
        Left _err -> Nothing -- discard errors/warnings :(
        Right [] -> error "empty DimensionDeclarator list (should not be parseable)"
        Right (a:as) -> Just $ DimsAssumedShape $ a :| as
  | otherwise =
      case resolveDimsExplicitShape symt dds of
        Left _err -> Nothing -- discard errors/warnings :(
        Right [] -> error "empty DimensionDeclarator list (should not be parseable)"
        Right (a:as) -> Just $ DimsExplicitShape $ a :| as

-- | Assumed-size arrays have the special 'ValStar' upper bound (whereas
--   explicit-shape and assumed-shape arrays never do).
dimDeclLooksLikeAssumedSize :: DimensionDeclarator a -> Bool
dimDeclLooksLikeAssumedSize = \case
  DimensionDeclarator _ _ _ (Just (ExpValue _ _ ValStar)) -> True
  _ -> False

-- | Assumed-shape arrays have no upper bounds (whereas explicit-shape and
--   assumed-size arrays always do).
dimDeclLooksLikeAssumedShape :: DimensionDeclarator a -> Bool
dimDeclLooksLikeAssumedShape = \case
  DimensionDeclarator _ _ _ Nothing -> True
  _ -> False

evalStaticDimBoundExpr :: SymbolTable -> Expression a -> Either String Int
evalStaticDimBoundExpr symt expr =
    case eval' symt expr of
      Right (Int val) -> pure val
      Right{} -> throwError $ "wrong type for array dimension bound"
      Left err -> throwError $ "error evaluating array dimension bound expression: "<>err

-- | Returns @'Right' 'Nothing'@ for dynamic bounds (e.g. which use dummy vars).
evalDynamicDimBoundExpr :: SymbolTable -> Expression a -> Either String (Maybe Int)
evalDynamicDimBoundExpr symt expr =
    case eval' symt expr of
      Right (Int val) -> pure $ Just val
      Right{} -> throwError $ "wrong type for array dimension bound"
      Left{} -> pure Nothing

resolveDimsExplicitShape
    :: SymbolTable -> [DimensionDeclarator a] -> Either String [Dim (Maybe Int)]
resolveDimsExplicitShape symt = traverse (resolveDimExplicitShape symt)

resolveDimExplicitShape
    :: SymbolTable -> DimensionDeclarator a -> Either String (Dim (Maybe Int))
resolveDimExplicitShape symt (DimensionDeclarator _ _ mlb mub) =
    case mub of
      Nothing -> throwError "explicit-shape array must have an upper bound for every dimension"
      Just ubExpr -> do
        lb <- case mlb of
                Nothing -> pure $ Just 1
                Just lbExpr -> evalDynamicDimBoundExpr symt lbExpr
        ub <- evalDynamicDimBoundExpr symt ubExpr
        pure $ Dim lb ub

resolveDimsAssumedShape
    :: SymbolTable -> [DimensionDeclarator a] -> Either String [Maybe Int]
resolveDimsAssumedShape symt = traverse go
  where
    go (DimensionDeclarator _ _ mlb mub) =
        case mub of
          Just{} ->
            throwError "assumed-shape array can't have a dimension with an upper bound"
          Nothing ->
            case mlb of
              Nothing -> pure $ Just 1
              Just lbExpr -> evalDynamicDimBoundExpr symt lbExpr

resolveDimsAssumedSize
    :: SymbolTable -> [DimensionDeclarator a]
    -> Either String (Maybe [Dim (Maybe Int)], Maybe Int)
resolveDimsAssumedSize symt = \case
  []   -> throwError "resolveDimsAssumedSize: array can't have zero dimensions"
  d:[] ->
    case resolveDimStar symt d of
      Left err -> Left err
      Right a -> Right (Nothing, a)
  ds   ->
    case go [] ds of
      Left err -> Left err
      Right (l, r) -> Right (Just l, r)
  where
    go
        :: [Dim (Maybe Int)] -> [DimensionDeclarator a]
        -> Either String ([Dim (Maybe Int)], Maybe Int)
    go ds = \case
      []     -> Right (reverse ds, Nothing)
      dd:[]  ->
        case resolveDimStar symt dd of
          Left  err -> Left err
          Right d   -> Right (ds, d)
      dd:dds ->
        case resolveDimExplicitShape symt dd of
          Left  err -> Left err
          Right d   -> go (d:ds) dds

resolveDimStar
    :: SymbolTable -> DimensionDeclarator a -> Either String (Maybe Int)
resolveDimStar symt (DimensionDeclarator _ _ mlb mub) = do
    lb <- case mlb of
            Nothing -> pure $ Just 1
            Just lbExpr -> evalDynamicDimBoundExpr symt lbExpr
    () <- resolveDimBoundStar mub
    pure lb

resolveDimBoundStar :: Maybe (Expression a) -> Either String ()
resolveDimBoundStar = \case
  Just (ExpValue _ _ ValStar) -> pure ()
  Just{}  -> throwError "resolveDimBoundStar: expression wasn't a ValStar"
  Nothing -> throwError "resolveDimBoundStar: upper bound must be present"