{-# LANGUAGE PolyKinds #-} {-# LANGUAGE TypeInType #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Language.Symantic.Typing.Document where import Data.Function (id) import Data.Map.Strict (Map) import Data.Maybe (fromMaybe) import Data.Monoid (Monoid(..)) import Data.Semigroup (Semigroup(..)) import Data.Set (Set) import Data.Text (Text) import Data.Typeable import Symantic.Document (DocFrom(..)) import qualified Data.List as L import qualified Data.Map.Strict as Map import qualified Data.Set as Set import qualified Data.Text as T import qualified Symantic.Document as Doc import Language.Symantic.Grammar import Language.Symantic.Typing.Kind import Language.Symantic.Typing.Variable import Language.Symantic.Typing.Module import Language.Symantic.Typing.Type -- * Type 'Config_Doc_Type' data Config_Doc_Type = Config_Doc_Type { config_Doc_Type_vars_numbering :: Bool -- ^ Style of /type variables/: -- -- * if 'True': use name and position (as in @a0@ then @a1@) -- * if 'False': use name, and a counter when names collide (as in @a@ then @a1@) -- -- NOTE: if the name is empty, a 'freshName' is taken from 'poolNames'. , config_Doc_Type_imports :: Imports NameTy } config_Doc_Type :: Config_Doc_Type config_Doc_Type = Config_Doc_Type { config_Doc_Type_vars_numbering = True , config_Doc_Type_imports = mempty } -- * Document 'Type' docType :: forall src vs t d. Semigroup d => DocFrom (Doc.Word Char) d => DocFrom (Doc.Word Text) d => Doc.Spaceable d => Doc.Colorable16 d => Config_Doc_Type -> Precedence -> Type src vs t -> d docType conf@Config_Doc_Type{config_Doc_Type_imports=imps} pr ty = let (v2n, _) = var2Name conf mempty ty in go v2n (infixB SideL pr, SideL) ty where go :: forall kx (x::kx). Map IndexVar Name -> -- names of variables (Infix, Side) -> Type src vs x -> d -- Var go v2n _po (TyVar _src _n v) = let iv = indexVar v in case Map.lookup iv v2n of Nothing -> error "[BUG] docType: variable name missing" Just n -> docFrom (Doc.Word n) -- Const go _v2n _po (TyConst _src _vs c@Const{}) = (if isNameTyOp c then docParen else id) $ docConst imps c -- [] Const go v2n _po (TyApp _ (TyConst _ _ f@Const{}) a) | Just HRefl <- proj_ConstKi @(K []) @[] f = Doc.between (docFrom (Doc.Word '[')) (docFrom (Doc.Word ']')) $ go v2n (infixB SideL 0, SideL) a -- Infix Const go v2n po (TyApp _ (TyApp _ (TyConst _ _ f@Const{}) a) b) -- () => | Just HRefl <- proj_ConstKiTy @Constraint @(()::Constraint) a , Just HRefl <- proj_ConstKi @(K (#>)) @(#>) f = go v2n po b | Just (Fixity2 op) <- fixityOf f = (if isPairNeeded po op then docParen else id) $ go v2n (op, SideL) a <> prettyConst f <> go v2n (op, SideR) b where d_op :: Text -> d d_op = Doc.yellower . docFrom . Doc.Word prettyConst :: forall k c. Const src (c::k) -> d prettyConst c | Just HRefl <- proj_ConstKi @(K (#>)) @(#>) c = Doc.space <> d_op "=>" <> Doc.space prettyConst c | Just HRefl <- proj_ConstKi @(K (#)) @(#) c = d_op "," <> Doc.space prettyConst c | Just HRefl <- proj_ConstKi @(K (,)) @(,) c = d_op "," <> Doc.space prettyConst c@Const{} | r <- typeRepTyCon (typeRep c) , tyConName r =="#~" , tyConModule r =="Language.Symantic.Typing.Type" -- XXX: module name must be in sync with where (#~) is defined. -- NOTE: cannot use 'proj_ConstKi' here -- because (#~) has a polymorphic kind. = Doc.space <> d_op "~" <> Doc.space | otherwise = Doc.space <> d_op (docConst imps c) <> Doc.space -- TyApp go v2n po (TyApp _src f a) = let op = infixL 11 in (if isPairNeeded po op then docParen else id) $ go v2n (op, SideL) f <> Doc.space <> go v2n (op, SideR) a -- TyFam go v2n po (TyFam _src _len fam args) = let op = infixL 11 in (if isPairNeeded po op then docParen else id) $ docConst imps fam <> foldlTys (\t acc -> Doc.space <> go v2n (op, SideL) t <> acc ) args mempty -- | Return a 'Map' associating a distinct 'Name' -- for all the variables of the given 'Type'. var2Name :: Config_Doc_Type -> (Map IndexVar Name, Names) -> Type src vs t -> (Map IndexVar Name, Names) var2Name _cfg st TyConst{} = st var2Name cfg st@(v2n, ns) (TyVar _src (NameVar n) v) = let iv = indexVar v in case Map.lookup iv v2n of Just{} -> st Nothing -> let n' = if config_Doc_Type_vars_numbering cfg && not (T.null n) then n <> T.pack (show iv) else freshifyName ns n in let v2n' = Map.insert iv n' v2n in let ns' = Set.insert n' ns in (v2n', ns') var2Name cfg st (TyApp _src f a) = var2Name cfg (var2Name cfg st f) a var2Name cfg st (TyFam _src _len _fam as) = foldlTys (flip $ var2Name cfg) as st -- ** Type 'Names' type Names = Set Name -- | Return given 'Name' renamed a bit to avoid -- conflicting with any given 'Names'. freshifyName :: Names -> Name -> Name freshifyName ns "" = freshName ns freshifyName ns n = let ints = [1..] :: [Int] in L.head [ fresh | suffix <- "" : (show <$> ints) , fresh <- [n <> T.pack suffix] , fresh `Set.notMember` ns ] freshName :: Names -> Name freshName ns = L.head $ poolNames L.\\ Set.toList ns -- | Infinite list of unique 'Name's: -- @a, b, .., z, a1, b1 .., z1, a2, ..@ poolNames :: [Name] poolNames = [ T.singleton n | n <- ['a'..'z'] ] <> [ T.pack (n:show i) | n <- ['a'..'z'] , i <- [1 :: Int ..] ] -- * Document 'Types' docTypes :: forall src vs ts d. Semigroup d => DocFrom (Doc.Word Char) d => DocFrom (Doc.Word Text) d => Doc.Spaceable d => Doc.Colorable16 d => Config_Doc_Type -> Types src vs ts -> d docTypes conf tys = d_op (docFrom (Doc.Word '[')) <> go tys <> d_op (docFrom (Doc.Word ']')) where d_op = Doc.yellower go :: forall xs. Types src vs xs -> d go TypesZ = mempty go (TypesS t0 (TypesS t1 ts)) = docType conf 10 t0 <> d_op (docFrom (Doc.Word ',')) <> Doc.space <> docType conf 10 t1 <> go ts go (TypesS t ts) = docType conf 10 t <> go ts -- * Document 'Const' docConst :: Monoid d => DocFrom (Doc.Word Char) d => DocFrom (Doc.Word Text) d => Imports NameTy -> Const src c -> d docConst imps c@Const{} = docMod docNameTy $ maybe mn (`Mod` n) $ revlookupImports f (m `Mod` n) imps where f = fixyOfFixity $ Fixity2 infixN5 `fromMaybe` fixityOf c mn@(m `Mod` n) = nameTyOf c -- * Document 'NameTy' docNameTy :: DocFrom (Doc.Word Text) d => NameTy -> d docNameTy (NameTy t) = docFrom (Doc.Word t) -- * Document 'Mod' docMod :: Monoid d => DocFrom (Doc.Word Char) d => DocFrom (Doc.Word Text) d => (a -> d) -> Mod a -> d docMod a2d ([] `Mod` a) = a2d a docMod a2d (m `Mod` a) = docPathMod m <> (docFrom (Doc.Word '.')) <> a2d a -- * Document 'PathMod' docPathMod :: Monoid d => DocFrom (Doc.Word Char) d => DocFrom (Doc.Word Text) d => PathMod -> d docPathMod (p::PathMod) = mconcat $ L.intersperse (docFrom (Doc.Word '.')) $ (\(NameMod n) -> docFrom (Doc.Word n)) <$> p docParen :: Doc.Spaceable d => DocFrom (Doc.Word Char) d => d -> d docParen = Doc.between (docFrom (Doc.Word '(')) (docFrom (Doc.Word ')')) {- docModules :: Source src => Doc.Textable d => Doc.Colorable16 d => Doc.Decorationable d => ReadTe src ss => Sym.Modules src ss -> d docModules (Sym.Modules mods) = Map.foldrWithKey (\p m doc -> docModule p m <> doc) mempty mods docModule :: forall src ss d. Source src => Doc.Textable d => Doc.Colorable16 d => Doc.Decorationable d => ReadTe src ss => Sym.PathMod -> Sym.Module src ss -> d docModule m Sym.Module { Sym.module_infix , Sym.module_prefix , Sym.module_postfix } = go docFixityInfix module_infix <> go docFixityPrefix module_prefix <> go docFixityPostfix module_postfix where go :: (fixy -> d) -> ModuleFixy src ss fixy -> d go docFixy = Map.foldrWithKey (\n Sym.Tokenizer { Sym.token_fixity , Sym.token_term = t } doc -> docPathTe m n <> docFixy token_fixity <> Doc.space <> Doc.bold (Doc.yellower "::") <> Doc.space <> docTokenTerm (t Sym.noSource) <> Doc.eol <> doc) mempty docTokenTerm :: forall src ss d. Source src => Doc.Textable d => Doc.Colorable16 d => ReadTe src ss => Sym.Token_Term src ss -> d docTokenTerm t = let n2t = name2typeInj @ss in case Sym.readTerm n2t CtxTyZ (G.BinTree0 t) of Left{} -> error "[BUG] docTokenTerm: readTerm failed" Right (Sym.TermVT te) -> Sym.docType Sym.config_doc_type { config_Doc_Type_vars_numbering = False } 0 $ Sym.typeOfTerm te docFixityInfix :: (Doc.Decorationable t, Doc.Colorable16 t, Doc.Textable t) => Infix -> t docFixityInfix = \case Sym.Infix Nothing 5 -> mempty Sym.Infix a p -> let docAssoc = \case Sym.AssocL -> "l" Sym.AssocR -> "r" Sym.AssocB Sym.SideL -> "l" Sym.AssocB Sym.SideR -> "r" in Doc.magenta $ " infix" <> maybe mempty docAssoc a <> Doc.space <> Doc.bold (Doc.bluer (Doc.int p)) docFixityPrefix :: (Doc.Decorationable t, Doc.Colorable16 t, Doc.Textable t) => Unifix -> t docFixityPrefix p = Doc.magenta $ " prefix " <> Doc.bold (Doc.bluer (Doc.int $ Sym.unifix_prece p)) docFixityPostfix :: (Doc.Decorationable t, Doc.Colorable16 t, Doc.Textable t) => Unifix -> t docFixityPostfix p = Doc.magenta $ " postfix " <> Doc.bold (Doc.bluer (Doc.int $ Sym.unifix_prece p)) docPathTe :: Doc.Textable d => Doc.Colorable16 d => Sym.PathMod -> Sym.NameTe -> d docPathTe (ms::Sym.PathMod) (Sym.NameTe n) = Doc.catH $ L.intersperse (Doc.charH '.') $ ((\(Sym.NameMod m) -> Doc.textH m) <$> ms) <> [(if isOp n then id else Doc.yellower) $ Doc.text n] where isOp = T.all $ \case '_' -> True; '\'' -> True; c -> Char.isAlphaNum c -}