diff --git a/flake.nix b/flake.nix index 4da3cb91f..43dc0e957 100644 --- a/flake.nix +++ b/flake.nix @@ -415,6 +415,8 @@ # https://github.com/input-output-hk/haskell.nix/issues/1242 packages.mtl-compat.writeHieFiles = false; packages.bytestring-builder.writeHieFiles = false; + packages.fail.writeHieFiles = false; + packages.diagrams.writeHieFiles = false; } { #TODO This shouldn't be necessary - see the commented-out `build-tool-depends` in primer.cabal. diff --git a/primer-service/test/outputs/OpenAPI/openapi.json b/primer-service/test/outputs/OpenAPI/openapi.json index b852a7a8c..334c1e321 100644 --- a/primer-service/test/outputs/OpenAPI/openapi.json +++ b/primer-service/test/outputs/OpenAPI/openapi.json @@ -628,6 +628,24 @@ "contents" ], "type": "object" + }, + { + "properties": { + "contents": { + "type": "string" + }, + "tag": { + "enum": [ + "PrimAnimation" + ], + "type": "string" + } + }, + "required": [ + "tag", + "contents" + ], + "type": "object" } ] }, diff --git a/primer/gen/Primer/Gen/Core/Raw.hs b/primer/gen/Primer/Gen/Core/Raw.hs index 3137cc075..57027a175 100644 --- a/primer/gen/Primer/Gen/Core/Raw.hs +++ b/primer/gen/Primer/Gen/Core/Raw.hs @@ -165,6 +165,7 @@ genPrimCon = _ = \case PrimChar _ -> () PrimInt _ -> () + PrimAnimation _ -> () genType :: ExprGen Type genType = diff --git a/primer/gen/Primer/Gen/Core/Typed.hs b/primer/gen/Primer/Gen/Core/Typed.hs index 57e75b18c..783e2f486 100644 --- a/primer/gen/Primer/Gen/Core/Typed.hs +++ b/primer/gen/Primer/Gen/Core/Typed.hs @@ -487,7 +487,7 @@ genChk ty = do brs0 <- Gen.list (Range.linear 0 5) $ do p <- pg (p,) . CaseBranch (PatPrim p) [] <$> genChk ty - let brs = nubSortOn ((\case PrimInt n -> Left n; PrimChar c -> Right c) . fst) brs0 + let brs = nubSortOn ((\case PrimInt n -> Left (Left n); PrimChar c -> Left (Right c); PrimAnimation b -> Right b) . fst) brs0 fb <- genChk ty pure $ Case () e (snd <$> brs) (CaseFallback fb) @@ -676,6 +676,7 @@ genPrimCon = catMaybes <$> sequence [whenInScope PrimChar 'a' genChar, whenInSco _ = \case PrimChar _ -> () PrimInt _ -> () + PrimAnimation _ -> () -- We bias the distribution towards a small set, to make it more likely we -- generate name clashes on occasion diff --git a/primer/primer.cabal b/primer/primer.cabal index 06dd2fddd..590871b8d 100644 --- a/primer/primer.cabal +++ b/primer/primer.cabal @@ -23,6 +23,7 @@ library Primer.App.Utils Primer.Builtins Primer.Builtins.DSL + Primer.Builtins.Picture Primer.Core Primer.Core.DSL Primer.Core.Meta @@ -109,8 +110,11 @@ library , aeson >=2.0 && <2.2 , assoc ^>=1.1 , base >=4.12 && <4.19 + , base64-bytestring ^>=1.2.1 , containers >=0.6.0.1 && <0.7.0 , deriving-aeson >=0.2 && <0.3.0 + , diagrams-lib ^>=1.4.6 + , diagrams-rasterific ^>=1.4.2 , exceptions >=0.10.4 && <0.11.0 , extra >=1.7.10 && <1.8.0 , generic-optics >=2.0 && <2.3.0 diff --git a/primer/src/Primer/Builtins.hs b/primer/src/Primer/Builtins.hs index 8c1f2d06b..7c86a9b26 100644 --- a/primer/src/Primer/Builtins.hs +++ b/primer/src/Primer/Builtins.hs @@ -27,6 +27,14 @@ module Primer.Builtins ( cRight, eitherDef, builtinModuleName, + tPicture, + cCircle, + cRect, + cColour, + cRotate, + cTranslate, + cCompoundPicture, + pictureDef, ) where import Primer.Core.Meta ( @@ -50,6 +58,21 @@ builtinModuleName = mkSimpleModuleName "Builtins" builtin :: Name -> GlobalName k builtin = qualifyName builtinModuleName +tPicture :: TyConName +tPicture = builtin "Picture" +cCircle :: ValConName +cCircle = builtin "Circle" +cRect :: ValConName +cRect = builtin "Rectangle" +cColour :: ValConName +cColour = builtin "Colour" +cRotate :: ValConName +cRotate = builtin "Rotate" +cTranslate :: ValConName +cTranslate = builtin "Translate" +cCompoundPicture :: ValConName +cCompoundPicture = builtin "Compound" + tBool :: TyConName tBool = builtin "Bool" cTrue, cFalse :: ValConName @@ -151,3 +174,23 @@ eitherDef = , astTypeDefConstructors = [ValCon cLeft [TVar () "a"], ValCon cRight [TVar () "b"]] , astTypeDefNameHints = [] } + +pictureDef :: ASTTypeDef () () +pictureDef = + ASTTypeDef + { astTypeDefParameters = [] + , astTypeDefConstructors = + [ ValCon cCircle [TCon () tInt] + , ValCon cRect [TCon () tInt, TCon () tInt] + , ValCon cColour [TCon () tInt, TCon () tInt, TCon () tInt, TCon () tPicture] + , ValCon cRotate [TCon () tInt, TCon () tPicture] + , ValCon cTranslate [TCon () tInt, TCon () tInt, TCon () tPicture] + , -- Pictures are ordered foreground to background, i.e. those earlier in the list appear on top. + ValCon cCompoundPicture [TApp () (TCon () tList) (TCon () tPicture)] + ] + , astTypeDefNameHints = [] + } + where + -- TODO This can't currently be imported due to cyclic module issues. + tInt :: TyConName + tInt = qualifyName (mkSimpleModuleName "Primitives") "Int" diff --git a/primer/src/Primer/Builtins/Picture.hs b/primer/src/Primer/Builtins/Picture.hs new file mode 100644 index 000000000..cfb6f7498 --- /dev/null +++ b/primer/src/Primer/Builtins/Picture.hs @@ -0,0 +1,55 @@ +{-# LANGUAGE ViewPatterns #-} + +-- | A Haskell model of our built-in `Picture` type. +-- Using this type can make working with pictures more convenient, +-- including by giving us compile-time exhaustiveness checks. +module Primer.Builtins.Picture (Picture (..), exprToPicture) where + +import Foreword + +import Primer.Builtins ( + cCircle, + cColour, + cCompoundPicture, + cCons, + cNil, + cRect, + cRotate, + cTranslate, + ) +import Primer.Core (Expr' (..), PrimCon (..)) + +data Picture + = Circle Integer + | Rect Integer Integer + | Colour Integer Integer Integer Picture + | Rotate Integer Picture + | Translate Integer Integer Picture + | CompoundPicture [Picture] + +exprToPicture :: Expr' a b c -> Maybe Picture +exprToPicture = \case + Con _ c [PrimCon _ (PrimInt r)] + | c == cCircle -> + Just $ Circle r + Con _ c [PrimCon _ (PrimInt w), PrimCon _ (PrimInt h)] + | c == cRect -> + Just $ Rect w h + Con _ c [PrimCon _ (PrimInt r), PrimCon _ (PrimInt g), PrimCon _ (PrimInt b), exprToPicture -> Just p] + | c == cColour -> + Just $ Colour r g b p + Con _ c [PrimCon _ (PrimInt a), exprToPicture -> Just p] + | c == cRotate -> + Just $ Rotate a p + Con _ c [PrimCon _ (PrimInt x), PrimCon _ (PrimInt y), exprToPicture -> Just p] + | c == cTranslate -> + Just $ Translate x y p + Con _ c [exprToList -> Just (traverse exprToPicture -> Just ps)] + | c == cCompoundPicture -> + Just $ CompoundPicture ps + _ -> Nothing + where + exprToList = \case + Con _ c [] | c == cNil -> Just [] + Con _ c [x, exprToList -> Just xs] | c == cCons -> Just $ x : xs + _ -> Nothing diff --git a/primer/src/Primer/Core/Meta.hs b/primer/src/Primer/Core/Meta.hs index ced09b4e4..c2821cca4 100644 --- a/primer/src/Primer/Core/Meta.hs +++ b/primer/src/Primer/Core/Meta.hs @@ -195,6 +195,8 @@ instance HasMetadata (Meta a) where data PrimCon = PrimChar Char | PrimInt Integer + | -- | Contains a base-64 encoding of an animated GIF. + PrimAnimation Text deriving stock (Eq, Show, Read, Data, Generic) deriving (FromJSON, ToJSON) via PrimerJSON PrimCon deriving anyclass (NFData) diff --git a/primer/src/Primer/Module.hs b/primer/src/Primer/Module.hs index ee11fd19a..eedb1da62 100644 --- a/primer/src/Primer/Module.hs +++ b/primer/src/Primer/Module.hs @@ -32,12 +32,14 @@ import Primer.Builtins ( maybeDef, natDef, pairDef, + pictureDef, tBool, tEither, tList, tMaybe, tNat, tPair, + tPicture, ) import Primer.Core ( GVarName, @@ -148,6 +150,7 @@ builtinModule = do maybeDef' <- generateTypeDefIDs $ TypeDefAST maybeDef pairDef' <- generateTypeDefIDs $ TypeDefAST pairDef eitherDef' <- generateTypeDefIDs $ TypeDefAST eitherDef + pictureDef' <- generateTypeDefIDs $ TypeDefAST pictureDef pure $ Module { moduleName = builtinModuleName @@ -159,6 +162,7 @@ builtinModule = do , (baseName tMaybe, maybeDef') , (baseName tPair, pairDef') , (baseName tEither, eitherDef') + , (baseName tPicture, pictureDef') ] , moduleDefs = mempty } diff --git a/primer/src/Primer/Pretty.hs b/primer/src/Primer/Pretty.hs index 7ffdd2e32..bab4f20fd 100644 --- a/primer/src/Primer/Pretty.hs +++ b/primer/src/Primer/Pretty.hs @@ -185,6 +185,7 @@ prettyExpr opts = \case prim = \case PrimChar c -> "Char" <+> pretty @Text (show c) PrimInt n -> "Int" <+> pretty @Text (show n) + PrimGlossProg n -> pretty @Text (show n) typeann e t = brac Round Yellow (pE e) <+> col Yellow "::" <> line <> brac Round Yellow (pT t) -- When grouped: " x " diff --git a/primer/src/Primer/Primitives.hs b/primer/src/Primer/Primitives.hs index e12ace782..caaaf78d2 100644 --- a/primer/src/Primer/Primitives.hs +++ b/primer/src/Primer/Primitives.hs @@ -1,4 +1,6 @@ +{-# LANGUAGE BlockArguments #-} {-# LANGUAGE ImpredicativeTypes #-} +{-# LANGUAGE NegativeLiterals #-} {-# LANGUAGE ViewPatterns #-} module Primer.Primitives ( @@ -6,6 +8,7 @@ module Primer.Primitives ( allPrimTypeDefs, tInt, tChar, + tAnimation, primitive, primitiveGVar, primConName, @@ -16,12 +19,32 @@ module Primer.Primitives ( primitiveModuleName, ) where -import Foreword +import Foreword hiding (rotate) import Control.Monad.Fresh (MonadFresh) import Data.Aeson (FromJSON (..), ToJSON (..)) +import Data.ByteString.Base64 qualified as B64 import Data.Data (Data) import Data.Map qualified as M +import Diagrams.Backend.Rasterific ( + GifLooping (LoopingForever), + defaultPaletteOptions, + rasterGif, + ) +import Diagrams.Prelude ( + V2 (..), + circle, + deg, + fillColor, + mkP2, + mkSizeSpec, + rect, + rectEnvelope, + rotate, + sRGB24, + translate, + (@@), + ) import Numeric.Natural (Natural) import Primer.Builtins ( cSucc, @@ -29,16 +52,22 @@ import Primer.Builtins ( tBool, tMaybe, tNat, + tPicture, ) import Primer.Builtins.DSL (boolAnn, maybeAnn, nat) +import Primer.Builtins.Picture ( + Picture (..), + exprToPicture, + ) import Primer.Core ( Expr, - Expr' (Con, PrimCon), + Expr' (..), GVarName, GlobalName, ID, ModuleName, - PrimCon (PrimChar, PrimInt), + PrimCon (PrimAnimation, PrimChar, PrimInt), + TmVarRef (LocalVarRef), TyConName, Type' (..), mkSimpleModuleName, @@ -48,6 +77,7 @@ import Primer.Core.DSL ( ann, char, int, + prim, tcon, ) import Primer.Core.Utils (generateIDs) @@ -74,6 +104,7 @@ primConName :: PrimCon -> TyConName primConName = \case PrimChar _ -> tChar PrimInt _ -> tInt + PrimAnimation _ -> tAnimation primitive :: Name -> GlobalName k primitive = qualifyName primitiveModuleName @@ -84,6 +115,9 @@ tChar = primitive "Char" tInt :: TyConName tInt = primitive "Int" +tAnimation :: TyConName +tAnimation = primitive "Animation" + -- | Construct a reference to a primitive definition. primitiveGVar :: PrimDef -> GVarName primitiveGVar = primitive . primDefName @@ -107,6 +141,13 @@ allPrimTypeDefs = , primTypeDefNameHints = ["i", "j", "k", "m", "n"] } ) + , let name = tAnimation + in ( name + , PrimTypeDef + { primTypeDefParameters = [] + , primTypeDefNameHints = [] + } + ) ] where -- This ensures that when we modify the constructors of `PrimCon` (i.e. we add/remove primitive types), @@ -114,6 +155,7 @@ allPrimTypeDefs = _ = \case PrimChar _ -> () PrimInt _ -> () + PrimAnimation _ -> () primDefName :: PrimDef -> Name primDefName = \case @@ -137,6 +179,7 @@ primDefName = \case IntNeq -> "Int.≠" IntToNat -> "Int.toNat" IntFromNat -> "Int.fromNat" + Animate -> "animate" PrimConst -> "const" primDefType :: PrimDef -> Type' () () @@ -164,12 +207,20 @@ primFunTypes = \case IntNeq -> ([c tInt, c tInt], c tBool) IntToNat -> ([c tInt], c tMaybe `a` c tNat) IntFromNat -> ([c tNat], c tInt) + Animate -> + ( + [ c tInt -- Loop time, in seconds. + , c tInt `f` c tPicture -- A function from frame number to output. + ] + , c tAnimation + ) -- Arbitrarily limited to `Int` and `Bool` since we our system doesn't allow polymorphic primitives. -- Note that this primitive is only for testing anyway. PrimConst -> ([c tBool, c tNat], c tBool) where c = TCon () a = TApp () + f = TFun () primFunDef :: PrimDef -> [Expr' () () ()] -> Either PrimFunError (forall m. MonadFresh ID m => m Expr) primFunDef def args = case def of @@ -276,6 +327,46 @@ primFunDef def args = case def of [exprToNat -> Just n] -> Right $ int $ fromIntegral n _ -> err + Animate -> case args of + -- Note that `diagramExpr` is an expression of type `Picture` with one free variable, `time`. + [PrimCon () (PrimInt duration), Lam () time diagramExpr] + | Just frames <- traverse diagramAtTime [0 .. (duration * 100) `div` frameLength] -> + Right + $ prim + $ PrimAnimation + $ either + -- This case really shouldn't be able to happen, unless `diagrams-rasterific` is broken. + -- In fact, the default behaviour (`animatedGif`) is just to write the error to `stdout`, + -- and we only have to handle this because we need to use the lower-level `rasterGif`, + -- for unrelated reasons (getting the `Bytestring` without dumping it to a file). + mempty + (decodeUtf8 . B64.encode . toS) + $ rasterGif @Double + (mkSizeSpec $ Just . fromInteger <$> V2 width height) + gifLooping + defaultPaletteOptions + $ map + ( (,fromInteger frameLength) + . rectEnvelope (mkP2 -80 -45) (V2 160 90) + ) + frames + where + -- Note that this simple substitution hack only allows for trivial functions, + -- i.e. those where only substitution is needed for the function body to reach a normal form. + -- Our primitives system doesn't yet support further evaluation here. + diagramAtTime t = exprToDiagram $ substTime (PrimCon () (PrimInt t)) diagramExpr + where + substTime a = \case + Var () (LocalVarRef t') | t' == time -> a + Con () c es -> Con () c $ map (substTime a) es + e -> e + -- Values which are hardcoded, for now at least, for the sake of keeping the student-facing API simple. + -- We keep the frame rate and resolution low to avoid serialising huge GIFs. + gifLooping = LoopingForever + frameLength = 10 -- in hundredths of a second, as per the GIF spec + width = 160 + height = 90 + _ -> err PrimConst -> case args of [x, _] -> Right $ generateIDs x `ann` tcon tBool @@ -285,4 +376,16 @@ primFunDef def args = case def of Con _ c [] | c == cZero -> Just 0 Con _ c [x] | c == cSucc -> succ <$> exprToNat x _ -> Nothing + exprToDiagram e = + exprToPicture e >>= fix \f -> \case + Circle r -> + pure + if r == 0 -- `diagrams` crashes with a divide-by-zero if we don't catch this case + then mempty + else circle (fromInteger r) + Rect w h -> pure $ rect (fromInteger w) (fromInteger h) + Colour r g b p -> f p <&> fillColor (sRGB24 (fromInteger r) (fromInteger g) (fromInteger b)) + Rotate a p -> f p <&> rotate (fromInteger a @@ deg) + Translate x y p -> f p <&> translate (V2 (fromInteger x) (fromInteger y)) + CompoundPicture ps -> foldMap' f ps err = Left $ PrimFunError def args diff --git a/primer/src/Primer/Primitives/PrimDef.hs b/primer/src/Primer/Primitives/PrimDef.hs index f23bbaf29..1e6644a7c 100644 --- a/primer/src/Primer/Primitives/PrimDef.hs +++ b/primer/src/Primer/Primitives/PrimDef.hs @@ -34,6 +34,7 @@ data PrimDef | IntNeq | IntToNat | IntFromNat + | Animate | -- | Only for testing PrimConst deriving stock (Eq, Show, Read, Enum, Bounded, Data, Generic) diff --git a/primer/test/Tests/Action/Available.hs b/primer/test/Tests/Action/Available.hs index 278915a0a..edaca521e 100644 --- a/primer/test/Tests/Action/Available.hs +++ b/primer/test/Tests/Action/Available.hs @@ -290,7 +290,7 @@ tasty_available_actions_accepted = withTests 500 $ propertyWT [] $ do l <- forAllT $ Gen.element enumerate - cxt <- forAllT $ Gen.choice $ map sequence [[], [builtinModule], [builtinModule, primitiveModule]] + cxt <- forAllT $ Gen.choice $ map sequence [[], [builtinModule, primitiveModule]] -- We only test SmartHoles mode (which is the only supported student-facing -- mode - NoSmartHoles is only used for internal sanity testing etc) a <- forAllT $ genApp SmartHoles cxt diff --git a/primer/test/outputs/available-actions/M.comprehensive/Beginner-Editable.fragment b/primer/test/outputs/available-actions/M.comprehensive/Beginner-Editable.fragment index b03355fcd..6756aeab0 100644 --- a/primer/test/outputs/available-actions/M.comprehensive/Beginner-Editable.fragment +++ b/primer/test/outputs/available-actions/M.comprehensive/Beginner-Editable.fragment @@ -1397,6 +1397,18 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Picture" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Animation" + , context = Just + ( "Primitives" :| [] ) + , matchesType = False + } , Option { option = "Char" , context = Just @@ -1681,6 +1693,18 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Picture" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Animation" + , context = Just + ( "Primitives" :| [] ) + , matchesType = False + } , Option { option = "Char" , context = Just diff --git a/primer/test/outputs/available-actions/M.comprehensive/Expert-Editable.fragment b/primer/test/outputs/available-actions/M.comprehensive/Expert-Editable.fragment index 81ac37f9c..12fd83032 100644 --- a/primer/test/outputs/available-actions/M.comprehensive/Expert-Editable.fragment +++ b/primer/test/outputs/available-actions/M.comprehensive/Expert-Editable.fragment @@ -613,6 +613,42 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Circle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rectangle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Colour" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rotate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Translate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Compound" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } ] , free = FreeNone } @@ -833,6 +869,42 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Circle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rectangle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Colour" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rotate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Translate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Compound" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } ] , free = FreeNone } @@ -2157,6 +2229,42 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Circle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rectangle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Colour" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rotate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Translate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Compound" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } ] , free = FreeNone } @@ -2497,6 +2605,42 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Circle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rectangle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Colour" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rotate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Translate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Compound" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } ] , free = FreeNone } @@ -2739,6 +2883,18 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Picture" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Animation" + , context = Just + ( "Primitives" :| [] ) + , matchesType = False + } , Option { option = "Char" , context = Just @@ -3722,6 +3878,18 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Picture" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Animation" + , context = Just + ( "Primitives" :| [] ) + , matchesType = False + } , Option { option = "Char" , context = Just diff --git a/primer/test/outputs/available-actions/M.comprehensive/Intermediate-Editable.fragment b/primer/test/outputs/available-actions/M.comprehensive/Intermediate-Editable.fragment index bd4bf0e0b..702c5ae4f 100644 --- a/primer/test/outputs/available-actions/M.comprehensive/Intermediate-Editable.fragment +++ b/primer/test/outputs/available-actions/M.comprehensive/Intermediate-Editable.fragment @@ -421,6 +421,42 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Circle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rectangle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Colour" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rotate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Translate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Compound" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } ] , free = FreeNone } @@ -617,6 +653,42 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Circle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rectangle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Colour" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rotate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Translate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Compound" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } ] , free = FreeNone } @@ -1465,6 +1537,42 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Circle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rectangle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Colour" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rotate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Translate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Compound" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } ] , free = FreeNone } @@ -1733,6 +1841,42 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Circle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rectangle" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Colour" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Rotate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Translate" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Compound" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } ] , free = FreeNone } @@ -1876,6 +2020,18 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Picture" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Animation" + , context = Just + ( "Primitives" :| [] ) + , matchesType = False + } , Option { option = "Char" , context = Just @@ -2160,6 +2316,18 @@ Output ( "Builtins" :| [] ) , matchesType = False } + , Option + { option = "Picture" + , context = Just + ( "Builtins" :| [] ) + , matchesType = False + } + , Option + { option = "Animation" + , context = Just + ( "Primitives" :| [] ) + , matchesType = False + } , Option { option = "Char" , context = Just