fortran-vars-0.3.1: src/Language/Fortran/Vars/BozConstant.hs
module Language.Fortran.Vars.BozConstant
( resolveBozConstant
, bozToInt
, bozToInt1
, bozToInt2
, bozToInt4
, bozToInt8
)
where
import qualified Data.Map as M
import qualified Language.Fortran.AST.Literal.Boz as AST
import Language.Fortran.Vars.Types ( SymbolTableEntry(..)
, SemType(..)
, Kind
, ExpVal(..)
, SymbolTable
)
import Data.Int
-- | Given 'SymbolTable', contextual symbol name and BOZ Constant
-- ('ExpVal' constructed with Boz String), resolve BOZ Constant considering
-- contextual symbol.
--
-- Currently, it only resolves BOZ Constants in context of INTEGER.
resolveBozConstant :: SymbolTable -> String -> ExpVal -> ExpVal
resolveBozConstant symTable assignSymbol (Boz boz) = go boz
where
go boz = case entry of
Just (SVariable (TInteger kind) _) ->
bozToInt kind boz
Just (SVariable ty _) ->
error
$ assignSymbol
<> " is a "
<> show ty
<> "\nBOZ constants can only be resolved in an INTEGER context"
Just _ -> error
(assignSymbol
++ " is not a scalar variable. \
\Invalid fortran sytax"
)
Nothing -> error
(assignSymbol
++ " could not be found. \
\Invalid fortran syntax"
)
entry = M.lookup assignSymbol symTable
resolveBozConstant _ _ _ = error "Can only resolve ExpVal Boz"
-- | Resolve a BOZ constant as an INTEGER(k).
--
-- Works on arbitrary kinds, including non-standard, assuming that kind
-- indicates size in bytes.
bozToInt :: Kind -> AST.Boz -> ExpVal
bozToInt kind boz = case kind of
-- handle regular kinds via bitwise operations on sized machine integers,
-- relying on overflow behaviour
1 -> bozToInt1 boz
2 -> bozToInt2 boz
4 -> bozToInt4 boz
8 -> bozToInt8 boz
-- handle irregular kinds via explicit numeric operations
-- (shouldn't really ever trigger, but no harm)
k -> bozAsTwosCompExplicit boz k
--------------------------------------------------------------------------------
-- | Resolve a BOZ constant as an INTEGER(1).
bozToInt1 :: AST.Boz -> ExpVal
bozToInt1 = Int . fromIntegral . AST.bozAsTwosComp @Int8
-- | Resolve a BOZ constant as an INTEGER(2).
bozToInt2 :: AST.Boz -> ExpVal
bozToInt2 = Int . fromIntegral . AST.bozAsTwosComp @Int16
-- | Resolve a BOZ constant as an INTEGER(4).
bozToInt4 :: AST.Boz -> ExpVal
bozToInt4 = Int . fromIntegral . AST.bozAsTwosComp @Int32
-- | Resolve a BOZ constant as an INTEGER(8).
bozToInt8 :: AST.Boz -> ExpVal
bozToInt8 = Int . fromIntegral . AST.bozAsTwosComp @Int64
--------------------------------------------------------------------------------
bozAsTwosCompExplicit :: AST.Boz -> Kind -> ExpVal
bozAsTwosCompExplicit boz kind =
let allowedBinaryLength = kind * 8
maxBinaryValue = 2 ^ (allowedBinaryLength - 1) - 1
minBinaryValue = (-1) * 2 ^ (allowedBinaryLength - 1)
decimal = AST.bozAsNatural boz
overflow = decimal - maxBinaryValue
in if overflow > 0 then Int (minBinaryValue + overflow - 1) else Int decimal