capnp-0.3.0.0: exe/capnpc-haskell/IR.hs
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}
-- This module defines datatypes that represent something between the capnp
-- schema and Haskell code. The representation has the following
-- chracteristics:
--
-- * Type definitions can map directly to idiomatic haskell types. Both the capnp
-- schema format itself and the types defined for it in schema.capnp have some
-- quirks that need to be ironed out to perform the mapping:
-- * Unions and structs are not separate types; a union is just a (possibly
-- anonymous) field within a struct.
-- * Groups are only kindof their own type.
-- * Schema can have nested, mutually recursive namespaces even within a
-- single file. We want to generate a module with a flat namespace.
-- * Even features that conceptually map simply have somewhat odd
-- representations and irregularities schema.capnp
-- Our intermediate representation just has data declarations, with
-- enough information attached to access each variant and argument.
-- * Names are fully-qualified; see the 'Name' type for more information
-- (TODO).
module IR
( Name(..)
, Namespace(..)
, Module(..)
, ModuleRef(..)
, Import(..)
, Type(..)
, CompositeType(..)
, WordType(..)
, PtrType(..)
, PrimWord(..)
, PrimPtr(..)
, AnyPtr(..)
, Variant(..)
, VariantParams(..)
, Field(..)
, Decl(..)
, DataDef(..)
, StructDef(..)
, Const(..)
, FieldLocType(..)
, DataLoc(..)
, StructInfo(..)
, subName
, prefixName
, valueName
) where
import Data.Word
import Data.Char (toLower)
import Data.String (IsString(fromString))
import Data.Text (Text)
import Data.Monoid ((<>))
import GHC.Exts (IsList(..))
import qualified Data.Map.Strict as M
import qualified Data.Text as T
import Util
import qualified Data.Capnp.Untyped.Pure as Untyped
newtype Namespace = Namespace [Text]
deriving(Show, Read, Eq, Ord)
instance IsList Namespace where
type Item Namespace = Text
fromList = Namespace
toList (Namespace parts) = parts
data Module = Module
{ modId :: Id
, modName :: Namespace
, modFile :: Text
, modImports :: [Import]
-- XXX TODO: 'Name' includes a 'ModuleRef', which means it is possible
-- for two different 'Name's to refer to the same actual variable; using
-- them as map keys is questionable. In practice, when building a module
-- all of the ModuleRefs are the same, though, so this won't *break*
-- anything -- but ideally we'd remove that information from the key.
, modDecls :: M.Map Name Decl
}
deriving(Show, Read, Eq)
data Decl
= DeclDef DataDef
| DeclConst Const
deriving(Show, Read, Eq)
newtype Import = Import ModuleRef
deriving(Show, Read, Eq)
data ModuleRef
= FullyQualified Namespace
| ByCapnpId Id
deriving(Show, Read, Eq, Ord)
data Name = Name
{ nameModule :: ModuleRef
, nameLocalNS :: Namespace
, nameUnqualified :: Text
}
deriving(Show, Read, Eq, Ord)
subName :: Name -> Text -> Name
subName name@Name{..} nextPart = name
{ nameLocalNS = fromList $ toList nameLocalNS ++ [nameUnqualified]
, nameUnqualified = nextPart
}
prefixName :: Text -> Name -> Name
prefixName prefix name@Name{nameLocalNS=(toList -> (x:xs))} =
name { nameLocalNS = fromList $ (prefix <> x):xs }
prefixName prefix name = name { nameLocalNS = fromList [prefix] }
-- | 'valueName' converts a name to one which starts with a lowercase
-- letter, so that it is valid to use as a name for a value (as opposed
-- to a type).
valueName :: Name -> Name
valueName name@Name{nameLocalNS=(toList -> (T.unpack -> (c:cs)):xs)} =
name { nameLocalNS = fromList $ T.pack (toLower c : cs) : xs }
valueName name = name
instance IsString Name where
fromString str = Name
{ nameModule = FullyQualified []
, nameLocalNS = []
, nameUnqualified = T.pack str
}
data Type
= CompositeType CompositeType
| VoidType
| WordType WordType
| PtrType PtrType
deriving(Show, Read, Eq)
data CompositeType
= StructType Name [Type]
deriving(Show, Read, Eq)
data WordType
= EnumType Name
| PrimWord PrimWord
deriving(Show, Read, Eq)
data PtrType
= ListOf Type
| PrimPtr PrimPtr
| PtrComposite CompositeType
deriving(Show, Read, Eq)
data PrimWord
= PrimInt { isSigned :: !Bool, size :: !Int }
| PrimFloat32
| PrimFloat64
| PrimBool
deriving(Show, Read, Eq)
data PrimPtr
= PrimText
| PrimData
| PrimAnyPtr AnyPtr
deriving(Show, Read, Eq)
data AnyPtr
= Struct
| List
| Cap
| Ptr
deriving(Show, Read, Eq)
data Variant = Variant
{ variantName :: Name
, variantParams :: VariantParams
, variantTag :: !Word16
}
deriving(Show, Read, Eq)
data VariantParams
= Unnamed Type FieldLocType
| Record [Field]
deriving(Show, Read, Eq)
data Field = Field
{ fieldName :: Text
, fieldLocType :: FieldLocType
}
deriving(Show, Read, Eq)
data Const
= WordConst
{ wordValue :: Word64
, wordType :: WordType
}
| VoidConst
| PtrConst
{ ptrValue :: Maybe Untyped.PtrType
, ptrType :: PtrType
}
deriving(Show, Read, Eq)
data DataDef
= DefUnion
{ dataVariants :: [Variant]
-- | The location of the tag for the union.
, dataTagLoc :: DataLoc
-- | The name of the containing struct.
, parentStructName :: Name
, parentStruct :: StructDef
}
| DefStruct StructDef
| DefEnum [Name]
deriving(Show, Read, Eq)
data StructDef = StructDef
{ fields :: [Field]
, info :: StructInfo
}
deriving(Show, Read, Eq)
data StructInfo
= IsGroup
-- ^ The struct is really a group.
| IsStandalone
{ dataSz :: !Word16
, ptrSz :: !Word16
}
-- ^ The struct is a full-fledged struct that can be directly
-- allocated, stored in a list, etc. Info contains the sizes
-- of its sections.
deriving(Show, Read, Eq)
-- | The type and location of a field.
data FieldLocType
-- | The field is in the struct's data section.
= DataField DataLoc WordType
-- | The field is in the struct's pointer section (the argument is the
-- index).
| PtrField !Word16 PtrType
-- | The field is a group or union; it's "location" is the whole struct.
| HereField CompositeType
-- | The field is of type void (and thus is zero-size).
| VoidField
deriving(Show, Read, Eq)
-- | The location of a field within a struct's data section.
data DataLoc = DataLoc
{ dataIdx :: !Int
-- ^ The index of the 64-bit word containing the field.
, dataOff :: !Int
, dataDef :: !Word64
-- ^ The value is stored xor-ed with this value. This is used
-- to allow for encoding default values. Note that this is xor-ed
-- with the bits representing the value, not the whole word.
}
deriving(Show, Read, Eq)