fortran-src 0.14.0 → 0.15.0
raw patch · 5 files changed
+145/−40 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Language.Fortran.Common.Array: dimsLength :: Foldable t => Dims t a -> Int
+ Language.Fortran.Common.Array: dimsTraverse :: (Traversable t, Applicative f) => Dims t (f a) -> f (Dims t a)
+ Language.Fortran.Repr.Eval.Value: ESpecial :: String -> Error
+ Language.Fortran.Repr.Eval.Value: evalConstExpr :: MonadFEvalValue m => Expression a -> m FValue
+ Language.Fortran.Repr.Eval.Value: evalIntrinsicInt :: MonadFEvalValue m => FValue -> FInt -> m FInt
+ Language.Fortran.Repr.Eval.Value: evalIntrinsicInt1 :: MonadFEvalValue m => FValue -> m Int8
+ Language.Fortran.Repr.Eval.Value: evalIntrinsicInt2 :: MonadFEvalValue m => FValue -> m Int16
+ Language.Fortran.Repr.Eval.Value: evalIntrinsicInt4 :: MonadFEvalValue m => FValue -> m Int32
+ Language.Fortran.Repr.Eval.Value: evalIntrinsicInt8 :: MonadFEvalValue m => FValue -> m Int64
+ Language.Fortran.Repr.Eval.Value: evalIntrinsicIntXCoerce :: forall r m. (MonadFEvalValue m, Integral r) => (FInt -> r) -> FValue -> m r
- Language.Fortran.Analysis.SemanticTypes: type Dimensions = Dims NonEmpty Int
+ Language.Fortran.Analysis.SemanticTypes: type Dimensions = Dims NonEmpty (Maybe Int)
Files
- CHANGELOG.md +9/−0
- fortran-src.cabal +1/−1
- src/Language/Fortran/Analysis/SemanticTypes.hs +11/−4
- src/Language/Fortran/Common/Array.hs +44/−13
- src/Language/Fortran/Repr/Eval/Value.hs +80/−22
CHANGELOG.md view
@@ -1,3 +1,12 @@+### 0.15.0 (May 04, 2023)+ * handle more `INT` intrinsic forms+ [#263](https://github.com/camfort/fortran-src/pull/263)+ * add more `Language.Fortran.Common.Array` utils+ [#263](https://github.com/camfort/fortran-src/pull/263)+ * Analysis.SemanticTypes: use `type Dimensions = Dims NonEmpty (Maybe Int)`+ instead of `type Dimensions = Dims NonEmpty Int`+ [#263](https://github.com/camfort/fortran-src/pull/263)+ ### 0.14.0 (Mar 21, 2023) * provide extended evaluated array dimensions type at `Language.Fortran.Common.Array` (#261, @raehik)
fortran-src.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: fortran-src-version: 0.14.0+version: 0.15.0 synopsis: Parsers and analyses for Fortran standards 66, 77, 90, 95 and 2003 (partial). description: Provides lexing, parsing, and basic analyses of Fortran code covering standards: FORTRAN 66, FORTRAN 77, Fortran 90, Fortran 95, Fortran 2003 (partial) and some legacy extensions. Includes data flow and basic block analysis, a renamer, and type analysis. For example usage, see the @<https://hackage.haskell.org/package/camfort CamFort>@ project, which uses fortran-src as its front end. category: Language
src/Language/Fortran/Analysis/SemanticTypes.hs view
@@ -52,7 +52,10 @@ deriving stock (Ord, Eq, Show, Data, Generic) deriving anyclass (NFData, Binary, Out) -type Dimensions = Dims NonEmpty Int+-- | The main dimension type is a non-empty list of dimensions where each bound+-- is @'Maybe' 'Int'@. @'Nothing'@ bounds indicate a dynamic bound (e.g. uses+-- a dummy variable).+type Dimensions = Dims NonEmpty (Maybe Int) instance Pretty SemType where pprint' v@@ -81,11 +84,15 @@ -- | Convert 'Dimensions' data type to its previous type synonym -- @(Maybe [(Int, Int)])@. ----- Will not return @Just []@.+-- Drops all information for array dimensions that aren't fully static/known. dimensionsToTuples :: Dimensions -> Maybe [(Int, Int)] dimensionsToTuples = \case- DimsExplicitShape ds ->- Just $ NonEmpty.toList $ fmap (\(Dim lb ub) -> (lb, ub)) ds+ DimsExplicitShape ds -> fmap NonEmpty.toList $ traverse go ds+ where+ go (Dim mlb mub) = do+ lb <- mlb+ ub <- mub+ pure $ (lb, ub) DimsAssumedSize _ds _d -> Nothing DimsAssumedShape _ss -> Nothing
src/Language/Fortran/Common/Array.hs view
@@ -12,6 +12,15 @@ import qualified Language.Fortran.PrettyPrint as F +-- | A single array dimension with bounds of type @a@.+--+-- * @'Num' a => 'Dim' a@ is a static, known-size dimension.+-- * @'Dim' ('Language.Fortran.AST.Expression' '()')@ is a dimension with+-- unevaluated bounds expressions. Note that these bounds may be constant+-- expressions, or refer to dummy variables, or be invalid.+-- * @'Num' a => 'Dim' ('Maybe' a)@ is a dimension where some bounds are+-- known, and others are not. This may be useful to record some information+-- about dynamic explicit-shape arrays. data Dim a = Dim { dimLower :: a -- ^ Dimension lower bound. , dimUpper :: a -- ^ Dimension upper bound.@@ -31,34 +40,38 @@ instance Out (Dim a) => F.Pretty (Dim a) where pprint' _ = doc --- | Evaluated dimensions of a Fortran array.------ A known-length dimension is defined by a lower bound and an upper bound. This--- data type takes a syntactic view, rather than normalizing lower bound to 0--- and passing just dimension extents.+-- | Fortran array dimensions, defined by a list of 'Dim's storing lower and+-- upper bounds. -- -- You select the list type @t@ (which should be 'Functor', 'Foldable' and--- 'Traversable') and the numeric index type @a@ (e.g. 'Int').+-- 'Traversable') and the bound type @a@ (e.g. 'Int'). ----- Note that using a non-empty list type such as 'Data.List.NonEmpty.NonEmpty'--- will disallow representing zero-dimension arrays, which may be useful for--- soundness.+-- Using a non-empty list type such as 'Data.List.NonEmpty.NonEmpty' will+-- disallow representing zero-dimension arrays, providing extra soundness. -- -- Note the following excerpt from the F2018 standard (8.5.8.2 Explicit-shape -- array): -- -- > If the upper bound is less than the lower bound, the range is empty, the -- > extent in that dimension is zero, and the array is of zero size.+--+-- Note that the 'Foldable' instance does not provide "dimension-like" access to+-- this type. That is, @'length' (a :: 'Dims' t a)@ will _not_ tell you how many+-- dimensions 'a' represents. Use 'dimsLength' for that. data Dims t a+ -- | Explicit-shape array. All dimensions are known. = DimsExplicitShape (t (Dim a)) -- ^ list of all dimensions + -- | Assumed-size array. The final dimension has no upper bound (it is+ -- obtained from its effective argument). Earlier dimensions may be defined+ -- like explicit-shape arrays. | DimsAssumedSize (Maybe (t (Dim a))) -- ^ list of all dimensions except last- a -- ^ lower bound of last dimension+ a -- ^ lower bound of last dimension - -- | Assumed-shape array dimensions. Here, we only have the lower bound for- -- each dimension, and the rank (via length).+ -- | Assumed-shape array. Shape is taken from effective argument. We store the+ -- lower bound for each dimension, and thus also the rank (via list length). | DimsAssumedShape (t a) -- ^ list of lower bounds @@ -105,7 +118,8 @@ instance Out (Dims t a) => F.Pretty (Dims t a) where pprint' _ = doc --- Faster is possible for non-@List@s, but this is OK for the general case.+-- Faster is possible for non @[]@ list-likes, but this is OK for the general+-- case. prettyIntersperse :: Foldable t => Pretty.Doc -> t Pretty.Doc -> Pretty.Doc prettyIntersperse dBetween ds = case foldMap (\d -> [dBetween, d]) ds of@@ -114,3 +128,20 @@ prettyAfter :: Foldable t => Pretty.Doc -> t Pretty.Doc -> Pretty.Doc prettyAfter dAfter = foldMap (\d -> d <> dAfter)++-- | Traverse over the functor in a 'Dims' value with a functor bound type.+--+-- For example, to turn a @'Dims' t ('Maybe' a)@ into a @'Maybe' ('Dims' t a)@.+dimsTraverse :: (Traversable t, Applicative f) => Dims t (f a) -> f (Dims t a)+dimsTraverse = traverse id+-- TODO provide a SPECIALIZE clause for the above Maybe case. performance! :)++-- | How many dimensions does the given 'Dims' represent?+dimsLength :: Foldable t => Dims t a -> Int+dimsLength = \case+ DimsExplicitShape ds -> length ds+ DimsAssumedShape ss -> length ss+ DimsAssumedSize mds _d ->+ case mds of+ Nothing -> 1+ Just ds -> length ds + 1
src/Language/Fortran/Repr/Eval/Value.hs view
@@ -29,6 +29,7 @@ import qualified Data.Text as Text import qualified Data.Char import qualified Data.Bits+import Data.Int import Control.Monad.Except @@ -45,11 +46,19 @@ = ENoSuchVar F.Name | EKindLitBadType F.Name FType | ENoSuchKindForType String FKindLit+ | EUnsupported String+ -- ^ Syntax which probably should be supported, but (currently) isn't.+ | EOp Op.Error | EOpTypeError String++ | ESpecial String+ -- ^ Special value-like expression that we can't evaluate usefully.+ | ELazy String -- ^ Catch-all for non-grouped errors.+ deriving stock (Generic, Show, Eq) -- | A convenience constraint tuple defining the base requirements of the@@ -156,13 +165,13 @@ warn "requested to evaluate BOZ literal with no context: defaulting to INTEGER(4)" pure $ FSVInt $ FInt4 $ F.bozAsTwosComp boz F.ValHollerith s -> pure $ FSVString $ Text.pack s- F.ValIntrinsic{} -> error "you tried to evaluate a lit, but it was an intrinsic name"- F.ValVariable{} -> error "you tried to evaluate a lit, but it was a variable name"- F.ValOperator{} -> error "you tried to evaluate a lit, but it was a custom operator name"- F.ValAssignment -> error "you tried to evaluate a lit, but it was an overloaded assignment name"- F.ValStar -> error "you tried to evaluate a lit, but it was a star"- F.ValColon -> error "you tried to evaluate a lit, but it was a colon"- F.ValType{} -> error "not used anywhere, don't know what it is"+ F.ValIntrinsic{} -> err $ ESpecial "lit was ValIntrinsic{} (intrinsic name)"+ F.ValVariable{} -> err $ ESpecial "lit was ValVariable{} (variable name)"+ F.ValOperator{} -> err $ ESpecial "lit was ValOperator{} (custom operator name)"+ F.ValAssignment -> err $ ESpecial "lit was ValAssignment (overloaded assignment name)"+ F.ValStar -> err $ ESpecial "lit was ValStar"+ F.ValColon -> err $ ESpecial "lit was ValColon"+ F.ValType{} -> err $ ELazy "lit was ValType: not used anywhere, don't know what it is" err :: MonadError Error m => Error -> m a err = throwError@@ -339,21 +348,20 @@ err $ EOpTypeError $ "not: expected INT(x), got "<>show (fScalarValueType v') - "int" -> do- -- TODO a real pain. just implementing common bits for now- -- TODO gfortran actually performs some range checks for constants!- -- @int(128, 1)@ errors with "this INT(4) is too big for INT(1)".- args' <- forceArgs 1 args- let [v] = args'- v' <- forceScalar v- case v' of- FSVInt{} ->- pure $ MkFScalarValue v'- FSVReal r ->- pure $ MkFScalarValue $ FSVInt $ FInt4 $ fRealUOp truncate r- _ ->- err $ EOpTypeError $- "int: unsupported or unimplemented type: "<>show (fScalarValueType v')+ "int" ->+ case args of+ [] -> err $ EOpTypeError $ "int: expected 1 or 2 arguments, got 0"+ [v] -> do+ -- @INT(x)@ == @INT(x, 4)@ (F2018 16.9.100:23, pg.381)+ (MkFScalarValue . FSVInt . FInt4) <$> evalIntrinsicInt4 v+ [v, vk] -> do+ vk' <- forceScalar vk+ case vk' of+ FSVInt vkI -> (MkFScalarValue . FSVInt) <$> evalIntrinsicInt v vkI+ _ ->+ err $ EOpTypeError $+ "int: kind argument must be INTEGER, got "<>show (fScalarValueType vk')+ _ -> err $ EOpTypeError $ "int: expected 1 or 2 arguments, got >2" -- TODO all lies "int2" -> do@@ -371,6 +379,52 @@ _ -> err $ EUnsupported $ "function call: " <> fname +-- TODO 2023-05-03 raehik: gfortran actually performs some range checks for+-- constants! @int(128, 1)@ errors with "this INT(4) is too big for INT(1)".+-- we don't do that currently. just means more plumbing+evalIntrinsicInt :: MonadFEvalValue m => FValue -> FInt -> m FInt+evalIntrinsicInt v = fIntUOp go+ where+ go :: (MonadFEvalValue m, Num a, Eq a) => a -> m FInt+ go = \case+ 1 -> FInt1 <$> evalIntrinsicInt1 v+ 2 -> FInt2 <$> evalIntrinsicInt2 v+ 4 -> FInt4 <$> evalIntrinsicInt4 v+ 8 -> FInt8 <$> evalIntrinsicInt8 v+ _ -> err $ ELazy "int: kind argument wasn't 1, 2, 4 or 8"++-- | @INT(a, 1)@+evalIntrinsicInt1 :: MonadFEvalValue m => FValue -> m Int8+evalIntrinsicInt1 = evalIntrinsicIntXCoerce coerceToI1+ where coerceToI1 = fIntUOp' id fromIntegral fromIntegral fromIntegral++-- | @INT(a, 2)@+evalIntrinsicInt2 :: MonadFEvalValue m => FValue -> m Int16+evalIntrinsicInt2 = evalIntrinsicIntXCoerce coerceToI2+ where coerceToI2 = fIntUOp' fromIntegral id fromIntegral fromIntegral++-- | @INT(a, 4)@, @INT(a)@+evalIntrinsicInt4 :: MonadFEvalValue m => FValue -> m Int32+evalIntrinsicInt4 = evalIntrinsicIntXCoerce coerceToI4+ where coerceToI4 = fIntUOp' fromIntegral fromIntegral id fromIntegral++-- | @INT(a, 8)@+evalIntrinsicInt8 :: MonadFEvalValue m => FValue -> m Int64+evalIntrinsicInt8 = evalIntrinsicIntXCoerce coerceToI8+ where coerceToI8 = fIntUOp' fromIntegral fromIntegral fromIntegral id++evalIntrinsicIntXCoerce+ :: forall r m+ . (MonadFEvalValue m, Integral r) => (FInt -> r) -> FValue -> m r+evalIntrinsicIntXCoerce coerceToIX v = do+ v' <- forceScalar v+ case v' of+ FSVInt i -> pure $ coerceToIX i+ FSVReal r -> pure $ fRealUOp truncate r+ _ ->+ err $ EOpTypeError $+ "int: unsupported or unimplemented type: "<>show (fScalarValueType v')+ evalArg :: MonadFEvalValue m => F.Argument a -> m FValue evalArg (F.Argument _ _ _ ae) = case ae of@@ -437,3 +491,7 @@ _ -> err $ EOpTypeError $ "max: unsupported type: "<> show (fScalarValueType vCurMax)++-- | Evaluate a constant expression (F2018 10.1.12).+evalConstExpr :: MonadFEvalValue m => F.Expression a -> m FValue+evalConstExpr = evalExpr