copilot-core 3.2 → 3.2.1
raw patch · 21 files changed
+353/−66 lines, 21 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG +4/−0
- copilot-core.cabal +1/−1
- src/Copilot/Core.hs +1/−3
- src/Copilot/Core/Error.hs +8/−2
- src/Copilot/Core/Expr.hs +9/−1
- src/Copilot/Core/External.hs +8/−0
- src/Copilot/Core/Interpret.hs +6/−2
- src/Copilot/Core/Interpret/Eval.hs +82/−17
- src/Copilot/Core/Interpret/Render.hs +17/−5
- src/Copilot/Core/Locals.hs +12/−0
- src/Copilot/Core/MakeTags.hs +1/−1
- src/Copilot/Core/Operators.hs +7/−1
- src/Copilot/Core/PrettyDot.hs +74/−17
- src/Copilot/Core/PrettyPrint.hs +28/−1
- src/Copilot/Core/Spec.hs +22/−7
- src/Copilot/Core/Type.hs +32/−4
- src/Copilot/Core/Type/Array.hs +10/−2
- src/Copilot/Core/Type/Dynamic.hs +8/−0
- src/Copilot/Core/Type/Equality.hs +9/−0
- src/Copilot/Core/Type/Show.hs +10/−1
- src/Copilot/Core/Type/Uninitialized.hs +4/−1
CHANGELOG view
@@ -1,3 +1,7 @@+2021-03-05+ * Version bump (3.2.1). (#31)+ * Completed the documentation. (#22)+ 2020-12-06 Ivan Perez <ivan.perez@acm.org> * Version bump (3.2). * Fixed implementation of tysize for n-dimensional arrays. (#20).
copilot-core.cabal view
@@ -1,6 +1,6 @@ cabal-version: >=1.10 name: copilot-core-version: 3.2+version: 3.2.1 synopsis: An intermediate representation for Copilot. description: Intermediate representation for Copilot.
src/Copilot/Core.hs view
@@ -3,14 +3,12 @@ -------------------------------------------------------------------------------- -- | Intermediate representation for Copilot specifications.--- The form of the representation is based on this paper:+-- The following articles might also be useful: -- -- * Carette, Jacques and Kiselyov, Oleg and Shan, Chung-chieh, -- \"/Finally tagless, partially evaluated: Tagless staged/ -- /interpreters for simpler typed languages/\", -- Journal of Functional Programming vol. 19, p. 509-543, 2009.------ The following article might also be useful: -- -- * Guillemette, Louis-Julien and Monnier, Stefan, -- \"/Type-Safe Code Transformations in Haskell/\",
src/Copilot/Core/Error.hs view
@@ -4,11 +4,15 @@ {-# LANGUAGE Safe #-} +-- | Custom functions to report error messages to users. module Copilot.Core.Error ( impossible , badUsage ) where -impossible :: String -> String -> a+-- | Report an error due to a bug in Copilot.+impossible :: String -- ^ Name of the function in which the error was detected.+ -> String -- ^ Name of the package in which the function is located.+ -> a impossible function package = error $ "\"Impossible\" error in function " ++ function ++ ", in package " ++ package@@ -16,5 +20,7 @@ ++ "https://github.com/Copilot-Language/copilot/issues" ++ "or email the maintainers at <dev@dedden.net>" -badUsage :: String -> a+-- | Report an error due to an error detected by Copilot (e.g., user error).+badUsage :: String -- ^ Description of the error.+ -> a badUsage msg = error $ "Copilot error: " ++ msg
src/Copilot/Core/Expr.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE Safe #-} {-# LANGUAGE GADTs, ExistentialQuantification #-} +-- | Internal representation of Copilot stream expressions. module Copilot.Core.Expr ( Id , Name@@ -42,6 +43,11 @@ -------------------------------------------------------------------------------- +-- | Internal representation of Copilot stream expressions.+--+-- The Core representation mimics the high-level Copilot stream, but the Core+-- representation contains information about the types of elements in the+-- stream. data Expr a where Const :: Typeable a => Type a -> a -> Expr a Drop :: Typeable a => Type a -> DropIdx -> Id -> Expr a@@ -55,7 +61,9 @@ -------------------------------------------------------------------------------- --- | A untyped expression (no phantom type).+-- | A untyped expression that carries the information about the type of the+-- expression as a value, as opposed to exposing it at type level (using an+-- existential). data UExpr = forall a. Typeable a => UExpr { uExprType :: Type a , uExprExpr :: Expr a }
src/Copilot/Core/External.hs view
@@ -6,6 +6,7 @@ {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE Rank2Types #-} +-- | Internal Copilot Core representation of Copilot externs. module Copilot.Core.External ( ExtVar (..) , externVars@@ -22,12 +23,15 @@ -------------------------------------------------------------------------------- +-- | An extern variable declaration, together with the type of the underlying+-- extern. data ExtVar = ExtVar { externVarName :: Name , externVarType :: UType } -------------------------------------------------------------------------------- +-- | List of all externs used in a specification. externVars :: Spec -> [ExtVar] externVars = nubBy eqExt . toList . all externVarsExpr where@@ -35,6 +39,7 @@ eqExt ExtVar { externVarName = name1 } ExtVar { externVarName = name2 } = name1 == name2 +-- | Extract all externs used in a Copilot expression. externVarsExpr :: Expr a -> DList ExtVar externVarsExpr e0 = case e0 of Const _ _ -> empty@@ -49,11 +54,14 @@ externVarsExpr e3 Label _ _ e -> externVarsExpr e +-- | Extract all expressions used in an untyped Copilot expression. externVarsUExpr :: UExpr -> DList ExtVar externVarsUExpr UExpr { uExprExpr = e } = externVarsExpr e -------------------------------------------------------------------------------- +-- | Apply a function to all expressions in a specification, concatenating the+-- results. all :: (forall a . Expr a -> DList b) -> Spec -> DList b all f spec = concat (fmap (allStream) (specStreams spec)) `append`
src/Copilot/Core/Interpret.hs view
@@ -16,10 +16,14 @@ import Copilot.Core.Interpret.Render import Copilot.Core.Type.Show (ShowType(..)) +-- | Output format for the results of a Copilot spec interpretation. data Format = Table | CSV --- | Interprets a Copilot specification.-interpret :: Format -> Int -> Spec -> String+-- | Interpret a Copilot specification.+interpret :: Format -- ^ Format to be used for the output.+ -> Int -- ^ Number of steps to interpret.+ -> Spec -- ^ Specification to interpret.+ -> String interpret format k spec = case format of Table -> renderAsTable e
src/Copilot/Core/Interpret/Eval.hs view
@@ -31,17 +31,25 @@ -------------------------------------------------------------------------------- +-- | Exceptions that may be thrown during interpretation of a Copilot+-- specification. data InterpException = -- NoValues Name -- | BadType Name- ArrayWrongSize Name Int- | ArrayIdxOutofBounds Name Int Int- | DivideByZero- | NotEnoughValues Name Int+ ArrayWrongSize Name Int -- ^ Extern array has incorrect size.+ | ArrayIdxOutofBounds Name Int Int -- ^ Index out-of-bounds exception.+ | DivideByZero -- ^ Division by zero.+ | NotEnoughValues Name Int -- ^ For one or more streams, not enough+ -- values are available to simulate the+ -- number of steps requested. -- | NoExtFunEval Name- | NoExtsInterp Name+ | NoExtsInterp Name -- ^ One of the externs used by the+ -- specification does not declare+ -- sample values to be used during+ -- simulation. deriving Typeable +-- | Show a descriptive message of the exception. instance Show InterpException where --------------------------------------- -- show (NoValues name) =@@ -79,10 +87,14 @@ ++ "some stream with which to simulate the function." --------------------------------------- +-- | Allow throwing and catching 'InterpException' using Haskell's standard+-- exception mechanisms. instance Exception InterpException -------------------------------------------------------------------------------- +-- | An environment that contains an association between (stream or extern)+-- names and their values. type Env nm = [(nm, Dynamic)] -- -- | External arrays environment.@@ -96,16 +108,23 @@ -------------------------------------------------------------------------------- +-- | The simulation output is defined as a string. Different backends may+-- choose to format their results differently. type Output = String +-- | An execution trace, containing the traces associated to each individual+-- monitor trigger and observer. data ExecTrace = ExecTrace- -- map from trigger names to their maybe output, which is a list of strings- -- representing their values. (Nothing output if the guard for the trigger- -- is false). The order is important, since we compare the arg lists- -- between the interpreter and backends. { interpTriggers :: [(String, [Maybe [Output]])]- -- map from observer names to their outputs. We also show observer outputs.- , interpObservers :: [(String, [Output])] }++ -- ^ Map from trigger names to their optional output, which is a list of+ -- strings representing their values. The output may be 'Nothing' if the+ -- guard for the trigger was false. The order is important, since we+ -- compare the arg lists between the interpreter and backends.++ , interpObservers :: [(String, [Output])]+ -- ^ Map from observer names to their outputs.+ } deriving Show --------------------------------------------------------------------------------@@ -129,7 +148,13 @@ -- We could write this in a beautiful lazy style like above, but that creates a -- space leak in the interpreter that is hard to fix while maintaining laziness. -- We take a more brute-force appraoch below.-eval :: ShowType -> Int -> Spec -> ExecTrace++-- | Evaluate a specification for a number of steps.+eval :: ShowType -- ^ Show booleans as @0@\/@1@ (C) or @True@\/@False@+ -- (Haskell).+ -> Int -- ^ Number of steps to evaluate.+ -> Spec -- ^ Specification to evaluate.+ -> ExecTrace eval showType k spec = -- let exts = take k $ reverse exts' in @@ -152,8 +177,12 @@ -------------------------------------------------------------------------------- +-- | An environment that contains an association between (stream or extern)+-- names and their values. type LocalEnv = [(Name, Dynamic)] +-- | Evaluate an expression for a number of steps, obtaining the value+-- of the sample at that time. evalExpr_ :: Typeable a => Int -> Expr a -> LocalEnv -> Env Id -> a evalExpr_ k e0 locs strms = case e0 of Const _ x -> x@@ -187,6 +216,8 @@ -------------------------------------------------------------------------------- +-- | Evaluate an extern stream for a number of steps, obtaining the value of+-- the sample at that time. evalExternVar :: Int -> Name -> Maybe [a] -> a evalExternVar k name exts = case exts of@@ -222,6 +253,9 @@ -------------------------------------------------------------------------------- +-- | Evaluate an 'Copilot.Core.Operators.Op1' by producing an equivalent+-- Haskell function operating on the same types as the+-- 'Copilot.Core.Operators.Op1'. evalOp1 :: Op1 a b -> (a -> b) evalOp1 op = case op of Not -> P.not@@ -250,6 +284,9 @@ -------------------------------------------------------------------------------- +-- | Evaluate an 'Copilot.Core.Operators.Op2' by producing an equivalent+-- Haskell function operating on the same types as the+-- 'Copilot.Core.Operators.Op2'. evalOp2 :: Op2 a b c -> (a -> b -> c) evalOp2 op = case op of And -> (&&)@@ -275,27 +312,39 @@ BwShiftR _ _ -> ( \ !a !b -> shiftR a $! fromIntegral b ) Index _ -> \xs n -> (arrayelems xs) !! (fromIntegral n) +-- | Apply a function to two numbers, so long as the second one is+-- not zero.+--+-- Used to detect attempts at dividing by zero and produce the appropriate+-- 'InterpException'. catchZero :: Integral a => (a -> a -> a) -> (a -> a -> a) catchZero _ _ 0 = throw DivideByZero catchZero f x y = f x y -------------------------------------------------------------------------------- +-- | Evaluate an 'Copilot.Core.Operators.Op3' by producing an equivalent+-- Haskell function operating on the same types as the+-- 'Copilot.Core.Operators.Op3'. evalOp3 :: Op3 a b c d -> (a -> b -> c -> d) evalOp3 (Mux _) = \ !v !x !y -> if v then x else y -------------------------------------------------------------------------------- +-- | Turn a stream into a key-value pair that can be added to an 'Env' for+-- simulation. initStrm :: Stream -> (Id, Dynamic) initStrm Stream { streamId = id , streamBuffer = buffer , streamExprType = t } = (id, toDyn (reverse buffer)) --- XXX actually only need to compute until shortest stream is of length k--- XXX this should just be a foldl' over [0,1..k]+-- | Evaluate several streams for a number of steps, producing the environment+-- at the end of the evaluation. evalStreams :: Int -> [Stream] -> Env Id -> Env Id evalStreams top specStrms initStrms =+ -- XXX actually only need to compute until shortest stream is of length k+ -- XXX this should just be a foldl' over [0,1..k] evalStreams_ 0 initStrms where evalStreams_ :: Int -> Env Id -> Env Id@@ -314,8 +363,14 @@ -------------------------------------------------------------------------------- -evalTrigger ::- ShowType -> Int -> Env Id -> Trigger -> [Maybe [Output]]+-- | Evaluate a trigger for a number of steps.+evalTrigger :: ShowType -- ^ Show booleans as @0@/@1@ (C) or+ -- @True@/@False@ (Haskell).+ -> Int -- ^ Number of steps to evaluate.+ -> Env Id -- ^ Environment to use with known+ -- stream-value associations.+ -> Trigger -- ^ Trigger to evaluate.+ -> [Maybe [Output]] evalTrigger showType k strms Trigger { triggerGuard = e@@ -341,7 +396,15 @@ map (showWithType showType t) (evalExprs_ k e1 strms) ---------------------------------------------------------------------------------evalObserver :: ShowType -> Int -> Env Id -> Observer -> [Output]++-- | Evaluate an observer for a number of steps.+evalObserver :: ShowType -- ^ Show booleans as @0@/@1@ (C) or @True@/@False@+ -- (Haskell).+ -> Int -- ^ Number of steps to evaluate.+ -> Env Id -- ^ Environment to use with known stream-value+ -- associations.+ -> Observer -- ^ Observer to evaluate.+ -> [Output] evalObserver showType k strms Observer { observerExpr = e@@ -350,6 +413,8 @@ -------------------------------------------------------------------------------- +-- | Evaluate an expression for a number of steps, producing a list with the+-- changing value of the expression until that time. evalExprs_ :: Typeable a => Int -> Expr a -> Env Id -> [a] evalExprs_ k e strms = map (\i -> evalExpr_ i e [] strms) [0..(k-1)]
src/Copilot/Core/Interpret/Render.hs view
@@ -2,7 +2,7 @@ -- Copyright © 2011 National Institute of Aerospace / Galois, Inc. -------------------------------------------------------------------------------- --- | An tagless interpreter for Copilot specifications.+-- | Pretty-print the results of a simulation. {-# LANGUAGE Safe #-} @@ -20,6 +20,7 @@ -------------------------------------------------------------------------------- +-- | Render an execution trace as a table, formatted to faciliate readability. renderAsTable :: ExecTrace -> String renderAsTable ExecTrace@@ -50,15 +51,20 @@ -------------------------------------------------------------------------------- +-- | Render an execution trace as using comma-separate value (CSV) format. renderAsCSV :: ExecTrace -> String renderAsCSV = render . unfold +-- | Pretty print all the steps of the execution trace and concatenate the+-- results. unfold :: ExecTrace -> Doc unfold r = case step r of (cs, Nothing) -> cs (cs, Just r') -> cs $$ unfold r' +-- | Pretty print the state of the triggers, and provide a continuation+-- for the execution trace at the next point in time. step :: ExecTrace -> (Doc, Maybe ExecTrace) step ExecTrace { interpTriggers = trigs@@ -101,18 +107,24 @@ asColumnsWithBuff :: [[Doc]] -> Int -> Doc asColumnsWithBuff lls q = normalize- where normalize = vcat $ map hsep - $ map (\x -> pad (length x) longColumnLen empty x) + where normalize = vcat $ map hsep+ $ map (\x -> pad (length x) longColumnLen empty x) $ pad' longEntryLen q $ transpose lls -- normalize column height longColumnLen = maximum (map length lls) longEntryLen = maximum $ map docLen (concat lls) -docLen d = length $ render d +docLen d = length $ render d +-- | Pad a string on the right to reach an expected length. pad :: Int -> Int -> a -> [a] -> [a] pad lx max b ls = ls ++ replicate (max - lx) b +-- | Pad a list of strings on the right with spaces.+pad' :: Int -- ^ Mininum number of spaces to add+ -> Int -- ^ Maximum number of spaces to add+ -> [[Doc]] -- ^ List of documents to pad+ -> [[Doc]] pad' _ _ [] = []-pad' mx q (ls:xs) = map buf ls : pad' mx q xs +pad' mx q (ls:xs) = map buf ls : pad' mx q xs where buf x = x <> (hcat $ replicate q space) <> (hcat $ replicate (mx - (docLen x)) space)
src/Copilot/Core/Locals.hs view
@@ -3,6 +3,11 @@ -------------------------------------------------------------------------------- -- | Let expressions.+--+-- Although Copilot is a DSL embedded in Haskell and Haskell does support let+-- expressions, we want Copilot to be able to implement explicit sharing, to+-- detect when the same stream is being used in multiple places in a+-- specification and avoid recomputing it unnecessarily. {-# LANGUAGE Trustworthy #-} {-# LANGUAGE ExistentialQuantification #-}@@ -19,15 +24,18 @@ -------------------------------------------------------------------------------- +-- | A local definition, with a given stream name and stream type. data Loc = forall a . Loc { localName :: Name , localType :: Type a } +-- | Show the underlying stream name. instance Show Loc where show Loc { localName = name } = name -------------------------------------------------------------------------------- +-- | Obtain all the local definitions in a specification. locals :: Spec -> [Loc] locals Spec@@ -47,11 +55,13 @@ -------------------------------------------------------------------------------- +-- | Obtain all the local definitions in a stream. locsStream :: Stream -> DList Loc locsStream Stream { streamExpr = e } = locsExpr e -------------------------------------------------------------------------------- +-- | Obtain all the local definitions in a trigger. locsTrigger :: Trigger -> DList Loc locsTrigger Trigger { triggerGuard = e, triggerArgs = args } = locsExpr e `append` concat (fmap locsUExpr args)@@ -63,11 +73,13 @@ -------------------------------------------------------------------------------- +-- | Obtain all the local definitions in an observer. locsObserver :: Observer -> DList Loc locsObserver Observer { observerExpr = e } = locsExpr e -------------------------------------------------------------------------------- +-- | Obtain all the local definitions in an expression. locsExpr :: Expr a -> DList Loc locsExpr e0 = case e0 of Const _ _ -> empty
src/Copilot/Core/MakeTags.hs view
@@ -2,7 +2,7 @@ -- Copyright © 2011 National Institute of Aerospace / Galois, Inc. -------------------------------------------------------------------------------- --- | Sets a unique tags for each external array/function/struct call.+-- | Sets a unique tags for each external array\/function\/struct call. {-# LANGUAGE Safe #-}
src/Copilot/Core/Operators.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE Safe #-} {-# LANGUAGE GADTs, Rank2Types #-} +-- | Internal representation of Copilot operators. module Copilot.Core.Operators ( Op1 (..) , Op2 (..)@@ -20,7 +21,7 @@ -------------------------------------------------------------------------------- --- Unary operators.+-- | Unary operators. data Op1 a b where -- Boolean operators. Not :: Op1 Bool Bool@@ -49,8 +50,11 @@ BwNot :: Bits a => Type a -> Op1 a a -- Casting operator. Cast :: (Integral a, Num b) => Type a -> Type b -> Op1 a b+ -- ^ Casting operator.+ -- Struct operator. GetField :: KnownSymbol s => Type a -> Type b -> (a -> Field s b) -> Op1 a b+ -- ^ Projection of a struct field. -- | Binary operators. data Op2 a b c where@@ -84,7 +88,9 @@ BwShiftL :: ( Bits a, Integral b ) => Type a -> Type b -> Op2 a b a BwShiftR :: ( Bits a, Integral b ) => Type a -> Type b -> Op2 a b a -- Array operator.+ Index :: Type (Array n t) -> Op2 (Array n t) Word32 t+ -- ^ Array access/projection of an array element. -- | Ternary operators. data Op3 a b c d where
src/Copilot/Core/PrettyDot.hs view
@@ -2,7 +2,7 @@ -- Copyright © 2011 National Institute of Aerospace / Galois, Inc. -------------------------------------------------------------------------------- --- | A pretty printer for Copilot specifications.+-- | A pretty printer for Copilot specifications as GraphViz/dot graphs. {-# LANGUAGE Safe #-} {-# LANGUAGE GADTs #-}@@ -19,6 +19,7 @@ import Data.List (intersperse) import Text.Printf +-- | Create a temporary/internal name from an extern variable name. mkExtTmpVar :: String -> String mkExtTmpVar = ("ext_" ++) @@ -31,7 +32,15 @@ -------------------------------------------------------------------------------- -ppExprDot :: Int -> Int -> Bool -> Expr a -> (Doc,Int)+-- | Pretty print a Copilot Expression as a GraphViz graph part.+--+-- See the+-- <https://github.com/Copilot-Language/copilot-discussion/tree/master/TutorialAndDevGuide/DevGuide development guide> for details.+ppExprDot :: Int -- ^ Index or ID of the next node in the graph.+ -> Int -- ^ Index or ID of the parent node in the graph.+ -> Bool -- ^ Mark externs with the prefix @externV:@.+ -> Expr a -- ^ The expression to pretty print.+ -> (Doc,Int) ppExprDot ii pere bb e0 = case e0 of Const t x -> (text (printf "%s [label=\"const: %s\",color=red1, style=filled]\n" (show ii::String) ((showWithType Haskell t x)::String) ) <> text (printf "%s -> %s\n" (show pere::String) (show ii::String)),ii+1)@@ -39,7 +48,9 @@ <> text (printf "%s -> %s\n" (show pere::String) (show ii::String)),ii+1) Drop _ i id -> (text (printf "%s [label=\"drop %s: \nstream: %s\",color=crimson, style=filled]\n" (show ii::String) (show i::String) (show id::String) ) <> text (printf "%s -> %s\n" (show pere::String) (show ii::String)),ii+1)- ExternVar _ name _ -> (if bb then (text (printf "%s [label=\"externV: %s\",color=cyan1, style=filled]\n" (show ii::String) (name::String)) <> text (printf "%s -> %s\n" (show pere::String) (show ii::String))) else (text (printf "%s [label=\"%s\",color=cyan1, style=filled]\n" (show ii::String) (mkExtTmpVar name)) <> text (printf "%s -> %s\n" (show pere::String) (show ii::String)))+ ExternVar _ name _ -> (if bb+ then (text (printf "%s [label=\"externV: %s\",color=cyan1, style=filled]\n" (show ii::String) (name::String)) <> text (printf "%s -> %s\n" (show pere::String) (show ii::String)))+ else (text (printf "%s [label=\"%s\",color=cyan1, style=filled]\n" (show ii::String) (mkExtTmpVar name)) <> text (printf "%s -> %s\n" (show pere::String) (show ii::String))) ,ii+1) Local _ _ name e1 e2 -> let (r1, i1) = ppExprDot (ii+2) (ii+1) bb e1 in let (r2, i2) = ppExprDot (i1) ii bb e2@@ -78,9 +89,18 @@ <> text (printf "%s -> %s\n" (show pere::String) (show ii::String)) <> r1,i1) -ppUExpr :: Int -> Int -> Bool -> UExpr -> (Doc, Int)+-- | Pretty print an untyped Copilot Expression as a graphiViz graph part.+--+-- See the+-- <https://github.com/Copilot-Language/copilot-discussion/tree/master/TutorialAndDevGuide/DevGuide development guide> for details.+ppUExpr :: Int -- ^ Index or ID of the next node in the graph.+ -> Int -- ^ Index or ID of the parent node in the graph.+ -> Bool -- ^ Mark externs with the prefix @externV:@.+ -> UExpr -- ^ The expression to pretty print.+ -> (Doc, Int) ppUExpr i pere bb UExpr { uExprExpr = e0 } = ppExprDot i pere bb e0 +-- | Pretty print a unary operator. ppOp1 :: Op1 a b -> String ppOp1 op = case op of Not -> "not"@@ -105,6 +125,7 @@ BwNot _ -> "~" Cast _ _ -> "(cast)" +-- | Pretty print a binary operator. ppOp2 :: Op2 a b c -> String ppOp2 op = case op of And -> "&&"@@ -129,13 +150,17 @@ BwShiftL _ _ -> "<<" BwShiftR _ _ -> ">>" +-- | Pretty print a ternary operator. ppOp3 :: Op3 a b c d -> String ppOp3 op = case op of Mux _ -> "mux" -------------------------------------------------------------------------------- -ppStream :: Int -> Stream -> (Doc, Int)+-- | Pretty print a stream as a GraphViz graph part.+ppStream :: Int -- ^ Index or ID of the next node in the graph.+ -> Stream -- ^ Stream to pretty print+ -> (Doc, Int) ppStream i Stream { streamId = id@@ -153,7 +178,10 @@ where (r1, i1) = ppExprDot (i+3) (i+1) True e -------------------------------------------------------------------------------- -ppTrigger :: Int -> Trigger -> (Doc, Int)+-- | Pretty print a trigger as a GraphViz graph part.+ppTrigger :: Int -- ^ Index or ID of the next node in the graph.+ -> Trigger -- ^ Trigger to pretty print+ -> (Doc, Int) ppTrigger i Trigger { triggerName = name@@ -171,7 +199,13 @@ (r1, i1) = ppExprDot (i+2) (i+1) True e (r2, i2) = ppUExprL (i1+1) (i1) True args -ppUExprL :: Int -> Int -> Bool -> [UExpr] -> ([Doc], Int)+-- | Pretty print a list of untyped Copilot Expressions as a GraphViz graph+-- part.+ppUExprL :: Int -- ^ Index or ID of the next node in the graph.+ -> Int -- ^ Index or ID of the parent node in the graph.+ -> Bool -- ^ Mark externs with the prefix @externV:@.+ -> [UExpr] -- ^ The list of expressions to pretty print.+ -> ([Doc], Int) ppUExprL i _ _ [] = ([], i) ppUExprL i pere bb (a:b) = ((r1:r2), i2)@@ -181,7 +215,10 @@ -------------------------------------------------------------------------------- -ppObserver :: Int -> Observer -> (Doc, Int)+-- | Pretty print an observer as a GraphViz graph part.+ppObserver :: Int -- ^ Index or ID of the next node in the graph.+ -> Observer -- ^ Observer to pretty print+ -> (Doc, Int) ppObserver i Observer { observerName = name@@ -192,7 +229,10 @@ where (r1, i1) = ppExprDot (i+1) i True e -------------------------------------------------------------------------------- -ppProperty :: Int -> Property -> (Doc, Int)+-- | Pretty print a property as a GraphViz graph part.+ppProperty :: Int -- ^ Index or ID of the next node in the graph.+ -> Property -- ^ Property to pretty print+ -> (Doc, Int) ppProperty i Property { propertyName = name@@ -207,7 +247,10 @@ -------------------------------------------------------------------------------- -ppStreamL :: Int -> [Stream] -> (Doc, Int)+-- | Pretty print a list of streams as a GraphViz graph part.+ppStreamL :: Int -- ^ Index or ID of the next node in the graph.+ -> [Stream] -- ^ List of streams to pretty print+ -> (Doc, Int) ppStreamL i [] = (empty,i) ppStreamL i (a:b) = ((s1$$s2),(i2))@@ -217,7 +260,10 @@ -------------------------------------------------------------------------------- -ppTriggerL :: Int -> [Trigger] -> (Doc, Int)+-- | Pretty print a list of triggers as a GraphViz graph part.+ppTriggerL :: Int -- ^ Index or ID of the next node in the graph.+ -> [Trigger] -- ^ List of triggers to pretty print+ -> (Doc, Int) ppTriggerL i [] = (empty,i) ppTriggerL i (a:b) = ((s1$$s2),(i2))@@ -227,7 +273,10 @@ -------------------------------------------------------------------------------- -ppObserverL :: Int -> [Observer] -> (Doc, Int)+-- | Pretty print a list of observers as a GraphViz graph part.+ppObserverL :: Int -- ^ Index or ID of the next node in the graph.+ -> [Observer] -- ^ List of observers to pretty print+ -> (Doc, Int) ppObserverL i [] = (empty,i) ppObserverL i (a:b) = ((s1$$s2),(i2))@@ -238,7 +287,10 @@ -------------------------------------------------------------------------------- -ppPropertyL :: Int -> [Property] -> (Doc, Int)+-- | Pretty print a list of properties as a GraphViz graph part.+ppPropertyL :: Int -- ^ Index or ID of the next node in the graph.+ -> [Property] -- ^ List of properties to pretty print+ -> (Doc, Int) ppPropertyL i [] = (empty,i) ppPropertyL i (a:b) = ((s1$$s2),(i2))@@ -248,7 +300,10 @@ -------------------------------------------------------------------------------- -ppSpecDot :: Int -> Spec -> (Doc, Int)+-- | Pretty-print a Copilot specification as a GraphViz/dot graph.+ppSpecDot :: Int -- ^ Index or ID of the next node in the graph.+ -> Spec -- ^ Spec to pretty print.+ -> (Doc, Int) ppSpecDot i spec = ((aa $$ cs $$ ds $$ es $$ fs $$ bb),i4) where@@ -261,14 +316,16 @@ -------------------------------------------------------------------------------- --- | Pretty-prints a Copilot expression.-prettyPrintExprDot :: Bool -> Expr a -> String+-- | Pretty-print a Copilot expression as a GraphViz/dot graph.+prettyPrintExprDot :: Bool -- ^ Mark externs with the prefix @externV:@.+ -> Expr a -- ^ The expression to pretty print.+ -> String prettyPrintExprDot bb s = render rr where (r1, _) = ppExprDot 1 0 bb s rr = text "digraph G {\nnode [shape=box]\n" $$ (text "0 [label=\"file: \n?????\",color=red, style=filled]\n") <> r1 $$ text "\n}\n" --- | Pretty-prints a Copilot specification.+-- | Pretty-print a Copilot specification as a GraphViz/dot graph. prettyPrintDot :: Spec -> String prettyPrintDot s = render r1 where (r1, _) = ppSpecDot 0 s
src/Copilot/Core/PrettyPrint.hs view
@@ -20,11 +20,16 @@ -------------------------------------------------------------------------------- +-- | Create a unique stream name by prefixing the given ID by a lowercase+-- letter @"s"@. strmName :: Int -> Doc strmName id = text "s" <> int id -------------------------------------------------------------------------------- +-- | Pretty-print a Copilot expression.+--+-- The type is ignored, and only the expression is pretty-printed. ppExpr :: Expr a -> Doc ppExpr e0 = case e0 of Const t x -> text (showWithType Haskell t x)@@ -39,9 +44,13 @@ Op3 op e1 e2 e3 -> ppOp3 op (ppExpr e1) (ppExpr e2) (ppExpr e3) Label _ s e -> text "label "<> doubleQuotes (text s) <+> (ppExpr e) +-- | Pretty-print an untyped expression.+--+-- The type is ignored, and only the expression is pretty-printed. ppUExpr :: UExpr -> Doc ppUExpr UExpr { uExprExpr = e0 } = ppExpr e0 +-- | Pretty-print a unary operation. ppOp1 :: Op1 a b -> Doc -> Doc ppOp1 op = case op of Not -> ppPrefix "not"@@ -68,6 +77,7 @@ GetField (Struct _) _ f -> \e -> ppInfix "#" e (text $ accessorname f) GetField _ _ _ -> impossible "ppOp1" "Copilot.Core.PrettyPrint" +-- | Pretty-print a binary operation. ppOp2 :: Op2 a b c -> Doc -> Doc -> Doc ppOp2 op = case op of And -> ppInfix "&&"@@ -93,6 +103,7 @@ BwShiftR _ _ -> ppInfix ">>" Index _ -> ppInfix ".!!" +-- | Pretty-print a ternary operation. ppOp3 :: Op3 a b c d -> Doc -> Doc -> Doc -> Doc ppOp3 op = case op of Mux _ -> \ doc1 doc2 doc3 ->@@ -102,14 +113,18 @@ -------------------------------------------------------------------------------- +-- | Parenthesize two 'Doc's, separated by an infix 'String'. ppInfix :: String -> Doc -> Doc -> Doc ppInfix cs doc1 doc2 = parens $ doc1 <+> text cs <+> doc2 +-- | Prefix a 'Doc' by a 'String'. ppPrefix :: String -> Doc -> Doc ppPrefix cs = (text cs <+>) -------------------------------------------------------------------------------- +-- | Pretty-print a Copilot stream as a case of a top-level function for+-- streams of that type, by pattern matching on the stream name. ppStream :: Stream -> Doc ppStream Stream@@ -130,6 +145,8 @@ -------------------------------------------------------------------------------- +-- | Pretty-print a Copilot trigger as a case of a top-level @trigger@+-- function, by pattern matching on the trigger name. ppTrigger :: Trigger -> Doc ppTrigger Trigger@@ -146,6 +163,8 @@ -------------------------------------------------------------------------------- +-- | Pretty-print a Copilot observer as a case of a top-level @observer@+-- function, by pattern matching on the observer name. ppObserver :: Observer -> Doc ppObserver Observer@@ -157,6 +176,8 @@ -------------------------------------------------------------------------------- +-- | Pretty-print a Copilot property as a case of a top-level @property@+-- function, by pattern matching on the property name. ppProperty :: Property -> Doc ppProperty Property@@ -168,6 +189,12 @@ -------------------------------------------------------------------------------- +-- | Pretty-print a Copilot specification, in the following order:+--+-- - Streams definitions+-- - Trigger definitions+-- - Observer definitions+-- - Property definitions ppSpec :: Spec -> Doc ppSpec spec = cs $$ ds $$ es $$ fs where@@ -178,7 +205,7 @@ -------------------------------------------------------------------------------- --- | Pretty-prints a Copilot specification.+-- | Pretty-print a Copilot specification. prettyPrint :: Spec -> String prettyPrint = render . ppSpec
src/Copilot/Core/Spec.hs view
@@ -5,6 +5,15 @@ {-# LANGUAGE Safe #-} {-# LANGUAGE ExistentialQuantification, GADTs #-} +-- | Copilot specifications constitute the main declaration of Copilot modules.+--+-- A specification normally contains the association between streams to monitor+-- and their handling functions, or streams to observe, or a theorem that must+-- be proved.+--+-- In order to be executed, high-level Copilot Language Spec must be turned+-- into Copilot Core's 'Spec'. This module defines the low-level Copilot Core+-- representations for Specs and the main types of element in a spec.. module Copilot.Core.Spec ( Stream (..) , Observer (..)@@ -19,7 +28,11 @@ -------------------------------------------------------------------------------- --- | A stream.+-- | A stream in an infinite succession of values of the same type.+--+-- Stream can carry different types of data. Boolean streams play a special+-- role: they are used by other parts (e.g., 'Trigger') to detect when the+-- properties being monitored are violated. data Stream = forall a. (Typeable a, Typed a) => Stream { streamId :: Id , streamBuffer :: [a]@@ -28,7 +41,8 @@ -------------------------------------------------------------------------------- --- | An observer.+-- | An observer, representing a stream that we observe during interpretation+-- at every sample. data Observer = forall a. Typeable a => Observer { observerName :: Name , observerExpr :: Expr a@@ -36,7 +50,8 @@ -------------------------------------------------------------------------------- --- | A trigger.+-- | A trigger, representing a function we execute when a boolean stream becomes+-- true at a sample. data Trigger = Trigger { triggerName :: Name , triggerGuard :: Expr Bool@@ -44,16 +59,16 @@ -------------------------------------------------------------------------------- --- | A property.+-- | A property, representing a boolean stream that is existentially or+-- universally quantified over time. data Property = Property { propertyName :: Name , propertyExpr :: Expr Bool } -------------------------------------------------------------------------------- --- | A Copilot specification consists of a list of variables bound to anonymous--- streams, a list of anomymous streams, a list of observers, a list of--- triggers, and a list of structs.+-- | A Copilot specification is a list of streams, together with monitors on+-- these streams implemented as observers, triggers or properties. data Spec = Spec { specStreams :: [Stream] , specObservers :: [Observer]
src/Copilot/Core/Type.hs view
@@ -3,6 +3,12 @@ -------------------------------------------------------------------------------- -- | Typing for Core.+--+-- All expressions and streams in Core are accompanied by a representation of+-- the types of the underlying expressions used or carried by the streams.+-- This information is needed by the compiler to generate code, since it must+-- initialize variables and equivalent representations for those types in+-- the target languages. {-# LANGUAGE Safe #-} {-# LANGUAGE ExistentialQuantification@@ -46,18 +52,27 @@ import Data.List (intercalate) -+-- | The value of that is a product or struct, defined as a constructor with+-- several fields. class Struct a where+ -- | Returns the name of struct in the target language. typename :: a -> String+ -- | Transforms all the struct's fields into a list of values. toValues :: a -> [Value a] +-- | The field of a struct, together with a representation of its type. data Value a = forall s t. (Typeable t, KnownSymbol s, Show t) => Value (Type t) (Field s t) +-- | A field in a struct. The name of the field is a literal at the type+-- level. data Field (s :: Symbol) t = Field t +-- | Extract the name of a field. fieldname :: forall s t. KnownSymbol s => Field s t -> String fieldname _ = symbolVal (Proxy :: Proxy s) +-- | Extract the name of an accessor (a function that returns a field of a+-- struct). accessorname :: forall a s t. (Struct a, KnownSymbol s) => (a -> Field s t) -> String accessorname _ = symbolVal (Proxy :: Proxy s) @@ -69,7 +84,12 @@ fields = intercalate "," $ map showfield (toValues t) showfield (Value _ field) = show field -+-- | A Type representing the types of expressions or values handled by+-- Copilot Core.+--+-- Note that both arrays and structs use dependently typed features. In the+-- former, the length of the array is part of the type. In the latter, the+-- names of the fields are part of the type. data Type :: * -> * where Bool :: Type Bool Int8 :: Type Int8@@ -89,11 +109,11 @@ ) => Type t -> Type (Array n t) Struct :: (Typed a, Struct a) => a -> Type a --- Get the length of an array from its type+-- | Return the length of an array from its type tylength :: forall n t. KnownNat n => Type (Array n t) -> Int tylength _ = fromIntegral $ natVal (Proxy :: Proxy n) --- Get the total (nested) size of an array from its type+-- | Return the total (nested) size of an array from its type tysize :: forall n t. KnownNat n => Type (Array n t) -> Int tysize ty@(Array ty'@(Array _)) = tylength ty * tysize ty' tysize ty@(Array _ ) = tylength ty@@ -114,6 +134,9 @@ -------------------------------------------------------------------------------- +-- | A simple, monomorphic representation of types that facilitates putting+-- variables in heterogeneous lists and environments in spite of their types+-- being different. data SimpleType where SBool :: SimpleType SInt8 :: SimpleType@@ -129,7 +152,10 @@ SArray :: Type t -> SimpleType SStruct :: SimpleType +-- | Type equality, used to help type inference.+ {- This instance is necessary, otherwise the type of SArray can't be inferred -}+ instance Eq SimpleType where SBool == SBool = True SInt8 == SInt8 = True@@ -149,6 +175,8 @@ -------------------------------------------------------------------------------- +-- | A typed expression, from which we can obtain the two type representations+-- used by Copilot: 'Type' and 'SimpleType'. class (Show a, Typeable a) => Typed a where typeOf :: Type a simpleType :: Type a -> SimpleType
src/Copilot/Core/Type/Array.hs view
@@ -30,12 +30,15 @@ import GHC.TypeLits (Nat, KnownNat, natVal) import Data.Proxy (Proxy (..)) +-- | Implementation of an array that uses type literals to store length. data Array (n :: Nat) t where Array :: [t] -> Array n t instance Show t => Show (Array n t) where show (Array xs) = show xs +-- | Smart array constructor that only type checks if the length of the given+-- list matches the length of the array at type level. array :: forall n t. KnownNat n => [t] -> Array n t array xs | datalen == typelen = Array xs | otherwise = error errmsg where@@ -45,26 +48,31 @@ ") does not match length of type (" ++ show typelen ++ ")." +-- | Association between an array and the type of the elements it contains. type family InnerType x where InnerType (Array _ x) = InnerType x InnerType x = x -+-- | Flattening or conversion of arrays to lists. class Flatten a b where+ -- | Flatten an array to a list. flatten :: Array n a -> [b] +-- | Flattening of plain arrays. instance Flatten a a where flatten (Array xs) = xs +-- | Flattening of nested arrays. instance Flatten a b => Flatten (Array n a) b where flatten (Array xss) = concat $ map flatten xss instance Foldable (Array n) where foldr f base (Array xs) = foldr f base xs -+-- | Total number of elements in a possibly nested array. size :: forall a n b. (Flatten a b, b ~ InnerType a) => Array n a -> Int size xs = length $ (flatten xs :: [b]) +-- | Return the elemts of an array. arrayelems :: Array n a -> [a] arrayelems (Array xs) = xs
src/Copilot/Core/Type/Dynamic.hs view
@@ -28,24 +28,32 @@ -------------------------------------------------------------------------------- +-- | Representation of a value accompanied by its type. data Dynamic :: (* -> *) -> * where Dynamic :: a -> t a -> Dynamic t +-- | Representation of a function accompanied by its type. data DynamicF :: (* -> *) -> (* -> *) -> * where DynamicF :: f a -> t a -> DynamicF f t +-- | Enclose a value and its type in a container. toDyn :: t a -> a -> Dynamic t toDyn t x = Dynamic x t +-- | Extract a value from a dynamic. Return 'Nothing' if the value is not of+-- the given type. fromDyn :: EqualType t => t a -> Dynamic t -> Maybe a fromDyn t1 (Dynamic x t2) = case t1 =~= t2 of Just Refl -> return x Nothing -> Nothing +-- | Enclose a function and its type in a container. toDynF :: t a -> f a -> DynamicF f t toDynF t fx = DynamicF fx t +-- | Extract a value from a dynamic function container. Return 'Nothing' if+-- the value is not of the given type. fromDynF :: EqualType t => t a -> DynamicF f t -> Maybe (f a) fromDynF t1 (DynamicF fx t2) = case t1 =~= t2 of
src/Copilot/Core/Type/Equality.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE Safe #-} {-# LANGUAGE GADTs, KindSignatures #-} +-- | Propositional equality and type equality. module Copilot.Core.Type.Equality ( Equal (..) , EqualType (..)@@ -15,23 +16,31 @@ , cong ) where +-- | Propositional equality. data Equal :: * -> * -> * where Refl :: Equal a a +-- | Type equality. If the value of @x =~= y@ is @Just Refl@, then the two+-- types @x@ and @y@ are equal. class EqualType t where (=~=) :: t a -> t b -> Maybe (Equal a b) +-- | Safe coercion, which requires proof of equality. coerce :: Equal a b -> a -> b coerce Refl x = x +-- | Proof of propositional equality. refl :: Equal a a refl = Refl +-- | Symmetry. symm :: Equal a b -> Equal b a symm Refl = Refl +-- | Transitivity. trans :: Equal a b -> Equal b c -> Equal a c trans Refl Refl = Refl +-- | Congruence. cong :: Equal a b -> Equal (f a) (f b) cong Refl = Refl
src/Copilot/Core/Type/Show.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE Safe #-} {-# LANGUAGE ExistentialQuantification, GADTs #-} +-- | Show Copilot Core types and typed values. module Copilot.Core.Type.Show ( ShowWit (..) , showWit@@ -17,10 +18,12 @@ -------------------------------------------------------------------------------- +-- | Witness datatype for showing a value, used by 'showWithType'. data ShowWit a = Show a => ShowWit -------------------------------------------------------------------------------- +-- | Turn a type into a show witness. showWit :: Type a -> ShowWit a showWit t = case t of@@ -40,6 +43,7 @@ -------------------------------------------------------------------------------- +-- | Show Copilot Core type. showType :: Type a -> String showType t = case t of@@ -61,10 +65,15 @@ -- Are we proving equivalence with a C backend, in which case we want to show -- Booleans as '0' and '1'.++-- | Target language for showing a typed value. Used to adapt the+-- representation of booleans. data ShowType = C | Haskell -------------------------------------------------------------------------------- +-- | Show a value. The representation depends on the type and the target+-- language. Booleans are represented differently depending on the backend. showWithType :: ShowType -> Type a -> a -> String showWithType showT t x = case showT of@@ -73,7 +82,7 @@ _ -> sw Haskell -> case t of Bool -> if x then "true" else "false"- _ -> sw + _ -> sw where sw = case showWit t of ShowWit -> show x
src/Copilot/Core/Type/Uninitialized.hs view
@@ -2,7 +2,7 @@ -- Copyright © 2011 National Institute of Aerospace / Galois, Inc. -------------------------------------------------------------------------------- --- | Initial values for give types.+-- | Initial values for a given type. {-# LANGUAGE Safe #-} {-# LANGUAGE GADTs #-}@@ -15,6 +15,9 @@ -------------------------------------------------------------------------------- +-- | Initial value for a given type.+--+-- Does not support structs or arrays. uninitialized :: Type a -> a uninitialized t = case t of