packages feed

ogma-language-fret-cs-1.0.6: src/Language/FRETComponentSpec/AST.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.
--
{-# LANGUAGE OverloadedStrings #-}
{- HLINT ignore "Functor law"   -}

-- | Representation and parser of FRET Component Specifications.
--
-- FRET files are JSON files, implemented in Haskell using type classes, so the
-- parser is defined in the same module as the AST to avoid having orphan
-- instances.
module Language.FRETComponentSpec.AST where

-- External imports
import           Data.Aeson          ( FromJSON (..), Value (Object), (.:) )
import           Data.Aeson.Types    ( prependFailure, typeMismatch )
import           Data.Aeson.Key      ( toString )
import qualified Data.Aeson.KeyMap   as M

-- Internal imports
import qualified Language.CoCoSpec.AbsCoCoSpec as CoCoSpec
import qualified Language.CoCoSpec.ParCoCoSpec as CoCoSpec ( myLexer,
                                                             pBoolSpec )

import qualified Language.SMV.AbsSMV   as SMV
import qualified Language.SMV.ParSMV   as SMV ( myLexer, pBoolSpec )

-- | Abstract representation of a FRET file.
data FRETComponentSpec = FRETComponentSpec
    { fretName              :: String
    , fretInternalVariables :: [ FRETInternalVariableDef ]
    , fretExternalVariables :: [ FRETExternalVariableDef ]
    , fretRequirements      :: [ FRETRequirement ]
    }
  deriving (Show)

-- | Instance to parse FRET semantics keys in JSON format.
instance FromJSON FRETComponentSpec where
  parseJSON (Object v)
      | (specName, Object specValues) <- head (M.toList v)
      = FRETComponentSpec (toString specName)
      <$> specValues .: "Internal_variables"
      <*> specValues .: "Other_variables"
      <*> specValues .: "Requirements"

      | (specName, specValues) <- head (M.toList v)
      = prependFailure "parsing FRET Component Specification failed, "
          (typeMismatch "Object" specValues)

  parseJSON invalid =
    prependFailure "parsing FRET Component Specification failed, "
      (typeMismatch "Object" invalid)

-- | Internal variable definition, with a given name, its type and either a
-- Lustre or a Copilot expression.
data FRETInternalVariableDef = FRETInternalVariableDef
    { fretInternalVariableName    :: String
    , fretInternalVariableType    :: String
    , fretInternalVariableLustre  :: String
    , fretInternalVariableCopilot :: String
    }
  deriving (Show)

instance FromJSON FRETInternalVariableDef where
  parseJSON (Object v) = FRETInternalVariableDef
    <$> v .: "name"
    <*> v .: "type"
    <*> v .: "assignmentLustre"
    <*> v .: "assignmentCopilot"

  parseJSON invalid =
    prependFailure "parsing FRET Internal Variable definition failed, "
      (typeMismatch "Object" invalid)

-- | External variable definition, with a given name and type.
--
-- The value of external variables is assigned outside Copilot, so they have no
-- defining expression in this type..
data FRETExternalVariableDef = FRETExternalVariableDef
    { fretExternalVariableName :: String
    , fretExternalVariableType :: String
    }
  deriving (Show)

instance FromJSON FRETExternalVariableDef where
  parseJSON (Object v) = FRETExternalVariableDef
    <$> v .: "name"
    <*> v .: "type"

  parseJSON invalid =
    prependFailure "parsing FRET External Variable failed, "
      (typeMismatch "Object" invalid)

-- | Requirement with a given name and a CoCoSpec expression.
data FRETRequirement = FRETRequirement
    { fretRequirementName       :: String
    , fretRequirementCoCoSpec   :: Maybe (Either String CoCoSpec.BoolSpec)
    , fretRequirementPTExpanded :: Maybe (Either String SMV.BoolSpec)
    , fretRequirementFretish    :: String
    }
  deriving (Show)

instance FromJSON FRETRequirement where
  parseJSON (Object v) = FRETRequirement
    <$> v .: "name"
    <*> (fmap (CoCoSpec.pBoolSpec . CoCoSpec.myLexer) <$> v .: "CoCoSpecCode")
    <*> (fmap (SMV.pBoolSpec . SMV.myLexer) <$> v .: "ptLTL")
    <*> (v .: "fretish")

  parseJSON invalid =
    prependFailure "parsing FRET Requirement failed, "
      (typeMismatch "Object" invalid)

-- | Apply a variable subsitution to variables and requirements in a FRET
-- file.
applySubstitution :: (String, String) -> FRETComponentSpec -> FRETComponentSpec
applySubstitution sub file =
    FRETComponentSpec tlName
                      tlInternalVariables
                      tlExternalVariables
                      tlReqs
  where

    -- Result component spec fields
    tlName              = fretName file
    tlInternalVariables = map internalVarMapF $ fretInternalVariables file
    tlExternalVariables = map externalVarMapF $ fretExternalVariables file
    tlReqs              = map reqMapF $ fretRequirements file

    -- Mapping function for fields with names to substitute
    internalVarMapF x = x { fretInternalVariableName =
                              subsName sub (fretInternalVariableName x) }

    externalVarMapF x = x { fretExternalVariableName =
                              subsName sub (fretExternalVariableName x)}

    reqMapF x = x { fretRequirementName =
                      subsName sub (fretRequirementName x)

                  , fretRequirementPTExpanded =
                      fmap (fmap (subBS sub)) (fretRequirementPTExpanded x)
                  }

    -- Substitute name x if it matches the old name oName
    subsName (oName, nName) x = if x == oName then nName else x

    -- Substitute a name in all identifiers in a boolean expression
    subBS sub' = mapBoolSpecIdent (subsName sub')

    -- Traverse a boolean expression applying a function to all identifiers
    mapBoolSpecIdent :: (String -> String) -> SMV.BoolSpec -> SMV.BoolSpec
    mapBoolSpecIdent f boolSpec =
      case boolSpec of
        SMV.BoolSpecSignal (SMV.Ident i) -> SMV.BoolSpecSignal (SMV.Ident (f i))

        SMV.BoolSpecConst bc -> SMV.BoolSpecConst bc

        SMV.BoolSpecNum e -> SMV.BoolSpecNum (mapNumExprIdent f e)

        SMV.BoolSpecCmp spec1 op2 spec2 -> SMV.BoolSpecCmp
                                             (mapBoolSpecIdent f spec1) op2
                                             (mapBoolSpecIdent f spec2)

        SMV.BoolSpecNeg spec -> SMV.BoolSpecNeg (mapBoolSpecIdent f spec)

        SMV.BoolSpecAnd spec1 spec2 -> SMV.BoolSpecAnd
                                             (mapBoolSpecIdent f spec1)
                                             (mapBoolSpecIdent f spec2)

        SMV.BoolSpecOr spec1 spec2 -> SMV.BoolSpecOr
                                            (mapBoolSpecIdent f spec1)
                                            (mapBoolSpecIdent f spec2)

        SMV.BoolSpecXor spec1 spec2 -> SMV.BoolSpecXor
                                             (mapBoolSpecIdent f spec1)
                                             (mapBoolSpecIdent f spec2)

        SMV.BoolSpecImplies spec1 spec2 -> SMV.BoolSpecImplies
                                                 (mapBoolSpecIdent f spec1)
                                                 (mapBoolSpecIdent f spec2)

        SMV.BoolSpecEquivs spec1 spec2 -> SMV.BoolSpecEquivs
                                                (mapBoolSpecIdent f spec1)
                                                (mapBoolSpecIdent f spec2)

        SMV.BoolSpecOp1 op spec -> SMV.BoolSpecOp1 op (mapBoolSpecIdent f spec)

        SMV.BoolSpecOp2 spec1 op2 spec2 -> SMV.BoolSpecOp2
                                             (mapBoolSpecIdent f spec1) op2
                                             (mapBoolSpecIdent f spec2)

    -- Traverse a numeric expression applying a function to all identifiers
    mapNumExprIdent :: (String -> String) -> SMV.NumExpr -> SMV.NumExpr
    mapNumExprIdent f numExpr =
      case numExpr of
        SMV.NumId (SMV.Ident i)    -> SMV.NumId (SMV.Ident (f i))
        SMV.NumConstI c            -> SMV.NumConstI c
        SMV.NumConstD c            -> SMV.NumConstD c
        SMV.NumAdd expr1 op expr2  -> SMV.NumAdd
                                            (mapNumExprIdent f expr1)
                                            op
                                            (mapNumExprIdent f expr2)
        SMV.NumMult expr1 op expr2 -> SMV.NumMult
                                            (mapNumExprIdent f expr1)
                                            op
                                            (mapNumExprIdent f expr2)