packages feed

copilot-visualizer-4.5: src/Copilot/Visualize/TypedTrace.hs

{-# LANGUAGE GADTs               #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- |
-- Copyright : (c) NASA, 2024-2025
-- License   : BSD-style (see the LICENSE file in the distribution)
--
-- Read and modify traces of specifications.
--
-- In the context of this module, a trace refers only to the values of
-- externs, since the state of a spec can be reproduced if the values of
-- externs are known.
module Copilot.Visualize.TypedTrace
    ( extractTrace
    , updateWithTrace
    , Trace(..)
    , UValues(..)
    )
  where

-- External imports
import           Data.List          hiding ((++))
import           Data.Maybe         (fromMaybe)
import qualified Data.Type.Equality as DE
import           Data.Typeable      (Typeable)
import           Prelude            hiding (div, not, (++), (<), (>))

-- External imports: Copilot
import qualified Copilot.Core     as Core
import           Language.Copilot hiding (interpret, typeOf)

-- | Map stream names to their values.
newtype Trace = Trace
  { traceMap :: [ (String, UValues) ]
  }

-- | Existentially typed list of values of a stream.
data UValues = forall a . Typeable a => UValues
  { uvType   :: Core.Type a
  , uvValues :: [ a ]
  }

-- | Extract a trace from a core specification.
extractTrace :: Core.Spec -> Trace
extractTrace spec = Trace $ concat $ concat
  [ fmap extractTraceStream (Core.specStreams spec)
  , fmap extractTraceObserver (Core.specObservers spec)
  , fmap extractTraceTrigger (Core.specTriggers spec)
  ]

-- | Extract the values of a core 'Stream'.
extractTraceStream :: Core.Stream -> [ (String, UValues) ]
extractTraceStream (Core.Stream _id _buf expr _ty) =
  extractTraceExpr expr

-- | Extract the values of a core 'Observer'.
extractTraceObserver :: Core.Observer -> [ (String, UValues) ]
extractTraceObserver (Core.Observer _name expr _ty) =
  extractTraceExpr expr

-- | Extract the values of a core 'Trigger'.
extractTraceTrigger :: Core.Trigger -> [ (String, UValues) ]
extractTraceTrigger (Core.Trigger _name expr args) = concat $
    extractTraceExpr expr
  : fmap extractTraceUExpr args

-- | Extract the values of a core 'Expr'.
extractTraceExpr :: Core.Expr a -> [ (String, UValues) ]
extractTraceExpr (Core.Local _ _ _ expr1 expr2) = concat
  [ extractTraceExpr expr1
  , extractTraceExpr expr2
  ]
extractTraceExpr (Core.ExternVar ty name values) =
  [ (name, UValues ty (fromMaybe [] values)) ]
extractTraceExpr (Core.Op1 _op expr) =
  extractTraceExpr expr
extractTraceExpr (Core.Op2 _op expr1 expr2) = concat
  [ extractTraceExpr expr1
  , extractTraceExpr expr2
  ]
extractTraceExpr (Core.Op3 _op expr1 expr2 expr3) = concat
  [ extractTraceExpr expr1
  , extractTraceExpr expr2
  , extractTraceExpr expr3
  ]
extractTraceExpr (Core.Label _ty _lbl expr) =
  extractTraceExpr expr
extractTraceExpr _ = []

-- | Extract the values of a core 'UExpr'.
extractTraceUExpr :: Core.UExpr -> [ (String, UValues) ]
extractTraceUExpr (Core.UExpr ty expr) = extractTraceExpr expr

-- | Update externs in a spec.
updateWithTrace :: Trace -> Core.Spec -> Core.Spec
updateWithTrace trace spec = spec
  { Core.specStreams =
      fmap (updateWithTraceStream trace) (Core.specStreams spec)
  , Core.specObservers =
      fmap (updateWithTraceObserver trace) (Core.specObservers spec)
  , Core.specTriggers =
      fmap (updateWithTraceTrigger trace) (Core.specTriggers spec)
  }

-- | Update the values of a 'Stream' based on an input trace.
updateWithTraceStream :: Trace -> Core.Stream -> Core.Stream
updateWithTraceStream trace (Core.Stream ident buf expr ty) =
  Core.Stream ident buf (updateWithTraceExpr trace expr) ty

-- | Update the values of an 'Observer' based on an input trace.
updateWithTraceObserver :: Trace -> Core.Observer -> Core.Observer
updateWithTraceObserver trace (Core.Observer name expr ty) =
  Core.Observer name (updateWithTraceExpr trace expr) ty

-- | Update the values of a 'Trigger' based on an input trace.
updateWithTraceTrigger :: Trace -> Core.Trigger -> Core.Trigger
updateWithTraceTrigger trace (Core.Trigger name expr args) =
  Core.Trigger
    name
    (updateWithTraceExpr trace expr)
    (fmap (updateWithTraceUExpr trace) args)

-- | Update the values of an 'Expr' based on an input trace.
updateWithTraceExpr :: Trace -> Core.Expr a -> Core.Expr a
updateWithTraceExpr trace (Core.Local ty1 ty2 name expr1 expr2) =
  Core.Local
    ty1
    ty2
    name
    (updateWithTraceExpr trace expr1)
    (updateWithTraceExpr trace expr2)
updateWithTraceExpr trace (Core.ExternVar ty name values) =
    Core.ExternVar ty name values'
  where
    values' | Just (UValues ty2 vals) <- lookup name (traceMap trace)
            , Just DE.Refl <- DE.testEquality ty ty2
            = Just vals
            | otherwise
            = values
updateWithTraceExpr trace (Core.Op1 op expr) =
  Core.Op1 op (updateWithTraceExpr trace expr)
updateWithTraceExpr trace (Core.Op2 op expr1 expr2) =
  Core.Op2 op
    (updateWithTraceExpr trace expr1)
    (updateWithTraceExpr trace expr2)
updateWithTraceExpr trace (Core.Op3 op expr1 expr2 expr3) =
  Core.Op3 op
    (updateWithTraceExpr trace expr1)
    (updateWithTraceExpr trace expr2)
    (updateWithTraceExpr trace expr3)
updateWithTraceExpr trace (Core.Label ty lbl expr) =
  Core.Label ty lbl (updateWithTraceExpr trace expr)
updateWithTraceExpr trace x = x

-- | Update the values of a 'UExpr' based on an input trace.
updateWithTraceUExpr :: Trace -> Core.UExpr -> Core.UExpr
updateWithTraceUExpr trace (Core.UExpr ty expr) =
  Core.UExpr ty (updateWithTraceExpr trace expr)