-- Environment for tracing transformation. -- Holds information about all identifiers (names) in scope. module Environment (Environment,Identifier,AuxiliaryInfo,Entity,HxEntity ,lookupExpEnv, lookupTypeEnv, eArity, eNo, eBody, eCons, eFields ,isClass,isSyn,isType ,TySynBody(TApp,TFun,THelper,TVar) ,Scope(Local,Global),isLocal ,globalEnv,moduleDefines,prettyEnv ,declsEnv,instanceEnv,maybeBindsEnv,bindsEnv, patsEnv ,arity,isUndefinedMethod,isLambdaBound,isTracedQName,fixPriority, hasPriority ,isExpandableTypeSynonym,typeSynonymBody ,nameTransTySynHelper,expandTypeSynonym ,imports,exports,hxEnvironmentToList,listToHxEnvironment ,defineNameEnv,env2Fixities,makeAllLambdaBound, lambdaVarEnv ,wiredEnv ) where import Language.Haskell.Exts hiding (Var,Con,Fixity,EVar,TApp) import qualified Language.Haskell.Exts as Syntax (Exp(Var,Con),Fixity(Fixity),ExportSpec(EVar) ,Namespace(NoNamespace,TypeNamespace)) -- import qualified Language.Haskell.Exts as -- Short(Assoc(..),QName(..),Name(..),SpecialCon(Cons)) import SynHelp (Id(getId),getQualified,mkQual,qual,isQual,isUnQual,notSupported ,tyVarBind2Name,declHeadTyVarBinds,declHeadName,instRuleQName,getArityFromConDecl ,getConDeclFromQualConDecl,getConstructorFromConDecl,eqName,mkName,mkQName ,getFieldNamesFromConDecl,decomposeFunType,isFunTyCon,tyAppN,getModuleNameFromModule ,UpdId(updateId),dropAnn,noSpan) import Wired (qNamePreludeTrue,qNamePreludeFalse) import qualified Data.Set as Set import qualified Data.Map as Map (singleton,adjust) import Relation import Data.Maybe (fromMaybe,fromJust) import Data.List (nubBy) import Data.Char (isAlpha) import Debug.Trace data Scope = Global | Local isLocal :: Scope -> Bool isLocal Local = True isLocal Global = False -- The following types for representing HxEntities are unnecessarily complicated and bizarr. -- However, their Show instances are used in the hx-files, and hence any type changes -- would mean changing the hx-file format and thus rebooting Hat from scratch. -- Only do that when it becomes really necessary. type HxEntity = (Identifier,AuxiliaryInfo) -- Identifier is used to distinguish varids from conids, and relate -- conids back to the type they belong to. It also relates methods -- to their class. data Identifier = Var String | Con TypeSort String{-type-} String{-con-} | Field String{-type-} String{-field-} | Method String{-class-} String{-method-} | TypeClass String deriving (Show,Read,Eq,Ord) data TypeSort = Data | Newtype deriving (Show,Read,Eq,Ord) toTypeSort :: DataOrNew l -> TypeSort toTypeSort (DataType _) = Data toTypeSort (NewType _) = Newtype -- AuxiliaryInfo is the extra information we need to know about identifiers. data AuxiliaryInfo = Value {- variable or constructor -} { args :: Int , fixity :: Fixity , priority :: Int , letBound :: Bool , traced :: Bool} | TyCls TyCls -- needed for im/export of (..) deriving (Eq,Ord,Show,Read) data Fixity = L | R | Pre String | Def | None deriving (Eq,Ord,Show,Read) data TyCls = Ty [String]{- data constructors -} [String]{- field labels -} | Cls [String]{- methods -} | Syn Int {- no. helper type syns -} TySynBody deriving (Eq,Ord,Show,Read) data TySynBody = TApp TySynBody TySynBody | TFun | THelper | TVar Int {- arg no. -} deriving (Eq,Ord,Show,Read) -- SrcSpanInfo for the definition of a name. -- For variables and methods the span encompasses the complete definition (equations). -- For data constructors and fields the span encompasses only the name itself. -- From infix declarations and type signatures only an empty src can be determined. -- Methods can have many spans, as there may be many definitions in a class and several instances. -- Fields can have many spans, but only one is currently being used (dubious). -- For types and classes there is no span. data Entity = EVar {eId :: String, eSrc :: SrcSpanInfo, eArity :: Int, eFixity :: Fixity, ePriority :: Int ,eLetBound :: Bool, eTraced :: Bool} | ECon {eId :: String, eSrc :: SrcSpanInfo, eArity :: Int, eFixity :: Fixity, ePriority :: Int ,eTraced :: Bool, eTy :: String, eTySort :: TypeSort, eFields :: [String]} | EField {eId :: String, eSrcs :: [SrcSpanInfo], eArity :: Int, eFixity :: Fixity, ePriority :: Int ,eTraced :: Bool, eTy :: String} | EMethod {eId :: String, eSrcs :: [SrcSpanInfo], eArity :: Int, eFixity :: Fixity, ePriority :: Int ,eTraced :: Bool, eCls :: String} | EType {eId :: String, eCons :: [String], eFields :: [String]} | ESyn {eId :: String, eNo :: Int, eBody :: TySynBody} | EClass {eId :: String, eMethods :: [String]} | ESig {eId :: String} -- type signature for an expression identifier, needed for methods in classes | EInfix {eId :: String, eFixity :: Fixity, ePriority :: Int} -- infix info for an expression identifier deriving (Eq, Ord, Show) defaultVar :: Entity defaultVar = EVar {eId = undefined, eSrc = noSpan, eArity = undefined, eFixity = Def ,ePriority = 9, eLetBound = True, eTraced = undefined} eVar :: String -> SrcSpanInfo -> Int -> Bool -> Entity eVar id s a t = defaultVar {eId = id, eSrc = s, eArity = a, eTraced = t} defaultCon :: Entity defaultCon = ECon {eId = undefined, eSrc = noSpan, eArity = undefined, eFixity = Def ,ePriority = 9, eTraced = undefined ,eTy = undefined, eTySort = undefined, eFields = undefined} eCon :: String -> SrcSpanInfo -> Int -> String -> TypeSort -> [String] -> Bool -> Entity eCon id s a ty ts fields t = defaultCon {eId = id, eSrc = s, eArity = a, eTy = ty, eTySort = ts, eFields = fields, eTraced = t} defaultField :: Entity defaultField = EField {eId = undefined, eSrcs = [], eFixity = Def, ePriority = 9 ,eArity = 1, eTraced = False, eTy = undefined} eField :: String -> SrcSpanInfo -> String -> Bool -> Entity eField id s ty t = defaultField {eId = id, eSrcs = [s], eTy = ty, eTraced = t} defaultMethod :: Entity defaultMethod = EMethod {eId = undefined, eSrcs = [], eFixity = Def, ePriority = 9 ,eArity = -1, eTraced = False, eCls = undefined} eMethod :: String -> [SrcSpanInfo] -> String -> Bool -> Entity eMethod id s cls t = defaultMethod {eId = id, eSrcs = s, eCls = cls, eTraced = t} eType :: String -> [String] -> [String] -> Entity eType id cons fields = EType {eId = id, eCons = cons, eFields = fields} eSyn :: String -> Int -> TySynBody -> Entity eSyn id n body = ESyn {eId = id, eNo = n, eBody = body} eClass :: String -> [String] -> Entity eClass id methods = EClass {eId = id, eMethods = methods} eSig :: String -> Entity eSig id = ESig {eId = id} eInfix :: String -> Fixity -> Int -> Entity eInfix id f p = EInfix {eId = id, eFixity = f, ePriority = p} type Environment = Relation (QName ()) Entity isVar :: Entity -> Bool isVar EVar{} = True isVar _ = False isCon :: Entity -> Bool isCon ECon{} = True isCon _ = False isField :: Entity -> Bool isField EField{} = True isField _ = False isMethod :: Entity -> Bool isMethod EMethod{} = True isMethod _ = False isType :: Entity -> Bool isType EType{} = True isType _ = False isSyn :: Entity -> Bool isSyn ESyn{} = True isSyn _ = False isClass :: Entity -> Bool isClass EClass{} = True isClass _ = False isSig :: Entity -> Bool isSig ESig{} = True isSig _ = False isInfix :: Entity -> Bool isInfix EInfix{} = True isInfix _ = False -- Is an expression entity, that is, variable, data constructor, method or field. isExp :: Entity -> Bool isExp e = isVar e || isCon e || isField e || isMethod e || isSig e || isInfix e -- Is a type, type synoym or class isTySynClass :: Entity -> Bool isTySynClass e = isType e || isSyn e || isClass e entity2HxEntity :: Entity -> HxEntity entity2HxEntity e | isVar e = (Var (eId e) ,Value{args = eArity e, fixity = eFixity e, priority = ePriority e, letBound = eLetBound e ,traced = eTraced e}) | isCon e = (Con (eTySort e) (eTy e) (eId e) ,Value{args = eArity e, fixity = eFixity e, priority = ePriority e, letBound = True, traced = eTraced e}) | isField e = (Field (eTy e) (eId e) ,Value{args = eArity e, fixity = eFixity e, priority = ePriority e, letBound = True, traced = eTraced e}) | isMethod e = (Method (eCls e) (eId e) ,Value{args = eArity e, fixity = eFixity e, priority = ePriority e, letBound = True, traced = eTraced e}) | isType e = (TypeClass (eId e), TyCls (Ty (eCons e) (eFields e))) | isSyn e = (TypeClass (eId e), TyCls (Syn (eNo e) (eBody e))) | isClass e = (TypeClass (eId e), TyCls (Cls (eMethods e))) | otherwise = error "Environment.entity2HxEntity: type signature or infix without definition." hxEntity2Entity :: HxEntity -> Entity hxEntity2Entity (Var id, val) = EVar {eId = id, eSrc = noSpan, eArity = args val, eFixity = fixity val ,ePriority = priority val, eLetBound = letBound val, eTraced = traced val} hxEntity2Entity (Con s t id, val) = ECon {eId = id, eSrc = noSpan, eArity = args val, eFixity = fixity val, ePriority = priority val ,eTraced = traced val, eTy = t, eTySort = s, eFields = []} hxEntity2Entity (Field t id, val) = EField {eId = id, eSrcs = [], eArity = args val, eFixity = fixity val, ePriority = priority val ,eTraced = traced val, eTy = t} hxEntity2Entity (Method cls id, val) = EMethod {eId = id, eSrcs = [], eArity = args val, eFixity = fixity val, ePriority = priority val ,eTraced = traced val, eCls = cls} hxEntity2Entity (TypeClass id, TyCls (Ty cons fields)) = EType {eId = id, eCons = cons, eFields = fields} hxEntity2Entity (TypeClass id, TyCls (Syn n body)) = ESyn {eId = id, eNo = n, eBody = body} hxEntity2Entity (TypeClass id, TyCls (Cls methods)) = EClass {eId = id, eMethods = methods} -- Produce environment for one unqualified name. singleton :: Entity -> Environment singleton entity = Map.singleton (UnQual () (mkName () (eId entity))) (Set.singleton entity) -- Produce environment of unqualified names. fromList :: [Entity] -> Environment fromList = listToRelation . map (\entity -> (UnQual () (mkName () (eId entity)),entity)) -- Is the given entity owned by any entity in the given set? -- Ownership means constructor/field of a data type or method of a class. isOwned :: Entity -> Set.Set Entity -> Bool isOwned e owners | isCon e = not . Set.null . Set.filter (isTypeId (eTy e)) $ owners | isField e = not . Set.null . Set.filter (isTypeId (eTy e)) $ owners | isMethod e = not . Set.null . Set.filter (isClassId (eCls e)) $ owners | otherwise = False -- Check whether given entity is a type of given id. isTypeId :: String -> Entity -> Bool isTypeId id e = isType e && id == eId e -- Check whether given entity is a class of given id. isClassId :: String -> Entity -> Bool isClassId id e = isClass e && id == eId e -- ----------------------------------------------------------------------------------- -- Obtain an environment from parts of a syntax tree. lambdaVarEnv :: Bool -> QName SrcSpanInfo -> Environment lambdaVarEnv tracing id = singleton ((eVar (getId id) (ann id) 0 tracing) {eLetBound=False}) -- Create global environment of this module, given whether it is tracing (or trusted) and -- the complete import environment. globalEnv :: Bool -> Module SrcSpanInfo -> Environment -> Environment globalEnv tracing mod importEnv = env where env = unionRelations [importEnv,unqualDefEnv,qualDefEnv] unqualDefEnv = moduleDefines tracing env mod -- tying knot of environments here qualDefEnv = qual (dropAnn (getModuleNameFromModule mod)) `mapDom` unqualDefEnv -- Get the environment for all top-level definitions of a module. -- Post-condition: Only produce unqualified QNames. -- Take complete environment of module for lookups (cycle) moduleDefines :: Bool -> Environment -> Module SrcSpanInfo -> Environment moduleDefines tracing fullEnv (Module _ _ _ _ decls) = declsEnv tracing fullEnv decls -- Determine local environment for given optional binds. -- Post-condition: environment will only contain local variable bindings, -- no types, classes, data constructors, fields or methods. maybeBindsEnv :: Bool -> Maybe (Binds SrcSpanInfo) -> Environment maybeBindsEnv tracing = maybe emptyRelation (bindsEnv tracing) -- Determine local environment for given binds. -- Post-condition: environment will only contain local variable bindings, -- no types, classes, data constructors, fields or methods. bindsEnv :: Bool -> Binds SrcSpanInfo -> Environment bindsEnv tracing (BDecls _ decls) = declsEnv tracing emptyRelation decls bindsEnv _ (IPBinds l _) = notSupported l "binding group for implicit parameters" -- Note that a fixity or type signature definition also yield an entity in the environment. -- Such an entity needs to be merged with its main entity. declsEnv :: Bool -> Environment -> [Decl SrcSpanInfo] -> Environment declsEnv tracing fullEnv decls = unionRelationsWith mergeSets (map (declEnv tracing fullEnv) decls) where mergeSets :: Set.Set Entity -> Set.Set Entity -> Set.Set Entity mergeSets s1 s2 = Set.unions [vs,e1s,e2s] where (v1s,e1s) = Set.partition isExp s1 (v2s,e2s) = Set.partition isExp s2 vs = if Set.null v1s then v2s else if Set.null v2s then v1s else if Set.size v1s > 1 || Set.size v2s > 1 then error "Environment.declsEnv: conflicting expr names." else Set.singleton (mergeEntities (Set.findMin v1s) (Set.findMin v2s)) -- pre-condition: both entities are expression entities. mergeEntities :: Entity -> Entity -> Entity mergeEntities e1 e2 | isSig e1 = e2 | isSig e2 = e1 | isInfix e1 = e2 {eFixity = eFixity e1, ePriority = ePriority e1} | isInfix e2 = e1 {eFixity = eFixity e2, ePriority = ePriority e2} | (isMethod e1 && isMethod e2) || (isField e1 && isField e2) = e1 {eSrcs = eSrcs e1 ++ eSrcs e2 ,eFixity = eFixity e1 `mergeFixities` eFixity e2 ,ePriority = ePriority e1 `mergePriorities` ePriority e2} | otherwise = error "Environment.mergeEntities: cannot be merged." mergeFixities :: Fixity -> Fixity -> Fixity mergeFixities Def f = f mergeFixities f Def = f mergeFixities _ _ = error "Environment.moduleDefines: conflicting fixities." mergePriorities :: Int -> Int -> Int mergePriorities p1 p2 | p1 == 9 = p2 | p2 == 9 = p1 | otherwise = error "Environment.moduleDefines: conflicting priorities." declEnv :: Bool -> Environment -> Decl SrcSpanInfo -> Environment declEnv _ fullEnv (TypeDecl _ declHead ty) = singleton (eSyn (getId tyName) n body) where tyName = declHeadName declHead Syn n body = splitSynonym fullEnv (map tyVarBind2Name (declHeadTyVarBinds declHead)) ty declEnv _ _ (TypeFamDecl l declHead _ _) = notSupported l "type family declaration" declEnv _ _ (ClosedTypeFamDecl l _ _ _ _) = notSupported l "closed type family declaration" declEnv tracing _ (DataDecl _ ts _ declHead qualConDecls _) = fromList (eType (getId name) (map getId consNames) (map getId fieldNames) : [eCon (getId consName) (ann consName) (getArityFromConDecl conDecl) (getId name) (toTypeSort ts) (map getId (getFieldNamesFromConDecl conDecl)) tracing |conDecl <- conDecls, let consName = getConstructorFromConDecl conDecl] ++ [eField (getId fieldName) (ann fieldName) (getId name) tracing |fieldName <- fieldNames]) where name = declHeadName declHead conDecls = map getConDeclFromQualConDecl qualConDecls consNames = map getConstructorFromConDecl conDecls fieldNames = nubBy eqName (concatMap getFieldNamesFromConDecl conDecls) declEnv _ _ (GDataDecl l _ _ declHead _ gadtDecls _) = notSupported l "gadt declaration" declEnv _ _ (DataFamDecl l declHead _ _) = notSupported l "data family declaration" declEnv _ _ (TypeInsDecl _ _ _) = emptyRelation declEnv _ _ (DataInsDecl l _ _ _ _) = notSupported l "data family instance declaration" declEnv _ _ (GDataInsDecl l _ _ _ _ _) = notSupported l "gadt family instance declaration" declEnv _ _ (ClassDecl _ _ declHead _ Nothing) = singleton (eClass (getId className) []) where className = declHeadName declHead declEnv tracing finalEnv (ClassDecl _ _ declHead _ (Just classDecls)) = unionRelations [singleton (eClass classId methodIds), methodRelation] where methodIds = map eId . Set.toAscList . rng $ methodRelation methodRelation = sig2Method `mapRng` (declsEnv tracing finalEnv decls) classId = getId className className = declHeadName declHead sig2Method :: Entity -> Entity sig2Method e | isSig e = eMethod (eId e) [] classId tracing | isVar e = eMethod (eId e) [eSrc e] classId tracing | otherwise = error "Environment.declEnv: unexpected declaration in class." decls = map getDecl classDecls getDecl :: ClassDecl SrcSpanInfo -> Decl SrcSpanInfo getDecl (ClsDecl _ decl) = decl getDecl (ClsDataFam l _ _ _) = notSupported l "associated data type declaration" getDecl (ClsTyFam l _ _ _) = notSupported l "associated type synonym declaration" getDecl (ClsTyDef l _) = notSupported l "default choice for an associated type synonym" declEnv _ _ (InstDecl _ _ _ Nothing) = emptyRelation declEnv tracing finalEnv (InstDecl _ _ instHead (Just instDecls)) = emptyRelation -- All entities defined in instance should be already in scope anyway; -- class declarations also declare the methods. -- Have to avoid duplicate entries in environment. -- var2Method `mapRng` (declsEnv tracing finalEnv (map getDecl instDecls)) -- where -- classId = getId (instHeadQName instHead) -- var2Method :: Entity -> Entity -- var2Method e | isVar e = eMethod (eId e) [eSrc e] classId tracing -- | otherwise = error "Environment.declEnv: unexpected declaration in instance." -- getDecl :: InstDecl SrcSpanInfo -> Decl SrcSpanInfo -- getDecl (InsDecl _ decl) = decl -- getDecl (InsType l _ _) = notSupported l "associated type definition" -- getDecl (InsData l _ _ _ _) = notSupported l "associated data type implementation" -- getDecl (InsGData l _ _ _ _ _) = notSupported l "GADT style assoicated data type type implementation" declEnv _ _ (DerivDecl _ _ _) = emptyRelation declEnv tracing _ (InfixDecl _ assoc maybePri ops) = fromList (map op2Entity ops) -- Environment only has valid entity information for fixity and priority, -- otherwise default values. where op2Entity :: Op l -> Entity op2Entity (VarOp _ name) = eInfix (getId name) fixityVal priorityVal op2Entity (ConOp _ name) = eInfix (getId name) fixityVal priorityVal -- Use Var here as well, as other arguments for Con are unknown here. fixityVal = case assoc of AssocNone _ -> None AssocLeft _ -> L AssocRight _ -> R priorityVal = fromMaybe 9 maybePri declEnv _ _ (DefaultDecl _ _) = emptyRelation declEnv _ _ (SpliceDecl l _) = notSupported l "splice declaration" declEnv tracing _ (TypeSig _ names _) = fromList [eSig (getId name) | name <- names] -- needed for methods in classes declEnv tracing _ (FunBind l matches) = matchEnv l tracing (head matches) declEnv tracing _ (PatBind l pat _ _) = patEnv l tracing pat declEnv tracing _ (ForImp l _ _ _ name ty) = -- only for NoHat. import singleton (eVar (getId name) l (length tyArgs) tracing) where (tyArgs,_) = decomposeFunType ty declEnv _ _ (ForExp _ _ _ _ _) = emptyRelation declEnv _ _ (RulePragmaDecl _ _) = emptyRelation declEnv _ _ (DeprPragmaDecl _ _) = emptyRelation declEnv _ _ (WarnPragmaDecl _ _) = emptyRelation declEnv _ _ (InlineSig _ _ _ _) = emptyRelation declEnv _ _ (InlineConlikeSig _ _ _) = emptyRelation declEnv _ _ (SpecSig _ _ _ _) = emptyRelation declEnv _ _ (SpecInlineSig _ _ _ _ _) = emptyRelation declEnv _ _ (InstSig _ _) = emptyRelation declEnv _ _ (AnnPragma _ _) = emptyRelation declEnv _ _ (MinimalPragma _ _) = emptyRelation -- Produce local environment for a non-empty class instance definition. instanceEnv :: Bool -> Environment -> Decl SrcSpanInfo -> Environment instanceEnv tracing finalEnv (InstDecl l maybeContext instRule (Just instDecls)) = var2Method `mapRng` (declsEnv tracing finalEnv (map getDecl instDecls)) where classId = getId (instRuleQName instRule) var2Method :: Entity -> Entity var2Method e | isVar e = eMethod (eId e) [eSrc e] classId tracing | otherwise = error "Environment.declEnv: unexpected declaration in instance." getDecl :: InstDecl SrcSpanInfo -> Decl SrcSpanInfo getDecl (InsDecl _ decl) = decl getDecl (InsType l _ _) = notSupported l "associated type definition" getDecl (InsData l _ _ _ _) = notSupported l "associated data type implementation" getDecl (InsGData l _ _ _ _ _) = notSupported l "GADT style assoicated data type type implementation" matchEnv :: SrcSpanInfo -> Bool -> Match SrcSpanInfo -> Environment matchEnv l tracing (Match _ name pats _ _) = singleton (eVar (getId name) l (length pats) tracing) matchEnv l tracing (InfixMatch _ pat name pats rhs maybeBinds) = matchEnv l tracing (Match l name (pat:pats) rhs maybeBinds) patsEnv :: SrcSpanInfo -> Bool -> [Pat SrcSpanInfo] -> Environment patsEnv l tracing pats = unionRelations (map (patEnv l tracing) pats) -- All occurring variables are let-bound, because this function is for -- pattern bindings. -- Pass location of the whole definition, because that is recorded in environment. patEnv :: SrcSpanInfo -> Bool -> Pat SrcSpanInfo -> Environment patEnv l tracing (PVar _ name) = singleton (eVar (getId name) l 0 tracing) patEnv _ _ (PLit _ _ _) = emptyRelation patEnv l tracing (PNPlusK l2 name _) = patEnv l tracing (PVar l2 name) patEnv l tracing (PInfixApp _ patl _ patr) = unionRelations [patEnv l tracing patl, patEnv l tracing patr] patEnv l tracing (PApp _ _ pats) = unionRelations . map (patEnv l tracing) $ pats patEnv l tracing (PTuple _ _ pats) = unionRelations . map (patEnv l tracing) $ pats patEnv l tracing (PList _ pats) = unionRelations . map (patEnv l tracing) $ pats patEnv l tracing (PParen _ pat) = patEnv l tracing pat patEnv l tracing (PRec _ _ patFields) = unionRelations . map (patField l tracing) $ patFields patEnv l tracing (PAsPat _ name pat) = unionRelations [singleton (eVar (getId name) l 0 tracing), patEnv l tracing pat] patEnv _ _ (PWildCard _) = emptyRelation patEnv l tracing (PIrrPat _ pat) = patEnv l tracing pat patEnv l tracing (PatTypeSig _ pat _) = patEnv l tracing pat patEnv l tracing (PViewPat _ _ pat) = patEnv l tracing pat patEnv _ _ (PRPat l _) = notSupported l "regular list pattern" patEnv _ _ (PXTag l _ _ _ _) = notSupported l "XML element pattern" patEnv _ _ (PXETag l _ _ _) = notSupported l "XML singleton element pattern" patEnv _ _ (PXPcdata l _) = notSupported l "XML PCDATA pattern" patEnv _ _ (PXPatTag l _) = notSupported l "XML embedded pattern" patEnv _ _ (PXRPats l _) = notSupported l "XML regular list pattern" patEnv _ _ (PQuasiQuote l _ _) = notSupported l "quasi quote pattern" patEnv l tracing (PBangPat _ pat) = patEnv l tracing pat patField :: SrcSpanInfo -> Bool -> PatField SrcSpanInfo -> Environment patField l tracing (PFieldPat _ _ pat) = patEnv l tracing pat patField _ _ (PFieldPun l _) = notSupported l "field pun" patField _ _ (PFieldWildcard _) = emptyRelation -- ----------------------------------------------------------------------------------- -- Determine export and import environments -- In the hx-file of a module only unqualified names are used type HxEnvironment = Relation (Name ()) Entity -- Note: not HxEntity for simplicity -- Determine the exports of a module exports :: Bool -> Module SrcSpanInfo -> Environment -> HxEnvironment exports tracing mod@(Module l maybeModuleHead _ _ _) env = case maybeModuleHead of Nothing -> exportList [Syntax.EVar l (UnQual l (Ident l "main"))] Just (ModuleHead _ _ _ Nothing) -> getQualified `mapDom` moduleDefines tracing env mod Just (ModuleHead _ _ _ (Just (ExportSpecList _ list))) -> exportList list where exportList list = getQualified `mapDom` unionRelations exports where exports = filterExportSpec env `map` list -- Determine exports for one export specification of the export list filterExportSpec :: Environment -> ExportSpec SrcSpanInfo -> Environment filterExportSpec env (EModuleContents _ moduleName) = (qual moduleNameT `mapDom` unqs) `intersectRelation` qs where moduleNameT = dropAnn moduleName (qs,nonqs) = partitionDom isQual env (unqs,_) = partitionDom isUnQual nonqs filterExportSpec env eSpec = unionRelations [mSpec, mSub] where mSpec = restrictRng (not . isCon) (restrictDom (== qNameT) env) allOwners = rng mSpec subs = restrictRng (`isOwned` allOwners) env qNameT = dropAnn qName (qName,mSub) = case eSpec of Syntax.EVar _ qName -> (qName, emptyRelation) EAbs _ (Syntax.NoNamespace _) qName -> (qName, emptyRelation) EAbs _ _ qName -> notSupported () "type or pattern namespace in export" EThingWith _ (NoWildcard _) qName cNames -> (qName, restrictDom ((`elem` map getId cNames) . getId) subs) EThingWith _ _ qName cNames -> notSupported () "wildcard in export" EModuleContents _ mName -> notSupported () "module name in export" -- Assumes that list is in ascending order without duplicate names. listToHxEnvironment :: [HxEntity] -> HxEnvironment listToHxEnvironment = (getQualified `mapDom`) . fromList . map hxEntity2Entity instance SrcInfo () where -- required for definition above toSrcInfo = undefined fromSrcInfo = undefined fileName _ = "" startLine _ = 0 startColumn _ = 0 hxEnvironmentToList :: HxEnvironment -> [HxEntity] hxEnvironmentToList = map entity2HxEntity . Set.toAscList . Relation.rng -- Filter with one import declaration from the export environment of the imported module imports :: HxEnvironment -> ImportDecl SrcSpanInfo -> Environment imports exports importDecl = if importQualified importDecl then qs else unionRelations [unqs, qs] where qs = mkQual (importQual importDecl) `mapDom` incoming unqs = UnQual () `mapDom` incoming listed = unionRelations (map (filterImportSpec isHiding exports) impSpecs) incoming = if isHiding then exports `minusRelation` listed else listed (isHiding,impSpecs) = case importSpecs importDecl of Nothing -> (True, []) Just (ImportSpecList _ h impSpecs) -> (h,impSpecs) -- Qualifier for any qualfied imports of the given import declaration. -- If exists, the 'as' module name; otherwise the name of the imported module itself. importQual :: ImportDecl l -> ModuleName () importQual imDecl = dropAnn (fromMaybe (importModule imDecl) (importAs imDecl)) -- Filter given environment with one import specification from an import list filterImportSpec :: Bool -> HxEnvironment -> ImportSpec SrcSpanInfo -> HxEnvironment filterImportSpec isHiding exports iSpec = unionRelations [fSpec,fSub] where fSpec = restrictRng consider (restrictDom (== nameT) exports) allOwners = rng fSpec subs = restrictRng (`isOwned` allOwners) exports nameT = dropAnn name (name,fSub,noSubSpec) = case iSpec of IVar _ name -> (name, emptyRelation, True) IAbs _ (NoNamespace _) name -> (name, emptyRelation, True) IAbs _ _ name -> notSupported () "type or pattern name space in import" IThingAll _ name -> (name, subs, False) IThingWith _ name cNames -> (name, restrictDom ((`elem` map getId cNames) . getId) subs, False) consider = if isHiding && noSubSpec then const True else (not . isCon) -- ------------------------------------------------------------------------------------- -- Handling type synonym -- determines the outer functional or applicative part of a type synonym body -- this part can then be expanded when transforming types for workers -- the *final* environment is needed, because in the body of a type synonym -- a type synonym may appear; because type synonyms shall not be recursive, -- a blackhole cannot occur for type correct programs. splitSynonym :: SrcInfo l => Environment -> [Name l] -> Type l -> TyCls splitSynonym env tyVars rhs = case go rhs [] of Syn 1 THelper -> Syn 0 THelper -- nothing to split off (bogus THelper) syn -> syn where -- it is vital that this 'go' agrees with the 'go' in 'splitSynonym' in TraceTrans. go ty@(TyForall _ _ _ _) [] = Syn 1 THelper go (TyFun _ tyL tyR) [] = let Syn h tyR' = go tyR [] in Syn (h+1) (TApp (TApp TFun THelper) tyR') go (TyTuple _ _ _) [] = Syn 1 THelper go (TyList _ _) [] = Syn 1 THelper go (TyApp _ tyL tyR) tys = go tyL (tyR:tys) go (TyVar _ tyVar) tys = Syn 0 (TVar (fromJust (elemIndexBy eqName tyVar tyVars))) go (TyCon _ tyCon) tys | isFunTyCon tyCon = case tys of [] -> Syn 1 THelper [ty] -> Syn 1 (TApp TFun THelper) [ty1,ty2] -> let Syn h tyR' = go ty2 [] in Syn (h+1) (TApp (TApp TFun THelper) tyR') | isExpandableTypeSynonym env tyCon = go (expandTypeSynonym env tyCon tys) [] | otherwise = Syn 1 THelper go (TyParen _ ty) tys = go ty tys go (TyInfix _ tyL tyCon tyR) tys = if isExpandableTypeSynonym env tyCon then go (expandTypeSynonym env tyCon (tyL:tyR:tys)) [] else Syn 1 THelper go (TyKind l ty kind) _ = notSupported l "kind annotation in type synonym" elemIndexBy :: (a -> a -> Bool) -> a -> [a] -> Maybe Int elemIndexBy eq x xs = go 0 xs where go c [] = Nothing go c (y:ys) = if x `eq` y then Just c else go (c+1) ys -- Expand only to expose function type constructors. -- Uses the helper type synonyms introduced by the transformation. expandTypeSynonym :: SrcInfo l => Environment -> QName l -> [Type l] -> Type l expandTypeSynonym env tySyn tys = case typeSynonymBody env tySyn of Nothing -> error ("TraceTrans.expandTypeSynonym: " ++ show (getId tySyn) ++ " is not a type synonym.") Just body -> fst (go body 1) where l = ann tySyn -- go :: TySynBody -> Int -> (Type l, Int) go THelper n = (tyAppN (TyCon l (nameTransTySynHelper tySyn n) : tys), n+1) go (TVar v) n = (tys!!v, n) go TFun n = (TyCon l (Special l (FunCon l)), n) go (TApp tyL tyR) n = (TyApp l tyL' tyR', n2) where (tyL', n1) = go tyL n (tyR', n2) = go tyR n1 -- Names of helper synonyms are a bit of a hack; a name conflict is possible. -- We just do not want to prefix all names in the namespace. nameTransTySynHelper :: UpdId i => i -> Int -> i nameTransTySynHelper syn no = updateId update syn where update (Ident l name) = Ident l (name ++ "___" ++ show no) update (Symbol _ _) = error "TraceTrans.nameTransTySynHelper: synonym name is a symbol." -- ------------------------------------------------------------------------------------- -- Looking up, inserting and mutating individual environment entries. -- For generating an appropriate error message. one :: Environment -> String -> [Entity] -> Entity one env msg [] = error (prettyEnv env ++ msg ++ " not found.") one env msg [x] = x one env msg xs = error (prettyEnv env ++ msg ++ " ambigious. " ++ show xs) prettyEnv :: (Pretty a, Ord a) => Relation a b -> String prettyEnv = ("Environment: " ++) . foldr space "\n" . map prettyPrint . Set.toAscList . Relation.dom where space xs ys = xs ++ "," ++ ys -- A name for a variable, data constructor, method or field: lookupExpEnv :: Environment -> QName l -> Entity lookupExpEnv env qName = one env ("Environment.lookupExpEnv: `" ++ prettyPrint qName ++ "'") (filter isExp . Set.toList . applyRelation env $ dropAnn qName) -- A name for a type, type synonym or class. lookupTypeEnv :: Environment -> QName l -> Entity lookupTypeEnv env qName = one env ("Environment.lookupTypeEnv: `" ++ prettyPrint qName ++ "'") (filter isTySynClass . Set.toList . applyRelation env $ dropAnn qName) -- Identifiers defined by pattern binding have arity 0. ar :: AuxiliaryInfo -> Int ar Value{args=a} = a ar _ = 0 arity :: Environment -> QName l -> Maybe Int arity env qName = Just (eArity (lookupExpEnv env qName)) isUndefinedMethod :: Environment -> Name l -> Bool isUndefinedMethod env name = null (eSrcs entity) where entity = lookupExpEnv env (UnQual undefined name) typeSynonymBody :: Environment -> QName l -> Maybe TySynBody typeSynonymBody env qName = if isSyn entity then Just (eBody entity) else Nothing where entity = lookupTypeEnv env qName isLambdaBound :: Environment -> QName l -> Bool isLambdaBound env qName = if isVar entity then not (eLetBound entity) else False where entity = lookupExpEnv env qName isTracedQName :: Environment -> QName l -> Bool isTracedQName env qName = eTraced (lookupExpEnv env qName) isExpandableTypeSynonym :: Environment -> QName l -> Bool isExpandableTypeSynonym env qName = isSyn entity && eNo entity > 0 where entity = lookupTypeEnv env qName -- Assumes all entities of given environment are expression variables. makeAllLambdaBound :: Environment -> Environment makeAllLambdaBound = mapRng (\e -> e {eLetBound = False}) -- Obtain combined fixity and priority eFixPriority :: Entity -> Int eFixPriority e = encode (eFixity e) (ePriority e) where encode Def _ = 3 encode L n = 2 + (n*4) encode R n = 1 + (n*4) encode None n = 0 + (n*4) encode (Pre _) n = 0 + (n*4) -- fixPriority _ = 3 -- default fixity and priority fixPriority :: Environment -> QName l -> Int fixPriority env qName = eFixPriority (lookupExpEnv env qName) -- Obtain priority of given local name. Result in 0-9. hasPriority :: Environment -> Name l -> Int hasPriority env name = ePriority (lookupExpEnv env (UnQual () (dropAnn name))) defineNameEnv :: Scope -> Environment -> (Name SrcSpanInfo -> Int -> Int -> Scope -> Scope -> a) -> (Name SrcSpanInfo -> [Name SrcSpanInfo] -> Int -> Int -> a) -> [a] defineNameEnv scope env defNameVar defNameCon = concatMap define nameEntries where hxEnv = getQualified `mapDom` env nameEntries = relationToList hxEnv define (name, e) | isVar e && eLetBound e = [defNameVar (fmap (const (eSrc e)) name) (eFixPriority e) (eArity e) scope scope] | isCon e = [defNameCon (fmap (const (eSrc e)) name) (map (mkName noSpan) (eFields e)) (eFixPriority e) (eArity e)] | isField e = [defNameVar (fmap (const (head (eSrcs e))) name) (eFixPriority e) (eArity e) Global Global] | isMethod e = map (\l -> defNameVar (fmap (const l) name) (eFixPriority e) (eArity e) Global Local) (eSrcs e) | otherwise = [] -- for types and classes -- ---------------- -- Obtain all fixities from a given enviroment, suitable for fixing the parse tree. -- Don't include identifiers that just have default fixity. env2Fixities :: Environment -> [Syntax.Fixity] env2Fixities env = map makeFixity . filter (\entity -> isExp entity && eFixity entity /= Def) . Set.toAscList . rng $ env where makeFixity :: Entity -> Syntax.Fixity makeFixity e = Syntax.Fixity (transFixity (eFixity e)) (ePriority e) (mkName (eId e)) where transFixity :: Fixity -> Assoc () transFixity None = AssocNone () transFixity L = AssocLeft () transFixity R = AssocRight () transFixity _ = error "Environment.env2Fixities: unexpected associativity." mkName :: String -> QName () mkName s = UnQual () (if isAlpha (head s) then Ident () s else Symbol () s) -- Intentionally never create a special name, because fixities must be UnQual. -- ------------------------- -- All identifiers whose bindings are fixed by the Haskell language -- plus a few for bootstrapping wiredEnv :: Environment wiredEnv = listToRelation $ [(qNamePreludeTrue (), eCon "True" noSpan 0 "Bool" Data [] False) ,(qNamePreludeFalse (), eCon "False" noSpan 0 "Bool" Data [] False) ,(Special () (UnitCon ()), eCon "()" noSpan 0 "()" Data [] False) ,(Special () (UnitCon ()), eType "()" ["()"] []) ,(Special () (ListCon ()), eType "[]" [":","[]"] []) ,(Special () (FunCon ()), eType "->" [] []) ,(Special () (Cons ()), (eCon ":" noSpan 2 "[]" Data [] False) {eFixity=R, ePriority=5})] ++ concatMap mkTuple [2..15] mkTuple :: Int -> [(QName (), Entity)] mkTuple n = [(Special () (TupleCon () Boxed n), eCon commas noSpan n commas Data [] False) ,(Special () (TupleCon () Boxed n), eType commas [commas] [])] where commas = replicate (n-1) ','