packages feed

flatbuffers 0.1.0.0 → 0.2.0.0

raw patch · 27 files changed

+2409/−985 lines, 27 filesdep +mono-traversablePVP ok

version bump matches the API change (PVP)

Dependencies added: mono-traversable

API changes (from Hackage documentation)

- FlatBuffers.Internal.Compiler.Display: displayFromShow :: Show a => a -> Text
- FlatBuffers.Internal.Compiler.SemanticAnalysis: modifyContext :: ValidationCtx m => (Ident -> Ident) -> m a -> m a
- FlatBuffers.Internal.Compiler.SemanticAnalysis: type ValidationCtx m = (MonadError Text m, MonadReader ValidationState m)
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.HasPosition (FlatBuffers.Internal.Read.Vector (FlatBuffers.Internal.Read.Table a))
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.HasPosition (FlatBuffers.Internal.Read.Vector GHC.Int.Int16)
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.HasPosition (FlatBuffers.Internal.Read.Vector GHC.Int.Int32)
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.HasPosition (FlatBuffers.Internal.Read.Vector GHC.Int.Int64)
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.HasPosition (FlatBuffers.Internal.Read.Vector GHC.Int.Int8)
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.HasPosition (FlatBuffers.Internal.Read.Vector GHC.Types.Bool)
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.HasPosition (FlatBuffers.Internal.Read.Vector GHC.Types.Double)
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.HasPosition (FlatBuffers.Internal.Read.Vector GHC.Types.Float)
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.HasPosition (FlatBuffers.Internal.Read.Vector GHC.Word.Word16)
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.HasPosition (FlatBuffers.Internal.Read.Vector GHC.Word.Word32)
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.HasPosition (FlatBuffers.Internal.Read.Vector GHC.Word.Word64)
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.HasPosition (FlatBuffers.Internal.Read.Vector GHC.Word.Word8)
- FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Read.VectorElement (FlatBuffers.Internal.Read.Struct a)
- FlatBuffers.Internal.Read: readStructVector :: forall a. IsStruct a => PositionInfo -> Either ReadError (Vector (Struct a))
- FlatBuffers.Internal.Util: Positive :: a -> Positive a
- FlatBuffers.Internal.Util: [getPositive] :: Positive a -> a
- FlatBuffers.Internal.Util: instance GHC.Classes.Eq a => GHC.Classes.Eq (FlatBuffers.Internal.Util.Positive a)
- FlatBuffers.Internal.Util: instance GHC.Show.Show a => GHC.Show.Show (FlatBuffers.Internal.Util.Positive a)
- FlatBuffers.Internal.Util: isPowerOfTwo :: (Num a, Bits a) => a -> Bool
- FlatBuffers.Internal.Util: newtype Positive a
- FlatBuffers.Internal.Util: nonEmptyUnzip3 :: NonEmpty (a, b, c) -> (NonEmpty a, NonEmpty b, NonEmpty c)
- FlatBuffers.Internal.Util: positive :: (Num a, Ord a) => a -> Maybe (Positive a)
- FlatBuffers.Internal.Util: roundUpToNearestMultipleOf :: Integral n => n -> n -> n
+ FlatBuffers.Internal.Compiler.Display: instance FlatBuffers.Internal.Compiler.Display.Display GHC.Base.String
+ FlatBuffers.Internal.Compiler.Display: instance FlatBuffers.Internal.Compiler.Display.Display GHC.Int.Int16
+ FlatBuffers.Internal.Compiler.Display: instance FlatBuffers.Internal.Compiler.Display.Display GHC.Int.Int32
+ FlatBuffers.Internal.Compiler.Display: instance FlatBuffers.Internal.Compiler.Display.Display GHC.Int.Int64
+ FlatBuffers.Internal.Compiler.Display: instance FlatBuffers.Internal.Compiler.Display.Display GHC.Int.Int8
+ FlatBuffers.Internal.Compiler.Display: instance FlatBuffers.Internal.Compiler.Display.Display GHC.Types.Int
+ FlatBuffers.Internal.Compiler.Display: instance FlatBuffers.Internal.Compiler.Display.Display GHC.Word.Word16
+ FlatBuffers.Internal.Compiler.Display: instance FlatBuffers.Internal.Compiler.Display.Display GHC.Word.Word32
+ FlatBuffers.Internal.Compiler.Display: instance FlatBuffers.Internal.Compiler.Display.Display GHC.Word.Word64
+ FlatBuffers.Internal.Compiler.Display: instance FlatBuffers.Internal.Compiler.Display.Display GHC.Word.Word8
+ FlatBuffers.Internal.Compiler.NamingConventions: enumBitFlagsAllFun :: EnumDecl -> Text
+ FlatBuffers.Internal.Compiler.NamingConventions: enumBitFlagsConstant :: EnumDecl -> EnumVal -> Text
+ FlatBuffers.Internal.Compiler.NamingConventions: enumBitFlagsNamesFun :: EnumDecl -> Text
+ FlatBuffers.Internal.Compiler.NamingConventions: enumNameFun :: EnumDecl -> Text
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: Validation :: ReaderT ValidationState (Either String) a -> Validation a
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: [runValidation] :: Validation a -> ReaderT ValidationState (Either String) a
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: class Monad m => MonadValidation m
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: getContext :: MonadValidation m => m [Ident]
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: getDeclaredAttributes :: MonadValidation m => m (Set AttributeDecl)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: insertSymbol :: HasIdent a => Namespace -> a -> Map (Namespace, Ident) a -> Validation (Map (Namespace, Ident) a)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: instance FlatBuffers.Internal.Compiler.SemanticAnalysis.MonadValidation FlatBuffers.Internal.Compiler.SemanticAnalysis.Validation
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: instance FlatBuffers.Internal.Compiler.SemanticAnalysis.MonadValidation m => FlatBuffers.Internal.Compiler.SemanticAnalysis.MonadValidation (Control.Monad.Trans.State.Lazy.StateT s m)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: instance GHC.Base.Applicative FlatBuffers.Internal.Compiler.SemanticAnalysis.Validation
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: instance GHC.Base.Functor FlatBuffers.Internal.Compiler.SemanticAnalysis.Validation
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: instance GHC.Base.Monad FlatBuffers.Internal.Compiler.SemanticAnalysis.Validation
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: isPowerOfTwo :: (Num a, Bits a) => a -> Bool
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: newtype Validation a
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: resetContext :: MonadValidation m => m a -> m a
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: roundUpToNearestMultipleOf :: Integral n => n -> n -> n
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: scientificToInteger :: forall a. (Integral a, Bounded a, Display a) => Scientific -> String -> Validation (DefaultVal Integer)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: type ValidatedStructs = Map (Namespace, Ident) StructDecl
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validating :: (MonadValidation m, HasIdent a) => a -> m b -> m b
+ FlatBuffers.Internal.Compiler.TH: fixMsg :: String -> String
+ FlatBuffers.Internal.Compiler.TH: inlinePragma :: Name -> Dec
+ FlatBuffers.Internal.Compiler.TH: mkEnumBitFlags :: EnumDecl -> Q [Dec]
+ FlatBuffers.Internal.Compiler.TH: mkEnumBitFlagsAllValls :: EnumDecl -> [Name] -> [Dec]
+ FlatBuffers.Internal.Compiler.TH: mkEnumBitFlagsConstants :: EnumDecl -> [Name] -> [Dec]
+ FlatBuffers.Internal.Compiler.TH: mkEnumBitFlagsNames :: EnumDecl -> [Name] -> Q [Dec]
+ FlatBuffers.Internal.Compiler.TH: mkEnumNameFun :: Name -> EnumDecl -> NonEmpty (EnumVal, Name) -> Q [Dec]
+ FlatBuffers.Internal.Compiler.TH: mkEnumNormal :: EnumDecl -> Q [Dec]
+ FlatBuffers.Internal.Compiler.TH: nonEmptyUnzip3 :: NonEmpty (a, b, c) -> (NonEmpty a, NonEmpty b, NonEmpty c)
+ FlatBuffers.Internal.Compiler.ValidSyntaxTree: [enumBitFlags] :: EnumDecl -> !Bool
+ FlatBuffers.Internal.Compiler.ValidSyntaxTree: instance Data.Bits.Bits a => Data.Bits.Bits (FlatBuffers.Internal.Compiler.ValidSyntaxTree.DefaultVal a)
+ FlatBuffers.Internal.Read: Positive :: a -> Positive a
+ FlatBuffers.Internal.Read: [getPositive] :: Positive a -> a
+ FlatBuffers.Internal.Read: clamp :: Int32 -> Int32 -> Int32
+ FlatBuffers.Internal.Read: drop :: VectorElement a => Int32 -> Vector a -> Vector a
+ FlatBuffers.Internal.Read: instance FlatBuffers.Internal.Types.IsStruct a => FlatBuffers.Internal.Read.VectorElement (FlatBuffers.Internal.Read.Struct a)
+ FlatBuffers.Internal.Read: instance GHC.Classes.Eq a => GHC.Classes.Eq (FlatBuffers.Internal.Read.Positive a)
+ FlatBuffers.Internal.Read: instance GHC.Show.Show a => GHC.Show.Show (FlatBuffers.Internal.Read.Positive a)
+ FlatBuffers.Internal.Read: newtype Positive a
+ FlatBuffers.Internal.Read: positive :: (Num a, Ord a) => a -> Maybe (Positive a)
+ FlatBuffers.Internal.Read: take :: VectorElement a => Int32 -> Vector a -> Vector a
+ FlatBuffers.Internal.Read: toByteString :: Vector Word8 -> ByteString
+ FlatBuffers.Internal.Types: instance FlatBuffers.Internal.Compiler.Display.Display FlatBuffers.Internal.Types.Alignment
+ FlatBuffers.Internal.Write: FromFoldable :: f a -> FromFoldable f a
+ FlatBuffers.Internal.Write: fromByteString :: ByteString -> WriteVector Word8
+ FlatBuffers.Internal.Write: fromLazyByteString :: ByteString -> WriteVector Word8
+ FlatBuffers.Internal.Write: fromMonoFoldable :: (WriteVectorElement a, MonoFoldable mono, Element mono ~ a) => Int32 -> mono -> WriteVector a
+ FlatBuffers.Internal.Write: fromMonoFoldable' :: (WriteVectorElement a, MonoFoldable mono, Element mono ~ a) => mono -> WriteVector a
+ FlatBuffers.Internal.Write: instance Data.Foldable.Foldable f => Data.Foldable.Foldable (FlatBuffers.Internal.Write.FromFoldable f)
+ FlatBuffers.Internal.Write: instance Data.Foldable.Foldable f => Data.MonoTraversable.MonoFoldable (FlatBuffers.Internal.Write.FromFoldable f a)
+ FlatBuffers.Internal.Write: newtype FromFoldable f a
+ FlatBuffers.Vector: drop :: VectorElement a => Int32 -> Vector a -> Vector a
+ FlatBuffers.Vector: fromByteString :: ByteString -> WriteVector Word8
+ FlatBuffers.Vector: fromLazyByteString :: ByteString -> WriteVector Word8
+ FlatBuffers.Vector: fromMonoFoldable :: (WriteVectorElement a, MonoFoldable mono, Element mono ~ a) => Int32 -> mono -> WriteVector a
+ FlatBuffers.Vector: fromMonoFoldable' :: (WriteVectorElement a, MonoFoldable mono, Element mono ~ a) => mono -> WriteVector a
+ FlatBuffers.Vector: take :: VectorElement a => Int32 -> Vector a -> Vector a
+ FlatBuffers.Vector: toByteString :: Vector Word8 -> ByteString
- FlatBuffers.Internal.Compiler.Display: display :: Display a => a -> Text
+ FlatBuffers.Internal.Compiler.Display: display :: Display a => a -> String
- FlatBuffers.Internal.Compiler.ParserIO: parseImportedSchema :: MonadState (Map FilePath Schema) m => MonadIO m => MonadError Text m => [FilePath] -> FilePath -> FilePath -> m ()
+ FlatBuffers.Internal.Compiler.ParserIO: parseImportedSchema :: MonadState (Map FilePath Schema) m => MonadIO m => MonadError String m => [FilePath] -> FilePath -> FilePath -> m ()
- FlatBuffers.Internal.Compiler.ParserIO: parseSchemas :: MonadIO m => MonadError Text m => FilePath -> [FilePath] -> m (FileTree Schema)
+ FlatBuffers.Internal.Compiler.ParserIO: parseSchemas :: MonadIO m => MonadError String m => FilePath -> [FilePath] -> m (FileTree Schema)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: MatchE :: !(Namespace, enum) -> Match enum struct table union
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: MatchE :: !Namespace -> !enum -> Match enum struct table union
- FlatBuffers.Internal.Compiler.SemanticAnalysis: MatchS :: !(Namespace, struct) -> Match enum struct table union
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: MatchS :: !Namespace -> !struct -> Match enum struct table union
- FlatBuffers.Internal.Compiler.SemanticAnalysis: MatchT :: !(Namespace, table) -> Match enum struct table union
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: MatchT :: !Namespace -> !table -> Match enum struct table union
- FlatBuffers.Internal.Compiler.SemanticAnalysis: MatchU :: !(Namespace, union) -> Match enum struct table union
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: MatchU :: !Namespace -> !union -> Match enum struct table union
- FlatBuffers.Internal.Compiler.SemanticAnalysis: SymbolTable :: ![(Namespace, enum)] -> ![(Namespace, struct)] -> ![(Namespace, table)] -> ![(Namespace, union)] -> SymbolTable enum struct table union
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: SymbolTable :: !Map (Namespace, Ident) enum -> !Map (Namespace, Ident) struct -> !Map (Namespace, Ident) table -> !Map (Namespace, Ident) union -> SymbolTable enum struct table union
- FlatBuffers.Internal.Compiler.SemanticAnalysis: ValidationState :: !Ident -> !Set AttributeDecl -> ValidationState
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: ValidationState :: ![Ident] -> !Set AttributeDecl -> ValidationState
- FlatBuffers.Internal.Compiler.SemanticAnalysis: [allEnums] :: SymbolTable enum struct table union -> ![(Namespace, enum)]
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: [allEnums] :: SymbolTable enum struct table union -> !Map (Namespace, Ident) enum
- FlatBuffers.Internal.Compiler.SemanticAnalysis: [allStructs] :: SymbolTable enum struct table union -> ![(Namespace, struct)]
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: [allStructs] :: SymbolTable enum struct table union -> !Map (Namespace, Ident) struct
- FlatBuffers.Internal.Compiler.SemanticAnalysis: [allTables] :: SymbolTable enum struct table union -> ![(Namespace, table)]
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: [allTables] :: SymbolTable enum struct table union -> !Map (Namespace, Ident) table
- FlatBuffers.Internal.Compiler.SemanticAnalysis: [allUnions] :: SymbolTable enum struct table union -> ![(Namespace, union)]
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: [allUnions] :: SymbolTable enum struct table union -> !Map (Namespace, Ident) union
- FlatBuffers.Internal.Compiler.SemanticAnalysis: [validationStateCurrentContext] :: ValidationState -> !Ident
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: [validationStateCurrentContext] :: ValidationState -> ![Ident]
- FlatBuffers.Internal.Compiler.SemanticAnalysis: checkDuplicateIdentifiers :: (ValidationCtx m, Foldable f, Functor f, HasIdent a) => f a -> m ()
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: checkDuplicateIdentifiers :: (MonadValidation m, Foldable f, Functor f, HasIdent a) => f a -> m ()
- FlatBuffers.Internal.Compiler.SemanticAnalysis: checkNoDefault :: ValidationCtx m => Maybe DefaultVal -> m ()
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: checkNoDefault :: Maybe DefaultVal -> Validation ()
- FlatBuffers.Internal.Compiler.SemanticAnalysis: checkNoRequired :: ValidationCtx m => Metadata -> m ()
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: checkNoRequired :: Metadata -> Validation ()
- FlatBuffers.Internal.Compiler.SemanticAnalysis: checkStructCycles :: forall m. ValidationCtx m => FileTree Stage2 -> (Namespace, StructDecl) -> m ()
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: checkStructCycles :: forall m. MonadValidation m => FileTree Stage2 -> (Namespace, StructDecl) -> m ()
- FlatBuffers.Internal.Compiler.SemanticAnalysis: checkUndeclaredAttributes :: (ValidationCtx m, HasMetadata a) => a -> m ()
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: checkUndeclaredAttributes :: (MonadValidation m, HasMetadata a) => a -> m ()
- FlatBuffers.Internal.Compiler.SemanticAnalysis: createSymbolTables :: FileTree Schema -> FileTree Stage1
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: createSymbolTables :: FileTree Schema -> Validation (FileTree Stage1)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: findDecl :: ValidationCtx m => (HasIdent e, HasIdent s, HasIdent t, HasIdent u) => Namespace -> FileTree (SymbolTable e s t u) -> TypeRef -> m (Match e s t u)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: findDecl :: MonadValidation m => Namespace -> FileTree (SymbolTable e s t u) -> TypeRef -> m (Match e s t u)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: findIntAttr :: ValidationCtx m => Text -> Metadata -> m (Maybe Integer)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: findIntAttr :: MonadValidation m => Text -> Metadata -> m (Maybe Integer)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: findStringAttr :: ValidationCtx m => Text -> Metadata -> m (Maybe Text)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: findStringAttr :: Text -> Metadata -> Validation (Maybe Text)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: getRootInfo :: forall m. ValidationCtx m => Schema -> FileTree ValidDecls -> m (Maybe RootInfo)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: getRootInfo :: Schema -> FileTree ValidDecls -> Validation (Maybe RootInfo)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: throwErrorMsg :: ValidationCtx m => Text -> m a
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: throwErrorMsg :: MonadValidation m => String -> m a
- FlatBuffers.Internal.Compiler.SemanticAnalysis: updateRootTable :: forall m. ValidationCtx m => Schema -> FileTree ValidDecls -> m (FileTree ValidDecls)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: updateRootTable :: Schema -> FileTree ValidDecls -> Validation (FileTree ValidDecls)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateDefaultAsEnum :: ValidationCtx m => Maybe DefaultVal -> EnumDecl -> m (DefaultVal Integer)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateDefaultAsEnum :: Maybe DefaultVal -> EnumDecl -> Validation (DefaultVal Integer)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateDefaultValAsBool :: ValidationCtx m => Maybe DefaultVal -> m (DefaultVal Bool)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateDefaultValAsBool :: Maybe DefaultVal -> Validation (DefaultVal Bool)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateDefaultValAsInt :: forall m a. (ValidationCtx m, Integral a, Bounded a, Show a) => Maybe DefaultVal -> m (DefaultVal a)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateDefaultValAsInt :: forall a. (Integral a, Bounded a, Display a) => Maybe DefaultVal -> Validation (DefaultVal Integer)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateDefaultValAsScientific :: ValidationCtx m => Maybe DefaultVal -> m (DefaultVal Scientific)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateDefaultValAsScientific :: Maybe DefaultVal -> Validation (DefaultVal Scientific)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateEnum :: forall m. ValidationCtx m => (Namespace, EnumDecl) -> m EnumDecl
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateEnum :: (Namespace, Ident) -> EnumDecl -> Validation EnumDecl
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateEnums :: forall m. ValidationCtx m => FileTree Stage1 -> m (FileTree Stage2)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateEnums :: FileTree Stage1 -> Validation (FileTree Stage2)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateSchemas :: MonadError Text m => FileTree Schema -> m (FileTree ValidDecls)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateSchemas :: FileTree Schema -> Either String (FileTree ValidDecls)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateStruct :: forall m. (MonadState [(Namespace, StructDecl)] m, ValidationCtx m) => FileTree Stage2 -> (Namespace, StructDecl) -> m (Namespace, StructDecl)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateStruct :: forall m. (MonadState ValidatedStructs m, MonadValidation m) => FileTree Stage2 -> Namespace -> StructDecl -> m StructDecl
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateStructs :: ValidationCtx m => FileTree Stage2 -> m (FileTree Stage3)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateStructs :: FileTree Stage2 -> Validation (FileTree Stage3)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateTable :: forall m. ValidationCtx m => FileTree Stage3 -> (Namespace, TableDecl) -> m TableDecl
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateTable :: FileTree Stage3 -> (Namespace, Ident) -> TableDecl -> Validation TableDecl
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateTables :: ValidationCtx m => FileTree Stage3 -> m (FileTree Stage4)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateTables :: FileTree Stage3 -> Validation (FileTree Stage4)
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateUnion :: forall m. ValidationCtx m => FileTree Stage4 -> (Namespace, UnionDecl) -> m UnionDecl
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateUnion :: FileTree Stage4 -> (Namespace, Ident) -> UnionDecl -> Validation UnionDecl
- FlatBuffers.Internal.Compiler.SemanticAnalysis: validateUnions :: ValidationCtx m => FileTree Stage4 -> m (FileTree ValidDecls)
+ FlatBuffers.Internal.Compiler.SemanticAnalysis: validateUnions :: FileTree Stage4 -> Validation (FileTree ValidDecls)
- FlatBuffers.Internal.Compiler.SyntaxTree: DefaultRef :: !Ident -> DefaultVal
+ FlatBuffers.Internal.Compiler.SyntaxTree: DefaultRef :: !NonEmpty Text -> DefaultVal
- FlatBuffers.Internal.Compiler.TH: mkEnum :: (Namespace, EnumDecl) -> Q [Dec]
+ FlatBuffers.Internal.Compiler.TH: mkEnum :: EnumDecl -> Q [Dec]
- FlatBuffers.Internal.Compiler.TH: mkStruct :: (Namespace, StructDecl) -> Q [Dec]
+ FlatBuffers.Internal.Compiler.TH: mkStruct :: StructDecl -> Q [Dec]
- FlatBuffers.Internal.Compiler.TH: mkTable :: (Namespace, TableDecl) -> Q [Dec]
+ FlatBuffers.Internal.Compiler.TH: mkTable :: TableDecl -> Q [Dec]
- FlatBuffers.Internal.Compiler.TH: mkUnion :: (Namespace, UnionDecl) -> Q [Dec]
+ FlatBuffers.Internal.Compiler.TH: mkUnion :: UnionDecl -> Q [Dec]
- FlatBuffers.Internal.Compiler.ValidSyntaxTree: EnumDecl :: !Ident -> !EnumType -> !NonEmpty EnumVal -> EnumDecl
+ FlatBuffers.Internal.Compiler.ValidSyntaxTree: EnumDecl :: !Ident -> !EnumType -> !Bool -> !NonEmpty EnumVal -> EnumDecl
- FlatBuffers.Internal.Compiler.ValidSyntaxTree: TInt16 :: !DefaultVal Int16 -> TableFieldType
+ FlatBuffers.Internal.Compiler.ValidSyntaxTree: TInt16 :: !DefaultVal Integer -> TableFieldType
- FlatBuffers.Internal.Compiler.ValidSyntaxTree: TInt32 :: !DefaultVal Int32 -> TableFieldType
+ FlatBuffers.Internal.Compiler.ValidSyntaxTree: TInt32 :: !DefaultVal Integer -> TableFieldType
- FlatBuffers.Internal.Compiler.ValidSyntaxTree: TInt64 :: !DefaultVal Int64 -> TableFieldType
+ FlatBuffers.Internal.Compiler.ValidSyntaxTree: TInt64 :: !DefaultVal Integer -> TableFieldType
- FlatBuffers.Internal.Compiler.ValidSyntaxTree: TInt8 :: !DefaultVal Int8 -> TableFieldType
+ FlatBuffers.Internal.Compiler.ValidSyntaxTree: TInt8 :: !DefaultVal Integer -> TableFieldType
- FlatBuffers.Internal.Compiler.ValidSyntaxTree: TWord16 :: !DefaultVal Word16 -> TableFieldType
+ FlatBuffers.Internal.Compiler.ValidSyntaxTree: TWord16 :: !DefaultVal Integer -> TableFieldType
- FlatBuffers.Internal.Compiler.ValidSyntaxTree: TWord32 :: !DefaultVal Word32 -> TableFieldType
+ FlatBuffers.Internal.Compiler.ValidSyntaxTree: TWord32 :: !DefaultVal Integer -> TableFieldType
- FlatBuffers.Internal.Compiler.ValidSyntaxTree: TWord64 :: !DefaultVal Word64 -> TableFieldType
+ FlatBuffers.Internal.Compiler.ValidSyntaxTree: TWord64 :: !DefaultVal Integer -> TableFieldType
- FlatBuffers.Internal.Compiler.ValidSyntaxTree: TWord8 :: !DefaultVal Word8 -> TableFieldType
+ FlatBuffers.Internal.Compiler.ValidSyntaxTree: TWord8 :: !DefaultVal Integer -> TableFieldType
- FlatBuffers.Internal.Read: inlineVectorToList :: HasPosition pos => Get a -> pos -> Either ReadError [a]
+ FlatBuffers.Internal.Read: inlineVectorToList :: Get a -> Int32 -> Position -> Either ReadError [a]
- FlatBuffers.Internal.Read: length :: VectorElement a => Vector a -> Either ReadError Int32
+ FlatBuffers.Internal.Read: length :: VectorElement a => Vector a -> Int32
- FlatBuffers.Internal.Read: readPrimVector :: (Position -> Vector a) -> PositionInfo -> Either ReadError (Vector a)
+ FlatBuffers.Internal.Read: readPrimVector :: (Int32 -> Position -> Vector a) -> PositionInfo -> Either ReadError (Vector a)
- FlatBuffers.Internal.Read: runGet :: Get a -> ByteString -> Either ReadError a
+ FlatBuffers.Internal.Read: runGet :: ByteString -> Get a -> Either ReadError a
- FlatBuffers.Internal.Write: fromFoldable' :: WriteVectorElement a => Foldable f => f a -> WriteVector a
+ FlatBuffers.Internal.Write: fromFoldable' :: (WriteVectorElement a, Foldable f) => f a -> WriteVector a
- FlatBuffers.Internal.Write: inlineVector :: Foldable f => (a -> Builder) -> Alignment -> InlineSize -> Int32 -> f a -> WriteTableField
+ FlatBuffers.Internal.Write: inlineVector :: (MonoFoldable mono, Element mono ~ a) => (a -> Builder) -> Alignment -> InlineSize -> Int32 -> mono -> WriteTableField
- FlatBuffers.Vector: fromFoldable' :: WriteVectorElement a => Foldable f => f a -> WriteVector a
+ FlatBuffers.Vector: fromFoldable' :: (WriteVectorElement a, Foldable f) => f a -> WriteVector a
- FlatBuffers.Vector: length :: VectorElement a => Vector a -> Either ReadError Int32
+ FlatBuffers.Vector: length :: VectorElement a => Vector a -> Int32

Files

