packages feed

ogma-core-1.0.6: src/Language/Trans/FRETReqsDB2Copilot.hs

-- Copyright 2020 United States Government as represented by the Administrator
-- of the National Aeronautics and Space Administration. All Rights Reserved.
--
-- Disclaimers
--
-- No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY
-- OF ANY KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
-- LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
-- SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-- PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT THE
-- SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF
-- PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN
-- ANY MANNER, CONSTITUTE AN ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR
-- RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, HARDWARE, SOFTWARE PRODUCTS OR
-- ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT SOFTWARE. FURTHER,
-- GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING
-- THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES
-- IT "AS IS."
--
-- Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST
-- THE UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS
-- ANY PRIOR RECIPIENT. IF RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN
-- ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES OR LOSSES ARISING FROM SUCH USE,
-- INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING FROM, RECIPIENT'S
-- USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE
-- UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY
-- PRIOR RECIPIENT, TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY
-- FOR ANY SUCH MATTER SHALL BE THE IMMEDIATE, UNILATERAL TERMINATION OF THIS
-- AGREEMENT.
--

-- | Transform a FRET temporal logic specification contained in a requirements
-- database into a Copilot specification.
--
-- Ideally, this module would be implemented as a conversion between ASTs, but
-- we want to add comments to the generated code, which are not representable
-- in the abstract syntax tree.
module Language.Trans.FRETReqsDB2Copilot
    ( FRETReqsDB2CopilotOptions(..)
    , fret2CopilotModule
    )
  where

-- External imports
import Data.List ( nub, sort )

-- Internal imports: SMV
import qualified Language.SMV.AbsSMV        as SMV ( BoolSpec (..) )
import qualified Language.Trans.SMV2Copilot as SMV ( boolSpec2Copilot,
                                                     boolSpecNames )

-- Internal imports: CoCoSpec
import qualified Language.CoCoSpec.AbsCoCoSpec   as CoCoSpec ( BoolSpec (..) )
import qualified Language.Trans.CoCoSpec2Copilot as CoCoSpec ( boolSpec2Copilot,
                                                               boolSpecNames )

import qualified Language.FRETReqsDB.AST as FRET ( FRETReqsDB, semantics,
                                                   semanticsCoCoSpec,
                                                   semanticsFretish )

-- | Options used to customize the conversion of FRET Component Specifications
-- to Copilot code.
data FRETReqsDB2CopilotOptions = FRETReqsDB2CopilotOptions
  { fretReqsDB2CopilotUseCoCoSpec :: Bool
  }

-- | Return a string with the contents of the Copilot module that implements a
-- CoCoSpec BoolSpec.
--
-- PRE: The BoolSpec does not use any identifiers that exist in Copilot,
-- or any of @prop@, @clock@, @ftp@, @ot@, @pre@. All identifiers used are
-- valid C99 identifiers.
fret2CopilotModule :: FRETReqsDB2CopilotOptions
                   -> FRET.FRETReqsDB
                   -> Either String String
fret2CopilotModule prefs x = do
  cocoSpec <- FRET.semanticsCoCoSpec $ FRET.semantics x
  smvSpec  <- FRET.semanticsFretish  $ FRET.semantics x
  pure $ fret2CopilotModule' prefs smvSpec cocoSpec

fret2CopilotModule' :: FRETReqsDB2CopilotOptions
                    -> SMV.BoolSpec
                    -> CoCoSpec.BoolSpec
                    -> String
fret2CopilotModule' prefs smvSpec cocoSpec = unlines $ concat sections
  where
    specS = if fretReqsDB2CopilotUseCoCoSpec prefs
              then CoCoSpec.boolSpec2Copilot cocoSpec
              else SMV.boolSpec2Copilot smvSpec

    idents   = nub $ sort $ if fretReqsDB2CopilotUseCoCoSpec prefs
                              then CoCoSpec.boolSpecNames cocoSpec
                              else SMV.boolSpecNames smvSpec

    sections | fretReqsDB2CopilotUseCoCoSpec prefs
             = [ imports, propDef, externs, clock, ftp, undef, spec, main' ]

             | otherwise
             = [ imports, propDef, externs, clock, ftp, spec, main' ]

    imports :: [String]
    imports =
      [ "import           Copilot.Compile.C99"
      , "import           Copilot.Language          hiding (prop)"
      , "import           Copilot.Language.Prelude"
      , "import           Copilot.Library.LTL       (next)"
      , "import           Copilot.Library.MTL       hiding (since,"
        ++ " alwaysBeen, trigger)"
      , "import           Copilot.Library.PTLTL     (since, previous,"
        ++ " alwaysBeen)"
      , "import qualified Copilot.Library.PTLTL     as PTLTL"
      , "import           Language.Copilot          (reify)"
      , "import Prelude                   hiding ((&&), (||), (++), (<=), (>=),"
        ++ " (<), (>), (==), not)"
      , ""
      ]

    propDef  = [ ""
               , "-- | Property to monitor."
               , "prop :: Stream Bool"
               , "prop = " ++ specS
               , ""
               ]

    externs  = map (\i -> i ++ " = extern " ++ show i ++ " Nothing") idents

    clock    = [ ""
               , "-- | Clock that increases in one-unit steps."
               , "clock :: Stream Int64"
               , "clock = [0] ++ (clock + 1)"
               , ""
               ]

    ftp      = [ ""
               , "-- | First Time Point"
               , "ftp :: Stream Bool"
               , "ftp = [True] ++ false"
               , ""
               ]

    undef    = [ "ot :: Int -> Int -> Stream Bool -> Stream Bool"
               , "ot = undefined"
               , "pre :: Stream Bool -> Stream Bool"
               , "pre = undefined"
               ]

    spec     = [ ""
               , "-- | Complete specification. Calls the C function void handler(); when"
               , "-- the property is violated."
               , "spec :: Spec"
               , "spec = do"
               , "  trigger  \"handler\" prop []"
               , ""
               ]

    main'    = [ ""
               , "main :: IO ()"
               , "main = reify spec >>= compile \"fret\""
               ]