-- Generates symbol information for the instrumentation library -- -- Note: we ignore the difference between static fields and instance fields. MODULE {GenInstrLib} {genInstrLib} {} INCLUDE "SymViewAst.ag" imports { import Data.Word import qualified Data.Set as Set import Data.Set(Set) import qualified Data.Map as Map import Data.Map(Map) import ProgInfo import PrettyUtil import SymInfo import SymView import Data.Monoid import Data.Maybe } WRAPPER FileV { genInstrLib :: String -> SymInfo -> [SymbolTables] -> Doc genInstrLib nm gEnv tbls = res where res = output_Syn_FileV syn syn = wrap_FileV sem inh inh = Inh_FileV { gEnv_Inh_FileV = gEnv, modNm_Inh_FileV = nm } sem = sem_FileV fil fil = tablesView tbls } ATTR FileV [ modNm : String | | ] ATTR FileV TablesV TableV ClassesV ClassV MbSuperV SuperV ItfsV ItfV TraitsV TraitV MethodsV MethodV SigV [ gEnv : SymInfo | | output USE {$+$} {empty} : Doc ] SEM FileV | File lhs.output = vert [ text "{-# LANGUAGE EmptyDataDecls, MultiParamTypeClasses, FunctionalDependencies, TypeSynonymInstances, OverlappingInstances, FlexibleInstances #-}" , text "module" <+> text @lhs.modNm <+> text "where" , text "import Instr" , text "import InstrBaseLib" , (vert $ map (\m -> text "import" <+> text m) $ Set.toList $ allModules @lhs.gEnv) , @loc.fieldsOutput , @tables.output ] -- Discover which types and fields are defined ATTR TablesV TableV ClassesV ClassV [ | | gathTypes USE {`mappend`} {mempty} : {Set String} ] ATTR TablesV TableV ClassesV ClassV TraitsV TraitV [ | | gathFields USE {`mappend`} {mempty} : {Set String} ] SEM ClassV | Class +gathTypes = maybe id Set.insert @nm.mbStrUni SEM TraitV | Field +gathFields = maybe id Set.insert @nm.mbStrUni | Method +gathFields = maybe id Set.insert @nm.mbStrUni ATTR TablesV TableV ClassesV ClassV MbSuperV SuperV TypeV ItfsV ItfV TraitsV TraitV MethodsV MethodV SigV ParamsV ParamV [ allTypes : {Set String} | | ] ATTR TablesV TableV ClassesV ClassV TraitsV TraitV [ allFields : {Set String} | | ] SEM FileV | File tables.allTypes = @tables.gathTypes `mappend` (allTypes @lhs.gEnv) tables.allFields = @tables.gathFields `mappend` (allFields @lhs.gEnv) ATTR TypeV [ | | tpKnown : Bool ] SEM TypeV | Type loc.tpKnown = not @isNull && maybe False (`Set.member` @lhs.allTypes) @nm.mbStrUni -- Unicode string of a name ATTR NmV TypeV [ | | mbStrUni : {Maybe String} ] SEM NmV | Qual loc.noPrefix = null @ns.str loc.strUni = if @loc.noPrefix then @nm.str else @ns.str ++ "˳" ++ @nm.str -- special unicode separator lhs.mbStrUni = Just @loc.strUni | Quals lhs.mbStrUni = Nothing | Other lhs.mbStrUni = Nothing ATTR NsV StrV [ | | str : String ] SEM StrV | Str lhs.str = @val ATTR MbStrV [ | | mbStrUni : {Maybe String} ] SEM MbStrV | Nothing lhs.mbStrUni = Nothing | Just lhs.mbStrUni = Just @just.str -- -- Generate classes for new fields -- SEM FileV | File loc.newFields = Set.toList $ Set.difference (Set.map unqualField @tables.gathFields) (Set.map unqualField $ allFields @lhs.gEnv) loc.fieldsOutput = vert $ map genFieldClass @loc.newFields { -- | Strips the qualifier from the qualified/encoded field name unqualField :: String -> String unqualField [] = error "unqualField: field name empty" unqualField nm = case break (== '˳') nm of (rem, []) -> last $ words $ map repl rem -- take the last of the normal dot chars (_, rem) -> tail rem -- has the special dot char where repl '.' = ' ' repl c = c -- | Assumes that the name is already unqualified genFieldClass :: String -> Doc genFieldClass nm = vert [ text "class K'" <> text str <+> text "a b | a -> b where" , nest 2 $ text "k'" <> text str <+> text ":: Key a b" ] where str = encodeNmHaskell nm encodeNmHaskell :: String -> String encodeNmHaskell = map repl where repl '.' = '\'' repl '˳' = '\'' repl '-' = '_' repl ':' = '_' repl '/' = '_' repl c = c } -- -- Generate data types types for newly introduced classes -- SEM ClassV | Class +output = maybe id (\str -> (genClassData str $+$)) @nm.mbStrUni { genClassData :: String -> Doc genClassData nm = vert [ text "data T'" <> text str , text "t'" <> text str <+> text ":: Type" <+> text "T'" <> text str , text "t'" <> text str <+> text "= TypeObject" <+> text (show nm) ] where str = encodeNmHaskell nm } -- -- Generate instances for inheritance relation among classes -- ATTR MbSuperV SuperV ItfsV ItfV TraitsV TraitV [ classNm : String | | ] SEM ClassV | Class loc.classNm = maybe "{-# ClassV_Class: improper name #-}" id @nm.mbStrUni SEM SuperV | Super loc.super = maybe "{-# SuperV_Super: improper name #-}" id @tp.mbStrUni +output = if @tp.tpKnown then (genSuper @loc.super @lhs.classNm $+$) else id SEM ItfV | Itf loc.super = maybe "{-# ItfV_Itf: improper name #-}" id @tp.mbStrUni +output = if @tp.tpKnown then (genSuper @loc.super @lhs.classNm $+$) else id { genSuper :: String -> String -> Doc genSuper super child = text "instance IsSuper" <+> text "T'" <> text superStr <+> text "T'" <> text childStr where superStr = encodeNmHaskell super childStr = encodeNmHaskell child } -- -- Generate keys for fields (fully qualified) -- ATTR TypeV [ | | strUni : String ] SEM TypeV | Type lhs.strUni = if @loc.tpKnown then maybe "{-# TypeV_Type: expecting a type name #}" id @nm.mbStrUni else "any" ATTR TypeV [ | | strAnnot : String ] SEM TypeV | Type lhs.strAnnot = maybe "" id @nm.mbStrUni SEM TraitV | Field lhs.output = maybe empty (\nm -> let strType = encodeNmHaskell @tp.strUni tp = text "T'" <> text strType val = text "Key" <+> text (show nm) in vert [ text "-- object field" <+> text (show nm) <+> text "::" <+> text (show @tp.strAnnot) , if nm `Set.member` @lhs.newFields then text "-- DUPLICATED value field!" else genTrait @lhs.classNm nm val tp ]) @nm.mbStrUni -- -- Generate method signatures -- SEM TraitV | Method lhs.output = maybe empty (\nm -> vert [ text "-- object method" <+> text (show nm) , if nm `Set.member` @lhs.newFields then text "-- DUPLICATED method field!" else genTrait @lhs.classNm nm (text "Key" <+> text (show nm)) @sig.tpSig ]) @nm.mbStrUni { genTrait :: String -> String -> Doc -> Doc -> Doc genTrait classNm fieldNm val tp = vert [ text "k'" <> text strClass <> text "'" <> text strField <+> text ":: Key T'" <> text strClass <+> parens tp , text "k'" <> text strClass <> text "'" <> text strField <+> text "=" <+> val , text "instance K'" <> text strField' <+> parens (text "T'" <> text strClass) <+> parens tp <+> text "where" , nest 2 (text "k'" <> text strField' <+> text "=" <+> text "k'" <> text strClass <> text "'" <> text strField) ] where strClass = encodeNmHaskell classNm strField = encodeNmHaskell fieldNm fieldNm' = unqualField fieldNm strField' = encodeNmHaskell fieldNm' } SEM MethodV | Method lhs.output = maybe empty (\nm -> vert [ text "-- method" <+> text (show nm) , if nm `Set.member` @lhs.newMethods then text "-- DUPLICATED method declaration" else genMethod nm @sig.output @sig.tpSig ]) @mbNm.mbStrUni { genMethod :: String -> Doc -> Doc -> Doc genMethod methodNm val tp = vert [ text "m'" <> text strMethod <+> text "::" <+> tp , text "m'" <> text strMethod <+> text "=" <+> val ] where strMethod = encodeNmHaskell methodNm } ATTR SigV [ | | tpSig : Doc ] -- 'SigV' has both 'output' and 'tpSig' as pretty docs SEM SigV | Sig lhs.output = text "FunSig" <+> parens (genSigVal @params.strsUni) <+> parens (genSigVal [@ret.strUni]) lhs.tpSig = text "FunSpec" <+> parens (genSigTp @params.strsUni) <+> parens (genSigTp [@ret.strUni]) ATTR ParamsV ParamV [ | | strsUni USE {++} {[]} : {[String]} ] SEM ParamV | Param lhs.strsUni = [@tp.strUni] { genSigVal :: [String] -> Doc genSigVal = foldr (\l r -> parens (text "SpecsCons" <+> text "t'" <> text (encodeNmHaskell l) <+> r)) (text "SpecsEmpty") genSigTp :: [String] -> Doc genSigTp = foldr (\l r -> parens (text "T'" <> text (encodeNmHaskell l) <+> text "," <+> r)) (text "()") } -- -- Compute duplicate fields and methods -- More symbols for these should not be generated -- ATTR TablesV TableV ClassesV ClassV TraitsV TraitV [ | newFields : {Set String} | ] ATTR TablesV TableV ClassesV ClassV MethodsV MethodV [ | newMethods : {Set String} | ] SEM FileV | File tables.newFields = Set.empty tables.newMethods = Set.empty SEM TraitV | Field Method +newFields = maybe id Set.insert @nm.mbStrUni SEM MethodV | Method +newMethods = maybe id Set.insert @mbNm.mbStrUni