ogma-core-1.15.0: src/Command/Overview.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- Copyright 2024 United States Government as represented by the Administrator
-- of the National Aeronautics and Space Administration. All Rights Reserved.
--
-- Disclaimers
--
-- Licensed under the Apache License, Version 2.0 (the "License"); you may
-- not use this file except in compliance with the License. You may obtain a
-- copy of the License at
--
-- https://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-- License for the specific language governing permissions and limitations
-- under the License.
--
-- | Produce an overview of the input files.
module Command.Overview
( command
, CommandOptions(..)
, OverviewFile(..)
, CommandSummary(..)
, CommandSummaryRequirements(..)
, CommandSummaryDiagram(..)
, ErrorCode
)
where
-- External imports
import Control.Monad (foldM)
import Control.Monad.Except (runExceptT)
import Data.Aeson (ToJSON (..))
import GHC.Generics (Generic)
-- External imports: Ogma
import Data.OgmaSpec (Spec (..))
-- Internal imports
import Command.Common (InputFile(..), parseInputFile)
import Command.Errors (ErrorCode, ErrorTriplet (..))
import Command.Result (Result (..))
import Data.Diagram.Analysis (AnalysisResult (..),
analyzeDiagram)
import Data.ExprPair (ExprPair(..), ExprPairT(..),
exprPair)
import Data.Location (Location (..))
import qualified Data.Spec.Analysis as SpecAnalysis
import Data.Spec.Extra (addMissingIdentifiers)
import qualified Language.Trans.Spec2Copilot as Spec2Copilot
-- | Generate overview of a spec given in an input file.
--
-- PRE: The file given is readable, contains a valid file with recognizable
-- format, the formulas in the file do not use any identifiers that exist in
-- Copilot, or any of @prop@, @clock@, @ftp@, @notPreviousNot@. All identifiers
-- used are valid C99 identifiers. The template, if provided, exists and uses
-- the variables needed by the overview application generator. The target
-- directory is writable and there's enough disk space to copy the files over.
command :: CommandOptions -- ^ Customization options
-> IO (Maybe CommandSummary, Result ErrorCode)
command options = do
fs <- foldM
processFile
(Right emptyCommandSummary)
(commandInputFiles options)
return $ commandResult options fs
where
processFile :: Either (FilePath, String) CommandSummary
-> OverviewFile
-> IO (Either (FilePath, String) CommandSummary)
processFile acc file = case acc of
Left _ -> return acc
Right acc' -> do
let functions = exprPair (overviewFilePropFormat file)
c <- command' (overviewFilePath file) file functions
case c of
Left msg -> return $ Left (overviewFilePath file, msg)
Right s -> return $ Right $ mergeCommandSummary acc' s
-- | Generate overview of a spec given in an input file.
--
-- PRE: The file given is readable, contains a valid file with recognizable
-- format, the formulas in the file do not use any identifiers that exist in
-- Copilot, or any of @prop@, @clock@, @ftp@, @notPreviousNot@. All identifiers
-- used are valid C99 identifiers. The template, if provided, exists and uses
-- the variables needed by the overview application generator. The target
-- directory is writable and there's enough disk space to copy the files over.
command' :: FilePath
-> OverviewFile
-> ExprPair
-> IO (Either String CommandSummary)
command' fp options (ExprPair exprT) = do
res <- runExceptT $
parseInputFile fp formatName propFormatName propVia exprT
case res of
Left (ErrorTriplet _ s _) -> return $ Left s
Right (InputFileDiagram diagramR) -> do
analysisResult <- analyzeDiagram diagramR
pure $ Right $ emptyCommandSummary
{ commandSummaryDiagrams =
[ CommandSummaryDiagram
fp
(numStates analysisResult)
(deterministic analysisResult)
]
}
Right (InputFileSpec spec') -> do
let specCompleted = addMissingIdentifiers ids spec'
specAnalyzed = Spec2Copilot.specAnalyze specCompleted
specFormalAnalysis <-
SpecAnalysis.specAnalyze [] replace printExpr specCompleted
pure $ do
numExterns <- length . externalVariables <$> specAnalyzed
numInternal <- length . internalVariables <$> specAnalyzed
numReqs <- length . requirements <$> specAnalyzed
numTrues <- SpecAnalysis.numAlwaysTrue <$> specFormalAnalysis
numFalses <- SpecAnalysis.numAlwaysFalse <$> specFormalAnalysis
consistent <- SpecAnalysis.consistent <$> specFormalAnalysis
pure $ emptyCommandSummary
{ commandSummaryRequirements =
[ CommandSummaryRequirements
fp
numExterns
numInternal
numReqs
numTrues
numFalses
consistent
]
}
where
formatName = overviewFileFormat options
propFormatName = overviewFilePropFormat options
propVia = overviewFilePropVia options
ExprPairT _parse replace printExpr ids _def = exprT
data CommandSummary = CommandSummary
{ commandSummaryRequirements :: [CommandSummaryRequirements]
, commandSummaryDiagrams :: [CommandSummaryDiagram]
}
deriving (Generic, Show)
instance ToJSON CommandSummary
-- | Summary with empty data.
emptyCommandSummary :: CommandSummary
emptyCommandSummary = CommandSummary [] []
-- | Merge two summaries.
mergeCommandSummary :: CommandSummary -> CommandSummary -> CommandSummary
mergeCommandSummary c1 c2 = CommandSummary
{ commandSummaryRequirements =
commandSummaryRequirements c1 ++ commandSummaryRequirements c2
, commandSummaryDiagrams =
commandSummaryDiagrams c1 ++ commandSummaryDiagrams c2
}
instance Semigroup CommandSummary where
(<>) = mergeCommandSummary
instance Monoid CommandSummary where
mempty = emptyCommandSummary
-- | Requirement data for inclusion in the summary.
data CommandSummaryRequirements = CommandSummaryRequirements
{ commandRequirementsFile :: FilePath
, commandExternalVariables :: Int
, commandInternalVariables :: Int
, commandRequirements :: Int
, commandRequirementsTrue :: Int
, commandRequirementsFalse :: Int
, commandRequirementsConsistent :: Bool
}
deriving (Generic, Show)
instance ToJSON CommandSummaryRequirements
-- | Diagram Data for inclusion in the summary.
data CommandSummaryDiagram = CommandSummaryDiagram
{ commandDiagramFile :: FilePath
, commandDiagramNumStates :: Int
, commandDiagramDeterministic :: Bool
}
deriving (Generic, Show)
instance ToJSON CommandSummaryDiagram
-- | Options used to customize the interpretation of input specifications.
data CommandOptions = CommandOptions
{ commandInputFiles :: [ OverviewFile ]
}
-- | Information about one file in the command options.
data OverviewFile = OverviewFile
{ overviewFilePath :: FilePath
, overviewFileFormat :: String
, overviewFilePropFormat :: String
, overviewFilePropVia :: Maybe String
}
-- * Error codes
-- | Error: the input file cannot be read due to it being unreadable or the
-- format being incorrect.
ecOverviewError :: ErrorCode
ecOverviewError = 1
-- * Result
-- | Process the result of the transformation function.
commandResult :: CommandOptions
-> Either (FilePath, String) a
-> (Maybe a, Result ErrorCode)
commandResult _options result = case result of
Left (fp, msg) -> (Nothing, Error ecOverviewError msg (LocationFile fp))
Right t -> (Just t, Success)