+ CHANGELOG.md view
@@ -0,0 +1,24 @@+# Changelog+++## 0.2.0.0 (2019-10-21)+++* Add support for bitmasks, i.e. enums with the `bit_flags` attribute.+* `FlatBuffers.Vector.length` changed from `Either ReadError Int32` to `Int32`.+  * Vector length is now read once upfront, rather than on every access.+* Added to `FlatBuffers.Vector`:+  * `fromByteString`+  * `fromLazyByteString`+  * `fromMonoFoldable` (supports `Data.Vector.Unboxed` and `Data.Vector.Storable`)+  * `take`+  * `drop`+  * `toByteString`+* TemplateHaskell:+  * Added `colorName` for enums.+  * Fixed error messages when running `ghcid` (they used to be truncated).+++## 0.1.0.0 (2019-09-22)++* First version.
README.md view
@@ -6,11 +6,12 @@ [![Hackage](https://img.shields.io/hackage/v/flatbuffers)](http://hackage.haskell.org/package/flatbuffers)  - [Getting started](#getting-started)+  - [Codegen](#codegen)   - [Enums](#enums)+  - [Bit flags / Bitmasks](#bit-flags--bitmasks)   - [Structs](#structs)   - [Unions](#unions)   - [File Identifiers](#file-identifiers)-- [Codegen](#codegen) - [TODO](#todo)  @@ -57,18 +58,20 @@ ```haskell {-# LANGUAGE OverloadedStrings #-} +import           Data.ByteString.Lazy (ByteString) import           FlatBuffers import qualified FlatBuffers.Vector as Vector  -- Writing-let byteString = encode $+byteString = encode $       monster         (Just "Poring")         (Just 50)         (Vector.fromList 2 ["Prontera Field", "Payon Forest"])  -- Reading-do+readMonster :: ByteString -> Either ReadError String+readMonster byteString = do   someMonster <- decode byteString   name        <- monsterName someMonster   hp          <- monsterHp someMonster@@ -76,12 +79,46 @@   Right ("Monster: " <> show name <> " (" <> show hp <> " HP) can be found in " <> show locations) ``` -For more info on code generation and examples, see [codegen](#codegen).+For the rest of this document, we'll assume these imports/extensions are enabled: +```haskell+{-# LANGUAGE OverloadedStrings #-}++import           Data.ByteString.Lazy (ByteString)+import           Data.Text (Text)+import qualified Data.Text as Text+import           FlatBuffers+import qualified FlatBuffers.Vector as Vector+```++### Codegen++You can check exactly which declarations were generated by browsing your module in ghci:++```plain+λ> :m Data.Game FlatBuffers FlatBuffers.Vector+λ> :browse Data.Game+data Monster+monster :: Maybe Int32 -> WriteTable Monster+monsterHp :: Table Monster -> Either ReadError Int32+```++Or by launching a local hoogle server with Stack:++```plain+> stack hoogle --rebuild --server+```++There are lots of examples in the [test/Examples][examples] folder and the [`THSpec`][thspec] module.++In particular, `test/Examples/schema.fbs` and `test/Examples/vector_of_unions.fbs` contain a variety of data structures and `Examples.HandWritten` demonstrates what the code generated by `mkFlatBuffers` would look like.+ ### Enums  ```-enum Color: short { Red, Green, Blue }+enum Color: short {+  Red, Green, Blue+} ```  Given the enum declarationa above, the following code will be generated:@@ -95,6 +132,8 @@  toColor   :: Int16 -> Maybe Color fromColor :: Color -> Int16++colorName :: Color -> Text ```  Usage:@@ -110,22 +149,81 @@  monster      :: Maybe Int16 -> WriteTable Monster monsterColor :: Table Monster -> Either ReadError Int16+``` +```haskell -- Writing-let byteString = encode $+byteString = encode $       monster (Just (fromColor ColorBlue))  -- Reading-do+readMonster :: ByteString -> Either ReadError Text+readMonster byteString = do   someMonster <- decode byteString-  short       <- monsterColor someMonster-  case toColor short of-    Just ColorRed   -> Right "This monster is red"-    Just ColorGreen -> Right "This monster is green"-    Just ColorBlue  -> Right "This monster is blue"-    Nothing         -> Left ("Unknown color: " <> show short) -- Forwards compatibility+  i           <- monsterColor someMonster+  case toColor i of+    Just color -> Right ("This monster is " <> colorName color)+    Nothing    -> Left ("Unknown color: " <> show i) -- Forwards compatibility ``` +### Bit flags / Bitmasks++```+enum Colors: uint16 (bit_flags) {+  Red, Green, Blue+}+```++Given the enum declarationa above, the following code will be generated:++```haskell+colorsRed, colorsGreen, colorsBlue :: Word16+colorsRed = 1+colorsGreen = 2+colorsBlue = 4++allColors :: [Word16]++colorsNames :: Word16 -> [Text]+```++Usage:++```+table Monster {+  colors: Colors = "Red Blue";+}+```++```haskell+data Monster++monster       :: Maybe Word16 -> WriteTable Monster+monsterColors :: Table Monster -> Either ReadError Word16+```++```haskell+import Control.Monad.Except (MonadError, MonadIO, liftEither, liftIO)+import Data.Bits ((.|.), (.&.))+import qualified Data.Text.IO as Text++-- Writing+byteString = encode $+      monster (Just (colorsBlue .|. colorsGreen))++-- Reading+readMonster :: (MonadIO m, MonadError ReadError m) => ByteString -> m ()+readMonster byteString = do+  someMonster <- liftEither $ decode byteString+  colors      <- liftEither $ monsterColors someMonster++  let isRed = colors .&. colorsRed /= 0+  liftIO $ putStrLn $ "Is this monster red? " <> if isRed then "Yes" else "No"++  liftIO $ Text.putStrLn $ "Monster colors: " <> Text.intercalate ", " (colorsNames colors)+```++ ### Structs  ```@@ -162,13 +260,16 @@  monster         :: WriteStruct Coord -> WriteTable Monster monsterPosition :: Table Monster -> Either ReadError (Struct Coord)+``` +```haskell -- Writing-let byteString = encode $+byteString = encode $       monster (coord 123 456)  -- Reading-do+readMonster :: ByteString -> Either ReadError String+readMonster byteString = do   someMonster <- decode byteString   pos         <- monsterPosition someMonster   x           <- coordX pos@@ -210,14 +311,17 @@  character       :: WriteUnion Weapon -> WriteTable Character characterWeapon :: Table Character -> Either ReadError (Union Weapon)+``` +```haskell -- Writing-let byteString = encode $+byteString = encode $       character         (weaponSword (sword (Just 1000)))  -- Reading-do+readCharacter :: ByteString -> Either ReadError String+readCharacter byteString = do   someCharacter <- decode byteString   weapon        <- characterWeapon someCharacter   case weapon of@@ -237,7 +341,7 @@ To create a character with no weapon, use `none :: WriteUnion a`  ```haskell-let byteString = encode $+byteString = encode $       character none ``` @@ -270,42 +374,22 @@ {-# LANGUAGE TypeApplications #-}  -- Writing-let byteString = encodeWithFileIdentifier $+byteString = encodeWithFileIdentifier $       monster (Just "Poring")  -- Reading-if checkFileIdentifier @Monster byteString then do-  someMonster <- decode byteString-  monsterName someMonster-else if checkFileIdentifier @Character byteString then do-  someCharacter <- decode byteString-  characterName someCharacter-else-  Left "Unexpected flatbuffer identifier"-```--## Codegen--You can check exactly which declarations were generated by browsing your module in ghci:--```plain-λ> :m Data.Game FlatBuffers FlatBuffers.Vector-λ> :browse Data.Game-data Monster-monster :: Maybe Int32 -> WriteTable Monster-monsterHp :: Table Monster -> Either ReadError Int32-```--Or by launching a local hoogle server with Stack:--```plain-> stack hoogle --rebuild --server+readName :: ByteString -> Either ReadError (Maybe Text)+readName byteString = do+  if checkFileIdentifier @Monster byteString then do+    someMonster <- decode byteString+    monsterName someMonster+  else if checkFileIdentifier @Character byteString then do+    someCharacter <- decode byteString+    characterName someCharacter+  else+    Left "Unexpected flatbuffer identifier" ``` -There are lots of examples in the [test/Examples][examples] folder and the [`THSpec`][thspec] module.--In particular, `test/Examples/schema.fbs` and `test/Examples/vector_of_unions.fbs` contain a variety of data structures and `Examples.HandWritten` demonstrates what the code generated by `mkFlatBuffers` would look like.- ## TODO  ### Features@@ -323,7 +407,6 @@     - [FlatCC](https://github.com/dvidelabs/flatcc/blob/master/doc/binary-format.md#unions) - [ ] `key` attribute (See ["Storing dictionaries in a FlatBuffer" section](https://google.github.io/flatbuffers/flatbuffers_guide_use_java_c-sharp.html)) - [ ] `nested_flatbuffer` attribute-- [ ] `bit_flags` attribute - [ ] `hash` attribute     - [Docs](https://google.github.io/flatbuffers/flatbuffers_guide_writing_schema.html)     - [Docs](https://google.github.io/flatbuffers/flatbuffers_guide_use_cpp.html#flatbuffers_cpp_object_based_api)@@ -334,13 +417,10 @@ ### Other  - [ ] TH: sort table fields by size + support `original_order` attribute-- [ ] Add support for storing unboxed vectors, which do not have a `Foldable` instance. Maybe use `MonoFoldable` from the `mono-traversable` package - [ ] Enrich `Vector` API: drop, take, null, folds, sum, elem, for_, traverse_, ideally support most of operations in `Data.Foldable`-- [ ] Add `MonoFoldable (Vector a)` instance - [ ] Improve error messages during `SemanticAnalysis` stage, provide source code location - [ ] Try alternative bytestring builders: `fast-builder`, `blaze-builder` - [ ] Try alternative bytestring parsers: `cereal`-- [ ] Better support for enums   [flatbuffers]: https://google.github.io/flatbuffers/  [schema]: https://google.github.io/flatbuffers/flatbuffers_guide_writing_schema.html
bench/DecodeVectors.hs view
@@ -5,6 +5,7 @@ {-# OPTIONS_GHC -Wno-incomplete-patterns #-}  {- HLINT ignore "Avoid lambda" -}+{- HLINT ignore "Use >=>" -}  module DecodeVectors where @@ -62,6 +63,11 @@             )             $ vectorsTable >>= vectorsG +        , bench "struct" $ nf (\(Right (Just vec)) ->+              forM [0..(n-1)] (\i -> vec `unsafeIndex` i >>= structWithOneIntX)+            )+            $ vectorsTable >>= vectorsM+         , bench "string" $ nf (\(Right (Just vec)) ->               forM [0..(n-1)] (\i -> vec `unsafeIndex` i)             )@@ -77,6 +83,11 @@               forM [0..(n-1)] (\i -> vec `index` i)             )             $ vectorsTable >>= vectorsG++        , bench "struct" $ nf (\(Right (Just vec)) ->+              forM [0..(n-1)] (\i -> vec `index` i >>= structWithOneIntX)+            )+            $ vectorsTable >>= vectorsM          , bench "string" $ nf (\(Right (Just vec)) ->               forM [0..(n-1)] (\i -> vec `index` i)
bench/EncodeVectors.hs view
@@ -4,20 +4,27 @@  module EncodeVectors where +{- HLINT ignore "Avoid lambda" -}+ import           Criterion -import           Data.Foldable      as F-import           Data.Functor       ( (<&>) )+import qualified Data.ByteString      as BS+import qualified Data.ByteString.Lazy as BSL+import           Data.Foldable        as F+import           Data.Functor         ( (<&>) ) import           Data.Int-import qualified Data.List          as L-import           Data.Text          ( Text )-import qualified Data.Vector        as V+import qualified Data.List            as L+import           Data.Text            ( Text )+import qualified Data.Vector          as V+import qualified Data.Vector.Storable as VS+import qualified Data.Vector.Unboxed  as VU  import           FlatBuffers-import qualified FlatBuffers.Vector as Vec+import qualified FlatBuffers.Vector   as Vec  import           Types + n :: Num a => a n = 10000 @@ -28,7 +35,7 @@       [ bench "of ints" $ nf (\xs ->           encode . vectorOfInts . Just . Vec.fromList (fromIntegral (F.length xs)) $             xs-        ) $ mkIntList n+        ) $ mkNumList n        , bench "of ints (with fusion)" $ nf (\xs ->           encode . vectorOfInts . Just . Vec.fromList (fromIntegral (F.length xs)) $@@ -38,7 +45,7 @@       , bench "of structs (1 int field)" $ nf (\xs ->           encode . vectorOfStructWithOneInt . Just . Vec.fromList (fromIntegral (F.length xs)) $             structWithOneInt <$> xs-        ) $ mkIntList n+        ) $ mkNumList n        , bench "of structs (2 ints fields)" $ nf (\xs ->           encode . vectorOfPairs . Just . Vec.fromList (fromIntegral (F.length xs)) $@@ -80,57 +87,83 @@      , bgroup "from vector"       [ bench "of ints" $ nf (\xs ->-          encode . vectorOfInts . Just . Vec.fromFoldable (fromIntegral (F.length xs)) $+          encode . vectorOfInts . Just . Vec.fromMonoFoldable (fromIntegral (F.length xs)) $             xs         ) $ mkIntVector n        , bench "of ints (with fusion)" $ nf (\xs ->-          encode . vectorOfInts . Just . Vec.fromFoldable (fromIntegral (F.length xs)) $+          encode . vectorOfInts . Just . Vec.fromMonoFoldable (fromIntegral (F.length xs)) $             userId <$> xs         ) $ mkUserVector n        , bench "of structs (1 int field)" $ nf (\xs ->-          encode . vectorOfStructWithOneInt . Just . Vec.fromFoldable (fromIntegral (F.length xs)) $+          encode . vectorOfStructWithOneInt . Just . Vec.fromMonoFoldable (fromIntegral (F.length xs)) $             structWithOneInt <$> xs         ) $ mkIntVector n        , bench "of structs (2 ints fields)" $ nf (\xs ->-          encode . vectorOfPairs . Just . Vec.fromFoldable (fromIntegral (F.length xs)) $+          encode . vectorOfPairs . Just . Vec.fromMonoFoldable (fromIntegral (F.length xs)) $             (\(User id age _) -> pair id age) <$> xs         ) $ mkUserVector n        , bench "of short strings" $ nf (\xs ->-          encode . vectorOfStrings . Just . Vec.fromFoldable (fromIntegral (F.length xs)) $+          encode . vectorOfStrings . Just . Vec.fromMonoFoldable (fromIntegral (F.length xs)) $             xs         ) $ mkTextVector n        , bench "of short strings (with fusion)" $ nf (\xs ->-          encode . vectorOfStrings . Just . Vec.fromFoldable (fromIntegral (F.length xs)) $+          encode . vectorOfStrings . Just . Vec.fromMonoFoldable (fromIntegral (F.length xs)) $             userName <$> xs         ) $ mkUserVector n        , bench "of long strings" $ nf (\xs ->-          encode . vectorOfStrings . Just . Vec.fromFoldable (fromIntegral (F.length xs)) $+          encode . vectorOfStrings . Just . Vec.fromMonoFoldable (fromIntegral (F.length xs)) $             xs         ) $ mkLongTextVector n        , bench "of tables (2 int fields)" $ nf (\xs ->-          encode . vectorOfTables . Just . Vec.fromFoldable (fromIntegral (F.length xs)) $+          encode . vectorOfTables . Just . Vec.fromMonoFoldable (fromIntegral (F.length xs)) $             (\(User id age _) -> pairTable (Just id) (Just age)) <$> xs         ) $ mkUserVector n        , bench "of tables (1 int field, 1 string field)" $ nf (\xs ->-          encode . vectorOfUsers . Just . Vec.fromFoldable (fromIntegral (F.length xs)) $+          encode . vectorOfUsers . Just . Vec.fromMonoFoldable (fromIntegral (F.length xs)) $             (\(User id _ name) -> userTable (Just id) (Just name)) <$> xs         ) $ mkUserVector n        , bench "of unions (1 int field each)" $ nf (\xs ->-          encode . vectorOfUnions . Just . Vec.fromFoldable (fromIntegral (F.length xs)) $+          encode . vectorOfUnions . Just . Vec.fromMonoFoldable (fromIntegral (F.length xs)) $             xs <&> \case               Sword x -> weaponUnionSword (swordTable (Just x))               Axe x   -> weaponUnionAxe   (axeTable   (Just x))         ) $ mkWeaponVector n       ]++    , bgroup "from unboxed vector"+      [ bench "of ints" $ nf (\xs ->+          encode . vectorOfInts . Just . Vec.fromMonoFoldable (fromIntegral (VU.length xs)) $+            xs+        ) $ mkIntUnboxedVector n+      ]++    , bgroup "from storable vector"+      [ bench "of ints" $ nf (\xs ->+          encode . vectorOfInts . Just . Vec.fromMonoFoldable (fromIntegral (VS.length xs)) $+            xs+        ) $ mkIntStorableVector n+      ]++    , bgroup "from bytestring"+      [ bench "strict" $ nf (\xs ->+          encode . vectorOfBytes . Just . Vec.fromByteString $+            xs+        ) $ mkByteString n++      , bench "lazy" $ nf (\xs ->+          encode . vectorOfBytes . Just . Vec.fromLazyByteString $+            xs+        ) $ mkLazyByteString n+      ]     ]   ] @@ -154,8 +187,8 @@       then Sword i       else Axe i -mkIntList :: Int32 -> [Int32]-mkIntList n = [1..n]+mkNumList :: Num a => Int32 -> [a]+mkNumList len = fromIntegral <$> [1 .. len]  mkTextList :: Int -> [Text] mkTextList n = L.replicate n "abcdefghijk"@@ -173,7 +206,19 @@ mkWeaponVector n = V.fromList (mkWeaponList n)  mkIntVector :: Int32 -> V.Vector Int32-mkIntVector n = V.fromList (mkIntList n)+mkIntVector n = V.fromList (mkNumList n)++mkIntUnboxedVector :: Int32 -> VU.Vector Int32+mkIntUnboxedVector n = VU.fromList (mkNumList n)++mkIntStorableVector :: Int32 -> VS.Vector Int32+mkIntStorableVector n = VS.fromList (mkNumList n)++mkByteString :: Int32 -> BS.ByteString+mkByteString = BS.pack . mkNumList++mkLazyByteString :: Int32 -> BSL.ByteString+mkLazyByteString = BSL.pack . mkNumList  mkTextVector :: Int -> V.Vector Text mkTextVector n = V.fromList (mkTextList n)
flatbuffers.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 772c6499fd4f79ba88bf1b70ce28991443be358e6e1e5b7dddc77d61585751ba+-- hash: 0fec28e13f905ed1bcbecf99c8430313dbcfab66b57aa92451de82fd8804e5ea  name:           flatbuffers-version:        0.1.0.0+version:        0.2.0.0 synopsis:       Haskell implementation of the FlatBuffers protocol. description:    Haskell implementation of the FlatBuffers protocol.                 .@@ -24,6 +24,7 @@ build-type:     Simple extra-source-files:     README.md+    CHANGELOG.md     cbits/cbits.c extra-doc-files:     README.md@@ -48,7 +49,6 @@       FlatBuffers.Internal.FileIdentifier       FlatBuffers.Internal.Read       FlatBuffers.Internal.Types-      FlatBuffers.Internal.Util       FlatBuffers.Internal.Write       FlatBuffers.Vector   other-modules:@@ -66,6 +66,7 @@     , directory >=1.3.1.2     , filepath >=1.4.2     , megaparsec >=7.0+    , mono-traversable >=1.0.1.2     , mtl >=2.2.1     , parser-combinators >=1.0     , scientific >=0.3.5.2@@ -114,6 +115,7 @@     , http-types     , hw-hspec-hedgehog     , megaparsec >=7.0+    , mono-traversable >=1.0.1.2     , mtl >=2.2.1     , parser-combinators >=1.0     , process@@ -149,6 +151,7 @@     , filepath >=1.4.2     , flatbuffers     , megaparsec >=7.0+    , mono-traversable >=1.0.1.2     , mtl >=2.2.1     , parser-combinators >=1.0     , scientific >=0.3.5.2
src/FlatBuffers/Internal/Compiler/Display.hs view
@@ -1,30 +1,38 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-}  module FlatBuffers.Internal.Compiler.Display where +import           Data.Int+import qualified Data.List          as List import           Data.List.NonEmpty ( NonEmpty ) import qualified Data.List.NonEmpty as NE import qualified Data.Text          as T-import           Data.Text          ( Text )+import           Data.Word  -- | Maps a value of type @a@ into a string that can be displayed to the user.--- move this to its own file class Display a where-  display :: a -> Text+  display :: a -> String -instance Display Text where+instance {-# OVERLAPPING #-} Display String where   display = id +instance Display T.Text where+  display = T.unpack+ instance Display a => Display (NonEmpty a) where   display = display . NE.toList  instance Display a => Display [a] where-  display xs = T.intercalate ", " (fmap displayOne xs)-    where-      displayOne x = "'" <> display x <> "'"+  display xs = List.intercalate ", " (fmap display xs) -instance Display Integer where-  display = displayFromShow+instance Display Int     where display = show+instance Display Integer where display = show+instance Display Int8    where display = show+instance Display Int16   where display = show+instance Display Int32   where display = show+instance Display Int64   where display = show+instance Display Word8   where display = show+instance Display Word16  where display = show+instance Display Word32  where display = show+instance Display Word64  where display = show -displayFromShow :: Show a => a -> Text-displayFromShow = T.pack . show
src/FlatBuffers/Internal/Compiler/NamingConventions.hs view
@@ -7,7 +7,7 @@ import qualified Data.Text                                     as T import qualified Data.Text.Manipulate                          as TM -import           FlatBuffers.Internal.Compiler.ValidSyntaxTree ( EnumDecl, HasIdent(..), Ident(..), Namespace(..), UnionDecl, UnionVal )+import           FlatBuffers.Internal.Compiler.ValidSyntaxTree ( EnumDecl, EnumVal, HasIdent(..), Ident(..), Namespace(..), UnionDecl, UnionVal )  -- Style guide: https://google.github.io/flatbuffers/flatbuffers_guide_writing_schema.html @@ -39,9 +39,25 @@ enumUnionMember (getIdent -> Ident parentIdent) (getIdent -> Ident valIdent) =   TM.toPascal parentIdent <> TM.toPascal valIdent +enumBitFlagsConstant :: EnumDecl -> EnumVal -> Text+enumBitFlagsConstant (getIdent -> Ident enumIdent) (getIdent -> Ident enumValIdent) =+  TM.toCamel enumIdent <> TM.toPascal enumValIdent++enumBitFlagsAllFun :: EnumDecl -> Text+enumBitFlagsAllFun (getIdent -> Ident enumIdent) =+  "all" <> TM.toPascal enumIdent++enumBitFlagsNamesFun :: EnumDecl -> Text+enumBitFlagsNamesFun (getIdent -> Ident enumIdent) =+  TM.toCamel enumIdent <> "Names"++enumNameFun :: EnumDecl -> Text+enumNameFun (getIdent -> Ident enumIdent) =+  TM.toCamel enumIdent <> "Name"+ unionConstructor :: UnionDecl -> UnionVal -> Text-unionConstructor union unionVal =-  TM.toCamel (unIdent $ getIdent union) <> TM.toPascal (unIdent $ getIdent unionVal)+unionConstructor (getIdent -> Ident unionIdent) (getIdent -> Ident unionValIdent) =+  TM.toCamel unionIdent <> TM.toPascal unionValIdent  readUnionFun :: HasIdent union => union -> Text readUnionFun (getIdent -> Ident unionIdent) =
src/FlatBuffers/Internal/Compiler/Parser.hs view
@@ -11,11 +11,12 @@  import qualified Data.ByteString                          as BS import           Data.Coerce                              ( coerce )-import           Data.Functor                             ( void )-import           Data.List.NonEmpty                       ( NonEmpty )+import           Data.Functor                             ( (<&>), void )+import           Data.List.NonEmpty                       ( NonEmpty((:|)) ) import qualified Data.List.NonEmpty                       as NE import qualified Data.Map.Strict                          as Map import           Data.Maybe                               ( catMaybes )+import           Data.Scientific                          ( Scientific ) import           Data.Text                                ( Text ) import qualified Data.Text                                as T import qualified Data.Text.Encoding                       as T@@ -28,6 +29,7 @@ import           Text.Megaparsec import           Text.Megaparsec.Char import qualified Text.Megaparsec.Char.Lexer               as L+import           Text.Read                                ( readMaybe )   type Parser = Parsec Void String@@ -231,7 +233,18 @@     [ DefaultBool True <$ rword "true"     , DefaultBool False <$ rword "false"     , DefaultNum <$> label "number literal" (lexeme (L.signed sc L.scientific))-    , DefaultRef <$> ident+    , ident <&> \(Ident ref) -> DefaultRef (ref :| [])+    , stringLiteral >>= \(StringLiteral str) ->+        case T.strip str of+          "true"  -> pure $ DefaultBool True+          "false" -> pure $ DefaultBool False+          other ->+            case readMaybe @Scientific (T.unpack other) of+              Just n  -> pure $ DefaultNum n+              Nothing ->+                case NE.nonEmpty (T.words str) of+                  Just refs -> pure $ DefaultRef refs+                  Nothing   -> fail "Expected 'true', 'false', a number, or one or more identifiers"     ]  metadata :: Parser Metadata
src/FlatBuffers/Internal/Compiler/ParserIO.hs view
@@ -11,7 +11,6 @@ import           Data.Foldable                            ( traverse_ ) import           Data.Map.Strict                          ( Map ) import qualified Data.Map.Strict                          as Map-import           Data.Text                                ( Text ) import qualified Data.Text                                as T  import           FlatBuffers.Internal.Compiler.Display    ( display )@@ -25,14 +24,14 @@  parseSchemas ::      MonadIO m-  => MonadError Text m+  => MonadError String m   => FilePath -- ^ Filepath of the root schema. It must be a path relative to the project root or an absolute path.   -> [FilePath] -- ^ Directories to search for @include@s.   -> m (FileTree Schema) parseSchemas rootFilePath includeDirs = do   fileContent <- liftIO $ readFile rootFilePath   case parse schema rootFilePath fileContent of-    Left err -> throwError . T.pack . errorBundlePretty $ err+    Left err -> throwError $ errorBundlePretty err     Right rootSchema -> do       rootFilePathCanon <- liftIO $ Dir.canonicalizePath rootFilePath       let importedFilePaths = T.unpack . coerce <$> includes rootSchema@@ -50,7 +49,7 @@ parseImportedSchema ::      MonadState (Map FilePath Schema) m   => MonadIO m-  => MonadError Text m+  => MonadError String m   => [FilePath]   -> FilePath   -> FilePath@@ -68,18 +67,17 @@       case actualFilePathCanonMaybe of         Nothing -> throwError $           "File '"-          <> T.pack filePath+          <> filePath           <> "' (imported from '"-          <> T.pack parentSchemaPath-          <> "') not found.\n Searched in these directories: ["-          <> display (T.pack <$> dirCandidates)-          <> "]"+          <> parentSchemaPath+          <> "') not found.\nSearched in these directories: "+          <> display dirCandidates         Just actualFilePathCanon -> do           importedSchemas <- get           when (actualFilePathCanon /= rootFilePathCanon && actualFilePathCanon `Map.notMember` importedSchemas) $ do             fileContent <- liftIO $ readFile actualFilePathCanon             case parse schema actualFilePathCanon fileContent of-              Left err -> throwError . T.pack . errorBundlePretty $ err+              Left err -> throwError $ errorBundlePretty err               Right importedSchema -> do                 put (Map.insert actualFilePathCanon importedSchema importedSchemas)                 traverse_ (go actualFilePathCanon . T.unpack . coerce) (includes importedSchema)
src/FlatBuffers/Internal/Compiler/SemanticAnalysis.hs view
@@ -5,14 +5,18 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}  module FlatBuffers.Internal.Compiler.SemanticAnalysis where  import           Control.Monad                                 ( forM_, join, when )-import           Control.Monad.Except                          ( MonadError, throwError )-import           Control.Monad.Reader                          ( MonadReader(..), asks, runReaderT )-import           Control.Monad.State                           ( MonadState, State, StateT, evalState, evalStateT, get, modify, put )+import           Control.Monad.Except                          ( throwError )+import           Control.Monad.Reader                          ( ReaderT, asks, local, runReaderT )+import           Control.Monad.State                           ( MonadState, State, StateT, evalState, evalStateT, get, mapStateT, modify, put )+import           Control.Monad.Trans                           ( lift ) +import           Data.Bits                                     ( (.&.), (.|.), Bits, FiniteBits, bit, finiteBitSize ) import           Data.Coerce                                   ( coerce ) import           Data.Foldable                                 ( asum, find, foldlM, traverse_ ) import qualified Data.Foldable                                 as Foldable@@ -20,7 +24,7 @@ import           Data.Int import           Data.Ix                                       ( inRange ) import qualified Data.List                                     as List-import           Data.List.NonEmpty                            ( NonEmpty )+import           Data.List.NonEmpty                            ( NonEmpty((:|)) ) import qualified Data.List.NonEmpty                            as NE import           Data.Map.Strict                               ( Map ) import qualified Data.Map.Strict                               as Map@@ -41,31 +45,75 @@ import           FlatBuffers.Internal.Compiler.ValidSyntaxTree import           FlatBuffers.Internal.Constants import           FlatBuffers.Internal.Types-import           FlatBuffers.Internal.Util                     ( isPowerOfTwo, roundUpToNearestMultipleOf )  import           Text.Read                                     ( readMaybe )  -type ValidationCtx m = (MonadError Text m, MonadReader ValidationState m)+----------------------------------+------- MonadValidation ----------+----------------------------------+newtype Validation a = Validation+  { runValidation :: ReaderT ValidationState (Either String) a+  }+  deriving newtype (Functor, Applicative, Monad)  data ValidationState = ValidationState-  { validationStateCurrentContext :: !Ident+  { validationStateCurrentContext :: ![Ident]     -- ^ The thing being validated (e.g. a fully-qualified struct name, or a table field name).   , validationStateAllAttributes  :: !(Set ST.AttributeDecl)     -- ^ All the attributes declared in all the schemas (including imported ones).   } +class Monad m => MonadValidation m where+  -- | Start validating an item @a@+  validating :: HasIdent a => a -> m b -> m b+  -- | Clear validation context, i.e. forget which item is currently being validated, if any.+  resetContext :: m a -> m a+  -- | Get the path to the item currently being validated+  getContext :: m [Ident]+  -- | Get a list of all the attributes declared in every loaded schema+  getDeclaredAttributes :: m (Set ST.AttributeDecl)+  -- | Fail validation with a message+  throwErrorMsg :: String -> m a -modifyContext :: ValidationCtx m => (Ident -> Ident) -> m a -> m a-modifyContext f =-  local $ \s ->-    s { validationStateCurrentContext = f (validationStateCurrentContext s) }+instance MonadValidation Validation where+  validating a (Validation v) = Validation (local addIdent v)+    where+      addIdent (ValidationState ctx attrs) = ValidationState (getIdent a : ctx) attrs+  resetContext (Validation v) = Validation (local reset v)+    where+      reset (ValidationState _ attrs) = ValidationState [] attrs+  getContext            = Validation (asks (List.reverse . validationStateCurrentContext))+  getDeclaredAttributes = Validation (asks validationStateAllAttributes)+  throwErrorMsg msg = do+    idents <- getContext+    if null idents+      then Validation (throwError msg)+      else Validation . throwError $ "[" <> List.intercalate "." (T.unpack . unIdent <$> idents) <> "]: " <> msg +instance MonadValidation m => MonadValidation (StateT s m)  where+  validating            = mapStateT . validating+  resetContext          = mapStateT resetContext+  getContext            = lift getContext+  getDeclaredAttributes = lift getDeclaredAttributes+  throwErrorMsg         = lift . throwErrorMsg++----------------------------------+------- Validation stages --------+----------------------------------+{-+During validation, we translate `SyntaxTree.XDecl` declarations into+`ValidSyntaxTree.XDecl` declarations.++This is done in stages: we first translate enums, then structs, then tables,+and lastly unions.+-}+ data SymbolTable enum struct table union = SymbolTable-  { allEnums   :: ![(Namespace, enum)]-  , allStructs :: ![(Namespace, struct)]-  , allTables  :: ![(Namespace, table)]-  , allUnions  :: ![(Namespace, union)]+  { allEnums   :: !(Map (Namespace, Ident) enum)+  , allStructs :: !(Map (Namespace, Ident) struct)+  , allTables  :: !(Map (Namespace, Ident) table)+  , allUnions  :: !(Map (Namespace, Ident) union)   }   deriving (Eq, Show) @@ -74,7 +122,7 @@     SymbolTable (e1 <> e2) (s1 <> s2) (t1 <> t2) (u1 <> u2)  instance Monoid (SymbolTable e s t u) where-  mempty = SymbolTable [] [] [] []+  mempty = SymbolTable mempty mempty mempty mempty  type Stage1     = SymbolTable ST.EnumDecl ST.StructDecl ST.TableDecl ST.UnionDecl type Stage2     = SymbolTable    EnumDecl ST.StructDecl ST.TableDecl ST.UnionDecl@@ -82,42 +130,24 @@ type Stage4     = SymbolTable    EnumDecl    StructDecl    TableDecl ST.UnionDecl type ValidDecls = SymbolTable    EnumDecl    StructDecl    TableDecl    UnionDecl --- | Takes a collection of schemas, and pairs each type declaration with its corresponding namespace-createSymbolTables :: FileTree Schema -> FileTree Stage1-createSymbolTables = fmap (pairDeclsWithNamespaces . ST.decls)-  where-    pairDeclsWithNamespaces :: [ST.Decl] -> Stage1-    pairDeclsWithNamespaces = snd . foldl go ("", mempty)--    go :: (Namespace, Stage1) -> ST.Decl -> (Namespace, Stage1)-    go (currentNamespace, decls) decl =-      case decl of-        ST.DeclN (ST.NamespaceDecl newNamespace) -> (newNamespace, decls)-        ST.DeclE enum   -> (currentNamespace, decls <> SymbolTable [(currentNamespace, enum)] [] [] [])-        ST.DeclS struct -> (currentNamespace, decls <> SymbolTable [] [(currentNamespace, struct)] [] [])-        ST.DeclT table  -> (currentNamespace, decls <> SymbolTable [] [] [(currentNamespace, table)] [])-        ST.DeclU union  -> (currentNamespace, decls <> SymbolTable [] [] [] [(currentNamespace, union)])-        _               -> (currentNamespace, decls)--validateSchemas :: MonadError Text m => FileTree Schema -> m (FileTree ValidDecls)+validateSchemas :: FileTree Schema -> Either String (FileTree ValidDecls) validateSchemas schemas =-  flip runReaderT (ValidationState "" allAttributes) $ do-    checkDuplicateIdentifiers allQualifiedTopLevelIdentifiers+  flip runReaderT (ValidationState [] allAttributes) $ runValidation $ do+    symbolTables <- createSymbolTables schemas+    checkDuplicateIdentifiers (allQualifiedTopLevelIdentifiers symbolTables)     validateEnums symbolTables       >>= validateStructs       >>= validateTables       >>= validateUnions       >>= updateRootTable (fileTreeRoot schemas)   where-    symbolTables = createSymbolTables schemas--    allQualifiedTopLevelIdentifiers =+    allQualifiedTopLevelIdentifiers symbolTables =       flip concatMap symbolTables $ \symbolTable ->         join-          [ uncurry qualify <$> allEnums symbolTable-          , uncurry qualify <$> allStructs symbolTable-          , uncurry qualify <$> allTables symbolTable-          , uncurry qualify <$> allUnions symbolTable+          [ uncurry qualify <$> Map.keys (allEnums symbolTable)+          , uncurry qualify <$> Map.keys (allStructs symbolTable)+          , uncurry qualify <$> Map.keys (allTables symbolTable)+          , uncurry qualify <$> Map.keys (allUnions symbolTable)           ]      declaredAttributes =@@ -126,6 +156,38 @@      allAttributes = Set.fromList $ declaredAttributes <> knownAttributes +-- | Takes a collection of schemas, and pairs each type declaration with its corresponding namespace+createSymbolTables :: FileTree Schema -> Validation (FileTree Stage1)+createSymbolTables = traverse (createSymbolTable . ST.decls)+  where+    createSymbolTable :: [ST.Decl] -> Validation Stage1+    createSymbolTable decls = snd <$> foldlM go ("", mempty) decls++    go :: (Namespace, Stage1) -> ST.Decl -> Validation (Namespace, Stage1)+    go (currentNamespace, symbolTable) decl =+      case decl of+        ST.DeclE enum   -> addEnum symbolTable currentNamespace enum     <&> \symbolTable' -> (currentNamespace, symbolTable')+        ST.DeclS struct -> addStruct symbolTable currentNamespace struct <&> \symbolTable' -> (currentNamespace, symbolTable')+        ST.DeclT table  -> addTable symbolTable currentNamespace table   <&> \symbolTable' -> (currentNamespace, symbolTable')+        ST.DeclU union  -> addUnion symbolTable currentNamespace union   <&> \symbolTable' -> (currentNamespace, symbolTable')+        ST.DeclN (ST.NamespaceDecl newNamespace) -> pure (newNamespace, symbolTable)+        _               -> pure (currentNamespace, symbolTable)++    addEnum (SymbolTable es ss ts us) namespace enum     = insertSymbol namespace enum es   <&> \es' -> SymbolTable es' ss ts us+    addStruct (SymbolTable es ss ts us) namespace struct = insertSymbol namespace struct ss <&> \ss' -> SymbolTable es ss' ts us+    addTable (SymbolTable es ss ts us) namespace table   = insertSymbol namespace table ts  <&> \ts' -> SymbolTable es ss ts' us+    addUnion (SymbolTable es ss ts us) namespace union   = insertSymbol namespace union us  <&> \us' -> SymbolTable es ss ts us'++-- | Fails if the key is already present in the map.+insertSymbol :: HasIdent a => Namespace -> a -> Map (Namespace, Ident) a -> Validation (Map (Namespace, Ident) a)+insertSymbol namespace symbol map =+  if Map.member key map+    then throwErrorMsg $ display (qualify namespace symbol) <> " declared more than once"+    else pure $ Map.insert key symbol map+  where+    key = (namespace, getIdent symbol)++ ---------------------------------- ------------ Root Type ----------- ----------------------------------@@ -138,7 +200,7 @@ -- | Finds the root table (if any) and sets the `tableIsRoot` flag accordingly. -- We only care about @root_type@ declarations in the root schema. Imported schemas are not scanned for @root_type@s. -- The root type declaration can point to a table in any schema (root or imported).-updateRootTable :: forall m. ValidationCtx m => Schema -> FileTree ValidDecls -> m (FileTree ValidDecls)+updateRootTable :: Schema -> FileTree ValidDecls -> Validation (FileTree ValidDecls) updateRootTable schema symbolTables =   getRootInfo schema symbolTables <&> \case     Just rootInfo -> updateSymbolTable rootInfo <$> symbolTables@@ -146,32 +208,31 @@    where     updateSymbolTable :: RootInfo -> ValidDecls -> ValidDecls-    updateSymbolTable rootInfo st = st { allTables = updateTable rootInfo <$> allTables st}+    updateSymbolTable rootInfo st = st { allTables = Map.mapWithKey (updateTable rootInfo) (allTables st) } -    updateTable :: RootInfo -> (Namespace, TableDecl) -> (Namespace, TableDecl)-    updateTable (RootInfo rootTableNamespace rootTable fileIdent) pair@(namespace, table) =+    updateTable :: RootInfo -> (Namespace, Ident) -> TableDecl -> TableDecl+    updateTable (RootInfo rootTableNamespace rootTable fileIdent) (namespace, _) table =       if namespace == rootTableNamespace && table == rootTable-        then (namespace, table { tableIsRoot = IsRoot fileIdent })-        else pair+        then table { tableIsRoot = IsRoot fileIdent }+        else table -getRootInfo :: forall m. ValidationCtx m => Schema -> FileTree ValidDecls -> m (Maybe RootInfo)+getRootInfo :: Schema -> FileTree ValidDecls -> Validation (Maybe RootInfo) getRootInfo schema symbolTables =   foldlM go ("", Nothing, Nothing) (ST.decls schema) <&> \case     (_, Just (rootTableNamespace, rootTable), fileIdent) -> Just $ RootInfo rootTableNamespace rootTable fileIdent     _ -> Nothing   where-    go :: (Namespace, Maybe (Namespace, TableDecl), Maybe Text) -> ST.Decl -> m (Namespace, Maybe (Namespace, TableDecl), Maybe Text)+    go :: (Namespace, Maybe (Namespace, TableDecl), Maybe Text) -> ST.Decl -> Validation (Namespace, Maybe (Namespace, TableDecl), Maybe Text)     go state@(currentNamespace, rootInfo, fileIdent) decl =       case decl of         ST.DeclN (ST.NamespaceDecl newNamespace)       -> pure (newNamespace, rootInfo, fileIdent)         ST.DeclFI (ST.FileIdentifierDecl newFileIdent) -> pure (currentNamespace, rootInfo, Just (coerce newFileIdent))         ST.DeclR (ST.RootDecl typeRef)                 ->           findDecl currentNamespace symbolTables typeRef >>= \case-            MatchT (rootTableNamespace, rootTable)  -> pure (currentNamespace, Just (rootTableNamespace, rootTable), fileIdent)-            _                                       -> throwErrorMsg "root type must be a table"+            MatchT rootTableNamespace rootTable  -> pure (currentNamespace, Just (rootTableNamespace, rootTable), fileIdent)+            _                                    -> throwErrorMsg "root type must be a table"         _ -> pure state - ---------------------------------- ----------- Attributes ----------- ----------------------------------@@ -217,16 +278,14 @@ --------- Symbol search ---------- ---------------------------------- data Match enum struct table union-  = MatchE !(Namespace, enum)-  | MatchS !(Namespace, struct)-  | MatchT !(Namespace, table)-  | MatchU !(Namespace, union)+  = MatchE !Namespace !enum+  | MatchS !Namespace !struct+  | MatchT !Namespace !table+  | MatchU !Namespace !union  -- | Looks for a type reference in a set of type declarations.--- If none is found, the list of namespaces in which the type reference was searched for is returned. findDecl ::-     ValidationCtx m-  => (HasIdent e, HasIdent s, HasIdent t, HasIdent u)+     MonadValidation m   => Namespace   -> FileTree (SymbolTable e s t u)   -> TypeRef@@ -238,10 +297,10 @@         let candidateNamespace = parentNamespace <> refNamespace         let searchSymbolTable symbolTable =               asum-                [ MatchE <$> find (\(ns, e) -> ns == candidateNamespace && getIdent e == refIdent) (allEnums symbolTable)-                , MatchS <$> find (\(ns, e) -> ns == candidateNamespace && getIdent e == refIdent) (allStructs symbolTable)-                , MatchT <$> find (\(ns, e) -> ns == candidateNamespace && getIdent e == refIdent) (allTables symbolTable)-                , MatchU <$> find (\(ns, e) -> ns == candidateNamespace && getIdent e == refIdent) (allUnions symbolTable)+                [ MatchE candidateNamespace <$> Map.lookup (candidateNamespace, refIdent) (allEnums symbolTable)+                , MatchS candidateNamespace <$> Map.lookup (candidateNamespace, refIdent) (allStructs symbolTable)+                , MatchT candidateNamespace <$> Map.lookup (candidateNamespace, refIdent) (allTables symbolTable)+                , MatchU candidateNamespace <$> Map.lookup (candidateNamespace, refIdent) (allUnions symbolTable)                 ]         pure $ asum $ fmap searchSymbolTable symbolTables   in@@ -249,9 +308,9 @@       Just match -> pure match       Nothing    ->         throwErrorMsg $-          "type '"+          "type "           <> display typeRef-          <> "' does not exist (checked in these namespaces: "+          <> " does not exist (checked in these namespaces: "           <> display parentNamespaces'           <> ")" @@ -267,25 +326,21 @@ ---------------------------------- ------------- Enums -------------- -----------------------------------validateEnums :: forall m. ValidationCtx m => FileTree Stage1 -> m (FileTree Stage2)+validateEnums :: FileTree Stage1 -> Validation (FileTree Stage2) validateEnums symbolTables =   for symbolTables $ \symbolTable -> do-    let enums = allEnums symbolTable-    let validate (namespace, enum) = do-          validEnum <- validateEnum (namespace, enum)-          pure (namespace, validEnum)-    validEnums <- traverse validate enums+    validEnums <- Map.traverseWithKey validateEnum (allEnums symbolTable)     pure symbolTable { allEnums = validEnums } --- TODO: add support for `bit_flags` attribute-validateEnum :: forall m. ValidationCtx m => (Namespace, ST.EnumDecl) -> m EnumDecl-validateEnum (currentNamespace, enum) =-  modifyContext (\_ -> qualify currentNamespace enum) $ do-    checkBitFlags+validateEnum :: (Namespace, Ident) -> ST.EnumDecl -> Validation EnumDecl+validateEnum (currentNamespace, _) enum =+  validating (qualify currentNamespace enum) $ do     checkDuplicateFields     checkUndeclaredAttributes enum     validEnum   where+    isBitFlags = hasAttribute bitFlagsAttr (ST.enumMetadata enum)+     validEnum = do       enumType <- validateEnumType (ST.enumType enum)       let enumVals = flip evalState Nothing . traverse mapEnumVal $ ST.enumVals enum@@ -294,7 +349,8 @@       pure EnumDecl         { enumIdent = getIdent enum         , enumType = enumType-        , enumVals = enumVals+        , enumBitFlags = isBitFlags+        , enumVals = shiftBitFlags <$> enumVals         }      mapEnumVal :: ST.EnumVal -> State (Maybe Integer) EnumVal@@ -310,15 +366,27 @@       put (Just thisInt)       pure (EnumVal (getIdent enumVal) thisInt) -    validateOrder :: NonEmpty EnumVal -> m ()+    validateOrder :: NonEmpty EnumVal -> Validation ()     validateOrder xs =-      if all (\(x, y) -> enumValInt x < enumValInt y) (NE.toList xs `zip` NE.tail xs)-        then pure ()-        else throwErrorMsg "enum values must be specified in ascending order"+      let consecutivePairs = NE.toList xs `zip` NE.tail xs+          outOfOrderPais = filter (\(x, y) -> enumValInt x >= enumValInt y) consecutivePairs+      in+          case outOfOrderPais of+            []         -> pure ()+            (x, y) : _ -> throwErrorMsg $+              "enum values must be specified in ascending order. "+              <> display (enumValIdent y)+              <> " ("+              <> display (enumValInt y)+              <> ") should be greater than "+              <> display (enumValIdent x)+              <> " ("+              <> display (enumValInt x)+              <> ")" -    validateBounds :: EnumType -> EnumVal -> m ()+    validateBounds :: EnumType -> EnumVal -> Validation ()     validateBounds enumType enumVal =-      modifyContext (\context -> context <> "." <> getIdent enumVal) $+      validating enumVal $         case enumType of           EInt8 -> validateBounds' @Int8 enumVal           EInt16 -> validateBounds' @Int16 enumVal@@ -329,59 +397,72 @@           EWord32 -> validateBounds' @Word32 enumVal           EWord64 -> validateBounds' @Word64 enumVal -    validateBounds' :: forall a. (Integral a, Bounded a, Show a) => EnumVal -> m ()+    validateBounds' :: forall a. (FiniteBits a, Integral a, Bounded a) => EnumVal -> Validation ()     validateBounds' e =-      if inRange (toInteger (minBound @a), toInteger (maxBound @a)) (enumValInt e)+      if inRange (lower, upper) (enumValInt e)         then pure ()         else throwErrorMsg $-              "enum value does not fit ["-              <> T.pack (show (minBound @a))+              "enum value of "+              <> display (enumValInt e)+              <> " does not fit ["+              <> display lower               <> "; "-              <> T.pack (show (maxBound @a))+              <> display upper               <> "]"+      where+        lower = if isBitFlags+                  then 0+                  else toInteger (minBound @a)+        upper = if isBitFlags+                  then toInteger (finiteBitSize @a (undefined :: a) - 1)+                  else toInteger (maxBound @a) -    validateEnumType :: ST.Type -> m EnumType+    validateEnumType :: ST.Type -> Validation EnumType     validateEnumType t =       case t of-        ST.TInt8 -> pure EInt8-        ST.TInt16 -> pure EInt16-        ST.TInt32 -> pure EInt32-        ST.TInt64 -> pure EInt64-        ST.TWord8 -> pure EWord8+        ST.TInt8   -> unlessIsBitFlags EInt8+        ST.TInt16  -> unlessIsBitFlags EInt16+        ST.TInt32  -> unlessIsBitFlags EInt32+        ST.TInt64  -> unlessIsBitFlags EInt64+        ST.TWord8  -> pure EWord8         ST.TWord16 -> pure EWord16         ST.TWord32 -> pure EWord32         ST.TWord64 -> pure EWord64         _          -> throwErrorMsg "underlying enum type must be integral"+      where+        unlessIsBitFlags x =+          if isBitFlags+            then throwErrorMsg "underlying type of bit_flags enum must be unsigned"+            else pure x -    checkDuplicateFields :: m ()+    -- If this enum has the `bit_flags` attribute, convert its int value to the corresponding bitmask.+    -- E.g., 2 -> 00000100+    shiftBitFlags :: EnumVal -> EnumVal+    shiftBitFlags e =+      if isBitFlags+        then e { enumValInt = bit (fromIntegral @Integer @Int (enumValInt e)) }+        else e++    checkDuplicateFields :: Validation ()     checkDuplicateFields =       checkDuplicateIdentifiers         (ST.enumVals enum) -    checkBitFlags :: m ()-    checkBitFlags =-      when (hasAttribute bitFlagsAttr (ST.enumMetadata enum)) $-        throwErrorMsg "`bit_flags` are not supported yet" - ---------------------------------- ------------ Tables -------------- ---------------------------------- data TableFieldWithoutId = TableFieldWithoutId !Ident !TableFieldType !Bool -validateTables :: ValidationCtx m => FileTree Stage3 -> m (FileTree Stage4)+validateTables :: FileTree Stage3 -> Validation (FileTree Stage4) validateTables symbolTables =   for symbolTables $ \symbolTable -> do-    let tables = allTables symbolTable-    let validate (namespace, table) = do-          validTable <- validateTable symbolTables (namespace, table)-          pure (namespace, validTable)-    validTables <- traverse validate tables+    validTables <- Map.traverseWithKey (validateTable symbolTables) (allTables symbolTable)     pure symbolTable { allTables = validTables } -validateTable :: forall m. ValidationCtx m => FileTree Stage3 -> (Namespace, ST.TableDecl) -> m TableDecl-validateTable symbolTables (currentNamespace, table) =-  modifyContext (\_ -> qualify currentNamespace table) $ do+validateTable :: FileTree Stage3 -> (Namespace, Ident) -> ST.TableDecl -> Validation TableDecl+validateTable symbolTables (currentNamespace, _) table =+  validating (qualify currentNamespace table) $ do      let fields = ST.tableFields table     let fieldsMetadata = ST.tableFieldMetadata <$> fields@@ -399,10 +480,10 @@       }    where-    checkDuplicateFields :: [ST.TableField] -> m ()+    checkDuplicateFields :: [ST.TableField] -> Validation ()     checkDuplicateFields = checkDuplicateIdentifiers -    assignFieldIds :: [ST.Metadata] -> [TableFieldWithoutId] -> m [TableField]+    assignFieldIds :: [ST.Metadata] -> [TableFieldWithoutId] -> Validation [TableField]     assignFieldIds metadata fieldsWithoutIds = do       ids <- catMaybes <$> traverse (findIntAttr idAttr) metadata       if null ids@@ -427,10 +508,10 @@       put fieldId       pure (TableField fieldId ident typ depr) -    checkFieldId :: TableField -> StateT Integer m ()+    checkFieldId :: TableField -> StateT Integer Validation ()     checkFieldId field = do       lastId <- get-      modifyContext (\context -> context <> "." <> getIdent field) $ do+      validating field $ do         case tableFieldType field of           TUnion _ _ ->             when (tableFieldId field /= lastId + 2) $@@ -443,9 +524,9 @@               throwErrorMsg $ "field ids must be consecutive from 0; id " <> display (lastId + 1) <> " is missing"         put (tableFieldId field) -    validateTableField :: ST.TableField -> m TableFieldWithoutId+    validateTableField :: ST.TableField -> Validation TableFieldWithoutId     validateTableField tf =-      modifyContext (\context -> context <> "." <> getIdent tf) $ do+      validating tf $ do         checkUndeclaredAttributes tf         validFieldType <- validateTableFieldType (ST.tableFieldMetadata tf) (ST.tableFieldDefault tf) (ST.tableFieldType tf) @@ -454,30 +535,30 @@           validFieldType           (hasAttribute deprecatedAttr (ST.tableFieldMetadata tf)) -    validateTableFieldType :: ST.Metadata -> Maybe ST.DefaultVal -> ST.Type -> m TableFieldType+    validateTableFieldType :: ST.Metadata -> Maybe ST.DefaultVal -> ST.Type -> Validation TableFieldType     validateTableFieldType md dflt tableFieldType =       case tableFieldType of-        ST.TInt8 -> checkNoRequired md >> validateDefaultValAsInt dflt <&> TInt8-        ST.TInt16 -> checkNoRequired md >> validateDefaultValAsInt dflt <&> TInt16-        ST.TInt32 -> checkNoRequired md >> validateDefaultValAsInt dflt <&> TInt32-        ST.TInt64 -> checkNoRequired md >> validateDefaultValAsInt dflt <&> TInt64-        ST.TWord8 -> checkNoRequired md >> validateDefaultValAsInt dflt <&> TWord8-        ST.TWord16 -> checkNoRequired md >> validateDefaultValAsInt dflt <&> TWord16-        ST.TWord32 -> checkNoRequired md >> validateDefaultValAsInt dflt <&> TWord32-        ST.TWord64 -> checkNoRequired md >> validateDefaultValAsInt dflt <&> TWord64+        ST.TInt8 -> checkNoRequired md >> validateDefaultValAsInt @Int8 dflt <&> TInt8+        ST.TInt16 -> checkNoRequired md >> validateDefaultValAsInt @Int16 dflt <&> TInt16+        ST.TInt32 -> checkNoRequired md >> validateDefaultValAsInt @Int32 dflt <&> TInt32+        ST.TInt64 -> checkNoRequired md >> validateDefaultValAsInt @Int64 dflt <&> TInt64+        ST.TWord8 -> checkNoRequired md >> validateDefaultValAsInt @Word8 dflt <&> TWord8+        ST.TWord16 -> checkNoRequired md >> validateDefaultValAsInt @Word16 dflt <&> TWord16+        ST.TWord32 -> checkNoRequired md >> validateDefaultValAsInt @Word32 dflt <&> TWord32+        ST.TWord64 -> checkNoRequired md >> validateDefaultValAsInt @Word64 dflt <&> TWord64         ST.TFloat -> checkNoRequired md >> validateDefaultValAsScientific dflt <&> TFloat         ST.TDouble -> checkNoRequired md >> validateDefaultValAsScientific dflt <&> TDouble         ST.TBool -> checkNoRequired md >> validateDefaultValAsBool dflt <&> TBool         ST.TString -> checkNoDefault dflt $> TString (isRequired md)         ST.TRef typeRef ->           findDecl currentNamespace symbolTables typeRef >>= \case-            MatchE (ns, enum) -> do+            MatchE ns enum -> do               checkNoRequired md               validDefault <- validateDefaultAsEnum dflt enum               pure $ TEnum (TypeRef ns (getIdent enum)) (enumType enum) validDefault-            MatchS (ns, struct) -> checkNoDefault dflt $> TStruct (TypeRef ns (getIdent struct))  (isRequired md)-            MatchT (ns, table)  -> checkNoDefault dflt $> TTable  (TypeRef ns (getIdent table)) (isRequired md)-            MatchU (ns, union)  -> checkNoDefault dflt $> TUnion  (TypeRef ns (getIdent union)) (isRequired md)+            MatchS ns struct -> checkNoDefault dflt $> TStruct (TypeRef ns (getIdent struct))  (isRequired md)+            MatchT ns table  -> checkNoDefault dflt $> TTable  (TypeRef ns (getIdent table)) (isRequired md)+            MatchU ns union  -> checkNoDefault dflt $> TUnion  (TypeRef ns (getIdent union)) (isRequired md)         ST.TVector vecType ->           checkNoDefault dflt >> TVector (isRequired md) <$>             case vecType of@@ -496,20 +577,20 @@               ST.TVector _ -> throwErrorMsg "nested vector types not supported"               ST.TRef typeRef ->                 findDecl currentNamespace symbolTables typeRef <&> \case-                  MatchE (ns, enum) ->+                  MatchE ns enum ->                     VEnum (TypeRef ns (getIdent enum))                           (enumType enum)-                  MatchS (ns, struct) ->+                  MatchS ns struct ->                     VStruct (TypeRef ns (getIdent struct))-                  MatchT (ns, table) -> VTable (TypeRef ns (getIdent table))-                  MatchU (ns, union) -> VUnion (TypeRef ns (getIdent union))+                  MatchT ns table -> VTable (TypeRef ns (getIdent table))+                  MatchU ns union -> VUnion (TypeRef ns (getIdent union)) -checkNoRequired :: ValidationCtx m => ST.Metadata -> m ()+checkNoRequired :: ST.Metadata -> Validation () checkNoRequired md =   when (hasAttribute requiredAttr md) $     throwErrorMsg "only non-scalar fields (strings, vectors, unions, structs, tables) may be 'required'" -checkNoDefault :: ValidationCtx m => Maybe ST.DefaultVal -> m ()+checkNoDefault :: Maybe ST.DefaultVal -> Validation () checkNoDefault dflt =   when (isJust dflt) $     throwErrorMsg@@ -518,77 +599,118 @@ isRequired :: ST.Metadata -> Required isRequired md = if hasAttribute requiredAttr md then Req else Opt -validateDefaultValAsInt :: forall m a. (ValidationCtx m, Integral a, Bounded a, Show a) => Maybe ST.DefaultVal -> m (DefaultVal a)+validateDefaultValAsInt :: forall a. (Integral a, Bounded a, Display a) => Maybe ST.DefaultVal -> Validation (DefaultVal Integer) validateDefaultValAsInt dflt =   case dflt of-    Nothing -> pure (DefaultVal 0)-    Just (ST.DefaultNum n) ->-      if not (Scientific.isInteger n)-        then throwErrorMsg "default value must be integral"-        else case Scientific.toBoundedInteger @a n of-          Nothing ->-            throwErrorMsg $-              "default value does not fit ["-              <> T.pack (show (minBound @a))-              <> "; "-              <> T.pack (show (maxBound @a))-              <> "]"-          Just i -> pure (DefaultVal i)-    Just _ -> throwErrorMsg "default value must be integral"+    Nothing                -> pure (DefaultVal 0)+    Just (ST.DefaultNum n) -> scientificToInteger @a n "default value must be integral"+    Just _                 -> throwErrorMsg "default value must be integral" -validateDefaultValAsScientific :: ValidationCtx m => Maybe ST.DefaultVal -> m (DefaultVal Scientific)+validateDefaultValAsScientific :: Maybe ST.DefaultVal -> Validation (DefaultVal Scientific) validateDefaultValAsScientific dflt =   case dflt of     Nothing                 -> pure (DefaultVal 0)     Just (ST.DefaultNum n)  -> pure (DefaultVal n)     Just _                  -> throwErrorMsg "default value must be a number" -validateDefaultValAsBool :: ValidationCtx m => Maybe ST.DefaultVal -> m (DefaultVal Bool)+validateDefaultValAsBool :: Maybe ST.DefaultVal -> Validation (DefaultVal Bool) validateDefaultValAsBool dflt =   case dflt of     Nothing                 -> pure (DefaultVal False)     Just (ST.DefaultBool b) -> pure (DefaultVal b)     Just _                  -> throwErrorMsg "default value must be a boolean" -validateDefaultAsEnum :: ValidationCtx m => Maybe ST.DefaultVal -> EnumDecl -> m (DefaultVal Integer)+validateDefaultAsEnum :: Maybe ST.DefaultVal -> EnumDecl -> Validation (DefaultVal Integer) validateDefaultAsEnum dflt enum =-  DefaultVal <$>-    case dflt of-      Nothing ->-        case find (\val -> enumValInt val == 0) (enumVals enum) of-          Just zeroVal -> pure (enumValInt zeroVal)-          Nothing -> throwErrorMsg "enum does not have a 0 value; please manually specify a default for this field"-      Just (ST.DefaultNum n) ->-        case Scientific.floatingOrInteger @Float n of-          Left _float -> throwErrorMsg $ "default value must be integral or one of: " <> display (getIdent <$> enumVals enum)-          Right i ->-            case find (\val -> enumValInt val == i) (enumVals enum) of-              Just matchingVal -> pure (enumValInt matchingVal)-              Nothing -> throwErrorMsg $ "default value of " <> display i <> " is not part of enum " <> display (getIdent enum)-      Just (ST.DefaultRef ref) ->-        case find (\val -> getIdent val == ref) (enumVals enum) of-          Just matchingVal ->  pure (enumValInt matchingVal)-          Nothing          -> throwErrorMsg $ "default value of " <> display ref <> " is not part of enum " <> display (getIdent enum)+  case dflt of+    Nothing ->+      if enumBitFlags enum+        then pure 0+        else+          case find (\val -> enumValInt val == 0) (enumVals enum) of+            Just _  -> pure 0+            Nothing -> throwErrorMsg "enum does not have a 0 value; please manually specify a default for this field"+    Just (ST.DefaultNum n) ->+      if enumBitFlags enum+        then+          case enumType enum of+            EWord8  -> scientificToInteger @Word8  n defaultErrorMsg+            EWord16 -> scientificToInteger @Word16 n defaultErrorMsg+            EWord32 -> scientificToInteger @Word32 n defaultErrorMsg+            EWord64 -> scientificToInteger @Word64 n defaultErrorMsg+            _       -> throwErrorMsg "The 'impossible' has happened: bit_flags enum with signed integer"+        else+          case Scientific.floatingOrInteger @Float n of+            Left _float -> throwErrorMsg defaultErrorMsg+            Right i ->+              case find (\val -> enumValInt val == i) (enumVals enum) of+                Just matchingVal -> pure (DefaultVal (enumValInt matchingVal))+                Nothing -> throwErrorMsg $ "default value of " <> display i <> " is not part of enum " <> display (getIdent enum)+    Just (ST.DefaultRef refs) ->+      if enumBitFlags enum+        then+          foldr1 (.|.) <$> traverse findEnumByRef refs+        else+          case refs of+            ref :| [] -> findEnumByRef ref+            _         -> throwErrorMsg $ "default value must be a single identifier, found "+                          <> display (NE.length refs)+                          <> ": "+                          <> display (fmap (\ref -> "'" <> ref <> "'") refs)+    Just (ST.DefaultBool _) ->+      throwErrorMsg defaultErrorMsg+  where+    defaultErrorMsg =+      if enumBitFlags enum+        then case enumVals enum of+          x :| y : _ ->+            "default value must be integral, one of ["+            <> display (getIdent <$> enumVals enum)+            <> "], or a combination of the latter in double quotes (e.g. \""+            <> T.unpack (unIdent (getIdent x))+            <> " "+            <> T.unpack (unIdent (getIdent y))+            <> "\")"+          _ ->+            "default value must be integral or one of: " <> display (getIdent <$> enumVals enum)+        else+          "default value must be integral or one of: " <> display (getIdent <$> enumVals enum) -      Just (ST.DefaultBool _) -> throwErrorMsg $ "default value must be integral or one of: " <> display (getIdent <$> enumVals enum)+    findEnumByRef :: Text -> Validation (DefaultVal Integer)+    findEnumByRef ref =+      case find (\val -> unIdent (getIdent val) == ref) (enumVals enum) of+        Just matchingVal -> pure (DefaultVal (enumValInt matchingVal))+        Nothing          -> throwErrorMsg $ "default value of " <> display ref <> " is not part of enum " <> display (getIdent enum) +scientificToInteger ::+  forall a. (Integral a, Bounded a, Display a)+  => Scientific -> String -> Validation (DefaultVal Integer)+scientificToInteger n notIntegerErrorMsg =+  if not (Scientific.isInteger n)+    then throwErrorMsg notIntegerErrorMsg+    else+      case Scientific.toBoundedInteger @a n of+        Nothing ->+          throwErrorMsg $+            "default value does not fit ["+            <> display (minBound @a)+            <> "; "+            <> display (maxBound @a)+            <> "]"+        Just i -> pure (DefaultVal (toInteger i))  ---------------------------------- ------------ Unions -------------- -----------------------------------validateUnions :: ValidationCtx m => FileTree Stage4 -> m (FileTree ValidDecls)+validateUnions :: FileTree Stage4 -> Validation (FileTree ValidDecls) validateUnions symbolTables =   for symbolTables $ \symbolTable -> do-    let unions = allUnions symbolTable-    let validate (namespace, union) = do-          validUnion <- validateUnion symbolTables (namespace, union)-          pure (namespace, validUnion)-    validUnions <- traverse validate unions+    validUnions <- Map.traverseWithKey (validateUnion symbolTables) (allUnions symbolTable)     pure symbolTable { allUnions = validUnions } -validateUnion :: forall m. ValidationCtx m => FileTree Stage4 -> (Namespace, ST.UnionDecl) -> m UnionDecl-validateUnion symbolTables (currentNamespace, union) =-  modifyContext (\_ -> qualify currentNamespace union) $ do+validateUnion :: FileTree Stage4 -> (Namespace, Ident) -> ST.UnionDecl -> Validation UnionDecl+validateUnion symbolTables (currentNamespace, _) union =+  validating (qualify currentNamespace union) $ do     validUnionVals <- traverse validateUnionVal (ST.unionVals union)     checkDuplicateVals validUnionVals     checkUndeclaredAttributes union@@ -597,67 +719,79 @@       , unionVals = validUnionVals       }   where-    validateUnionVal :: ST.UnionVal -> m UnionVal+    validateUnionVal :: ST.UnionVal -> Validation UnionVal     validateUnionVal uv = do       let tref = ST.unionValTypeRef uv       let partiallyQualifiedTypeRef = qualify (typeRefNamespace tref) (typeRefIdent tref)       let ident = fromMaybe partiallyQualifiedTypeRef (ST.unionValIdent uv)       let identFormatted = coerce $ T.replace "." "_" $ coerce ident-      modifyContext (\context -> context <> "." <> identFormatted) $ do+      validating identFormatted $ do         tableRef <- validateUnionValType tref         pure $ UnionVal           { unionValIdent = identFormatted           , unionValTableRef = tableRef           } -    validateUnionValType :: TypeRef -> m TypeRef+    validateUnionValType :: TypeRef -> Validation TypeRef     validateUnionValType typeRef =       findDecl currentNamespace symbolTables typeRef >>= \case-        MatchT (ns, table)        -> pure $ TypeRef ns (getIdent table)-        _                         -> throwErrorMsg "union members may only be tables"+        MatchT ns table -> pure $ TypeRef ns (getIdent table)+        _               -> throwErrorMsg "union members may only be tables" -    checkDuplicateVals :: NonEmpty UnionVal -> m ()+    checkDuplicateVals :: NonEmpty UnionVal -> Validation ()     checkDuplicateVals vals = checkDuplicateIdentifiers (NE.cons "NONE" (fmap getIdent vals))   ---------------------------------- ------------ Structs ------------- -----------------------------------validateStructs :: ValidationCtx m => FileTree Stage2 -> m (FileTree Stage3)++-- | Cache of already validated structs.+--+-- When we're validating a struct @A@, it may contain an inner struct @B@ which also needs validating.+-- @B@ needs to be fully validated before we can consider @A@ valid.+--+-- If we've validated @B@ in a previous iteration, we will find it in this Map+-- and therefore avoid re-validating it.+type ValidatedStructs = Map (Namespace, Ident) StructDecl+++validateStructs :: FileTree Stage2 -> Validation (FileTree Stage3) validateStructs symbolTables =-  flip evalStateT [] $ traverse validateFile symbolTables+  flip evalStateT Map.empty $ traverse validateFile symbolTables   where-  validateFile :: (MonadState [(Namespace, StructDecl)] m, ValidationCtx m) => Stage2 -> m Stage3+  validateFile :: Stage2 -> StateT ValidatedStructs Validation Stage3   validateFile symbolTable = do     let structs = allStructs symbolTable -    traverse_ (checkStructCycles symbolTables) structs-    validStructs <- traverse (validateStruct symbolTables) structs+    traverse_ (\((ns, _), struct) -> checkStructCycles symbolTables (ns, struct)) (Map.toList structs)+    validStructs <- Map.traverseWithKey (\(ns, _) struct -> validateStruct symbolTables ns struct) structs      pure symbolTable { allStructs = validStructs } -checkStructCycles :: forall m. ValidationCtx m => FileTree Stage2 -> (Namespace, ST.StructDecl) -> m ()+checkStructCycles :: forall m. MonadValidation m => FileTree Stage2 -> (Namespace, ST.StructDecl) -> m () checkStructCycles symbolTables = go []   where     go :: [Ident] -> (Namespace, ST.StructDecl) -> m ()-    go visited (currentNamespace, struct) =+    go visited (currentNamespace, struct) = do       let qualifiedName = qualify currentNamespace struct-      in  modifyContext (const qualifiedName) $-            if qualifiedName `elem` visited-              then-                throwErrorMsg $-                  "cyclic dependency detected ["-                  <> display (T.intercalate " -> " . coerce $ List.dropWhile (/= qualifiedName) $ List.reverse (qualifiedName : visited))-                  <>"] - structs cannot contain themselves, directly or indirectly"-              else-                forM_ (ST.structFields struct) $ \field ->-                  modifyContext (\context -> context <> "." <> getIdent field) $-                    case ST.structFieldType field of-                      ST.TRef typeRef ->-                        findDecl currentNamespace symbolTables typeRef >>= \case-                          MatchS struct -> go (qualifiedName : visited) struct-                          _             -> pure () -- The TypeRef points to an enum (or is invalid), so no further validation is needed at this point-                      _ -> pure () -- Field is not a TypeRef, no validation needed+      resetContext $+        validating qualifiedName $+          if qualifiedName `elem` visited+            then+              throwErrorMsg $+                "cyclic dependency detected ["+                <> display (T.intercalate " -> " . coerce $ List.dropWhile (/= qualifiedName) $ List.reverse (qualifiedName : visited))+                <>"] - structs cannot contain themselves, directly or indirectly"+            else+              forM_ (ST.structFields struct) $ \field ->+                validating field $+                  case ST.structFieldType field of+                    ST.TRef typeRef ->+                      findDecl currentNamespace symbolTables typeRef >>= \case+                        MatchS ns struct -> go (qualifiedName : visited) (ns, struct)+                        _                -> pure () -- The TypeRef points to an enum (or is invalid), so no further validation is needed at this point+                    _                    -> pure () -- Field is not a TypeRef, no validation needed  data UnpaddedStructField = UnpaddedStructField   { unpaddedStructFieldIdent :: !Ident@@ -665,15 +799,17 @@   } deriving (Show, Eq)  validateStruct ::-     forall m. (MonadState [(Namespace, StructDecl)] m, ValidationCtx m)+     forall m. (MonadState ValidatedStructs m, MonadValidation m)   => FileTree Stage2-  -> (Namespace, ST.StructDecl)-  -> m (Namespace, StructDecl)-validateStruct symbolTables (currentNamespace, struct) =-  modifyContext (\_ -> qualify currentNamespace struct) $ do+  -> Namespace+  -> ST.StructDecl+  -> m StructDecl+validateStruct symbolTables currentNamespace struct =+  resetContext $+  validating (qualify currentNamespace struct) $ do     validStructs <- get     -- Check if this struct has already been validated in a previous iteration-    case find (\(ns, s) -> ns == currentNamespace && getIdent s == getIdent struct) validStructs of+    case Map.lookup (currentNamespace, getIdent struct) validStructs of       Just match -> pure match       Nothing -> do         checkDuplicateFields@@ -696,8 +832,8 @@               , structSize       = size               , structFields     = paddedFields               }-        modify ((currentNamespace, validStruct) :)-        pure (currentNamespace, validStruct)+        modify (Map.insert (currentNamespace, getIdent validStruct) validStruct)+        pure validStruct    where     invalidStructFieldType = "struct fields may only be integers, floating point, bool, enums, or other structs"@@ -742,7 +878,7 @@      validateStructField :: ST.StructField -> m UnpaddedStructField     validateStructField sf =-      modifyContext (\context -> context <> "." <> getIdent sf) $ do+      validating sf $ do         checkUnsupportedAttributes sf         checkUndeclaredAttributes sf         structFieldType <- validateStructFieldType (ST.structFieldType sf)@@ -769,11 +905,12 @@         ST.TVector _ -> throwErrorMsg invalidStructFieldType         ST.TRef typeRef ->           findDecl currentNamespace symbolTables typeRef >>= \case-            MatchE (enumNamespace, enum) ->+            MatchE enumNamespace enum ->               pure (SEnum (TypeRef enumNamespace (getIdent enum)) (enumType enum))-            MatchS (nestedNamespace, nestedStruct) ->+            MatchS nestedNamespace nestedStruct -> do               -- if this is a reference to a struct, we need to validate it first-              SStruct <$> validateStruct symbolTables (nestedNamespace, nestedStruct)+              validNestedStruct <- validateStruct symbolTables nestedNamespace nestedStruct+              pure $ SStruct (nestedNamespace, validNestedStruct)             _ -> throwErrorMsg invalidStructFieldType      checkUnsupportedAttributes :: ST.StructField -> m ()@@ -795,7 +932,7 @@         then pure (fromIntegral @Integer @Alignment forceAlign)         else throwErrorMsg $               "force_align must be a power of two integer ranging from the struct's natural alignment (in this case, "-              <> T.pack (show naturalAlignment)+              <> display naturalAlignment               <> ") to 16"      checkDuplicateFields :: m ()@@ -856,7 +993,7 @@     SEnum _ enumType -> fromIntegral @Word8 @InlineSize (enumSize enumType)     SStruct (_, nestedStruct) -> structSize nestedStruct -checkDuplicateIdentifiers :: (ValidationCtx m, Foldable f, Functor f, HasIdent a) => f a -> m ()+checkDuplicateIdentifiers :: (MonadValidation m, Foldable f, Functor f, HasIdent a) => f a -> m () checkDuplicateIdentifiers xs =   case findDups (getIdent <$> xs) of     [] -> pure ()@@ -871,17 +1008,17 @@     occurrences xs =       Map.unionsWith (<>) $ Foldable.toList $ fmap (\x -> Map.singleton x (Sum 1)) xs -checkUndeclaredAttributes :: (ValidationCtx m, HasMetadata a) => a -> m ()+checkUndeclaredAttributes :: (MonadValidation m, HasMetadata a) => a -> m () checkUndeclaredAttributes a = do-  allAttributes <- asks validationStateAllAttributes+  allAttributes <- getDeclaredAttributes   forM_ (Map.keys . ST.unMetadata . getMetadata $ a) $ \attr ->     when (coerce attr `Set.notMember` allAttributes) $-      throwErrorMsg $ "user defined attributes must be declared before use: " <> attr+      throwErrorMsg $ "user defined attributes must be declared before use: " <> display attr  hasAttribute :: Text -> ST.Metadata -> Bool hasAttribute name (ST.Metadata attrs) = Map.member name attrs -findIntAttr :: ValidationCtx m => Text -> ST.Metadata -> m (Maybe Integer)+findIntAttr :: MonadValidation m => Text -> ST.Metadata -> m (Maybe Integer) findIntAttr name (ST.Metadata attrs) =   case Map.lookup name attrs of     Nothing                  -> pure Nothing@@ -895,12 +1032,12 @@     err =       throwErrorMsg $         "expected attribute '"-        <> name+        <> display name         <> "' to have an integer value, e.g. '"-        <> name+        <> display name         <> ": 123'" -findStringAttr :: ValidationCtx m => Text -> ST.Metadata -> m (Maybe Text)+findStringAttr :: Text -> ST.Metadata -> Validation (Maybe Text) findStringAttr name (ST.Metadata attrs) =   case Map.lookup name attrs of     Nothing                  -> pure Nothing@@ -908,17 +1045,17 @@     Just _ ->       throwErrorMsg $         "expected attribute '"-        <> name+        <> display name         <> "' to have a string value, e.g. '"-        <> name+        <> display name         <> ": \"abc\"'" -throwErrorMsg :: ValidationCtx m => Text -> m a-throwErrorMsg msg = do-  context <- asks validationStateCurrentContext-  if context == ""-    then throwError msg-    else throwError $ "[" <> display context <> "]: " <> msg--+isPowerOfTwo :: (Num a, Bits a) => a -> Bool+isPowerOfTwo 0 = False+isPowerOfTwo n = (n .&. (n - 1)) == 0 +roundUpToNearestMultipleOf :: Integral n => n -> n -> n+roundUpToNearestMultipleOf x y =+  case x `rem` y of+    0         -> x+    remainder -> (y - remainder) + x
src/FlatBuffers/Internal/Compiler/SyntaxTree.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ViewPatterns #-}  module FlatBuffers.Internal.Compiler.SyntaxTree where @@ -39,8 +40,11 @@  newtype Ident = Ident   { unIdent :: Text-  } deriving newtype (Show, Eq, IsString, Ord, Semigroup, Display)+  } deriving newtype (Show, Eq, IsString, Ord, Semigroup) +instance Display Ident where+  display (Ident i) = "'" <> display i <> "'"+ newtype Include = Include   { unInclude :: StringLiteral   } deriving newtype (Show, Eq, IsString)@@ -61,7 +65,7 @@ data DefaultVal   = DefaultNum !Scientific   | DefaultBool !Bool-  | DefaultRef !Ident+  | DefaultRef !(NonEmpty Text)   deriving (Show, Eq)  newtype Metadata = Metadata@@ -161,10 +165,10 @@   deriving newtype (Eq, Ord, Semigroup)  instance Display Namespace where-  display (Namespace ns) = T.intercalate "." ns+  display (Namespace ns) = "'" <> T.unpack (T.intercalate "." ns) <> "'"  instance Show Namespace where-  show = show . T.unpack . display+  show = show . display  instance IsString Namespace where   fromString "" = Namespace []@@ -172,7 +176,8 @@  qualify :: HasIdent a => Namespace -> a -> Ident qualify "" a = getIdent a-qualify ns a = Ident (display ns <> "." <> display (getIdent a))+qualify (Namespace ns) (getIdent -> Ident ident) =+  Ident (T.intercalate "." ns <> "." <> ident)  class HasIdent a where   getIdent :: a -> Ident
src/FlatBuffers/Internal/Compiler/TH.hs view
@@ -6,9 +6,11 @@ import           Control.Monad                                   ( join ) import           Control.Monad.Except                            ( runExceptT ) +import           Data.Bits                                       ( (.&.) ) import           Data.Foldable                                   ( traverse_ ) import           Data.Functor                                    ( (<&>) ) import           Data.Int+import qualified Data.List                                       as List import           Data.List.NonEmpty                              ( NonEmpty(..) ) import qualified Data.List.NonEmpty                              as NE import qualified Data.Map.Strict                                 as Map@@ -26,7 +28,6 @@ import           FlatBuffers.Internal.FileIdentifier             ( HasFileIdentifier(..), unsafeFileIdentifier ) import           FlatBuffers.Internal.Read import           FlatBuffers.Internal.Types-import           FlatBuffers.Internal.Util                       ( Positive(getPositive), nonEmptyUnzip3 ) import           FlatBuffers.Internal.Write  import           Language.Haskell.TH@@ -77,7 +78,6 @@ -- > {-# LANGUAGE TemplateHaskell #-} -- > -- > module Data.Game where--- > -- > import FlatBuffers -- > -- > $(mkFlatBuffers "schemas/game.fbs" defaultOptions)@@ -87,11 +87,11 @@    parseResult <- runIO $ runExceptT $ ParserIO.parseSchemas rootFilePath (includeDirectories opts) -  schemaFileTree <- either (fail . T.unpack) pure parseResult+  schemaFileTree <- either (fail . fixMsg) pure parseResult    registerFiles schemaFileTree -  symbolTables <- either (fail . T.unpack) pure $ SemanticAnalysis.validateSchemas schemaFileTree+  symbolTables <- either (fail . fixMsg) pure $ SemanticAnalysis.validateSchemas schemaFileTree    let symbolTable =         if compileAllSchemas opts@@ -110,39 +110,124 @@      filterByCurrentModule currentModule (SymbolTable enums structs tables unions) =       SymbolTable-        { allEnums   = filter (isCurrentModule currentModule) enums-        , allStructs = filter (isCurrentModule currentModule) structs-        , allTables  = filter (isCurrentModule currentModule) tables-        , allUnions  = filter (isCurrentModule currentModule) unions+        { allEnums   = Map.filterWithKey (isCurrentModule currentModule) enums+        , allStructs = Map.filterWithKey (isCurrentModule currentModule) structs+        , allTables  = Map.filterWithKey (isCurrentModule currentModule) tables+        , allUnions  = Map.filterWithKey (isCurrentModule currentModule) unions         } -    isCurrentModule currentModule (ns, _) = NC.namespace ns == currentModule+    isCurrentModule currentModule (ns, _) _ = NC.namespace ns == currentModule +-- | This does two things:+--+-- 1. ghcid stops parsing an error when it finds a line that start with alphabetical characters or an empty lines,+--    so we prepend each line with an empty space to avoid this.+-- 2. we also remove any trailing \n, otherwise ghcid would stop parsing here and not show the source code location.+fixMsg :: String -> String+fixMsg = List.intercalate "\n" . fmap fixLine . lines+  where+    fixLine line = " " <> line+ compileSymbolTable :: SemanticAnalysis.ValidDecls -> Q [Dec] compileSymbolTable symbolTable = do-  enumDecs <- join <$> traverse mkEnum (allEnums symbolTable)-  structDecs <- join <$> traverse mkStruct (allStructs symbolTable)-  tableDecs <- join <$> traverse mkTable (allTables symbolTable)-  unionDecs <- join <$> traverse mkUnion (allUnions symbolTable)+  enumDecs <- join <$> traverse mkEnum (Map.elems (allEnums symbolTable))+  structDecs <- join <$> traverse mkStruct (Map.elems (allStructs symbolTable))+  tableDecs <- join <$> traverse mkTable (Map.elems (allTables symbolTable))+  unionDecs <- join <$> traverse mkUnion (Map.elems (allUnions symbolTable))   pure $ enumDecs <> structDecs <> tableDecs <> unionDecs -mkEnum :: (Namespace, EnumDecl) -> Q [Dec]-mkEnum (_, enum) = do-  enumName <- newName' $ NC.dataTypeName enum+mkEnum :: EnumDecl -> Q [Dec]+mkEnum enum =+  if enumBitFlags enum+    then mkEnumBitFlags enum+    else mkEnumNormal enum ++mkEnumBitFlags :: EnumDecl -> Q [Dec]+mkEnumBitFlags enum = do+  nameFun <- mkEnumBitFlagsNames enum enumValNames+  pure $+    mkEnumBitFlagsConstants enum enumValNames+    <> mkEnumBitFlagsAllValls enum enumValNames+    <> nameFun+  where+    enumValNames = mkName . T.unpack . NC.enumBitFlagsConstant enum <$> NE.toList (enumVals enum)++mkEnumBitFlagsConstants :: EnumDecl -> [Name] -> [Dec]+mkEnumBitFlagsConstants enum enumValNames =+  NE.toList (enumVals enum) `zip` enumValNames >>= \(enumVal, enumValName) ->+    let sig = SigD enumValName (enumTypeToType (enumType enum))+        fun = FunD enumValName [Clause [] (NormalB (intLitE (enumValInt enumVal))) []]+    in  [sig, fun]++-- | Generates a list with all the enum values, e.g.+--+-- > allColors = [colorsRed, colorsGreen, colorsBlue]+mkEnumBitFlagsAllValls :: EnumDecl -> [Name] -> [Dec]+mkEnumBitFlagsAllValls enum enumValNames =+  let name = mkName $ T.unpack $ NC.enumBitFlagsAllFun enum+      sig = SigD name (ListT `AppT` enumTypeToType (enumType enum))+      fun = FunD name [ Clause [] (NormalB body)  []]+      body = ListE (VarE <$> enumValNames)+  in  [sig, fun, inlinePragma name]++-- | Generates @colorsNames@.+mkEnumBitFlagsNames :: EnumDecl -> [Name] -> Q [Dec]+mkEnumBitFlagsNames enum enumValNames = do+  inputName <- newName "c"+  firstRes <- newName "res0"+  firstClause <- [d| $(varP firstRes) = [] |]+  (clauses, lastRes) <- mkClauses namesAndIdentifiers 1 inputName firstRes firstClause+  let fun = FunD funName+        [ Clause+            [VarP inputName]+            (NormalB (VarE lastRes))+            (List.reverse clauses)+        ]+  pure+    [ sig+    , fun+    , inlinePragma funName+    ]+  where+    funName = mkName $ T.unpack $ NC.enumBitFlagsNamesFun enum+    sig = SigD funName (enumTypeToType (enumType enum) ~> ListT `AppT` ConT ''Text)++    namesAndIdentifiers :: [(Name, Ident)]+    namesAndIdentifiers = List.reverse (enumValNames `zip` fmap enumValIdent (NE.toList (enumVals enum)))++    mkClauses :: [(Name, Ident)] -> Int -> Name -> Name -> [Dec] -> Q ([Dec], Name)+    mkClauses [] _ _ previousRes clauses = pure (clauses, previousRes)+    mkClauses ((name, Ident ident) : rest) ix inputName previousRes clauses = do+      res <- newName ("res" <> show ix)+      clause <-+        [d|+          $(varP res) = if $(varE name) .&. $(varE inputName) /= 0+                            then $(pure (textLitE ident)) : $(varE previousRes)+                            else $(varE previousRes)+        |]+      mkClauses rest (ix + 1) inputName res (clause <> clauses)++-- | Generated declarations for a non-bit-flags enum.+mkEnumNormal :: EnumDecl -> Q [Dec]+mkEnumNormal enum = do+  let enumName = mkName' $ NC.dataTypeName enum+   let enumValNames = enumVals enum <&> \enumVal ->         mkName $ T.unpack $ NC.enumUnionMember enum enumVal    let enumDec = mkEnumDataDec enumName enumValNames-  toEnumDecs <- mkToEnum enumName enum (enumVals enum `NE.zip` enumValNames)-  fromEnumDecs <- mkFromEnum enumName enum (enumVals enum `NE.zip` enumValNames)+  let enumValsAndNames = enumVals enum `NE.zip` enumValNames+  toEnumDecs <- mkToEnum enumName enum enumValsAndNames+  fromEnumDecs <- mkFromEnum enumName enum enumValsAndNames+  enumNameDecs <- mkEnumNameFun enumName enum enumValsAndNames -  pure $ enumDec : toEnumDecs <> fromEnumDecs+  pure $ enumDec : toEnumDecs <> fromEnumDecs <> enumNameDecs  mkEnumDataDec :: Name -> NonEmpty Name -> Dec mkEnumDataDec enumName enumValNames =   DataD [] enumName [] Nothing-    (NE.toList $ fmap (\n -> NormalC n []) enumValNames)+    (fmap (\n -> NormalC n []) (NE.toList enumValNames))     [ DerivClause Nothing       [ ConT ''Eq       , ConT ''Show@@ -164,7 +249,7 @@         (NormalB (CaseE (VarE argName) matches))         []       ]-    , PragmaD $ InlineP funName Inline FunLike AllPhases+    , inlinePragma funName     ]   where     matches =@@ -194,7 +279,7 @@         (NormalB (CaseE (VarE argName) (mkMatch <$> NE.toList enumValsAndNames)))         []       ]-    , PragmaD $ InlineP funName Inline FunLike AllPhases+    , inlinePragma funName     ]   where     mkMatch (enumVal, enumName) =@@ -203,9 +288,31 @@         (NormalB (intLitE (enumValInt enumVal)))         [] +-- | Generates @colorsName@.+mkEnumNameFun :: Name -> EnumDecl -> NonEmpty (EnumVal, Name) -> Q [Dec]+mkEnumNameFun enumName enum enumValsAndNames = do+  let funName = mkName' $ NC.enumNameFun enum+  argName <- newName "c"+  pure+    [ SigD funName (ConT enumName ~> ConT ''Text)+    , FunD funName+      [ Clause+        [VarP argName]+        (NormalB (CaseE (VarE argName) (mkMatch <$> NE.toList enumValsAndNames)))+        []+      ]+    , inlinePragma funName+    ]+  where+    mkMatch (enumVal, enumName) =+      Match+        (ConP enumName [])+        (NormalB (textLitE (unIdent (getIdent enumVal))))+        [] -mkStruct :: (Namespace, StructDecl) -> Q [Dec]-mkStruct (_, struct) = do++mkStruct :: StructDecl -> Q [Dec]+mkStruct struct = do   let structName = mkName' $ NC.dataTypeName struct   isStructInstance <- mkIsStructInstance structName struct @@ -322,8 +429,8 @@         SEnum _ enumType -> mkReadExp $ enumTypeToStructFieldType enumType         SStruct _ -> VarE 'readStruct -mkTable :: (Namespace, TableDecl) -> Q [Dec]-mkTable (_, table) = do+mkTable :: TableDecl -> Q [Dec]+mkTable table = do   let tableName = mkName' $ NC.dataTypeName table   (consSig, cons) <- mkTableConstructor tableName table @@ -499,7 +606,7 @@         VBool            -> mkFunWithBody $ bodyForNonScalar req $ VarE 'readPrimVector `AppE` ConE 'VectorBool         VString          -> mkFunWithBody $ bodyForNonScalar req $ VarE 'readPrimVector `AppE` ConE 'VectorText         VEnum _ enumType -> mkFunForVector req (enumTypeToVectorElementType enumType)-        VStruct _        -> mkFunWithBody $ bodyForNonScalar req $ VarE 'readStructVector+        VStruct _        -> mkFunWithBody $ bodyForNonScalar req $ VarE 'readPrimVector `AppE` ConE 'VectorStruct         VTable _         -> mkFunWithBody $ bodyForNonScalar req $ VarE 'readTableVector         VUnion (TypeRef ns ident) ->           mkFunWithBody $@@ -544,9 +651,9 @@         , defaultValExp         ] -mkUnion :: (Namespace, UnionDecl) -> Q [Dec]-mkUnion (_, union) = do-  unionName <- newName' $ NC.dataTypeName union+mkUnion :: UnionDecl -> Q [Dec]+mkUnion union = do+  let unionName = mkName' $ NC.dataTypeName union   let unionValNames = unionVals union <&> \unionVal ->         mkName $ T.unpack $ NC.enumUnionMember union unionVal @@ -840,9 +947,20 @@ stringLitE :: Text -> Exp stringLitE t = LitE (StringL (T.unpack t)) +inlinePragma :: Name -> Dec+inlinePragma funName = PragmaD $ InlineP funName Inline FunLike AllPhases+ -- | Applies a function to multiple arguments. Assumes the list is not empty. app :: [Exp] -> Exp app = foldl1 AppE  compose :: [Exp] -> Exp compose = foldr1 (\e1 e2 -> InfixE (Just e1) (VarE '(.)) (Just e2))+++nonEmptyUnzip3 :: NonEmpty (a,b,c) -> (NonEmpty a, NonEmpty b, NonEmpty c)+nonEmptyUnzip3 xs =+  ( (\(x, _, _) -> x) <$> xs+  , (\(_, x, _) -> x) <$> xs+  , (\(_, _, x) -> x) <$> xs+  )
src/FlatBuffers/Internal/Compiler/ValidSyntaxTree.hs view
@@ -28,7 +28,7 @@   , UnionVal(..)   ) where -import           Data.Int+import           Data.Bits                                ( Bits ) import           Data.List.NonEmpty                       ( NonEmpty ) import           Data.Scientific                          ( Scientific ) import           Data.String                              ( IsString(..) )@@ -53,6 +53,7 @@ data EnumDecl = EnumDecl   { enumIdent     :: !Ident   , enumType      :: !EnumType+  , enumBitFlags  :: !Bool   , enumVals      :: !(NonEmpty EnumVal)   } deriving (Show, Eq) @@ -111,7 +112,7 @@ ------------ Tables -------------- ---------------------------------- newtype DefaultVal a = DefaultVal a-  deriving newtype (Eq, Show, Num, IsString, Ord, Enum, Real, Integral, Fractional)+  deriving newtype (Eq, Show, Num, IsString, Ord, Enum, Real, Integral, Fractional, Bits)  data Required = Req | Opt   deriving (Eq, Show)@@ -135,14 +136,14 @@   } deriving (Eq, Show)  data TableFieldType-  = TInt8   !(DefaultVal Int8)-  | TInt16  !(DefaultVal Int16)-  | TInt32  !(DefaultVal Int32)-  | TInt64  !(DefaultVal Int64)-  | TWord8  !(DefaultVal Word8)-  | TWord16 !(DefaultVal Word16)-  | TWord32 !(DefaultVal Word32)-  | TWord64 !(DefaultVal Word64)+  = TInt8   !(DefaultVal Integer)+  | TInt16  !(DefaultVal Integer)+  | TInt32  !(DefaultVal Integer)+  | TInt64  !(DefaultVal Integer)+  | TWord8  !(DefaultVal Integer)+  | TWord16 !(DefaultVal Integer)+  | TWord32 !(DefaultVal Integer)+  | TWord64 !(DefaultVal Integer)   | TFloat  !(DefaultVal Scientific)   | TDouble !(DefaultVal Scientific)   | TBool   !(DefaultVal Bool)
src/FlatBuffers/Internal/Read.hs view
@@ -41,9 +41,8 @@ import           FlatBuffers.Internal.Constants import           FlatBuffers.Internal.FileIdentifier ( FileIdentifier(..), HasFileIdentifier(..) ) import           FlatBuffers.Internal.Types-import           FlatBuffers.Internal.Util           ( Positive, positive ) -import           Prelude                             hiding ( length )+import           Prelude                             hiding ( drop, length, take )  type ReadError = String @@ -68,7 +67,6 @@   { structPos :: Position   } - -- | A union that is being read from a flatbuffer. data Union a   = Union !a@@ -129,15 +127,21 @@         BSL.drop uoffsetSize $           bs +-- | Proof that a number is strictly positive.+newtype Positive a = Positive { getPositive :: a }+  deriving newtype (Eq, Show) +{-# INLINE positive #-}+positive :: (Num a, Ord a) => a -> Maybe (Positive a)+positive n = if n > 0 then Just (Positive n) else Nothing+ ---------------------------------- ------------ Vectors ------------- ---------------------------------- {-# INLINE moveToElem #-} moveToElem :: HasPosition pos => pos -> Int32 -> Int32 -> pos moveToElem pos elemSize ix =-  let elemOffset = int32Size + (ix * elemSize)-  in  move pos elemOffset+  move pos (ix * elemSize)  {-# INLINE checkIndexBounds #-} checkIndexBounds :: Int32 -> Int32 -> Int32@@ -147,181 +151,238 @@   | otherwise    = ix  {-# INLINE inlineVectorToList #-}-inlineVectorToList :: HasPosition pos => Get a -> pos -> Either ReadError [a]-inlineVectorToList get (getPosition -> pos) =-  flip runGet pos $ do-    len <- G.getInt32le+inlineVectorToList :: Get a -> Int32 -> Position -> Either ReadError [a]+inlineVectorToList get len pos =+  runGet pos $     sequence $ L.replicate (fromIntegral @Int32 @Int len) get +-- | @clamp n upperBound@ truncates a value to stay between @0@ and @upperBound@.+clamp :: Int32 -> Int32 -> Int32+clamp n upperBound = n `min` upperBound `max` 0+ class VectorElement a where    -- | A vector that is being read from a flatbuffer.   data Vector a    -- | Returns the size of the vector.-  length :: Vector a -> Either ReadError Int32--  -- | Returns the item at the given index.-  -- If the given index is negative or too large, an `error` is thrown.-  index :: Vector a -> Int32 -> Either ReadError a-  index vec ix = unsafeIndex vec . checkIndexBounds ix =<< length vec+  --+  -- /O(1)/.+  length :: Vector a -> Int32    -- | Returns the item at the given index without performing the bounds check.   --   -- Given an invalid index, @unsafeIndex@ will likely read garbage data or return a `ReadError`.   -- In the case of @Vector Word8@, using a negative index carries the same risks as `BSU.unsafeIndex`   -- (i.e. reading from outside the buffer's  boundaries).+  --+  -- /O(c)/, where /c/ is the number of chunks in the underlying `ByteString`.   unsafeIndex :: Vector a -> Int32 -> Either ReadError a    -- | Converts the vector to a list.+  --+  -- /O(n)/.   toList :: Vector a -> Either ReadError [a] +  -- | @take n xs@ returns the prefix of @xs@ of length @n@, or @xs@ itself if @n > length xs@.+  --+  -- /O(1)/.+  --+  -- @since 0.2.0.0+  take :: Int32 -> Vector a -> Vector a +  -- | @drop n xs@ returns the suffix of @xs@ after the first @n@ elements, or @[]@ if @n > length xs@.+  --+  -- /O(c)/, where /c/ is the number of chunks in the underlying `ByteString`.+  --+  -- @since 0.2.0.0+  drop :: Int32 -> Vector a -> Vector a++-- | Returns the item at the given index.+-- If the given index is negative or too large, an `error` is thrown.+--+-- /O(c)/, where /c/ is the number of chunks in the underlying `ByteString`.+index :: VectorElement a => Vector a -> Int32 -> Either ReadError a+index vec ix = unsafeIndex vec . checkIndexBounds ix $ length vec++-- | Convert the vector to a lazy `ByteString`.+--+-- /O(c)/, where /c/ is the number of chunks in the underlying `ByteString`.+--+-- @since 0.2.0.0+toByteString :: Vector Word8 -> ByteString+toByteString (VectorWord8 len pos) =+  BSL.take (fromIntegral @Int32 @Int64 len) pos++ instance VectorElement Word8 where-  newtype Vector Word8 = VectorWord8 Position-    deriving newtype HasPosition+  data Vector Word8 = VectorWord8 !Int32 !Position -  length = readInt32-  index vec ix = byteStringSafeIndex (coerce vec) . (+ int32Size) . checkIndexBounds ix =<< length vec-  unsafeIndex vec ix = byteStringSafeIndex (coerce vec) (int32Size + ix)-  toList vec =-    length vec <&> \len ->-      BSL.unpack $-        BSL.take (fromIntegral @Int32 @Int64 len) $-          BSL.drop int32Size-            (coerce vec)+  length (VectorWord8 len _)      = len+  unsafeIndex (VectorWord8 _ pos) = byteStringSafeIndex pos+  take n (VectorWord8 len pos)    = VectorWord8 (clamp n len) pos+  drop n (VectorWord8 len pos)    = VectorWord8 (clamp (len - n) len) (BSL.drop (fromIntegral @Int32 @Int64 n) pos)+  toList                          = Right . BSL.unpack . toByteString  instance VectorElement Word16 where-  newtype Vector Word16 = VectorWord16 Position-    deriving newtype HasPosition+  data Vector Word16 = VectorWord16 !Int32 !Position -  length = readInt32-  unsafeIndex vec = readWord16 . moveToElem vec word16Size-  toList = inlineVectorToList G.getWord16le+  length (VectorWord16 len _)      = len+  unsafeIndex (VectorWord16 _ pos) = readWord16 . moveToElem pos word16Size+  take n (VectorWord16 len pos)    = VectorWord16 (clamp n len) pos+  drop n (VectorWord16 len pos)    = VectorWord16 (len - n') (moveToElem pos word16Size n')+    where n' = clamp n len+  toList (VectorWord16 len pos)    = inlineVectorToList G.getWord16le len pos  instance VectorElement Word32 where-  newtype Vector Word32 = VectorWord32 Position-    deriving newtype HasPosition+  data Vector Word32 = VectorWord32 !Int32 !Position -  length = readInt32-  unsafeIndex vec = readWord32 . moveToElem vec word32Size-  toList = inlineVectorToList G.getWord32le+  length (VectorWord32 len _)      = len+  unsafeIndex (VectorWord32 _ pos) = readWord32 . moveToElem pos word32Size+  take n (VectorWord32 len pos)    = VectorWord32 (clamp n len) pos+  drop n (VectorWord32 len pos)    = VectorWord32 (len - n') (moveToElem pos word32Size n')+    where n' = clamp n len+  toList (VectorWord32 len pos)    = inlineVectorToList G.getWord32le len pos  instance VectorElement Word64 where-  newtype Vector Word64 = VectorWord64 Position-    deriving newtype HasPosition+  data Vector Word64 = VectorWord64 !Int32 !Position -  length = readInt32-  unsafeIndex vec = readWord64 . moveToElem vec word64Size-  toList = inlineVectorToList G.getWord64le+  length (VectorWord64 len _)      = len+  unsafeIndex (VectorWord64 _ pos) = readWord64 . moveToElem pos word64Size+  take n (VectorWord64 len pos)    = VectorWord64 (clamp n len) pos+  drop n (VectorWord64 len pos)    = VectorWord64 (len - n') (moveToElem pos word64Size n')+    where n' = clamp n len+  toList (VectorWord64 len pos)    = inlineVectorToList G.getWord64le len pos  instance VectorElement Int8 where-  newtype Vector Int8 = VectorInt8 Position-    deriving newtype HasPosition+  data Vector Int8 = VectorInt8 !Int32 !Position -  length = readInt32-  unsafeIndex vec = readInt8 . moveToElem vec int8Size-  toList = inlineVectorToList G.getInt8+  length (VectorInt8 len _)        = len+  unsafeIndex (VectorInt8 _ pos)   = readInt8 . moveToElem pos int8Size+  take n (VectorInt8 len pos)      = VectorInt8 (clamp n len) pos+  drop n (VectorInt8 len pos)      = VectorInt8 (len - n') (moveToElem pos int8Size n')+    where n' = clamp n len+  toList (VectorInt8 len pos)      = inlineVectorToList G.getInt8 len pos  instance VectorElement Int16 where-  newtype Vector Int16 = VectorInt16 Position-    deriving newtype HasPosition+  data Vector Int16 = VectorInt16 !Int32 !Position -  length = readInt32-  unsafeIndex vec = readInt16 . moveToElem vec int16Size-  toList = inlineVectorToList G.getInt16le+  length (VectorInt16 len _)       = len+  unsafeIndex (VectorInt16 _ pos)  = readInt16 . moveToElem pos int16Size+  take n (VectorInt16 len pos)     = VectorInt16 (clamp n len) pos+  drop n (VectorInt16 len pos)     = VectorInt16 (len - n') (moveToElem pos int16Size n')+    where n' = clamp n len+  toList (VectorInt16 len pos)     = inlineVectorToList G.getInt16le len pos  instance VectorElement Int32 where-  newtype Vector Int32 = VectorInt32 Position-    deriving newtype HasPosition+  data Vector Int32 = VectorInt32 !Int32 !Position -  length = readInt32-  unsafeIndex vec = readInt32 . moveToElem vec int32Size-  toList = inlineVectorToList G.getInt32le+  length (VectorInt32 len _)       = len+  unsafeIndex (VectorInt32 _ pos)  = readInt32 . moveToElem pos int32Size+  take n (VectorInt32 len pos)     = VectorInt32 (clamp n len) pos+  drop n (VectorInt32 len pos)     = VectorInt32 (len - n') (moveToElem pos int32Size n')+    where n' = clamp n len+  toList (VectorInt32 len pos)     = inlineVectorToList G.getInt32le len pos  instance VectorElement Int64 where-  newtype Vector Int64 = VectorInt64 Position-    deriving newtype HasPosition+  data Vector Int64 = VectorInt64 !Int32 !Position -  length = readInt32-  unsafeIndex vec = readInt64 . moveToElem vec int64Size-  toList = inlineVectorToList G.getInt64le+  length (VectorInt64 len _)       = len+  unsafeIndex (VectorInt64 _ pos)  = readInt64 . moveToElem pos int64Size+  take n (VectorInt64 len pos)     = VectorInt64 (clamp n len) pos+  drop n (VectorInt64 len pos)     = VectorInt64 (len - n') (moveToElem pos int64Size n')+    where n' = clamp n len+  toList (VectorInt64 len pos)     = inlineVectorToList G.getInt64le len pos  instance VectorElement Float where-  newtype Vector Float = VectorFloat Position-    deriving newtype HasPosition+  data Vector Float = VectorFloat !Int32 !Position -  length = readInt32-  unsafeIndex vec = readFloat . moveToElem vec floatSize-  toList = inlineVectorToList G.getFloatle+  length (VectorFloat len _)       = len+  unsafeIndex (VectorFloat _ pos)  = readFloat . moveToElem pos floatSize+  take n (VectorFloat len pos)     = VectorFloat (clamp n len) pos+  drop n (VectorFloat len pos)     = VectorFloat (len - n') (moveToElem pos floatSize n')+    where n' = clamp n len+  toList (VectorFloat len pos)     = inlineVectorToList G.getFloatle len pos  instance VectorElement Double where-  newtype Vector Double = VectorDouble Position-    deriving newtype HasPosition+  data Vector Double = VectorDouble !Int32 !Position -  length = readInt32-  unsafeIndex vec = readDouble . moveToElem vec doubleSize-  toList = inlineVectorToList G.getDoublele+  length (VectorDouble len _)      = len+  unsafeIndex (VectorDouble _ pos) = readDouble . moveToElem pos doubleSize+  take n (VectorDouble len pos)    = VectorDouble (clamp n len) pos+  drop n (VectorDouble len pos)    = VectorDouble (len - n') (moveToElem pos doubleSize n')+    where n' = clamp n len+  toList (VectorDouble len pos)    = inlineVectorToList G.getDoublele len pos  instance VectorElement Bool where-  newtype Vector Bool = VectorBool Position-    deriving newtype HasPosition+  data Vector Bool = VectorBool !Int32 !Position -  length = readInt32-  unsafeIndex vec = readBool . moveToElem vec boolSize-  toList (VectorBool pos) = fmap word8ToBool <$> toList (VectorWord8 pos)+  length (VectorBool len _)      = len+  unsafeIndex (VectorBool _ pos) = readBool . moveToElem pos boolSize+  take n (VectorBool len pos)    = VectorBool (clamp n len) pos+  drop n (VectorBool len pos)    = VectorBool (len - n') (moveToElem pos boolSize n')+    where n' = clamp n len+  toList (VectorBool len pos)    = fmap word8ToBool <$> toList (VectorWord8 len pos)  instance VectorElement Text where-  newtype Vector Text = VectorText Position-  length (VectorText pos) = readInt32 pos-  unsafeIndex (VectorText pos) = readText . moveToElem pos textRefSize+  data Vector Text = VectorText !Int32 !Position +  length (VectorText len _)      = len+  unsafeIndex (VectorText _ pos) = readText . moveToElem pos textRefSize+  take n (VectorText len pos)    = VectorText (clamp n len) pos+  drop n (VectorText len pos)    = VectorText (len - n') (moveToElem pos textRefSize n')+    where n' = clamp n len+   toList :: Vector Text -> Either ReadError [Text]-  toList (VectorText pos) = do-    offsets <- toList (VectorInt32 pos)+  toList (VectorText len pos) = do+    offsets <- inlineVectorToList G.getInt32le len pos     L.reverse <$> go offsets 0 []     where       go :: [Int32] -> Int32 -> [Text] -> Either ReadError [Text]       go [] _ acc = Right acc       go (offset : xs) ix acc = do-        let textPos = move pos (offset + (ix * 4) + 4)-        text <- join $ runGet readText' textPos+        let textPos = move pos (offset + (ix * 4))+        text <- join $ runGet textPos readText'         go xs (ix + 1) (text : acc) -instance VectorElement (Struct a) where-  data Vector (Struct a) = VectorStruct-    { vectorStructStructSize :: !InlineSize-    , vectorStructPos        :: !Position-    }-  length = readInt32 . vectorStructPos-  unsafeIndex (VectorStruct structSize pos) =-    let elemSize = fromIntegral @InlineSize @Int32 structSize-    in Right . readStruct . moveToElem pos elemSize-  toList vec@(VectorStruct structSize pos) =-    length vec <&> \len ->-      go len (move pos (int32Size :: Int64))++instance IsStruct a => VectorElement (Struct a) where+  data Vector (Struct a) = VectorStruct !Int32 !Position++  length (VectorStruct len _)      = len+  unsafeIndex (VectorStruct _ pos) = Right . readStruct . moveToElem pos (fromIntegral (structSizeOf @a))+  take n (VectorStruct len pos)    = VectorStruct (clamp n len) pos+  drop n (VectorStruct len pos)    = VectorStruct (len - n') (moveToElem pos (fromIntegral (structSizeOf @a)) n')+    where n' = clamp n len++  toList (VectorStruct len pos) =+    Right (go len pos)     where       go :: Int32 -> Position -> [Struct a]       go 0 _ = []       go !len pos =         let head = readStruct pos-            tail = go (len - 1) (move pos structSize)+            tail = go (len - 1) (move pos (structSizeOf @a))         in  head : tail  instance VectorElement (Table a) where-  newtype Vector (Table a) = VectorTable PositionInfo-    deriving newtype HasPosition+  data Vector (Table a) = VectorTable !Int32 !PositionInfo -  length = readInt32-  unsafeIndex vec = readTable . coerce . moveToElem vec tableRefSize-  toList (VectorTable vectorPos) = do-    offsets <- toList (VectorInt32 (posCurrent vectorPos))++  length (VectorTable len _)      = len+  unsafeIndex (VectorTable _ pos) = readTable . moveToElem pos tableRefSize+  take n (VectorTable len pos)    = VectorTable (clamp n len) pos+  drop n (VectorTable len pos)    = VectorTable (len - n') (moveToElem pos tableRefSize n')+    where n' = clamp n len++  toList (VectorTable len vectorPos) = do+    offsets <- inlineVectorToList G.getInt32le len (getPosition vectorPos)     go offsets 0     where       go :: [Int32] -> Int32 -> Either ReadError [Table a]       go [] _ = Right []       go (offset : offsets) !ix = do-        let tablePos = move vectorPos (offset + (ix * 4) + 4)+        let tablePos = move vectorPos (offset + (ix * 4))         table <- readTable' tablePos         tables <- go offsets (ix + 1)         pure (table : tables)@@ -347,9 +408,13 @@         tablePos <- readUOffsetAndSkip $ moveToElem valuesPos tableRefSize ix         readElem unionType' tablePos -  toList (VectorUnion typesPos valuesPos readElem) = do+  take n (VectorUnion typesPos valuesPos readElem)     = VectorUnion (take n typesPos) valuesPos readElem+  drop n vec@(VectorUnion typesPos valuesPos readElem) = VectorUnion (drop n typesPos) (moveToElem valuesPos tableRefSize n') readElem+    where n' = clamp n (length vec)++  toList vec@(VectorUnion typesPos valuesPos readElem) = do     unionTypes <- toList typesPos-    offsets <- toList (VectorInt32 (posCurrent valuesPos))+    offsets <- inlineVectorToList G.getInt32le (length vec) (getPosition valuesPos)     go unionTypes offsets 0     where       go :: [Word8] -> [Int32] -> Int32 -> Either ReadError [Union a]@@ -359,7 +424,7 @@           case positive unionType of             Nothing -> Right UnionNone             Just unionType' ->-              let tablePos = move valuesPos (offset + (ix * 4) + 4)+              let tablePos = move valuesPos (offset + (ix * 4))               in  readElem unionType' tablePos         unions <- go unionTypes offsets (ix + 1)         pure (union : unions)@@ -439,43 +504,43 @@ ---------------------------------- {-# INLINE readInt8 #-} readInt8 :: HasPosition a => a -> Either ReadError Int8-readInt8 (getPosition -> pos) = runGet G.getInt8 pos+readInt8 (getPosition -> pos) = runGet pos G.getInt8  {-# INLINE readInt16 #-} readInt16 :: HasPosition a => a -> Either ReadError Int16-readInt16 (getPosition -> pos) = runGet G.getInt16le pos+readInt16 (getPosition -> pos) = runGet pos G.getInt16le  {-# INLINE readInt32 #-} readInt32 :: HasPosition a => a -> Either ReadError Int32-readInt32 (getPosition -> pos) = runGet G.getInt32le pos+readInt32 (getPosition -> pos) = runGet pos G.getInt32le  {-# INLINE readInt64 #-} readInt64 :: HasPosition a => a -> Either ReadError Int64-readInt64 (getPosition -> pos) = runGet G.getInt64le pos+readInt64 (getPosition -> pos) = runGet pos G.getInt64le  {-# INLINE readWord8 #-} readWord8 :: HasPosition a => a -> Either ReadError Word8-readWord8 (getPosition -> pos) = runGet G.getWord8 pos+readWord8 (getPosition -> pos) = runGet pos G.getWord8  {-# INLINE readWord16 #-} readWord16 :: HasPosition a => a -> Either ReadError Word16-readWord16 (getPosition -> pos) = runGet G.getWord16le pos+readWord16 (getPosition -> pos) = runGet pos G.getWord16le  {-# INLINE readWord32 #-} readWord32 :: HasPosition a => a -> Either ReadError Word32-readWord32 (getPosition -> pos) = runGet G.getWord32le pos+readWord32 (getPosition -> pos) = runGet pos G.getWord32le  {-# INLINE readWord64 #-} readWord64 :: HasPosition a => a -> Either ReadError Word64-readWord64 (getPosition -> pos) = runGet G.getWord64le pos+readWord64 (getPosition -> pos) = runGet pos G.getWord64le  {-# INLINE readFloat #-} readFloat :: HasPosition a => a -> Either ReadError Float-readFloat (getPosition -> pos) = runGet G.getFloatle pos+readFloat (getPosition -> pos) = runGet pos G.getFloatle  {-# INLINE readDouble #-} readDouble :: HasPosition a => a -> Either ReadError Double-readDouble (getPosition -> pos) = runGet G.getDoublele pos+readDouble (getPosition -> pos) = runGet pos G.getDoublele  {-# INLINE readBool #-} readBool :: HasPosition a => a -> Either ReadError Bool@@ -486,20 +551,21 @@ word8ToBool 0 = False word8ToBool _ = True + readPrimVector ::-     (Position -> Vector a)+     (Int32 -> Position -> Vector a)   -> PositionInfo   -> Either ReadError (Vector a)-readPrimVector vecConstructor (posCurrent -> pos) =-  vecConstructor <$> readUOffsetAndSkip pos+readPrimVector vecConstructor (posCurrent -> pos) = do+  vecPos <- readUOffsetAndSkip pos+  vecLength <- readInt32 vecPos+  Right $! vecConstructor vecLength (move vecPos (int32Size :: Int64))  readTableVector :: PositionInfo -> Either ReadError (Vector (Table a))-readTableVector pos =-  VectorTable <$> readUOffsetAndSkip pos--readStructVector :: forall a. IsStruct a => PositionInfo -> Either ReadError (Vector (Struct a))-readStructVector (posCurrent -> pos) =-  VectorStruct (structSizeOf @a) <$> readUOffsetAndSkip pos+readTableVector pos = do+  vecPos <- readUOffsetAndSkip pos+  vecLength <- readInt32 vecPos+  Right $! VectorTable vecLength (move vecPos (int32Size :: Int64))  readUnionVector ::      (Positive Word8 -> PositionInfo -> Either ReadError (Union a))@@ -512,14 +578,14 @@     valuesVec <- readUOffsetAndSkip valuesPos     Right $! VectorUnion       typesVec-      valuesVec+      (move valuesVec (int32Size :: Int64))       readUnion  -- | Follow a pointer to the position of a string and read it. {-# INLINE readText #-} readText :: HasPosition a => a -> Either ReadError Text readText (getPosition -> pos) =-  join $ flip runGet pos $ do+  join $ runGet pos $ do     uoffset <- G.getInt32le     -- NOTE: this might overflow in systems where Int has less than 32 bits     G.skip (fromIntegral @Int32 @Int (uoffset - uoffsetSize))@@ -566,7 +632,7 @@ {-# INLINE tableIndexToVOffset #-} tableIndexToVOffset :: Table t -> TableIndex -> Either ReadError (Maybe VOffset) tableIndexToVOffset Table{..} ix =-  flip runGet vtable $ do+  runGet vtable $ do     vtableSize <- G.getWord16le     let vtableIndex = 4 + (unTableIndex ix * 2)     if vtableIndex >= vtableSize@@ -583,8 +649,8 @@   move pos <$> readInt32 pos  {-# INLINE runGet #-}-runGet :: Get a -> ByteString -> Either ReadError a-runGet get bs =+runGet :: ByteString -> Get a -> Either ReadError a+runGet bs get =   case G.runGetOrFail get bs of     Right (_, _, a)  -> Right a     Left (_, _, msg) -> Left msg
src/FlatBuffers/Internal/Types.hs view
@@ -6,7 +6,8 @@  module FlatBuffers.Internal.Types where -import Data.Word+import           Data.Word+import           FlatBuffers.Internal.Compiler.Display ( Display )  -- | Metadata for a struct type. class IsStruct a where@@ -24,5 +25,5 @@ -- E.g., `Data.Int.Int32` are always aligned to 4 bytes. -- This number should always be a power of 2 in the range [1, 16]. newtype Alignment = Alignment { unAlignment :: Word8 }-  deriving newtype (Show, Eq, Num, Enum, Ord, Real, Integral, Bounded)+  deriving newtype (Show, Eq, Num, Enum, Ord, Real, Integral, Bounded, Display) 
− src/FlatBuffers/Internal/Util.hs
@@ -1,35 +0,0 @@-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--module FlatBuffers.Internal.Util where--import           Data.Bits          ( (.&.), Bits )-import           Data.List.NonEmpty ( NonEmpty(..) )--{-# INLINE isPowerOfTwo #-}-isPowerOfTwo :: (Num a, Bits a) => a -> Bool-isPowerOfTwo 0 = False-isPowerOfTwo n = (n .&. (n - 1)) == 0--{-# INLINE roundUpToNearestMultipleOf #-}-roundUpToNearestMultipleOf :: Integral n => n -> n -> n-roundUpToNearestMultipleOf x y =-  case x `rem` y of-    0         -> x-    remainder -> (y - remainder) + x--{-# INLINE nonEmptyUnzip3 #-}-nonEmptyUnzip3 :: NonEmpty (a,b,c) -> (NonEmpty a, NonEmpty b, NonEmpty c)-nonEmptyUnzip3 xs =-  ( (\(x, _, _) -> x) <$> xs-  , (\(_, x, _) -> x) <$> xs-  , (\(_, _, x) -> x) <$> xs-  )---- | Proof that a number is strictly positive.-newtype Positive a = Positive { getPositive :: a }-  deriving newtype (Eq, Show)--{-# INLINE positive #-}-positive :: (Num a, Ord a) => a -> Maybe (Positive a)-positive n = if n > 0 then Just (Positive n) else Nothing
src/FlatBuffers/Internal/Write.hs view
@@ -7,6 +7,8 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UnliftedFFITypes #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}  {-# OPTIONS_HADDOCK not-home #-} @@ -16,14 +18,17 @@  import           Control.Monad.State.Strict +import           Data.Bits                           ( (.&.), complement )+import qualified Data.ByteString                     as BS import           Data.ByteString.Builder             ( Builder ) import qualified Data.ByteString.Builder             as B import qualified Data.ByteString.Lazy                as BSL import           Data.Coerce                         ( coerce )-import qualified Data.Foldable                       as Foldable import           Data.Int import qualified Data.List                           as L import qualified Data.Map.Strict                     as M+import           Data.MonoTraversable                ( Element, MonoFoldable )+import qualified Data.MonoTraversable                as Mono import           Data.Monoid                         ( Sum(..) ) import           Data.Semigroup                      ( Max(..) ) import           Data.Text                           ( Text )@@ -343,56 +348,60 @@   -- If @n@ is smaller than the length of @xs@, all elements of @xs@ will still be written to the buffer,   -- but the client will only be able to read the first @n@ elements.   ---  -- Note: `fromFoldable` asks for the collection's length to be passed in as an argument rather than use @Foldable.length@ because:+  -- Note: `fromMonoFoldable` asks for the collection's length to be passed in as an argument rather than use `Mono.olength` because:   ---  -- 1. @Foldable.length@ is often O(n), and in some use cases there may be a better way to know the collection's length ahead of time.-  -- 2. Calling @Foldable.length@ inside `fromFoldable` can inhibit some fusions which would otherwise be possible.+  -- 1. `Mono.olength` is often O(n), and in some use cases there may be a better way to know the collection's length ahead of time.+  -- 2. Calling `Mono.olength` inside `fromMonoFoldable` can inhibit some fusions which would otherwise be possible.+  --+  -- @since 0.2.0.0     -- Implementer's note:   -- To elaborate on point 2., here's an example.-  -- This version of `fromFoldable` that calls @Foldable.length@ internally:+  -- This version of `fromMonoFoldable` that calls `Mono.olength` internally:   --   -- > encodeUserIds' :: [User] -> BSL.ByteString-  -- > encodeUserIds' = encode . userIdsTable $ fromFoldable (userId <$> users))+  -- > encodeUserIds' = encode . userIdsTable $ fromMonoFoldable (userId <$> users))   -- >-  -- > {-# INLINE fromFoldable #-}-  -- > fromFoldable xs =-  -- >   let length = Foldable.length xs+  -- > {-# INLINE fromMonoFoldable #-}+  -- > fromMonoFoldable xs =+  -- >   let length = Mono.olength xs   -- >       buffer = foldr ... ... xs   -- >   in  ...   --   -- ...prevents `<$>` and `foldr` from being fused, and so it's much slower than when the length is passed in:   --   -- > encodeUserIds :: [User] -> BSL.ByteString-  -- > encodeUserIds = encode . userIdsTable $ fromFoldable (userId <$> users) (fromIntegral (Foldable.length users))+  -- > encodeUserIds = encode . userIdsTable $ fromMonoFoldable (userId <$> users) (fromIntegral (Mono.olength users))   -- >-  -- > {-# INLINE fromFoldable #-}-  -- > fromFoldable xs length =+  -- > {-# INLINE fromMonoFoldable #-}+  -- > fromMonoFoldable xs length =   -- >   let buffer = foldr ... ... xs   -- >   in  ...-  fromFoldable ::-       Foldable f+  fromMonoFoldable ::+       (MonoFoldable mono, Element mono ~ a)     => Int32      -- ^ @n@: the number of elements in @xs@-    -> f a        -- ^ @xs@: a collection+    -> mono       -- ^ @xs@: a collection     -> WriteVector a  -- | Convenience function, equivalent to: ----- > fromFoldable' xs = fromFoldable (fromIntegral (Foldable.length xs)) xs+-- > fromMonoFoldable' xs = fromMonoFoldable (fromIntegral (olength xs)) xs ----- In some cases it may be slower than using `fromFoldable` directly.-{-# INLINE fromFoldable' #-}-fromFoldable' :: WriteVectorElement a => Foldable f => f a -> WriteVector a-fromFoldable' xs = fromFoldable (fromIntegral $ Foldable.length xs) xs+-- In some cases it may be slower than using `fromMonoFoldable` directly.+--+-- @since 0.2.0.0+{-# INLINE fromMonoFoldable' #-}+fromMonoFoldable' :: (WriteVectorElement a, MonoFoldable mono, Element mono ~ a) => mono -> WriteVector a+fromMonoFoldable' xs = fromMonoFoldable (fromIntegral $ Mono.olength xs) xs --- | `fromFoldable` specialized to list+-- | `fromMonoFoldable` specialized to list fromList :: WriteVectorElement a => Int32 -> [a] -> WriteVector a-fromList = fromFoldable+fromList = fromMonoFoldable --- | `fromFoldable'` specialized to list+-- | `fromMonoFoldable'` specialized to list fromList' :: WriteVectorElement a => [a] -> WriteVector a-fromList' = fromFoldable'+fromList' = fromMonoFoldable'  -- | Creates a flatbuffers vector with a single element singleton :: WriteVectorElement a => a -> WriteVector a@@ -403,9 +412,63 @@ empty = fromList 0 []  +newtype FromFoldable f a = FromFoldable (f a)+  deriving newtype Foldable +type instance Element (FromFoldable f a) = a+instance Foldable f => MonoFoldable (FromFoldable f a)++-- | `fromMonoFoldable` for types that implement `Foldable` but not `MonoFoldable`.+fromFoldable :: (WriteVectorElement a, Foldable f) => Int32 -> f a -> WriteVector a+fromFoldable n = fromMonoFoldable n . FromFoldable++-- | `fromMonoFoldable'` for types that implement `Foldable` but not `MonoFoldable`.+fromFoldable' :: (WriteVectorElement a, Foldable f) => f a -> WriteVector a+fromFoldable' = fromMonoFoldable' . FromFoldable++-- | Efficiently creates a vector from a `BS.ByteString`.+-- Large `BS.ByteString`s are inserted directly, but small ones are copied to ensure that the generated chunks are large on average.+--+-- @since 0.2.0.0+fromByteString :: BS.ByteString -> WriteVector Word8+fromByteString bs = WriteVectorWord8 . WriteTableField $ do+  modify' $!+    writeInt32 len . writeByteString . alignTo int32Size len+  uoffsetFromHere+  where+    len = fromIntegral @Int @Int32 (BS.length bs)+    writeByteString fbs =+      fbs+        { builder = B.byteString bs <> builder fbs+        , bufferSize = bufferSize fbs <> Sum len+        }++-- | Efficiently creates a vector from a lazy `BSL.ByteString`.+--  Large chunks of the `BSL.ByteString` are inserted directly, but small ones are copied to ensure that the generated chunks are large on average.+--+-- @since 0.2.0.0+fromLazyByteString :: BSL.ByteString -> WriteVector Word8+fromLazyByteString bs = WriteVectorWord8 . WriteTableField $ do+  modify' $!+    writeInt32 len . writeByteString . alignTo int32Size len+  uoffsetFromHere+  where+    len = fromIntegral @Int64 @Int32 (BSL.length bs)+    writeByteString fbs =+      fbs+        { builder = B.lazyByteString bs <> builder fbs+        , bufferSize = bufferSize fbs <> Sum len+        }+ {-# INLINE inlineVector #-}-inlineVector :: Foldable f => (a -> Builder) -> Alignment -> InlineSize -> Int32 -> f a -> WriteTableField+inlineVector ::+     (MonoFoldable mono, Element mono ~ a)+  => (a -> Builder)+  -> Alignment+  -> InlineSize+  -> Int32+  -> mono+  -> WriteTableField inlineVector build elemAlignment elemSize elemCount elems = WriteTableField $ do   modify' $!     writeInt32 elemCount . writeVec . alignTo (coerce elemAlignment `max` int32Size) vecByteLength@@ -413,7 +476,7 @@   uoffsetFromHere   where     vecByteLength = elemCount * fromIntegral @InlineSize @Int32 elemSize-    vecBuilder = foldr (\a b -> build a <> b) mempty elems+    vecBuilder = Mono.ofoldr (\a b -> build a <> b) mempty elems     writeVec fbs =       fbs         { builder = vecBuilder <> builder fbs@@ -423,86 +486,86 @@ instance WriteVectorElement Word8 where   newtype WriteVector Word8 = WriteVectorWord8 { writeVectorWord8TableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f Word8 -> WriteVector Word8-  fromFoldable n = WriteVectorWord8 . inlineVector B.word8 word8Size word8Size n+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ Word8) => Int32 -> mono -> WriteVector Word8+  fromMonoFoldable n = WriteVectorWord8 . inlineVector B.word8 word8Size word8Size n  instance WriteVectorElement Word16 where   newtype WriteVector Word16 = WriteVectorWord16 { writeVectorWord16TableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f Word16 -> WriteVector Word16-  fromFoldable n = WriteVectorWord16 . inlineVector B.word16LE word16Size word16Size n+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ Word16) => Int32 -> mono -> WriteVector Word16+  fromMonoFoldable n = WriteVectorWord16 . inlineVector B.word16LE word16Size word16Size n  instance WriteVectorElement Word32 where   newtype WriteVector Word32 = WriteVectorWord32 { writeVectorWord32TableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f Word32 -> WriteVector Word32-  fromFoldable n = WriteVectorWord32 . inlineVector B.word32LE word32Size word32Size n+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ Word32) => Int32 -> mono -> WriteVector Word32+  fromMonoFoldable n = WriteVectorWord32 . inlineVector B.word32LE word32Size word32Size n  instance WriteVectorElement Word64 where   newtype WriteVector Word64 = WriteVectorWord64 { writeVectorWord64TableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f Word64 -> WriteVector Word64-  fromFoldable n = WriteVectorWord64 . inlineVector B.word64LE word64Size word64Size n+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ Word64) => Int32 -> mono -> WriteVector Word64+  fromMonoFoldable n = WriteVectorWord64 . inlineVector B.word64LE word64Size word64Size n  instance WriteVectorElement Int8 where   newtype WriteVector Int8 = WriteVectorInt8 { writeVectorInt8TableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f Int8 -> WriteVector Int8-  fromFoldable n = WriteVectorInt8 . inlineVector B.int8 int8Size int8Size n+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ Int8) => Int32 -> mono -> WriteVector Int8+  fromMonoFoldable n = WriteVectorInt8 . inlineVector B.int8 int8Size int8Size n  instance WriteVectorElement Int16 where   newtype WriteVector Int16 = WriteVectorInt16 { writeVectorInt16TableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f Int16 -> WriteVector Int16-  fromFoldable n = WriteVectorInt16 . inlineVector B.int16LE int16Size int16Size n+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ Int16) => Int32 -> mono -> WriteVector Int16+  fromMonoFoldable n = WriteVectorInt16 . inlineVector B.int16LE int16Size int16Size n  instance WriteVectorElement Int32 where   newtype WriteVector Int32 = WriteVectorInt32 { writeVectorInt32TableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f Int32 -> WriteVector Int32-  fromFoldable n = WriteVectorInt32 . inlineVector B.int32LE int32Size int32Size n+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ Int32) => Int32 -> mono -> WriteVector Int32+  fromMonoFoldable n = WriteVectorInt32 . inlineVector B.int32LE int32Size int32Size n  instance WriteVectorElement Int64 where   newtype WriteVector Int64 = WriteVectorInt64 { writeVectorInt64TableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f Int64 -> WriteVector Int64-  fromFoldable n = WriteVectorInt64 . inlineVector B.int64LE int64Size int64Size n+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ Int64) => Int32 -> mono -> WriteVector Int64+  fromMonoFoldable n = WriteVectorInt64 . inlineVector B.int64LE int64Size int64Size n  instance WriteVectorElement Float where   newtype WriteVector Float = WriteVectorFloat { writeVectorFloatTableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f Float -> WriteVector Float-  fromFoldable n = WriteVectorFloat . inlineVector B.floatLE floatSize floatSize n+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ Float) => Int32 -> mono -> WriteVector Float+  fromMonoFoldable n = WriteVectorFloat . inlineVector B.floatLE floatSize floatSize n  instance WriteVectorElement Double where   newtype WriteVector Double = WriteVectorDouble { writeVectorDoubleTableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f Double -> WriteVector Double-  fromFoldable n = WriteVectorDouble . inlineVector B.doubleLE doubleSize doubleSize n+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ Double) => Int32 -> mono -> WriteVector Double+  fromMonoFoldable n = WriteVectorDouble . inlineVector B.doubleLE doubleSize doubleSize n  instance WriteVectorElement Bool where   newtype WriteVector Bool = WriteVectorBool { writeVectorBoolTableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f Bool -> WriteVector Bool-  fromFoldable n = WriteVectorBool . inlineVector (B.word8 . boolToWord8) word8Size word8Size n+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ Bool) => Int32 -> mono -> WriteVector Bool+  fromMonoFoldable n = WriteVectorBool . inlineVector (B.word8 . boolToWord8) word8Size word8Size n  instance IsStruct a => WriteVectorElement (WriteStruct a) where   newtype WriteVector (WriteStruct a) = WriteVectorStruct { writeVectorStructTableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f (WriteStruct a) -> WriteVector (WriteStruct a)-  fromFoldable n = WriteVectorStruct . inlineVector coerce (structAlignmentOf @a) (structSizeOf @a) n+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ WriteStruct a) => Int32 -> mono -> WriteVector (WriteStruct a)+  fromMonoFoldable n = WriteVectorStruct . inlineVector coerce (structAlignmentOf @a) (structSizeOf @a) n   data TextInfos = TextInfos ![TextInfo] {-# UNPACK #-} !BufferSize@@ -522,9 +585,9 @@ instance WriteVectorElement Text where   newtype WriteVector Text = WriteVectorText { writeVectorTextTableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f Text -> WriteVector Text-  fromFoldable elemCount texts = WriteVectorText . WriteTableField $ do+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ Text) => Int32 -> mono -> WriteVector Text+  fromMonoFoldable elemCount texts = WriteVectorText . WriteTableField $ do     modify' $ \fbs ->       let (builder2, bsize2) =             writeVectorSizePrefix . writeOffsets . align . writeStrings $ (builder fbs, bufferSize fbs)@@ -542,7 +605,7 @@           -- we have loops dedicated to merging Builders to avoid wrapping Builders in data structures.           -- See "Performance tips": http://hackage.haskell.org/package/fast-builder-0.1.0.1/docs/Data-ByteString-FastBuilder.html         let TextInfos textInfos bsize2 =-              foldr+              Mono.ofoldr                 (\t (TextInfos infos bsize) ->                   let textLength = utf8length t                       padding = calcPadding 4 (textLength + 1) bsize@@ -606,12 +669,12 @@ instance WriteVectorElement (WriteTable a) where   newtype WriteVector (WriteTable a) = WriteVectorTable { writeVectorTableTableField :: WriteTableField } -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f (WriteTable a) -> WriteVector (WriteTable a)-  fromFoldable elemCount tables = WriteVectorTable . WriteTableField $ do+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ WriteTable a) => Int32 -> mono -> WriteVector (WriteTable a)+  fromMonoFoldable elemCount tables = WriteVectorTable . WriteTableField $ do     fbs1 <- get     let !(TableInfo fbs2 positions) =-          foldr+          Mono.ofoldr             (\(WriteTable writeTable) (TableInfo fbs positions) ->               let (pos, fbs') = runState writeTable fbs               in  TableInfo fbs' (pos : positions)@@ -632,7 +695,7 @@             (OffsetInfo 0 [])             positions -    coerce $ fromFoldable elemCount offsets+    coerce $ fromMonoFoldable elemCount offsets  data Vecs a = Vecs ![Word8] ![Maybe (State FBState Position)] @@ -644,11 +707,11 @@ instance WriteVectorElement (WriteUnion a) where   data WriteVector (WriteUnion a) = WriteVectorUnion !WriteTableField !WriteTableField -  {-# INLINE fromFoldable #-}-  fromFoldable :: Foldable f => Int32 -> f (WriteUnion a) -> WriteVector (WriteUnion a)-  fromFoldable elemCount unions =+  {-# INLINE fromMonoFoldable #-}+  fromMonoFoldable :: (MonoFoldable mono, Element mono ~ WriteUnion a) => Int32 -> mono -> WriteVector (WriteUnion a)+  fromMonoFoldable elemCount unions =     let Vecs types values =-          foldr+          Mono.ofoldr             go             (Vecs [] [])             unions@@ -691,9 +754,9 @@                       (OffsetInfo 0 [])                       positions -              coerce $ fromFoldable elemCount offsets+              coerce $ fromMonoFoldable elemCount offsets -    in  WriteVectorUnion (coerce $ fromFoldable elemCount types) writeUnionTables+    in  WriteVectorUnion (coerce $ fromMonoFoldable elemCount types) writeUnionTables   @@ -702,13 +765,7 @@ {-# INLINE calcPadding #-} calcPadding :: Alignment {- ^ n -} -> Int32 {- ^ additionalBytes -} -> BufferSize -> Int32 calcPadding !n !additionalBytes (Sum size) =-  -- TODO: optimize this: https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Bits.html-  if n == 0-    then 0-    else-      let remainder = (size + additionalBytes) `rem` fromIntegral @Alignment @Int32 n-          needed = if remainder == 0 then 0 else fromIntegral @Alignment @Int32 n - remainder-      in  needed+  (complement (size + additionalBytes) + 1) .&. (fromIntegral n - 1)  -- | Add enough 0-padding so that the buffer becomes aligned to @n@ after writing @additionalBytes@. {-# INLINE alignTo #-}
src/FlatBuffers/Vector.hs view
@@ -7,14 +7,20 @@   (     -- * Creating a vector     W.WriteVectorElement(..)+  , W.fromMonoFoldable'+  , W.fromFoldable   , W.fromFoldable'   , W.fromList   , W.fromList'   , W.singleton   , W.empty+  , W.fromByteString+  , W.fromLazyByteString      -- * Reading a vector   , R.VectorElement(..)+  , R.index+  , R.toByteString   ) where  import           FlatBuffers.Internal.Read  as R
test/Examples/Generated.hs view
@@ -2,7 +2,7 @@  module Examples.Generated where -import           FlatBuffers+import           FlatBuffers ( defaultOptions, mkFlatBuffers )  $(mkFlatBuffers "test/Examples/schema.fbs"           defaultOptions) $(mkFlatBuffers "test/Examples/vector_of_unions.fbs" defaultOptions)
test/Examples/HandWritten.hs view
@@ -2,6 +2,7 @@  module Examples.HandWritten where +import           Data.Bits                           ( (.&.) ) import           Data.Int import           Data.Text                           ( Text ) import           Data.Word@@ -10,7 +11,6 @@ import           FlatBuffers.Internal.FileIdentifier ( HasFileIdentifier(..), unsafeFileIdentifier ) import           FlatBuffers.Internal.Read import           FlatBuffers.Internal.Types-import           FlatBuffers.Internal.Util           ( Positive(getPositive) ) import           FlatBuffers.Internal.Write  ----------------------------------@@ -116,6 +116,16 @@     ColorGray  -> 5     ColorBlack -> 8 +{-# INLINE colorName #-}+colorName :: Color -> Text+colorName c =+  case c of+    ColorRed   -> "Red"+    ColorGreen -> "Green"+    ColorBlue  -> "Blue"+    ColorGray  -> "Gray"+    ColorBlack -> "Black"+ ---------------------------------- ------------- Enums -------------- ----------------------------------@@ -144,7 +154,7 @@ enumsXs = readTableFieldOpt (readPrimVector VectorInt16) 2  enumsYs :: Table Enums -> Either ReadError (Maybe (Vector (Struct StructWithEnum)))-enumsYs = readTableFieldOpt readStructVector 3+enumsYs = readTableFieldOpt (readPrimVector VectorStruct) 3   @@ -169,7 +179,76 @@ structWithEnumZ :: Struct StructWithEnum -> Either ReadError Int8 structWithEnumZ = readStructField readInt8 4 + ----------------------------------+--------- EnumsBitFlags ----------+----------------------------------+colorsRed, colorsGreen, colorsBlue, colorsGray, colorsBlack :: Word16+colorsRed = 1+colorsGreen = 4+colorsBlue = 8+colorsGray = 16+colorsBlack = 32++{-# INLINE allColors #-}+allColors :: [Word16]+allColors = [colorsRed, colorsGreen, colorsBlue, colorsGray, colorsBlack]++{-# INLINE colorsNames #-}+colorsNames :: Word16 -> [Text]+colorsNames c = res5+  where+    res0 = []+    res1 = if colorsBlack .&. c /= 0 then "Black" : res0 else res0+    res2 = if colorsGray  .&. c /= 0 then "Gray"  : res1 else res1+    res3 = if colorsBlue  .&. c /= 0 then "Blue"  : res2 else res2+    res4 = if colorsGreen .&. c /= 0 then "Green" : res3 else res3+    res5 = if colorsRed   .&. c /= 0 then "Red"   : res4 else res4+++data EnumsBitFlags++enumsBitFlags ::+    Maybe Word16+  -> Maybe (WriteStruct StructWithEnumBitFlags)+  -> Maybe (WriteVector Word16)+  -> Maybe (WriteVector (WriteStruct StructWithEnumBitFlags))+  -> WriteTable EnumsBitFlags+enumsBitFlags x y xs ys = writeTable+  [ optionalDef 0 writeWord16TableField x+  , optional writeStructTableField y+  , optional writeVectorWord16TableField xs+  , optional writeVectorStructTableField ys+  ]++enumsBitFlagsX :: Table EnumsBitFlags -> Either ReadError Word16+enumsBitFlagsX = readTableFieldWithDef readWord16 0 0++enumsBitFlagsY :: Table EnumsBitFlags -> Either ReadError (Maybe (Struct StructWithEnumBitFlags))+enumsBitFlagsY = readTableFieldOpt (Right . readStruct) 1++enumsBitFlagsXs :: Table EnumsBitFlags -> Either ReadError (Maybe (Vector Word16))+enumsBitFlagsXs = readTableFieldOpt (readPrimVector VectorWord16) 2++enumsBitFlagsYs :: Table EnumsBitFlags -> Either ReadError (Maybe (Vector (Struct StructWithEnumBitFlags)))+enumsBitFlagsYs = readTableFieldOpt (readPrimVector VectorStruct) 3++++data StructWithEnumBitFlags++instance IsStruct StructWithEnumBitFlags where+  structAlignmentOf = 2+  structSizeOf = 2++structWithEnumBitFlags :: Word16 -> WriteStruct StructWithEnumBitFlags+structWithEnumBitFlags x = WriteStruct $+  buildWord16 x++structWithEnumBitFlagsX :: Struct StructWithEnumBitFlags -> Either ReadError Word16+structWithEnumBitFlagsX = readStructField readWord16 0++---------------------------------- ------------- Structs ------------ ---------------------------------- data Struct1@@ -464,16 +543,16 @@   ]  vectorOfStructsAs :: Table VectorOfStructs -> Either ReadError (Maybe (Vector (Struct Struct1)))-vectorOfStructsAs = readTableFieldOpt readStructVector 0+vectorOfStructsAs = readTableFieldOpt (readPrimVector VectorStruct) 0  vectorOfStructsBs :: Table VectorOfStructs -> Either ReadError (Maybe (Vector (Struct Struct2)))-vectorOfStructsBs = readTableFieldOpt readStructVector 1+vectorOfStructsBs = readTableFieldOpt (readPrimVector VectorStruct) 1  vectorOfStructsCs :: Table VectorOfStructs -> Either ReadError (Maybe (Vector (Struct Struct3)))-vectorOfStructsCs = readTableFieldOpt readStructVector 2+vectorOfStructsCs = readTableFieldOpt (readPrimVector VectorStruct) 2  vectorOfStructsDs :: Table VectorOfStructs -> Either ReadError (Maybe (Vector (Struct Struct4)))-vectorOfStructsDs = readTableFieldOpt readStructVector 3+vectorOfStructsDs = readTableFieldOpt (readPrimVector VectorStruct) 3   ----------------------------------@@ -521,8 +600,12 @@   -> Maybe Bool   -> Maybe Int16   -> Maybe Int16+  -> Maybe Word16+  -> Maybe Word16+  -> Maybe Word16+  -> Maybe Word16   -> WriteTable ScalarsWithDefaults-scalarsWithDefaults a b c d e f g h i j k l m n =+scalarsWithDefaults a b c d e f g h i j k l m n o p q r =   writeTable     [ optionalDef 8 writeWord8TableField          a     , optionalDef 16 writeWord16TableField        b@@ -538,6 +621,10 @@     , optionalDef False writeBoolTableField       l     , optionalDef 1 writeInt16TableField          m     , optionalDef 5 writeInt16TableField          n+    , optionalDef 0 writeWord16TableField         o+    , optionalDef 12 writeWord16TableField        p+    , optionalDef 1 writeWord16TableField         q+    , optionalDef 20 writeWord16TableField        r     ]  scalarsWithDefaultsA :: Table ScalarsWithDefaults -> Either ReadError Word8@@ -554,6 +641,10 @@ scalarsWithDefaultsL :: Table ScalarsWithDefaults -> Either ReadError Bool scalarsWithDefaultsM :: Table ScalarsWithDefaults -> Either ReadError Int16 scalarsWithDefaultsN :: Table ScalarsWithDefaults -> Either ReadError Int16+scalarsWithDefaultsO :: Table ScalarsWithDefaults -> Either ReadError Word16+scalarsWithDefaultsP :: Table ScalarsWithDefaults -> Either ReadError Word16+scalarsWithDefaultsQ :: Table ScalarsWithDefaults -> Either ReadError Word16+scalarsWithDefaultsR :: Table ScalarsWithDefaults -> Either ReadError Word16 scalarsWithDefaultsA = readTableFieldWithDef readWord8   0 8 scalarsWithDefaultsB = readTableFieldWithDef readWord16  1 16 scalarsWithDefaultsC = readTableFieldWithDef readWord32  2 32@@ -568,6 +659,10 @@ scalarsWithDefaultsL = readTableFieldWithDef readBool    11 False scalarsWithDefaultsM = readTableFieldWithDef readInt16   12 1 scalarsWithDefaultsN = readTableFieldWithDef readInt16   13 5+scalarsWithDefaultsO = readTableFieldWithDef readWord16  14 0+scalarsWithDefaultsP = readTableFieldWithDef readWord16  15 12+scalarsWithDefaultsQ = readTableFieldWithDef readWord16  16 1+scalarsWithDefaultsR = readTableFieldWithDef readWord16  17 20   ----------------------------------
test/FlatBuffers/AlignmentSpec.hs view
@@ -180,7 +180,7 @@   padding `isLessThan` fromIntegral alignment    -- The buffer is aligned to `alignment` bytes-  getSum (bufferSize finalState) `mod` fromIntegral alignment === 0+  bufferSize finalState `isAlignedTo` fromIntegral alignment   prop_inlineVectorAlignment ::
test/FlatBuffers/Integration/RoundTripThroughFlatcSpec.hs view
@@ -12,8 +12,9 @@  import           Control.Applicative  ( liftA3 ) -import           Data.Aeson           ( (.=), Value(..), object )+import           Data.Aeson           ( (.=), Value(..), object, toJSON ) import qualified Data.Aeson           as J+import           Data.Bits            ( (.|.) ) import qualified Data.ByteString.Lazy as BSL import           Data.Int import           Data.Maybe           ( isNothing )@@ -216,6 +217,74 @@         enumsXs decoded `shouldBeRightAnd` isNothing         enumsYs decoded `shouldBeRightAnd` isNothing +    describe "Enums with bit_flags" $ do+      it "present" $ do+        (json, decoded) <- flatc $ enumsBitFlags+          (Just (colorsRed .|. colorsGreen))+          (Just (structWithEnumBitFlags (colorsGreen .|. colorsGray)))+          (Just (Vec.fromList'+            [ colorsGreen .|. colorsGray+            , colorsBlack .|. colorsBlue+            , colorsGreen+            ]))+          (Just (Vec.fromList'+            [ structWithEnumBitFlags (colorsGreen .|. colorsGray)+            , structWithEnumBitFlags (colorsBlack .|. colorsBlue)+            , structWithEnumBitFlags colorsGreen+            ]))++        json `shouldBeJson` object+          [ "x" .= (colorsRed .|. colorsGreen)+          , "y" .= object [ "x" .= (colorsGreen .|. colorsGray) ]+          , "xs" .=+            [ toJSON (colorsGreen .|. colorsGray)+            , toJSON (colorsBlack .|. colorsBlue)+            , String "Green"+            ]+          , "ys" .=+            [ object [ "x" .= (colorsGreen .|. colorsGray) ]+            , object [ "x" .= (colorsBlack .|. colorsBlue) ]+            , object [ "x" .= String "Green" ]+            ]+          ]++        enumsBitFlagsX decoded `shouldBe` Right (colorsRed .|. colorsGreen)+        (enumsBitFlagsY decoded >>= traverse structWithEnumBitFlagsX) `shouldBe` Right (Just (colorsGreen .|. colorsGray))+        (enumsBitFlagsXs decoded >>= traverse Vec.toList) `shouldBe` Right (Just+          [ colorsGreen .|. colorsGray+          , colorsBlack .|. colorsBlue+          , colorsGreen+          ])+        (enumsBitFlagsYs decoded >>= traverse Vec.toList >>= traverse (traverse structWithEnumBitFlagsX)) `shouldBe` Right (Just+          [ colorsGreen .|. colorsGray+          , colorsBlack .|. colorsBlue+          , colorsGreen+          ])++      it "present with defaults" $ do+        (json, decoded) <- flatc $ enumsBitFlags+          (Just 0)+          Nothing+          Nothing+          Nothing++        json `shouldBeJson` object [ ]++        enumsBitFlagsX decoded `shouldBe` Right 0+        enumsBitFlagsY decoded `shouldBeRightAnd` isNothing+        enumsBitFlagsXs decoded `shouldBeRightAnd` isNothing+        enumsBitFlagsYs decoded `shouldBeRightAnd` isNothing++      it "missing" $ do+        (json, decoded) <- flatc $ enumsBitFlags Nothing Nothing Nothing Nothing++        json `shouldBeJson` object [ ]++        enumsBitFlagsX decoded `shouldBe` Right 0+        enumsBitFlagsY decoded `shouldBeRightAnd` isNothing+        enumsBitFlagsXs decoded `shouldBeRightAnd` isNothing+        enumsBitFlagsYs decoded `shouldBeRightAnd` isNothing+     describe "Structs" $ do       it "present" $ do         let readStruct1 = (liftA3 . liftA3) (,,) struct1X struct1Y struct1Z@@ -458,7 +527,7 @@         json `shouldBeJson` object [ "xs" .= [] @Value]          xs <- evalRightJust $ vectorOfTablesXs decoded-        Vec.length xs `shouldBe` Right 0+        Vec.length xs `shouldBe` 0        it "missing" $ do         (json, decoded) <- flatc $ vectorOfTables Nothing@@ -522,10 +591,10 @@         bs <- evalRightJust $ vectorOfStructsBs decoded         cs <- evalRightJust $ vectorOfStructsCs decoded         ds <- evalRightJust $ vectorOfStructsCs decoded-        Vec.length as `shouldBe` Right 0-        Vec.length bs `shouldBe` Right 0-        Vec.length cs `shouldBe` Right 0-        Vec.length ds `shouldBe` Right 0+        Vec.length as `shouldBe` 0+        Vec.length bs `shouldBe` 0+        Vec.length cs `shouldBe` 0+        Vec.length ds `shouldBe` 0        it "missing" $ do         (json, decoded) <- flatc $ vectorOfStructs Nothing Nothing Nothing Nothing@@ -558,14 +627,23 @@             scalarsWithDefaultsL decoded `shouldBe` Right False             toColor <$> scalarsWithDefaultsM decoded `shouldBe` Right (Just ColorBlue)             toColor <$> scalarsWithDefaultsN decoded `shouldBe` Right (Just ColorGray)+            scalarsWithDefaultsO decoded `shouldBe` Right 0+            scalarsWithDefaultsP decoded `shouldBe` Right (colorsGreen .|. colorsBlue)+            scalarsWithDefaultsQ decoded `shouldBe` Right colorsRed+            scalarsWithDefaultsR decoded `shouldBe` Right (colorsGreen .|. colorsGray)        it "present with defaults" $ runTest $ scalarsWithDefaults         (Just 8) (Just 16) (Just 32) (Just 64)         (Just (-1)) (Just (-2)) (Just (-4)) (Just (-8))         (Just 3.9) (Just (-2.3e10)) (Just True) (Just False)         (Just (fromColor ColorBlue)) (Just (fromColor ColorGray))+        (Just 0)+        (Just (colorsGreen .|. colorsBlue))+        (Just colorsRed)+        (Just (colorsGreen .|. colorsGray))        it "missing" $ runTest $ scalarsWithDefaults+        Nothing Nothing Nothing Nothing         Nothing Nothing Nothing Nothing         Nothing Nothing Nothing Nothing         Nothing Nothing Nothing Nothing
test/FlatBuffers/Internal/Compiler/ParserSpec.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE NegativeLiterals #-}  module FlatBuffers.Internal.Compiler.ParserSpec where @@ -19,237 +20,281 @@ spec :: Spec spec =   describe "Parser" $ do-    describe "include" $ do-      it "parses correctly" $-        parseEof include [r|include "abc";|] `shouldParse` "abc"-      it "parses strings with semicolons" $-        parseEof include [r|include "abc;";|] `shouldParse` "abc;"-      it "parses escaped strings" $ do-        parseEof include [r|include "abc \" " ;|] `shouldParse` "abc \" "-        parseEof include [r|include "abc \" escaped \" rest" ;|] `shouldParse` "abc \" escaped \" rest"-      describe "fails to parse" $ do-        it "unmatched quotes" $-          parseEof include "include \"abc;" `shouldFailWithError` "unexpected end of input\nexpecting '\"' or literal character\n"-        it "more than one string constant" $-          parseEof include "include \"abc\" \"def\";" `shouldFailWithError` "unexpected '\"'\nexpecting ';'\n"-        it "if there's no semicolon" $-          parseEof include "include \"abc\"" `shouldFailWithError` "unexpected end of input\nexpecting ';'\n"-    describe "schema" $ do-      it "empty schema" $-        [r||] `parses` Schema [] []+    it "empty schema" $+      [r||] `parses` Schema [] [] -      it "includes" $-        [r|-          include "somefile";-          include "other \"escaped\" File";-        |] `parses` Schema ["somefile", "other \"escaped\" File"] []+    it "includes" $+      [r|+        include "somefile";+        include "other \"escaped\" File" ;+      |] `parses` Schema ["somefile", "other \"escaped\" File"] [] -      it "includes" $-        [r|-          include "a";-          native_include "b";-          include "c";-          native_include "d";-          include "e";-        |] `parses` Schema ["a", "c", "e"] []+    it "includes" $+      [r|+        include "a";+        native_include "b";+        include "c";+        native_include "d";+        include "e";+      |] `parses` Schema ["a", "c", "e"] [] -      it "namespaces" $-        [r|-          include "somefile";-          namespace Ns;-          namespace My . Api . Domain;-          namespace My.Api.Domain2;-          namespace ;-        |] `parses`-          Schema-            ["somefile"]-            [ DeclN "Ns"-            , DeclN "My.Api.Domain"-            , DeclN "My.Api.Domain2"-            , DeclN ""-            ]+    it "namespaces" $+      [r|+        include "somefile";+        namespace Ns;+        namespace My . Api . Domain;+        namespace My.Api.Domain2;+        namespace ;+      |] `parses`+        Schema+          ["somefile"]+          [ DeclN "Ns"+          , DeclN "My.Api.Domain"+          , DeclN "My.Api.Domain2"+          , DeclN ""+          ] -      it "table declarations" $-        [r|-          table T {}+    it "table declarations" $+      [r|+        table T {} -          table ATable {-            abc : bool;-            b1 : bool = true;-            b2 : bool = false;-            d : Ref = 123;-            d : uint;-            d : uint_;-            d : X.uint;-            d : X.uint_;-            e : [uint] = - 99.2e9 ;-            e : [uint] = 99992873786287637862.298736756627897654e99 ;-            f : [uint_];-            g : My . Api . Ref = 123;-            h : [ MyApi.abc_ ] ;-            i: Color = Blue ;-          }-        |] `parses`-          Schema-            []-            [ DeclT $ TableDecl "T" (Metadata mempty) []-            , DeclT $ TableDecl "ATable" (Metadata mempty)-              [ TableField "abc" TBool Nothing (Metadata mempty)-              , TableField "b1" TBool (Just (DefaultBool True)) (Metadata mempty)-              , TableField "b2" TBool (Just (DefaultBool False)) (Metadata mempty)-              , TableField "d" (TRef (TypeRef "" "Ref")) (Just (DefaultNum 123)) (Metadata mempty)-              , TableField "d" TWord32 Nothing (Metadata mempty)-              , TableField "d" (TRef (TypeRef "" "uint_")) Nothing (Metadata mempty)-              , TableField "d" (TRef (TypeRef "X" "uint")) Nothing (Metadata mempty)-              , TableField "d" (TRef (TypeRef "X" "uint_")) Nothing (Metadata mempty)-              , TableField "e" (TVector TWord32) (Just (DefaultNum (-99.2e9))) (Metadata mempty)-              , TableField "e" (TVector TWord32) (Just (DefaultNum 99992873786287637862.298736756627897654e99)) (Metadata mempty)-              , TableField "f" (TVector (TRef (TypeRef "" "uint_"))) Nothing (Metadata mempty)-              , TableField "g" (TRef (TypeRef "My.Api" "Ref")) (Just (DefaultNum 123)) (Metadata mempty)-              , TableField "h" (TVector (TRef (TypeRef "MyApi" "abc_"))) Nothing (Metadata mempty)-              , TableField "i" (TRef (TypeRef "" "Color")) (Just (DefaultRef "Blue")) (Metadata mempty)-              ]+        table ATable {+          abc : bool;+          b1 : bool = true;+          b2 : bool = false;+          d : Ref;+          d : uint;+          d : uint_;+          d : X.uint;+          d : X.uint_;+          e : [uint] ;+          f : [uint_];+          g : My . Api . Ref ;+          h : [ MyApi.abc_ ] ;+          i: Color;+        }+      |] `parses`+        Schema+          []+          [ DeclT $ TableDecl "T" (Metadata mempty) []+          , DeclT $ TableDecl "ATable" (Metadata mempty)+            [ TableField "abc" TBool Nothing (Metadata mempty)+            , TableField "b1" TBool (Just (DefaultBool True)) (Metadata mempty)+            , TableField "b2" TBool (Just (DefaultBool False)) (Metadata mempty)+            , TableField "d" (TRef (TypeRef "" "Ref")) Nothing (Metadata mempty)+            , TableField "d" TWord32 Nothing (Metadata mempty)+            , TableField "d" (TRef (TypeRef "" "uint_")) Nothing (Metadata mempty)+            , TableField "d" (TRef (TypeRef "X" "uint")) Nothing (Metadata mempty)+            , TableField "d" (TRef (TypeRef "X" "uint_")) Nothing (Metadata mempty)+            , TableField "e" (TVector TWord32) Nothing (Metadata mempty)+            , TableField "f" (TVector (TRef (TypeRef "" "uint_"))) Nothing (Metadata mempty)+            , TableField "g" (TRef (TypeRef "My.Api" "Ref")) Nothing (Metadata mempty)+            , TableField "h" (TVector (TRef (TypeRef "MyApi" "abc_"))) Nothing (Metadata mempty)+            , TableField "i" (TRef (TypeRef "" "Color")) Nothing (Metadata mempty)             ]+          ] -      it "struct declarations" $-        [r|-          struct AStruct {-            abc : bool;-            d : Ref ;-            e : [uint] ;-            f : [uint_];-            g : My . Api . Ref ;-            h : [ MyApi.abc_ ] ;-          }-        |] `parses`-          Schema-            []-            [ DeclS $ StructDecl "AStruct" (Metadata mempty)-              [ StructField "abc" TBool (Metadata mempty)-              , StructField "d" (TRef (TypeRef "" "Ref")) (Metadata mempty)-              , StructField "e" (TVector TWord32) (Metadata mempty)-              , StructField "f" (TVector (TRef (TypeRef "" "uint_"))) (Metadata mempty)-              , StructField "g" (TRef (TypeRef "My.Api" "Ref")) (Metadata mempty)-              , StructField "h" (TVector (TRef (TypeRef "MyApi" "abc_"))) (Metadata mempty)-              ]+    it "table declarations with default values" $+      [r|+        table T {+          a : int = true;+          a : int=true ;+          a : int = "true";+          a : int = " true ";+          a : int = false;+          a : int = "false";+          a : int = 123;+          a : int = "123";+          a : int = -123;+          a : int = "-123";+          a : int = " -123 ";+          a : int = -99.2e9;+          a : int = - 99.2e9;+          a : int = "-99.2e9";+          a : int = " -99.2e9 ";+          a : int = 99992873786287637862.298736756627897654e99;+          a : int = Red;+          a : int = "Red";+          a : int = " Red ";+          a : int = " Red Blue ";+          a : int = "Red\nBlue";+          a : int = "Red 4";+          a : int = "1 4";+        }+      |] `parses`+        Schema+          []+          [ DeclT $ TableDecl "T" (Metadata mempty)+            [ TableField "a" TInt32 (Just (DefaultBool True)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultBool True)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultBool True)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultBool True)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultBool False)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultBool False)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultNum 123)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultNum 123)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultNum -123)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultNum -123)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultNum -123)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultNum -99.2e9)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultNum -99.2e9)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultNum -99.2e9)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultNum -99.2e9)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultNum 99992873786287637862.298736756627897654e99)) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultRef ["Red"])) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultRef ["Red"])) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultRef ["Red"])) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultRef ["Red", "Blue"])) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultRef ["Red", "Blue"])) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultRef ["Red", "4"])) (Metadata mempty)+            , TableField "a" TInt32 (Just (DefaultRef ["1", "4"])) (Metadata mempty)             ]+          ] -      it "table declarations with metadata" $-        [r|-          table ATable ( a , "b" : 9283 , c : "attr" ) {-            abc : bool = 99 ( def ) ;-          }-        |] `parses`-          Schema-            []-            [ DeclT $ TableDecl "ATable"-              (Metadata-                [ ("a", Nothing)-                , ("b", Just (AttrI 9283))-                , ("c", Just (AttrS "attr"))-                ]-              )-              (pure (TableField "abc" TBool (Just (DefaultNum 99)) (Metadata [("def", Nothing)])))-            ]+    it "default value cannot be an empty string" $ do+      parseEof schema [r| table T { a:int = "";     } |] `shouldFailWithError` "Expected 'true', 'false', a number, or one or more identifiers\n"+      parseEof schema [r| table T { a:int = " ";    } |] `shouldFailWithError` "Expected 'true', 'false', a number, or one or more identifiers\n"+      parseEof schema [r| table T { a:int = " \n "; } |] `shouldFailWithError` "Expected 'true', 'false', a number, or one or more identifiers\n" -      it "enum declarations" $-        [r|-          enum Color : short (attr) {-            Red,-            Blue = 18446744073709551615,-            Gray = -18446744073709551615,-            Black-          }-        |] `parses`-          Schema-            []-            [DeclE $ EnumDecl "Color" TInt16 (Metadata [("attr", Nothing)])-              [ EnumVal "Red" Nothing-              , EnumVal "Blue" (Just 18446744073709551615)-              , EnumVal "Gray" (Just (-18446744073709551615))-              , EnumVal "Black" Nothing+    it "table declarations with metadata" $+      [r|+        table ATable ( a , "b" : 9283 , c : "attr" ) {+          abc : bool = 99 ( def ) ;+        }+      |] `parses`+        Schema+          []+          [ DeclT $ TableDecl "ATable"+            (Metadata+              [ ("a", Nothing)+              , ("b", Just (AttrI 9283))+              , ("c", Just (AttrS "attr"))               ]-            ]+            )+            (pure (TableField "abc" TBool (Just (DefaultNum 99)) (Metadata [("def", Nothing)])))+          ] -      it "union declarations" $-        [r|-          union Weapon ( attr ) {-            Sword,-            mace: Stick,-            mace2: My.Api.Stick,-            Axe-          }-        |] `parses`-          Schema-            []-            [ DeclU $ UnionDecl-                "Weapon"-                (Metadata [("attr", Nothing)])-                [ UnionVal Nothing (TypeRef "" "Sword")-                , UnionVal (Just "mace") (TypeRef "" "Stick")-                , UnionVal (Just "mace2") (TypeRef "My.Api" "Stick")-                , UnionVal Nothing (TypeRef "" "Axe")-                ]+    it "struct declarations" $+      [r|+        struct AStruct {+          abc : bool;+          d : Ref ;+          e : [uint] ;+          f : [uint_];+          g : My . Api . Ref ;+          h : [ MyApi.abc_ ] ;+        }+      |] `parses`+        Schema+          []+          [ DeclS $ StructDecl "AStruct" (Metadata mempty)+            [ StructField "abc" TBool (Metadata mempty)+            , StructField "d" (TRef (TypeRef "" "Ref")) (Metadata mempty)+            , StructField "e" (TVector TWord32) (Metadata mempty)+            , StructField "f" (TVector (TRef (TypeRef "" "uint_"))) (Metadata mempty)+            , StructField "g" (TRef (TypeRef "My.Api" "Ref")) (Metadata mempty)+            , StructField "h" (TVector (TRef (TypeRef "MyApi" "abc_"))) (Metadata mempty)             ]+          ] -      it "root types, file extensions / identifiers, attribute declarations" $-        [r|-          attribute a;-          attribute "b";-          root_type c;-          root_type My.Api.C ;-          file_extension "d";-          file_identifier "abcd";-        |] `parses`-          Schema-            []-            [ DeclA $ AttributeDecl "a"-            , DeclA $ AttributeDecl "b"-            , DeclR $ RootDecl (TypeRef "" "c")-            , DeclR $ RootDecl (TypeRef "My.Api" "C")-            , DeclFI $ FileIdentifierDecl "abcd"+    it "enum declarations" $+      [r|+        enum Color : short (attr) {+          Red,+          Blue = 18446744073709551615,+          Gray = -18446744073709551615,+          Black+        }+      |] `parses`+        Schema+          []+          [DeclE $ EnumDecl "Color" TInt16 (Metadata [("attr", Nothing)])+            [ EnumVal "Red" Nothing+            , EnumVal "Blue" (Just 18446744073709551615)+            , EnumVal "Gray" (Just -18446744073709551615)+            , EnumVal "Black" Nothing             ]+          ] -      it "file identifier must have exactly 4 UTF-8 code units" $ do-        parseEof schema [r| file_identifier "";      |] `shouldFailWithError` "file_identifier must be exactly 4 characters\n"-        parseEof schema [r| file_identifier "abc";   |] `shouldFailWithError` "file_identifier must be exactly 4 characters\n"-        parseEof schema [r| file_identifier "abcde"; |] `shouldFailWithError` "file_identifier must be exactly 4 characters\n"-        parseEof schema [r| file_identifier "abc👬"; |] `shouldFailWithError` "file_identifier must be exactly 4 UTF-8 code units\n"-        parseEof schema [r| file_identifier "a👬";   |] `shouldFailWithError` "file_identifier must be exactly 4 UTF-8 code units\n"+    it "union declarations" $+      [r|+        union Weapon ( attr ) {+          Sword,+          mace: Stick,+          mace2: My.Api.Stick,+          Axe+        }+      |] `parses`+        Schema+          []+          [ DeclU $ UnionDecl+              "Weapon"+              (Metadata [("attr", Nothing)])+              [ UnionVal Nothing (TypeRef "" "Sword")+              , UnionVal (Just "mace") (TypeRef "" "Stick")+              , UnionVal (Just "mace2") (TypeRef "My.Api" "Stick")+              , UnionVal Nothing (TypeRef "" "Axe")+              ]+          ] -        [r| file_identifier "abcd"; |] `parses` Schema [] [ DeclFI "abcd" ]-        [r| file_identifier "👬";   |] `parses` Schema [] [ DeclFI "👬" ]+    it "root types, file extensions / identifiers, attribute declarations" $+      [r|+        attribute a;+        attribute "b";+        root_type c;+        root_type My.Api.C ;+        file_extension "d";+        file_identifier "abcd";+      |] `parses`+        Schema+          []+          [ DeclA $ AttributeDecl "a"+          , DeclA $ AttributeDecl "b"+          , DeclR $ RootDecl (TypeRef "" "c")+          , DeclR $ RootDecl (TypeRef "My.Api" "C")+          , DeclFI $ FileIdentifierDecl "abcd"+          ] -      it "json objects" $-        [r|-          include "a";+    it "file identifier must have exactly 4 UTF-8 code units" $ do+      parseEof schema [r| file_identifier "";      |] `shouldFailWithError` "file_identifier must be exactly 4 characters\n"+      parseEof schema [r| file_identifier "abc";   |] `shouldFailWithError` "file_identifier must be exactly 4 characters\n"+      parseEof schema [r| file_identifier "abcde"; |] `shouldFailWithError` "file_identifier must be exactly 4 characters\n"+      parseEof schema [r| file_identifier "abc👬"; |] `shouldFailWithError` "file_identifier must be exactly 4 UTF-8 code units\n"+      parseEof schema [r| file_identifier "a👬";   |] `shouldFailWithError` "file_identifier must be exactly 4 UTF-8 code units\n" -          {-            "a" : 3 ,-            b : "e" ,-            c : [ { d: [ [ ] , [ "a" , null , true , false , - 3 , -239.223e3 ] ] } ]-          }+      [r| file_identifier "abcd"; |] `parses` Schema [] [ DeclFI "abcd" ]+      [r| file_identifier "👬";   |] `parses` Schema [] [ DeclFI "👬" ] -          attribute b;-        |] `parses`-          Schema-            [ Include "a" ]-            [ DeclA $ AttributeDecl "b" ]+    it "json objects" $+      [r|+        include "a"; -      it "RPC services" $-        [r|-          include "a";+        {+          "a" : 3 ,+          b : "e" ,+          c : [ { d: [ [ ] , [ "a" , null , true , false , - 3 , -239.223e3 ] ] } ]+        } -          rpc_service MonsterStorage {-            Store(Monster) : Stat ;-            Retrieve(Stat) : Monster ( streaming : "server" , idempotent ) ;-          }+        attribute b;+      |] `parses`+        Schema+          [ Include "a" ]+          [ DeclA $ AttributeDecl "b" ] -        |] `parses`-          Schema-            [ Include "a" ] []+    it "RPC services" $+      [r|+        include "a"; -shouldFailWithError :: Show a => Either (ParseErrorBundle String Void) a -> String -> Expectation+        rpc_service MonsterStorage {+          Store(Monster) : Stat ;+          Retrieve(Stat) : Monster ( streaming : "server" , idempotent ) ;+        }++      |] `parses`+        Schema+          [ Include "a" ] []++shouldFailWithError :: (HasCallStack, Show a) => Either (ParseErrorBundle String Void) a -> String -> Expectation shouldFailWithError p s =   case p of     Left (ParseErrorBundle [x] _) -> parseErrorTextPretty x `shouldBe` s@@ -259,7 +304,7 @@ parseEof :: Parser a -> String -> Either (ParseErrorBundle String Void) a parseEof p = parse (p <* eof) "" -parses :: String -> Schema -> Expectation+parses :: HasCallStack => String -> Schema -> Expectation parses input expectedSchema =   case parse schema "" input of     l@(Left _) -> l `shouldParse` expectedSchema
test/FlatBuffers/Internal/Compiler/SemanticAnalysisSpec.hs view
@@ -4,10 +4,11 @@  module FlatBuffers.Internal.Compiler.SemanticAnalysisSpec where +import           Data.Bits                                      ( shiftL ) import           Data.Foldable                                  ( fold ) import           Data.Int-import           Data.Text                                      ( Text )-import qualified Data.Text                                      as T+import           Data.List.NonEmpty                             ( NonEmpty((:|)) )+import qualified Data.Map.Strict                                as Map  import qualified FlatBuffers.Internal.Compiler.Parser           as P import           FlatBuffers.Internal.Compiler.SemanticAnalysis@@ -19,18 +20,32 @@ import           Text.Megaparsec import           Text.RawString.QQ                              ( r ) + spec :: Spec spec =   describe "SemanticAnalysis" $ do     it "top-level identifiers cannot have duplicates in the same namespace" $ do-      [r| namespace A; enum E:int{x} enum E:int{x} |] `shouldFail` "'A.E' declared more than once"-      [r| enum E:int{x}     enum E:int{x}     |] `shouldFail` "'E' declared more than once"-      [r| struct S{x:int;}  struct S{x:int;}  |] `shouldFail` "'S' declared more than once"-      [r| table T{}         table T{}         |] `shouldFail` "'T' declared more than once"-      [r| union U{x}        union U{x}        |] `shouldFail` "'U' declared more than once"-      [r| union U{x}        union U{x}        |] `shouldFail` "'U' declared more than once"-      [r| union X{x}        table X{}         |] `shouldFail` "'X' declared more than once"+      [r| namespace A; enum E:int{x}     enum E:int{x}     |] `shouldFail` "'A.E' declared more than once"+      [r|              enum E:int{x}     enum E:int{x}     |] `shouldFail` "'E' declared more than once"+      [r|              struct S{x:int;}  struct S{x:int;}  |] `shouldFail` "'S' declared more than once"+      [r|              table T{}         table T{}         |] `shouldFail` "'T' declared more than once"+      [r|              union U{x}        union U{x}        |] `shouldFail` "'U' declared more than once"+      [r| namespace A; union X{x}        table X{}         |] `shouldFail` "'A.X' declared more than once"+      [r|              union X{x}        table X{}         |] `shouldFail` "'X' declared more than once" +    it "top-level identifiers cannot have duplicates in the same namespace, in different files" $ do+      [   [r| namespace A; enum E:int{x} |]+        , [r| namespace A; enum E:int{y} |]+        ] `shouldFail'` "'A.E' declared more than once"++      [   [r| namespace A; enum X:int{x} |]+        , [r| namespace A; table X {y: int;} |]+        ] `shouldFail'` "'A.X' declared more than once"++      [   [r| enum X:int{x} |]+        , [r| table X {y: int;} |]+        ] `shouldFail'` "'X' declared more than once"+     it "top-level identifiers can be duplicates, if they live in different namespaces" $       [r|         namespace A;@@ -139,7 +154,7 @@           namespace Ns;           enum Color : uint32 { Red, Green, Blue }         |] `shouldValidate`-          enum ("Ns", EnumDecl "Color" EWord32+          enum ("Ns", EnumDecl "Color" EWord32 False             [ EnumVal "Red" 0             , EnumVal "Green" 1             , EnumVal "Blue" 2@@ -158,14 +173,14 @@           enum Color3 : uint32 { Blue }          |] `shouldValidate` foldDecls-          [ enum ("A",      EnumDecl "Color1" EWord32 [EnumVal "Red" 0] )-          , enum ("",       EnumDecl "Color2" EWord32 [EnumVal "Green" 0] )-          , enum ("A.B.C",  EnumDecl "Color3" EWord32 [EnumVal "Blue" 0] )+          [ enum ("A",      EnumDecl "Color1" EWord32 False [EnumVal "Red" 0] )+          , enum ("",       EnumDecl "Color2" EWord32 False [EnumVal "Green" 0] )+          , enum ("A.B.C",  EnumDecl "Color3" EWord32 False [EnumVal "Blue" 0] )           ]        it "with explicit values" $         [r| enum Color : int32 { Red = -2, Green, Blue = 2 } |] `shouldValidate`-          enum ("", EnumDecl "Color" EInt32+          enum ("", EnumDecl "Color" EInt32 False             [ EnumVal "Red" (-2)             , EnumVal "Green" (-1)             , EnumVal "Blue" 2@@ -173,7 +188,7 @@        it "with explicit values (min/maxBound)" $         [r| enum Color : int8 { Red = -128, Green, Blue = 127 } |] `shouldValidate`-          enum ("", EnumDecl "Color" EInt8+          enum ("", EnumDecl "Color" EInt8 False           [ EnumVal "Red" (toInteger (minBound :: Int8))           , EnumVal "Green" (-127)           , EnumVal "Blue" (toInteger (maxBound :: Int8))@@ -184,34 +199,108 @@           namespace A.B;           enum Color : int8 { Red = -129, Green, Blue }         |] `shouldFail`-          "[A.B.Color.Red]: enum value does not fit [-128; 127]"+          "[A.B.Color.Red]: enum value of -129 does not fit [-128; 127]"         [r|           enum Color : int8 { Red, Green, Blue = 128 }         |] `shouldFail`-          "[Color.Blue]: enum value does not fit [-128; 127]"+          "[Color.Blue]: enum value of 128 does not fit [-128; 127]"        it "with values out of order" $ do         [r| enum Color : int8 { Red = 3, Green = 2, Blue } |] `shouldFail`-          "[Color]: enum values must be specified in ascending order"+          "[Color]: enum values must be specified in ascending order. 'Green' (2) should be greater than 'Red' (3)"         [r| enum Color : int8 { Red = 3, Green = 3, Blue } |] `shouldFail`-          "[Color]: enum values must be specified in ascending order"--      it "with bit_flags" $-        [r| enum Color : int8 (bit_flags) { Red, Green, Blue } |] `shouldFail`-          "[Color]: `bit_flags` are not supported yet"+          "[Color]: enum values must be specified in ascending order. 'Green' (3) should be greater than 'Red' (3)"        it "with duplicate values" $         [r| enum Color : int8 { Red, Green, Red, Gray, Green, Green, Black } |] `shouldFail`           "[Color]: 'Green', 'Red' declared more than once"        it "with invalid underlying type" $ do-        [r| enum Color : double { Red, Green, Blue } |] `shouldFail`-          "[Color]: underlying enum type must be integral"-        [r| enum Color : TypeRef { Red, Green, Blue } |] `shouldFail`-          "[Color]: underlying enum type must be integral"-        [r| enum Color : [int] { Red, Green, Blue } |] `shouldFail`-          "[Color]: underlying enum type must be integral"+        let expected = "[Color]: underlying enum type must be integral"+        [r| enum Color : double  { Red, Green, Blue } |] `shouldFail` expected+        [r| enum Color : TypeRef { Red, Green, Blue } |] `shouldFail` expected+        [r| enum Color : [int]   { Red, Green, Blue } |] `shouldFail` expected +    describe "enums with bit_flags" $ do+      it "simple" $+        [r|+          namespace Ns;+          enum Color : uint32 (bit_flags) { Red, Green, Blue }+        |] `shouldValidate`+          enum ("Ns", EnumDecl "Color" EWord32 True+            [ EnumVal "Red" 1+            , EnumVal "Green" 2+            , EnumVal "Blue" 4+            ])++      it "multiple enums in different namespaces" $+        [r|+          namespace A;+          enum Color1 : uint32 (bit_flags) { Red }++          namespace B;+          namespace ;+          enum Color2 : uint32 (bit_flags) { Green }++          namespace A.B.C;+          enum Color3 : uint32 (bit_flags) { Blue }++        |] `shouldValidate` foldDecls+          [ enum ("A",      EnumDecl "Color1" EWord32 True [EnumVal "Red" 1] )+          , enum ("",       EnumDecl "Color2" EWord32 True [EnumVal "Green" 1] )+          , enum ("A.B.C",  EnumDecl "Color3" EWord32 True [EnumVal "Blue" 1] )+          ]++      it "with explicit values" $+        [r| enum Color : uint8 (bit_flags) { Red = 2, Green, Blue = 6 } |] `shouldValidate`+          enum ("", EnumDecl "Color" EWord8 True+            [ EnumVal "Red" 4+            , EnumVal "Green" 8+            , EnumVal "Blue" 64+            ])++      it "with explicit values (min/maxBound)" $+        [r| enum Color : uint (bit_flags) { Red = 0, Green, Blue = 31 } |] `shouldValidate`+          enum ("", EnumDecl "Color" EWord32 True+          [ EnumVal "Red" 1+          , EnumVal "Green" 2+          , EnumVal "Blue" (1 `shiftL` 31)+          ])++      it "with out-of-bounds values" $ do+        [r|+          namespace A.B;+          enum Color : uint (bit_flags) { Red = -1, Green, Blue }+        |] `shouldFail`+          "[A.B.Color.Red]: enum value of -1 does not fit [0; 31]"+        [r|+          enum Color : uint (bit_flags) { Red, Green, Blue = 32 }+        |] `shouldFail`+          "[Color.Blue]: enum value of 32 does not fit [0; 31]"++      it "with values out of order" $ do+        [r| enum Color : uint8 (bit_flags) { Red = 3, Green = 2, Blue } |] `shouldFail`+          "[Color]: enum values must be specified in ascending order. 'Green' (2) should be greater than 'Red' (3)"+        [r| enum Color : uint8 (bit_flags) { Red = 3, Green = 3, Blue } |] `shouldFail`+          "[Color]: enum values must be specified in ascending order. 'Green' (3) should be greater than 'Red' (3)"++      it "with duplicate values" $+        [r| enum Color : uint8 (bit_flags) { Red, Green, Red, Gray, Green, Green, Black } |] `shouldFail`+          "[Color]: 'Green', 'Red' declared more than once"++      it "with invalid underlying type" $ do+        let expected = "[Color]: underlying enum type must be integral"+        [r| enum Color : double  (bit_flags) { Red, Green, Blue } |] `shouldFail` expected+        [r| enum Color : TypeRef (bit_flags) { Red, Green, Blue } |] `shouldFail` expected+        [r| enum Color : [int]   (bit_flags) { Red, Green, Blue } |] `shouldFail` expected++      it "with signed underlying type" $ do+        let expected = "[Color]: underlying type of bit_flags enum must be unsigned"+        [r| enum Color : int8  (bit_flags) { Red, Green, Blue } |] `shouldFail` expected+        [r| enum Color : int16 (bit_flags) { Red, Green, Blue } |] `shouldFail` expected+        [r| enum Color : int32 (bit_flags) { Red, Green, Blue } |] `shouldFail` expected+        [r| enum Color : int64 (bit_flags) { Red, Green, Blue } |] `shouldFail` expected+     describe "structs" $ do       it "simple" $         [r|@@ -240,7 +329,7 @@        it "when unqualified TypeRef is ambiguous, types in namespaces closer to the struct are preferred" $ do         let enumVal = EnumVal "x" 0-            mkEnum namespace ident = enum (namespace, EnumDecl ident EInt16 [enumVal])+            mkEnum namespace ident = enum (namespace, EnumDecl ident EInt16 False [enumVal])         [r|           namespace ;       enum E1 : short{x}   enum E2 : short{x}   enum E3 : short{x}           namespace A;      enum E1 : short{x}   enum E2 : short{x}@@ -267,7 +356,7 @@        it "when qualified TypeRef is ambiguous, types in namespaces closer to the struct are preferred" $ do         let enumVal = EnumVal "x" 0-            mkEnum namespace ident = enum (namespace, EnumDecl ident EInt16 [enumVal])+            mkEnum namespace ident = enum (namespace, EnumDecl ident EInt16 False [enumVal])         [r|           namespace ;         enum E1 : short{x}   enum E2 : short{x}   enum E3 : short{x}           namespace A;        enum E1 : short{x}   enum E2 : short{x}   enum E3 : short{x}@@ -316,12 +405,28 @@             x: Color;           }         |] `shouldValidate` foldDecls-          [ enum ("A", EnumDecl "Color" EWord16 [EnumVal "Blue" 0])+          [ enum ("A", EnumDecl "Color" EWord16 False [EnumVal "Blue" 0])           , struct ("A", StructDecl "S" 2 2               [ StructField "x" 0 0 (SEnum (TypeRef "A" "Color") EWord16)               ])           ] ++      it "with field referencing an enum with bit_flags" $+        [r|+          namespace A;+          enum Color : ushort (bit_flags) { Blue }++          struct S {+            x: Color;+          }+        |] `shouldValidate` foldDecls+          [ enum ("A", EnumDecl "Color" EWord16 True [EnumVal "Blue" 1])+          , struct ("A", StructDecl "S" 2 2+              [ StructField "x" 0 0 (SEnum (TypeRef "A" "Color") EWord16)+              ])+          ]+       it "with nested structs (backwards/forwards references)" $ do         let backwards = ("A.B", StructDecl "Backwards" 4 4 [ StructField "x" 0 0 SFloat ])         let forwards  = ("A.B", StructDecl "Forwards"  4 4 [ StructField "y" 0 0 (SStruct backwards) ])@@ -387,6 +492,11 @@         [r| struct S { x: string; } |] `shouldFail`           "[S.x]: struct fields may only be integers, floating point, bool, enums, or other structs" +      it "with invalid nested struct" $ do+        let expectedErrorMsg = "[S2.x]: struct fields may only be integers, floating point, bool, enums, or other structs"+        [r| struct S1 { x: S2; }     struct S2 { x: string; } |] `shouldFail` expectedErrorMsg+        [r| struct S2 { x: string; } struct S1 { x: S2; }     |] `shouldFail` expectedErrorMsg+       it "with duplicate fields" $         [r| struct S { x: byte; x: int; } |] `shouldFail`           "[S]: 'x' declared more than once"@@ -683,52 +793,73 @@               x: B.E;             }           |] `shouldValidate` foldDecls-            [ enum ("A.B", EnumDecl "E" EInt16 [ EnumVal "A" 0 ])+            [ enum ("A.B", EnumDecl "E" EInt16 False [ EnumVal "A" 0 ])             , table ("A.B", TableDecl "T" NotRoot                 [ TableField 0 "x" (TEnum (TypeRef "A.B" "E") EInt16 0) False ]               )             ]          it "with `required` attribute" $-          [r| table T { x: E (required); } enum E : short{A} |] `shouldFail`+          [r|+            table T { x: E (required); }+            enum E : short{A}+          |] `shouldFail`             "[T.x]: only non-scalar fields (strings, vectors, unions, structs, tables) may be 'required'"          it "with `deprecated` attribute" $-          [r| table T { x: E (deprecated); } enum E : short{A} |] `shouldValidate` foldDecls-            [ enum ("", EnumDecl "E" EInt16 [ EnumVal "A" 0 ])+          [r|+            table T { x: E (deprecated); }+            enum E : short{A}+          |] `shouldValidate` foldDecls+            [ enum ("", EnumDecl "E" EInt16 False [ EnumVal "A" 0 ])             , table ("", TableDecl "T" NotRoot                 [ TableField 0 "x" (TEnum (TypeRef "" "E") EInt16 0) True ]               )             ]          it "without default value, when enum has 0-value" $-          [r| table T { x: E; } enum E : short{ A = -1, B = 0, C = 1} |] `shouldValidate` foldDecls-            [ enum ("", EnumDecl "E" EInt16 [ EnumVal "A" (-1), EnumVal "B" 0, EnumVal "C" 1 ])+          [r|+            table T { x: E; }+            enum E : short{ A = -1, B = 0, C = 1}+          |] `shouldValidate` foldDecls+            [ enum ("", EnumDecl "E" EInt16 False [ EnumVal "A" (-1), EnumVal "B" 0, EnumVal "C" 1 ])             , table ("", TableDecl "T" NotRoot                 [ TableField 0 "x" (TEnum (TypeRef "" "E") EInt16 0) False ]               )             ]          it "without default value, when enum doesn't have 0-value" $-          [r| table T { x: E; } enum E : short{ A = -1, B = 1, C = 2} |] `shouldFail`+          [r|+            table T { x: E; }+            enum E : short{ A = -1, B = 1, C = 2}+          |] `shouldFail`             "[T.x]: enum does not have a 0 value; please manually specify a default for this field"          describe "with default value" $ do           it "valid integral" $-            [r| table T { x: E = 1; } enum E : short{ A, B, C } |] `shouldValidate` foldDecls-              [ enum ("", EnumDecl "E" EInt16 [ EnumVal "A" 0, EnumVal "B" 1, EnumVal "C" 2 ])+            [r|+              table T { x: E = 1; }+              enum E : short{ A, B, C }+            |] `shouldValidate` foldDecls+              [ enum ("", EnumDecl "E" EInt16 False [ EnumVal "A" 0, EnumVal "B" 1, EnumVal "C" 2 ])               , table ("", TableDecl "T" NotRoot                   [ TableField 0 "x" (TEnum (TypeRef "" "E") EInt16 1) False ]                 )               ] -          it "invalid integral" $-            [r| table T { x: E = 3; } enum E : short{ A, B, C } |] `shouldFail`-              "[T.x]: default value of 3 is not part of enum E"+          it "integral must match one of the enum values" $+            [r|+              table T { x: E = 3; }+              enum E : short{ A, B, C }+            |] `shouldFail`+              "[T.x]: default value of 3 is not part of enum 'E'"            it "valid identifier" $-            [r| table T { x: E = B; } enum E : short{ A, B, C } |] `shouldValidate` foldDecls-              [ enum ("", EnumDecl "E" EInt16 [ EnumVal "A" 0, EnumVal "B" 1, EnumVal "C" 2 ])+            [r|+              table T { x: E = B; }+              enum E : short{ A, B, C }+            |] `shouldValidate` foldDecls+              [ enum ("", EnumDecl "E" EInt16 False [ EnumVal "A" 0, EnumVal "B" 1, EnumVal "C" 2 ])               , table ("", TableDecl "T" NotRoot                   [ TableField 0 "x" (TEnum (TypeRef "" "E") EInt16 1) False ]                 )@@ -736,16 +867,135 @@            it "invalid identifier" $             [r| table T { x: E = D; } enum E : short{ A, B, C } |] `shouldFail`-              "[T.x]: default value of D is not part of enum E"+              "[T.x]: default value of D is not part of enum 'E'" +          it "multiple identifiers" $+            [r| table T { x: E = "B C"; } enum E : short{ A, B, C } |] `shouldFail`+              "[T.x]: default value must be a single identifier, found 2: 'B', 'C'"+           it "decimal number" $             [r| table T { x: E = 1.5; } enum E : short{ A, B, C } |] `shouldFail`               "[T.x]: default value must be integral or one of: 'A', 'B', 'C'"            it "boolean" $-            [r| table T { x: E = 1.5; } enum E : short{ A, B, C } |] `shouldFail`+            [r| table T { x: E = true; } enum E : short{ A, B, C } |] `shouldFail`               "[T.x]: default value must be integral or one of: 'A', 'B', 'C'" +      describe "with reference to enum with bit_flags" $ do+        it "simple" $+          [r|+            namespace A.B;+            enum E : ushort (bit_flags) { A }+            table T {+              x: B.E;+            }+          |] `shouldValidate` foldDecls+            [ enum ("A.B", EnumDecl "E" EWord16 True [ EnumVal "A" 1 ])+            , table ("A.B", TableDecl "T" NotRoot+                [ TableField 0 "x" (TEnum (TypeRef "A.B" "E") EWord16 0) False ]+              )+            ]++        it "with `required` attribute" $+          [r|+            table T { x: E (required); }+            enum E : ushort (bit_flags) {A}+          |] `shouldFail`+            "[T.x]: only non-scalar fields (strings, vectors, unions, structs, tables) may be 'required'"++        it "with `deprecated` attribute" $+          [r|+            table T { x: E (deprecated); }+            enum E : ushort (bit_flags) {A}+          |] `shouldValidate` foldDecls+            [ enum ("", EnumDecl "E" EWord16 True [ EnumVal "A" 1 ])+            , table ("", TableDecl "T" NotRoot+                [ TableField 0 "x" (TEnum (TypeRef "" "E") EWord16 0) True ]+              )+            ]++        it "without default value, when enum doesn't have 1-value" $+          [r|+            table T { x: E; }+            enum E : ushort (bit_flags) { A = 9 }+          |] `shouldValidate` foldDecls+            [ enum ("", EnumDecl "E" EWord16 True [ EnumVal "A" 512 ])+            , table ("", TableDecl "T" NotRoot+                [ TableField 0 "x" (TEnum (TypeRef "" "E") EWord16 0) False ]+              )+            ]++        describe "with default value" $ do+          it "valid integral" $+            [r|+              table T { x: E = 2; }+              enum E : ushort (bit_flags) { A, B, C }+            |] `shouldValidate` foldDecls+              [ enum ("", EnumDecl "E" EWord16 True [ EnumVal "A" 1, EnumVal "B" 2, EnumVal "C" 4 ])+              , table ("", TableDecl "T" NotRoot+                  [ TableField 0 "x" (TEnum (TypeRef "" "E") EWord16 2) False ]+                )+              ]++          it "integral doesn't have to match any enum value" $+            [r|+              table T { x: E = 65535; }+              enum E : ushort (bit_flags) { A, B, C }+            |] `shouldValidate` foldDecls+              [ enum ("", EnumDecl "E" EWord16 True [ EnumVal "A" 1, EnumVal "B" 2, EnumVal "C" 4 ])+              , table ("", TableDecl "T" NotRoot+                  [ TableField 0 "x" (TEnum (TypeRef "" "E") EWord16 65535) False ]+                )+              ]++          it "must be within the range of the enum's underlying type" $+            [r|+              table T { x: E = 65536; }+              enum E : ushort (bit_flags) { A, B, C }+            |] `shouldFail`+              "[T.x]: default value does not fit [0; 65535]"++          it "valid identifier" $+            [r|+              table T { x: E = B; }+              enum E : ushort (bit_flags) { A, B, C }+            |] `shouldValidate` foldDecls+              [ enum ("", EnumDecl "E" EWord16 True [ EnumVal "A" 1, EnumVal "B" 2, EnumVal "C" 4 ])+              , table ("", TableDecl "T" NotRoot+                  [ TableField 0 "x" (TEnum (TypeRef "" "E") EWord16 2) False ]+                )+              ]++          it "invalid identifier" $+            [r| table T { x: E = D; } enum E : ushort (bit_flags) { A, B, C } |] `shouldFail`+              "[T.x]: default value of D is not part of enum 'E'"++          it "multiple valid identifiers" $+            [r|+              table T { x: E = "B C"; }+              enum E : ushort (bit_flags) { A, B, C }+            |] `shouldValidate` foldDecls+              [ enum ("", EnumDecl "E" EWord16 True [ EnumVal "A" 1, EnumVal "B" 2, EnumVal "C" 4 ])+              , table ("", TableDecl "T" NotRoot+                  [ TableField 0 "x" (TEnum (TypeRef "" "E") EWord16 6) False ]+                )+              ]++          it "mix of valid and invalid identifiers" $+            [r|+              table T { x: E = "B X C"; }+              enum E : ushort (bit_flags) { A, B, C }+            |] `shouldFail`+              "[T.x]: default value of X is not part of enum 'E'"++          it "decimal number" $+            [r| table T { x: E = 1.5; } enum E : ushort (bit_flags) { A, B, C } |] `shouldFail`+              "[T.x]: default value must be integral, one of ['A', 'B', 'C'], or a combination of the latter in double quotes (e.g. \"A B\")"++          it "boolean" $+            [r| table T { x: E = true; } enum E : ushort (bit_flags) { A, B, C } |] `shouldFail`+              "[T.x]: default value must be integral, one of ['A', 'B', 'C'], or a combination of the latter in double quotes (e.g. \"A B\")"+       describe "with reference to structs/table/union" $ do         it "simple" $           [r|@@ -831,24 +1081,27 @@               , TableField 2 "z" (TVector Opt VBool) False               ]) -        it "where the elements are references" $+        it "where the elements are references (enum, struct, table, union)" $           [r|             namespace A;-            table Table { w: [B.E]; x: [B.S]; y: [B.T]; z: [B.U]; }+            table Table { a: [B.E]; b: [B.EBF]; c: [B.S]; d: [B.T]; e: [B.U]; }              namespace A.B;             enum   E : int16 { EA }+            enum   EBF : uint16 (bit_flags) { EA }             struct S { x: ubyte; y: int64; }             table  T {}             union  U { T }           |] `shouldValidate` foldDecls             [ table ("A", TableDecl "Table" NotRoot-                [ TableField 0 "w" (TVector Opt (VEnum   (TypeRef "A.B" "E") EInt16)) False-                , TableField 1 "x" (TVector Opt (VStruct (TypeRef "A.B" "S"))) False-                , TableField 2 "y" (TVector Opt (VTable  (TypeRef "A.B" "T"))) False-                , TableField 4 "z" (TVector Opt (VUnion  (TypeRef "A.B" "U"))) False+                [ TableField 0 "a" (TVector Opt (VEnum   (TypeRef "A.B" "E") EInt16)) False+                , TableField 1 "b" (TVector Opt (VEnum   (TypeRef "A.B" "EBF") EWord16)) False+                , TableField 2 "c" (TVector Opt (VStruct (TypeRef "A.B" "S"))) False+                , TableField 3 "d" (TVector Opt (VTable  (TypeRef "A.B" "T"))) False+                , TableField 5 "e" (TVector Opt (VUnion  (TypeRef "A.B" "U"))) False                 ])-            , enum   ("A.B", EnumDecl "E" EInt16 [EnumVal "EA" 0])+            , enum   ("A.B", EnumDecl "E" EInt16 False [EnumVal "EA" 0])+            , enum   ("A.B", EnumDecl "EBF" EWord16 True [EnumVal "EA" 1])             , struct ("A.B", StructDecl "S" 8 16 [StructField "x" 7 0 SWord8, StructField "y" 0 8 SInt64])             , table  ("A.B", TableDecl "T" NotRoot [])             , union  ("A.B", UnionDecl "U" [UnionVal "T" (TypeRef "A.B" "T")])@@ -1097,28 +1350,36 @@               ])           ] -+      it "can use the same table twice, if using a qualified name and an alias" $+        [r|+          namespace A;+          table T{}+          union U {T, alias:A.T}+        |] `shouldValidate` foldDecls+          [ table ("A", TableDecl "T" NotRoot [])+          , union ("A", UnionDecl "U"+              [ UnionVal "T"     (TypeRef "A" "T")+              , UnionVal "alias" (TypeRef "A" "T")+              ])+          ]    ---           -- property: struct size (including paddings) = multiple of alignment---           -- property: alignment max alignment ???- foldDecls :: [ValidDecls] -> ValidDecls foldDecls = fold  enum :: (Namespace, EnumDecl) -> ValidDecls-enum e = SymbolTable [e] [] [] []+enum (ns, e) = SymbolTable (Map.singleton (ns, getIdent e) e) Map.empty Map.empty Map.empty  struct :: (Namespace, StructDecl) -> ValidDecls-struct s = SymbolTable [] [s] [] []+struct (ns, s) = SymbolTable Map.empty (Map.singleton (ns, getIdent s) s) Map.empty Map.empty  table :: (Namespace, TableDecl) -> ValidDecls-table t = SymbolTable [] [] [t] []+table (ns, t) = SymbolTable Map.empty Map.empty (Map.singleton (ns, getIdent t) t) Map.empty  union :: (Namespace, UnionDecl) -> ValidDecls-union u = SymbolTable [] [] [] [u]+union (ns, u) = SymbolTable Map.empty Map.empty Map.empty (Map.singleton (ns, getIdent u) u)  shouldSucceed :: HasCallStack => String -> Expectation shouldSucceed input =@@ -1127,8 +1388,8 @@     Right schema ->       let schemas = FileTree "" schema []       in  case validateSchemas schemas of-            Right _ -> pure ()-            Left err -> expectationFailure (T.unpack err)+            Right _  -> pure ()+            Left err -> expectationFailure err  shouldValidate :: HasCallStack => String -> ValidDecls -> Expectation shouldValidate input expectation =@@ -1138,13 +1399,23 @@       let schemas = FileTree "" schema []       in  validateSchemas schemas `shouldBe` Right (FileTree "" expectation []) -shouldFail :: String -> Text -> Expectation+shouldFail :: HasCallStack => String -> String -> Expectation shouldFail input expectedErrorMsg =   case parse P.schema "" input of     Left e -> expectationFailure $ "Parsing failed with error:\n" <> showBundle e     Right schema ->       let schemas = FileTree "" schema []       in  validateSchemas schemas `shouldBe` Left expectedErrorMsg+++shouldFail' :: HasCallStack => NonEmpty String -> String -> Expectation+shouldFail' inputs expectedErrorMsg =+  case traverse (parse P.schema "") inputs of+    Left e -> expectationFailure $ "Parsing failed with error:\n" <> showBundle e+    Right (schema :| schemas) ->+      let importesFilepathsAndSchemas = Map.fromList (fmap (\s -> ("", s)) schemas)+          fileTree = FileTree "" schema importesFilepathsAndSchemas+      in  validateSchemas fileTree `shouldBe` Left expectedErrorMsg  showBundle :: (ShowErrorComponent e, Stream s) => ParseErrorBundle s e -> String showBundle = unlines . fmap indent . lines . errorBundlePretty
test/FlatBuffers/Internal/Compiler/THSpec.hs view
@@ -7,6 +7,7 @@  import           Control.Arrow                                  ( second ) +import           Data.Bits                                      ( (.&.) ) import           Data.Int import           Data.Text                                      ( Text ) import qualified Data.Text                                      as T@@ -20,7 +21,6 @@ import           FlatBuffers.Internal.FileIdentifier            ( HasFileIdentifier(..), unsafeFileIdentifier ) import           FlatBuffers.Internal.Read import           FlatBuffers.Internal.Types-import           FlatBuffers.Internal.Util                      ( Positive(getPositive) ) import           FlatBuffers.Internal.Write  import           Language.Haskell.TH@@ -294,10 +294,17 @@               fromColor :: Color -> Int8               fromColor n =                 case n of-                  ColorRed -> 1+                  ColorRed  -> 1                   ColorBlue -> 2               {-# INLINE fromColor #-} +              colorName :: Color -> Text+              colorName c =+                case c of+                  ColorRed  -> T.pack "Red"+                  ColorBlue -> T.pack "Blue"+              {-# INLINE colorName #-}+               data T                t :: Maybe Int8 -> WriteTable T@@ -307,6 +314,40 @@               tX = readTableFieldWithDef readInt8 0 2             |] +      describe "enum fields with bit_flags" $+        it "are encoded as fields of the underlying type" $+          [r|+            enum Colors: ubyte (bit_flags) { Red = 2, Blue }+            table T {x: Colors = Blue; }+          |] `shouldCompileTo`+            [d|+              colorsRed :: Word8+              colorsRed = 4++              colorsBlue :: Word8+              colorsBlue = 8++              allColors :: [Word8]+              allColors = [ colorsRed, colorsBlue ]+              {-# INLINE allColors #-}++              colorsNames :: Word8 -> [Text]+              colorsNames c = res2+                where+                  res0 = []+                  res1 = if colorsBlue .&. c /= 0 then T.pack "Blue" : res0 else res0+                  res2 = if colorsRed  .&. c /= 0 then T.pack "Red"  : res1 else res1+              {-# INLINE colorsNames #-}++              data T++              t :: Maybe Word8 -> WriteTable T+              t x = writeTable [ optionalDef 8 writeWord8TableField x ]++              tX :: Table T -> Either ReadError Word8+              tX = readTableFieldWithDef readWord8 0 8+            |]+       describe "struct fields" $ do         it "normal field" $           [r|@@ -732,6 +773,10 @@                 fromColor n = case n of ColorRed -> 0                 {-# INLINE fromColor #-} +                colorName :: Color -> Text+                colorName c = case c of ColorRed -> T.pack "red"+                {-# INLINE colorName #-}+                 data T1                 t1 :: Maybe (WriteVector Int16) -> WriteTable T1                 t1 a = writeTable@@ -741,6 +786,7 @@                 t1A :: Table T1 -> Either ReadError (Maybe (Vector Int16))                 t1A = readTableFieldOpt (readPrimVector VectorInt16) 0               |]+           it "required" $             [r|               table t1 { a: [color] (required); }@@ -761,6 +807,10 @@                 fromColor n = case n of ColorRed -> 0                 {-# INLINE fromColor #-} +                colorName :: Color -> Text+                colorName c = case c of ColorRed -> T.pack "red"+                {-# INLINE colorName #-}+                 data T1                 t1 :: WriteVector Int16 -> WriteTable T1                 t1 a = writeTable@@ -771,6 +821,67 @@                 t1A = readTableFieldReq (readPrimVector VectorInt16) 0 "a"               |] +        describe "vector of enums with bit_flags" $ do+          it "normal" $+            [r|+              table t1 { a: [colors]; }+              enum colors : ulong (bit_flags) { red = 20 }+            |] `shouldCompileTo`+              [d|+                colorsRed :: Word64+                colorsRed = 1048576++                allColors :: [Word64]+                allColors = [ colorsRed ]+                {-# INLINE allColors #-}++                colorsNames :: Word64 -> [Text]+                colorsNames c = res1+                  where+                    res0 = []+                    res1 = if colorsRed .&. c /= 0 then T.pack "red" : res0 else res0+                {-# INLINE colorsNames #-}++                data T1+                t1 :: Maybe (WriteVector Word64) -> WriteTable T1+                t1 a = writeTable+                  [ optional writeVectorWord64TableField a+                  ]++                t1A :: Table T1 -> Either ReadError (Maybe (Vector Word64))+                t1A = readTableFieldOpt (readPrimVector VectorWord64) 0+              |]++          it "required" $+            [r|+              table t1 { a: [colors] (required); }+              enum colors : uint64 (bit_flags) { red = 63 }+            |] `shouldCompileTo`+              [d|+                colorsRed :: Word64+                colorsRed = 9223372036854775808++                allColors :: [Word64]+                allColors = [ colorsRed ]+                {-# INLINE allColors #-}++                colorsNames :: Word64 -> [Text]+                colorsNames c = res1+                  where+                    res0 = []+                    res1 = if colorsRed .&. c /= 0 then T.pack "red" : res0 else res0+                {-# INLINE colorsNames #-}++                data T1+                t1 :: WriteVector Word64 -> WriteTable T1+                t1 a = writeTable+                  [ writeVectorWord64TableField a+                  ]++                t1A :: Table T1 -> Either ReadError (Vector Word64)+                t1A = readTableFieldReq (readPrimVector VectorWord64) 0 "a"+              |]+         describe "vector of structs" $ do           it "normal" $             [r|@@ -796,7 +907,7 @@                   ]                  t1A :: Table T1 -> Either ReadError (Maybe (Vector (Struct S1)))-                t1A = readTableFieldOpt readStructVector 0+                t1A = readTableFieldOpt (readPrimVector VectorStruct) 0               |]            it "required" $@@ -823,7 +934,7 @@                   ]                  t1A :: Table T1 -> Either ReadError (Vector (Struct S1))-                t1A = readTableFieldReq readStructVector 0 "a"+                t1A = readTableFieldReq (readPrimVector VectorStruct) 0 "a"               |]          describe "vector of tables" $ do@@ -917,7 +1028,7 @@      describe "Enums" $       it "naming conventions" $ do-        let expected =+        let expected redName greenName =               [d|                 data MyColor = MyColorIsRed | MyColorIsGreen                   deriving (Eq, Show, Read, Ord, Bounded)@@ -936,13 +1047,48 @@                     MyColorIsRed -> -2                     MyColorIsGreen -> -1                 {-# INLINE fromMyColor #-}++                myColorName :: MyColor -> Text+                myColorName c =+                  case c of+                    MyColorIsRed   -> T.pack $(stringE redName)+                    MyColorIsGreen -> T.pack $(stringE greenName)+                {-# INLINE myColorName #-}               |] -        [r| enum my_color: int16 { is_red = -2, is_green  } |] `shouldCompileTo` expected-        [r| enum My_Color: int16 { Is_Red = -2, Is_Green  } |] `shouldCompileTo` expected-        [r| enum MyColor:  int16 { IsRed = -2,  IsGreen   } |] `shouldCompileTo` expected-        [r| enum myColor:  int16 { isRed = -2,  isGreen   } |] `shouldCompileTo` expected+        [r| enum my_color: int16 { is_red = -2, is_green  } |] `shouldCompileTo` expected "is_red" "is_green"+        [r| enum My_Color: int16 { Is_Red = -2, Is_Green  } |] `shouldCompileTo` expected "Is_Red" "Is_Green"+        [r| enum MyColor:  int16 { IsRed = -2,  IsGreen   } |] `shouldCompileTo` expected "IsRed"  "IsGreen"+        [r| enum myColor:  int16 { isRed = -2,  isGreen   } |] `shouldCompileTo` expected "isRed"  "isGreen" ++    describe "Enums with bit_flags" $+      it "naming conventions" $ do+        let expected redName greenName =+              [d|+                myColorsIsRed :: Word16+                myColorsIsRed = 4+                myColorsIsGreen :: Word16+                myColorsIsGreen = 8++                allMyColors :: [Word16]+                allMyColors = [ myColorsIsRed, myColorsIsGreen ]+                {-# INLINE allMyColors #-}++                myColorsNames :: Word16 -> [Text]+                myColorsNames c = res2+                  where+                    res0 = []+                    res1 = if myColorsIsGreen .&. c /= 0 then T.pack $(stringE greenName) : res0 else res0+                    res2 = if myColorsIsRed   .&. c /= 0 then T.pack $(stringE redName)   : res1 else res1+                {-# INLINE myColorsNames #-}+              |]++        [r| enum my_colors: ushort (bit_flags) { is_red = 2, is_green  } |] `shouldCompileTo` expected "is_red" "is_green"+        [r| enum My_Colors: ushort (bit_flags) { Is_Red = 2, Is_Green  } |] `shouldCompileTo` expected "Is_Red" "Is_Green"+        [r| enum MyColors:  ushort (bit_flags) { IsRed = 2,  IsGreen   } |] `shouldCompileTo` expected "IsRed"  "IsGreen"+        [r| enum myColors:  ushort (bit_flags) { isRed = 2,  isGreen   } |] `shouldCompileTo` expected "isRed"  "isGreen"+     describe "Structs" $ do       it "naming conventions" $ do         let expected =@@ -1052,6 +1198,10 @@             fromE n = case n of EX -> 0             {-# INLINE fromE #-} +            eName :: E -> Text+            eName c = case c of EX -> T.pack "X"+            {-# INLINE eName #-}+             data S             instance IsStruct S where               structAlignmentOf = 1@@ -1064,6 +1214,38 @@             sE = readStructField readInt8 0           |] +      it "with enum fields with bit_flags" $+        [r|+          struct S { e: E; }+          enum E : ubyte (bit_flags) { X }+        |] `shouldCompileTo`+          [d|+            eX :: Word8+            eX = 1++            allE :: [Word8]+            allE = [ eX ]+            {-# INLINE allE #-}++            eNames :: Word8 -> [Text]+            eNames c = res1+              where+                res0 = []+                res1 = if eX .&. c /= 0 then T.pack "X" : res0 else res0+            {-# INLINE eNames #-}++            data S+            instance IsStruct S where+              structAlignmentOf = 1+              structSizeOf = 1++            s :: Word8 -> WriteStruct S+            s e = WriteStruct (buildWord8 e)++            sE :: Struct S -> Either ReadError Word8+            sE = readStructField readWord8 0+          |]+       it "with nested structs" $         [r|           struct S1 (force_align: 2) { s2: S2; }@@ -1133,7 +1315,7 @@     Right schema ->       let schemas = FileTree "" schema mempty       in  case validateSchemas schemas of-        Left err                  -> expectationFailure $ T.unpack err+        Left err                  -> expectationFailure err         Right (FileTree _ root _) -> do           ast <- runQ (compileSymbolTable root)           expected <- runQ expectedQ@@ -1243,6 +1425,7 @@     CaseE e matches -> CaseE (normalizeExp e) (normalizeMatch <$> matches)     ConE name -> ConE (normalizeName name)     InfixE l op r -> InfixE (normalizeExp <$> l) (normalizeExp op) (normalizeExp <$> r)+    CondE b t f -> CondE (normalizeExp b) (normalizeExp t) (normalizeExp f)     _ -> e  normalizeMatch :: Match -> Match
test/FlatBuffers/ReadSpec.hs view
@@ -1,6 +1,8 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NegativeLiterals #-}+{-# LANGUAGE ScopedTypeVariables #-}  {-# OPTIONS_GHC -Wno-incomplete-patterns #-} @@ -10,7 +12,10 @@  import           Data.Functor               ( ($>) ) import           Data.Int+import qualified Data.List                  as List import qualified Data.Maybe                 as Maybe+import qualified Data.Text                  as Text+import qualified Data.Text.Read             as Text  import           Examples @@ -18,8 +23,12 @@ import           FlatBuffers.Internal.Write import qualified FlatBuffers.Vector         as Vec +import qualified Hedgehog.Gen               as Gen+import qualified Hedgehog.Range             as Range+ import           TestImports + spec :: Spec spec =   describe "read" $ do@@ -72,16 +81,6 @@       vectorOfUnionsXs table `shouldBeLeft` "Union vector: 'type vector' found but 'value vector' is missing."       vectorOfUnionsXsReq table `shouldBeLeft` "Union vector: 'type vector' found but 'value vector' is missing." -    it "throws when union type vector and union value vector have different sizes" $ do-      let typesVec = Vec.singleton 1-      let valuesVec = Vec.empty-      table <- evalRight $ decode $ encode $ writeTable-        [ writeVectorWord8TableField typesVec-        , writeVectorTableTableField valuesVec-        ]-      vec <- evalRightJust $ vectorOfUnionsXs table-      toList vec `shouldBeLeft` "Union vector: 'type vector' and 'value vector' do not have the same length."-     describe "returns `UnionUnknown` when union type is not recognized" $ do       it "in union table fields" $ do         let union = writeUnion 99 (writeTable [])@@ -123,7 +122,7 @@               Left e  -> evaluate e $> ()             ) `shouldThrow` errorCall "FlatBuffers.Internal.Read.index: index too large: 98" -      let testLargeUnsafeIndex table getVector = do+      let testInvalidUnsafeIndex table getVector = do             case getIndex table getVector Vec.unsafeIndex 100 of               Right a -> evaluate a $> ()               Left e -> evaluate e $> ()@@ -147,18 +146,18 @@               (Just Vec.empty)          it "`unsafeIndex` does not throw when index is negative / too large" $ do-          testLargeUnsafeIndex table vectorsA-          testLargeUnsafeIndex table vectorsB-          testLargeUnsafeIndex table vectorsC-          testLargeUnsafeIndex table vectorsD-          testLargeUnsafeIndex table vectorsE-          testLargeUnsafeIndex table vectorsF-          testLargeUnsafeIndex table vectorsG-          testLargeUnsafeIndex table vectorsH-          testLargeUnsafeIndex table vectorsI-          testLargeUnsafeIndex table vectorsJ-          testLargeUnsafeIndex table vectorsK-          testLargeUnsafeIndex table vectorsL+          testInvalidUnsafeIndex table vectorsA+          testInvalidUnsafeIndex table vectorsB+          testInvalidUnsafeIndex table vectorsC+          testInvalidUnsafeIndex table vectorsD+          testInvalidUnsafeIndex table vectorsE+          testInvalidUnsafeIndex table vectorsF+          testInvalidUnsafeIndex table vectorsG+          testInvalidUnsafeIndex table vectorsH+          testInvalidUnsafeIndex table vectorsI+          testInvalidUnsafeIndex table vectorsJ+          testInvalidUnsafeIndex table vectorsK+          testInvalidUnsafeIndex table vectorsL          it "`index` throws when index is negative" $ do           testNegativeIndex table vectorsA@@ -188,6 +187,64 @@           testLargeIndex table vectorsK           testLargeIndex table vectorsL +        it "`take` and `drop` are consistent with Data.List.take and Data.List.drop" $+          requireProperty $ do+            listWord8  <- forAll $ Gen.list (Range.linear 0 20) (Gen.word8  (Range.linear 0 20))+            listWord16 <- forAll $ Gen.list (Range.linear 0 20) (Gen.word16 (Range.linear 0 20))+            listWord32 <- forAll $ Gen.list (Range.linear 0 20) (Gen.word32 (Range.linear 0 20))+            listWord64 <- forAll $ Gen.list (Range.linear 0 20) (Gen.word64 (Range.linear 0 20))+            listInt8   <- forAll $ Gen.list (Range.linear 0 20) (Gen.int8   (Range.linear -20 20))+            listInt16  <- forAll $ Gen.list (Range.linear 0 20) (Gen.int16  (Range.linear -20 20))+            listInt32  <- forAll $ Gen.list (Range.linear 0 20) (Gen.int32  (Range.linear -20 20))+            listInt64  <- forAll $ Gen.list (Range.linear 0 20) (Gen.int64  (Range.linear -20 20))+            listFloat  <- forAll $ Gen.list (Range.linear 0 20) (Gen.float  (Range.linearFrac -20 20))+            listDouble <- forAll $ Gen.list (Range.linear 0 20) (Gen.double (Range.linearFrac -20 20))+            listBool   <- forAll $ Gen.list (Range.linear 0 20) Gen.bool+            listText   <- forAll $ Gen.list (Range.linear 0 20) (Gen.text (Range.singleton 3) Gen.alpha)++            n <- forAll $ Gen.int32 (Range.linearFrom 0 -10 30)++            table <- evalEither $ decode $ encode $ vectors+              (Just (Vec.fromList' listWord8))+              (Just (Vec.fromList' listWord16))+              (Just (Vec.fromList' listWord32))+              (Just (Vec.fromList' listWord64))+              (Just (Vec.fromList' listInt8))+              (Just (Vec.fromList' listInt16))+              (Just (Vec.fromList' listInt32))+              (Just (Vec.fromList' listInt64))+              (Just (Vec.fromList' listFloat))+              (Just (Vec.fromList' listDouble))+              (Just (Vec.fromList' listBool))+              (Just (Vec.fromList' listText))++            prop_takeConsistency n listWord8  (vectorsA table) pure+            prop_takeConsistency n listWord16 (vectorsB table) pure+            prop_takeConsistency n listWord32 (vectorsC table) pure+            prop_takeConsistency n listWord64 (vectorsD table) pure+            prop_takeConsistency n listInt8   (vectorsE table) pure+            prop_takeConsistency n listInt16  (vectorsF table) pure+            prop_takeConsistency n listInt32  (vectorsG table) pure+            prop_takeConsistency n listInt64  (vectorsH table) pure+            prop_takeConsistency n listFloat  (vectorsI table) pure+            prop_takeConsistency n listDouble (vectorsJ table) pure+            prop_takeConsistency n listBool   (vectorsK table) pure+            prop_takeConsistency n listText   (vectorsL table) pure++            prop_dropConsistency n listWord8  (vectorsA table) pure+            prop_dropConsistency n listWord16 (vectorsB table) pure+            prop_dropConsistency n listWord32 (vectorsC table) pure+            prop_dropConsistency n listWord64 (vectorsD table) pure+            prop_dropConsistency n listInt8   (vectorsE table) pure+            prop_dropConsistency n listInt16  (vectorsF table) pure+            prop_dropConsistency n listInt32  (vectorsG table) pure+            prop_dropConsistency n listInt64  (vectorsH table) pure+            prop_dropConsistency n listFloat  (vectorsI table) pure+            prop_dropConsistency n listDouble (vectorsJ table) pure+            prop_dropConsistency n listBool   (vectorsK table) pure+            prop_dropConsistency n listText   (vectorsL table) pure++       describe "of structs" $ do         let Right table = decode $ encode $ vectorOfStructs               (Just Vec.empty)@@ -196,7 +253,7 @@               (Just Vec.empty)          it "`unsafeIndex` does not throw when index is negative / too large" $-          testLargeUnsafeIndex table vectorOfStructsAs+          testInvalidUnsafeIndex table vectorOfStructsAs          it "`index` throws when index is negative" $           testNegativeIndex table vectorOfStructsAs@@ -204,12 +261,26 @@         it "`index` throws when index is too large" $           testLargeIndex table vectorOfStructsAs +        it "`take` and `drop` are consistent with Data.List.take and Data.List.drop" $+          requireProperty $ do+            listInt16 <- forAll $ Gen.list (Range.linear 0 20) (Gen.int16 (Range.linear -20 20))+            n <- forAll $ Gen.int32 (Range.linearFrom 0 -10 30)++            table <- evalEither $ decode $ encode $ vectorOfStructs+              Nothing+              (Just (Vec.fromList' (struct2 <$> listInt16)))+              Nothing+              Nothing++            prop_takeConsistency n listInt16 (vectorOfStructsBs table) struct2X+            prop_dropConsistency n listInt16 (vectorOfStructsBs table) struct2X+       describe "of tables" $ do         let Right table = decode $ encode $ vectorOfTables               (Just Vec.empty)          it "`unsafeIndex` does not throw when index is negative / too large" $-          testLargeUnsafeIndex table vectorOfTablesXs+          testInvalidUnsafeIndex table vectorOfTablesXs          it "`index` throws when index is negative" $           testNegativeIndex table vectorOfTablesXs@@ -217,16 +288,88 @@         it "`index` throws when index is too large" $           testLargeIndex table vectorOfTablesXs +        it "`take` and `drop` are consistent with Data.List.take and Data.List.drop" $+          requireProperty $ do+            listInt32 <- forAll $ Gen.list (Range.linear 0 20) (Gen.int32 (Range.linear -20 20))+            n <- forAll $ Gen.int32 (Range.linearFrom 0 -10 30)++            table <- evalEither $ decode $ encode $ vectorOfTables+              (Just (Vec.fromList' (axe . Just <$> listInt32)))++            prop_takeConsistency n listInt32 (vectorOfTablesXs table) axeY+            prop_dropConsistency n listInt32 (vectorOfTablesXs table) axeY+       describe "of unions" $ do         let Right table = decode $ encode $ vectorOfUnions               (Just Vec.empty)               Vec.empty          it "`unsafeIndex` does not throw when index is negative / too large" $-          testLargeUnsafeIndex table vectorOfUnionsXs+          testInvalidUnsafeIndex table vectorOfUnionsXs          it "`index` throws when index is negative" $           testNegativeIndex table vectorOfUnionsXs          it "`index` throws when index is too large" $           testLargeIndex table vectorOfUnionsXs++        it "`take` and `drop` are consistent with Data.List.take and Data.List.drop" $+          requireProperty $ do+            listOfPairs :: [(String, Int32)] <- forAll $ Gen.list (Range.linear 0 20) $ do+              unionType <- Gen.element ["Axe", "Sword"]+              unionVal <- Gen.int32 (Range.linear -20 20)+              pure (unionType, unionVal)++            n <- forAll $ Gen.int32 (Range.linearFrom 0 -10 30)++            let pairToUnion :: (String, Int32) -> WriteUnion Weapon+                pairToUnion = \case+                  ("Axe", val)   -> weaponAxe (axe (Just val))+                  ("Sword", val) -> weaponSword (sword (Just (Text.pack (show val))))++            let unionToPair :: Union Weapon -> Either ReadError (String, Int32)+                unionToPair = \case+                  Union (WeaponAxe axe) -> do+                    val <- axeY axe+                    pure ("Axe", val)+                  Union (WeaponSword sword) -> do+                    textValMaybe <- swordX sword+                    case textValMaybe of+                      Just textVal ->+                        case Text.signed Text.decimal textVal of+                          Right (intVal, _) ->+                            pure ("Sword", intVal)++            table <- evalEither $ decode $ encode $ vectorOfUnions+              (Just (Vec.fromList' (pairToUnion <$> listOfPairs)))+              Vec.empty++            prop_takeConsistency n listOfPairs (vectorOfUnionsXs table) unionToPair+            prop_dropConsistency n listOfPairs (vectorOfUnionsXs table) unionToPair+++prop_takeConsistency ::+  (Eq a, Show a, VectorElement b)+  => Int32+  -> [a]+  -> Either ReadError (Maybe (Vector b))+  -> (b -> Either ReadError a)+  -> PropertyT IO ()+prop_takeConsistency n list vec extract = do+  Just vec <- evalEither vec+  (Vec.toList (Vec.take n vec) >>= traverse extract) === Right (List.take (fromIntegral n) list)+  Vec.length (Vec.take n vec) === fromIntegral (List.length (List.take (fromIntegral n) list))++prop_dropConsistency ::+  (Eq a, Show a, VectorElement b)+  => Int32+  -> [a]+  -> Either ReadError (Maybe (Vector b))+  -> (b -> Either ReadError a)+  -> PropertyT IO ()+prop_dropConsistency n list vec extract = do+  Just vec <- evalEither vec+  (Vec.toList (Vec.drop n vec) >>= traverse extract) === Right (List.drop (fromIntegral n) list)+  Vec.length (Vec.drop n vec) === fromIntegral (List.length (List.drop (fromIntegral n) list))++
test/FlatBuffers/RoundTripSpec.hs view
@@ -11,6 +11,7 @@  import           Control.Applicative ( liftA3 ) +import           Data.Bits           ( (.|.) ) import           Data.Functor        ( (<&>) ) import qualified Data.List           as L import           Data.Maybe          ( isNothing )@@ -95,7 +96,7 @@         (enumsYs x >>= traverse toList >>= traverse (traverse readStructWithEnum)) `shouldBe` Right (Just [(33, Just ColorRed, 44), (55, Just ColorGreen, 66)])        it "present with defaults" $ do-        x <- evalRight $ decode @Enums $ encode $ enums (Just (fromColor ColorGreen)) Nothing Nothing Nothing+        x <- evalRight $ decode @Enums $ encode $ enums (Just 0) Nothing Nothing Nothing          toColor <$> enumsX x `shouldBe` Right (Just ColorGreen)         enumsY x `shouldBeRightAnd` isNothing@@ -110,6 +111,51 @@         enumsXs x `shouldBeRightAnd` isNothing         enumsYs x `shouldBeRightAnd` isNothing +    describe "Enums with bit_flags" $ do+      it "present" $ do+        x <- evalRight $ decode $ encode $ enumsBitFlags+          (Just (colorsRed .|. colorsGreen))+          (Just (structWithEnumBitFlags (colorsGreen .|. colorsGray)))+          (Just (Vec.fromList'+            [ colorsGreen .|. colorsGray+            , colorsBlack .|. colorsBlue+            , colorsGreen+            ]))+          (Just (Vec.fromList'+            [ structWithEnumBitFlags (colorsGreen .|. colorsGray)+            , structWithEnumBitFlags (colorsBlack .|. colorsBlue)+            , structWithEnumBitFlags colorsGreen+            ]))++        enumsBitFlagsX x `shouldBe` Right (colorsRed .|. colorsGreen)+        (enumsBitFlagsY x >>= traverse structWithEnumBitFlagsX) `shouldBe` Right (Just (colorsGreen .|. colorsGray))+        (enumsBitFlagsXs x >>= traverse toList) `shouldBe` Right (Just+          [ colorsGreen .|. colorsGray+          , colorsBlack .|. colorsBlue+          , colorsGreen+          ])+        (enumsBitFlagsYs x >>= traverse toList >>= traverse (traverse structWithEnumBitFlagsX)) `shouldBe` Right (Just+          [ colorsGreen .|. colorsGray+          , colorsBlack .|. colorsBlue+          , colorsGreen+          ])++      it "present with defaults" $ do+        x <- evalRight $ decode @EnumsBitFlags $ encode $ enumsBitFlags (Just 0) Nothing Nothing Nothing++        enumsBitFlagsX x `shouldBe` Right 0+        enumsBitFlagsY x `shouldBeRightAnd` isNothing+        enumsBitFlagsXs x `shouldBeRightAnd` isNothing+        enumsBitFlagsYs x `shouldBeRightAnd` isNothing++      it "missing" $ do+        x <- evalRight $ decode @EnumsBitFlags $ encode $ enumsBitFlags Nothing Nothing Nothing Nothing++        enumsBitFlagsX x `shouldBe` Right 0+        enumsBitFlagsY x `shouldBeRightAnd` isNothing+        enumsBitFlagsXs x `shouldBeRightAnd` isNothing+        enumsBitFlagsYs x `shouldBeRightAnd` isNothing+     describe "Structs" $ do       let readStruct1 = (liftA3 . liftA3) (,,) struct1X struct1Y struct1Z       let readStruct2 = struct2X@@ -209,13 +255,13 @@         testPrimVector getVec expectedList = do           it "non empty" $ do             vec <- evalRightJust (getVec nonEmptyVecs)-            Vec.length vec `shouldBe` Right (L.genericLength expectedList)+            Vec.length vec `shouldBe` L.genericLength expectedList             Vec.toList vec `shouldBe` Right expectedList             traverse (\i -> vec `unsafeIndex` i) [0 .. L.genericLength expectedList - 1] `shouldBe` Right expectedList            it "empty" $ do             vec <- evalRightJust (getVec emptyVecs)-            Vec.length vec `shouldBe` Right 0+            Vec.length vec `shouldBe` 0             Vec.toList vec `shouldBe` Right []            it "missing" $@@ -245,7 +291,7 @@           )          Just xs <- evalRight $ vectorOfTablesXs x-        Vec.length xs `shouldBe` Right 3+        Vec.length xs `shouldBe` 3         (toList xs >>= traverse axeY) `shouldBe` Right [minBound, 0, maxBound]         (traverse (unsafeIndex xs) [0..2] >>= traverse axeY) `shouldBe` Right [minBound, 0, maxBound] @@ -253,7 +299,7 @@         x <- evalRight $ decode $ encode $ vectorOfTables (Just Vec.empty)          xs <- evalRightJust $ vectorOfTablesXs x-        Vec.length xs `shouldBe` Right 0+        Vec.length xs `shouldBe` 0         (toList xs >>= traverse axeY) `shouldBe` Right []        it "missing" $ do@@ -278,19 +324,19 @@         cs <- evalRightJust $ vectorOfStructsCs x         ds <- evalRightJust $ vectorOfStructsDs x -        Vec.length as `shouldBe` Right 2+        Vec.length as `shouldBe` 2         (toList as >>= traverse readStruct1) `shouldBe` Right [(1,2,3), (4,5,6)]         (traverse (unsafeIndex as) [0..1] >>= traverse readStruct1) `shouldBe` Right [(1,2,3), (4,5,6)] -        Vec.length bs `shouldBe` Right 3+        Vec.length bs `shouldBe` 3         (toList bs >>= traverse readStruct2) `shouldBe` Right [101, 102, 103]         (traverse (unsafeIndex bs) [0..2] >>= traverse readStruct2) `shouldBe` Right [101, 102, 103] -        Vec.length cs `shouldBe` Right 3+        Vec.length cs `shouldBe` 3         (toList cs >>= traverse readStruct3) `shouldBe` Right [(104, 105, 106), (107, 108, 109), (110, 111, 112)]         (traverse (unsafeIndex cs) [0..2] >>= traverse readStruct3) `shouldBe` Right [(104, 105, 106), (107, 108, 109), (110, 111, 112)] -        Vec.length ds `shouldBe` Right 3+        Vec.length ds `shouldBe` 3         (toList ds >>= traverse readStruct4) `shouldBe` Right [(120, 121, 122, True), (123, 124, 125, False), (126, 127, 128, True)]         (traverse (unsafeIndex ds) [0..2] >>= traverse readStruct4) `shouldBe` Right [(120, 121, 122, True), (123, 124, 125, False), (126, 127, 128, True)] @@ -303,16 +349,16 @@         cs <- evalRightJust $ vectorOfStructsCs x         ds <- evalRightJust $ vectorOfStructsDs x -        Vec.length as `shouldBe` Right 0+        Vec.length as `shouldBe` 0         (toList as >>= traverse readStruct1) `shouldBe` Right [] -        Vec.length bs `shouldBe` Right 0+        Vec.length bs `shouldBe` 0         (toList bs >>= traverse readStruct2) `shouldBe` Right [] -        Vec.length cs `shouldBe` Right 0+        Vec.length cs `shouldBe` 0         (toList cs >>= traverse readStruct3) `shouldBe` Right [] -        Vec.length ds `shouldBe` Right 0+        Vec.length ds `shouldBe` 0         (toList ds >>= traverse readStruct4) `shouldBe` Right []        it "missing" $ do@@ -346,7 +392,7 @@           )          Just xs <- evalRight $ vectorOfUnionsXs x-        Vec.length xs `shouldBe` Right 3+        Vec.length xs `shouldBe` 3         L.length <$> toList xs `shouldBe` Right 3         xs `unsafeIndex` 0 `shouldBeRightAndExpect` shouldBeSword "hi"         xs `unsafeIndex` 1 `shouldBeRightAndExpect` shouldBeNone@@ -356,7 +402,7 @@         (toList xs <&> (!! 2)) `shouldBeRightAndExpect` shouldBeAxe 98          xsReq <- evalRight $ vectorOfUnionsXsReq x-        Vec.length xsReq `shouldBe` Right 3+        Vec.length xsReq `shouldBe` 3         L.length <$> toList xsReq `shouldBe` Right 3         xsReq `unsafeIndex` 0 `shouldBeRightAndExpect` shouldBeSword "hi2"         xsReq `unsafeIndex` 1 `shouldBeRightAndExpect` shouldBeNone@@ -369,17 +415,17 @@         x <- evalRight $ decode $ encode $ vectorOfUnions (Just Vec.empty) Vec.empty          Just xs <- evalRight $ vectorOfUnionsXs x-        Vec.length xs `shouldBe` Right 0+        Vec.length xs `shouldBe` 0         L.length <$> toList xs `shouldBe` Right 0          xsReq <- evalRight $ vectorOfUnionsXsReq x-        Vec.length xsReq `shouldBe` Right 0+        Vec.length xsReq `shouldBe` 0         L.length <$> toList xsReq `shouldBe` Right 0        it "missing" $ do         x <- evalRight $ decode $ encode $ vectorOfUnions Nothing Vec.empty         vectorOfUnionsXs x `shouldBeRightAnd` isNothing-        (vectorOfUnionsXsReq x >>= Vec.length) `shouldBe` Right 0+        (Vec.length <$> vectorOfUnionsXsReq x) `shouldBe` Right 0      describe "ScalarsWithDefaults" $ do       let runTest buffer = do@@ -399,14 +445,23 @@             scalarsWithDefaultsL x `shouldBe` Right False             toColor <$> scalarsWithDefaultsM x `shouldBe` Right (Just ColorBlue)             toColor <$> scalarsWithDefaultsN x `shouldBe` Right (Just ColorGray)+            scalarsWithDefaultsO x `shouldBe` Right 0+            scalarsWithDefaultsP x `shouldBe` Right (colorsGreen .|. colorsBlue)+            scalarsWithDefaultsQ x `shouldBe` Right colorsRed+            scalarsWithDefaultsR x `shouldBe` Right (colorsGreen .|. colorsGray)        it "present with defaults" $ runTest $ scalarsWithDefaults         (Just 8) (Just 16) (Just 32) (Just 64)         (Just (-1)) (Just (-2)) (Just (-4)) (Just (-8))         (Just 3.9) (Just (-2.3e10)) (Just True) (Just False)         (Just (fromColor ColorBlue)) (Just (fromColor ColorGray))+        (Just 0)+        (Just (colorsGreen .|. colorsBlue))+        (Just colorsRed)+        (Just (colorsGreen .|. colorsGray))        it "missing" $ runTest $ scalarsWithDefaults+        Nothing Nothing Nothing Nothing         Nothing Nothing Nothing Nothing         Nothing Nothing Nothing Nothing         Nothing Nothing Nothing Nothing