Files
Ian Denhardt 59289f541a Use string literals for bytestrings constants
..rather than BS.pack [0,1,2,...]

This makes a *dramatic* difference on both the (haskell source) size and
(ghc) build time of the modules generated from the sandstorm schema.

One particularly pathological example: sandstorm's mime.capnp is
dominated by one giant table in the form of a constant declaration,
with both keys and values being Text. The source file is about 56KiB.
The size of the output, with the old version of the code generator, was
almost 4MiB! The new output size is 196KiB, which is much more
reasonable. Build time for that file is now quite fast, whereas before
it was one of the slowest ones.

Part of the problem is the way we were pretty-printing things, which
could have been solved in other ways, but this will also improve the
size of constants that are mostly text, even without that. I also
suspect this will improve build times more than just fixing the
pretty-printing would.
2019-01-23 10:56:06 -05:00

198 lines
5.1 KiB
Haskell

{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
-- A last-leg intermediate form, before emitting actually Haskell source code.
--
-- This module contains a *simplified* Haskell AST; it only includes the
-- features we actually need. We roll our own for two reasons:
--
-- 1. Full Haskell ASTs are actually quite complex, and the data types exposed
-- by libraries for working with Haskell source reflect this. What we need
-- is much simpler.
-- 2. At some point we'll want to inject Haddock comments into the source (see
-- #32), and I(zenhack) don't see a good way to do that with the libraries
-- I looked at.
module IR.Haskell
( modFilePath
, DataArgs(..)
, DataDecl(..)
, DataVariant(..)
, ClassDecl(..)
, Decl(..)
, Do(..)
, Exp(..)
, Export(..)
, Import(..)
, InstanceDef(..)
, Module(..)
, Pattern(..)
, Type(..)
, TypeAlias(..)
, ValueDef(..)
) where
import Data.List (intercalate)
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text as T
import qualified IR.Common as Common
import qualified IR.Name as Name
-- | A Haskell module
data Module = Module
{ modName :: [Name.UnQ]
-- ^ The parts of the module path
, modLangPragmas :: [T.Text]
-- ^ The language extensions enabled in this module.
, modExports :: Maybe [Export]
-- ^ The export list. If it is 'Nothing' that means there is no export
-- list (and therefore everything is exported).
, modDecls :: [Decl]
-- ^ The declarations in the module.
, modImports :: [Import]
-- ^ Modules to import.
}
deriving(Show, Read, Eq)
data Export
= ExportMod [Name.UnQ] -- module Foo.Bar
| ExportGCtors Name.GlobalQ -- Foo.Bar.Baz(..)
| ExportLCtors Name.LocalQ -- Baz(..)
| ExportGName Name.GlobalQ -- Foo.Bar.Baz
| ExportLName Name.LocalQ -- Baz
deriving(Show, Read, Eq)
data Import
= ImportAs -- import qualified Foo.Bar as Bar
{ importAs :: Name.UnQ
, parts :: [Name.UnQ]
}
| ImportQual -- import qualified Foo.Bar
{ parts :: [Name.UnQ]
}
| ImportAll -- import Foo.Bar
{ parts :: [Name.UnQ]
}
deriving(Show, Read, Eq)
-- | A declaration.
data Decl
= DcData DataDecl
| DcValue
{ typ :: Type
, def :: ValueDef
}
| DcInstance
{ ctx :: [Type]
, typ :: Type
, defs :: [InstanceDef]
}
| DcClass
{ ctx :: [Type]
, name :: Name.LocalQ
, params :: [Name.UnQ]
, decls :: [ClassDecl]
}
deriving(Show, Read, Eq)
data DataDecl = Data
{ dataName :: Name.UnQ
-- ^ The name of the declared type.
, typeArgs :: [Type]
, dataVariants :: [DataVariant]
-- ^ The variants/data constructors for the type.
, derives :: [Name.UnQ]
-- ^ A list of type classes to include in the deriving clause.
, dataNewtype :: !Bool
-- ^ Whether the declarationis a "newtype" or "data" declaration.
}
deriving(Show, Read, Eq)
data ClassDecl
= CdValueDecl Name.UnQ Type
| CdValueDef ValueDef
| CdMinimal [Name.UnQ]
-- ^ A MINIMAL pragma.
deriving(Show, Read, Eq)
data InstanceDef
= IdValue ValueDef
| IdData DataDecl
| IdType TypeAlias
deriving(Show, Read, Eq)
data TypeAlias = TypeAlias Name.UnQ [Type] Type
deriving(Show, Read, Eq)
data ValueDef = DfValue
{ name :: Name.UnQ
, value :: Exp
, params :: [Pattern]
}
deriving(Show, Read, Eq)
-- | A data constructor
data DataVariant = DataVariant
{ dvCtorName :: Name.UnQ
-- ^ The name of the constructor.
, dvArgs :: DataArgs
}
deriving(Show, Read, Eq)
-- | Arguments to a data constructor
data DataArgs
= APos [Type]
| ARec [(Name.UnQ, Type)]
deriving(Show, Read, Eq)
data Type
= TGName Name.GlobalQ
| TLName Name.LocalQ
| TVar T.Text
| TApp Type [Type]
| TFn [Type]
| TCtx [Type] Type
| TPrim Common.PrimWord
| TUnit
deriving(Show, Read, Eq)
data Exp
= EApp Exp [Exp]
| EFApp Exp [Exp]
-- ^ "Functorial" application, i.e. f <$> x <*> y <*> z.
| EGName Name.GlobalQ
| ELName Name.LocalQ
| EInt Integer
| EDo [Do] Exp
| EBind Exp Exp
-- ^ A call to (>>=)
| ETup [Exp]
| EBytes LBS.ByteString
| ECase Exp [(Pattern, Exp)]
| ETypeAnno Exp Type
| ELambda [Pattern] Exp
| ERecord Exp [(Name.UnQ, Exp)]
deriving(Show, Read, Eq)
data Do
= DoBind Name.UnQ Exp
| DoE Exp
deriving(Show, Read, Eq)
data Pattern
= PVar T.Text
| PLCtor Name.LocalQ [Pattern]
| PGCtor Name.GlobalQ [Pattern]
| PInt Integer
-- | Name{..}
| PLRecordWildCard Name.LocalQ
deriving(Show, Read, Eq)
-- | Get the file path for a module. For example, the module @Foo.Bar.Baz@ will
-- have a file path of @Foo/Bar/Baz.hs@.
modFilePath :: Module -> FilePath
modFilePath Module{modName} =
intercalate "/" (map (T.unpack . Name.renderUnQ) modName) ++ ".hs"