symantic-6.3.2.20180208: Language/Symantic/Typing/Document.hs
{-# 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.Semigroup (Semigroup(..))
import Data.Set (Set)
import Data.Typeable
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 Language.Symantic.Document.Sym 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 =>
Doc.Textable d =>
Doc.Colorable 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 -> Doc.textH 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 =
"[" <> 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 needsParenInfix po op then docParen else id) $
go v2n (op, SideL) a <>
prettyConst f <>
go v2n (op, SideR) b
where
d_op = Doc.yellower
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 needsParenInfix 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 needsParenInfix po op then docParen else id) $
docConst imps fam <>
foldlTys (\t acc ->
Doc.space <> go v2n (op, SideL) t <> acc
) args Doc.empty
-- | 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 =>
Doc.Textable d =>
Doc.Colorable d =>
Config_Doc_Type ->
Types src vs ts -> d
docTypes conf tys =
d_op (Doc.charH '[') <> go tys <> d_op (Doc.charH ']')
where
d_op = Doc.yellower
go :: forall xs. Types src vs xs -> d
go TypesZ = Doc.empty
go (TypesS t0 (TypesS t1 ts)) =
docType conf 10 t0 <>
d_op (Doc.charH ',') <> Doc.space <>
docType conf 10 t1 <>
go ts
go (TypesS t ts) = docType conf 10 t <> go ts
-- * Document 'Const'
docConst :: Doc.Textable 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 :: Doc.Textable d => NameTy -> d
docNameTy (NameTy t) = Doc.textH t
-- * Document 'Mod'
docMod :: Doc.Textable d => (a -> d) -> Mod a -> d
docMod a2d ([] `Mod` a) = a2d a
docMod a2d (m `Mod` a) = docPathMod m <> (Doc.charH '.') <> a2d a
-- * Document 'PathMod'
docPathMod :: Doc.Textable d => PathMod -> d
docPathMod (p::PathMod) =
Doc.catH $
L.intersperse (Doc.charH '.') $
(\(NameMod n) -> Doc.textH n) <$> p
docParen :: Doc.Textable d => d -> d
docParen = Doc.between (Doc.charH '(') (Doc.charH ')')
{-
docModules ::
Source src =>
Doc.Textable d =>
Doc.Colorable 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)
Doc.empty
mods
docModule ::
forall src ss d.
Source src =>
Doc.Textable d =>
Doc.Colorable 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)
Doc.empty
docTokenTerm ::
forall src ss d.
Source src =>
Doc.Textable d =>
Doc.Colorable 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.Colorable t, Doc.Textable t) => Infix -> t
docFixityInfix = \case
Sym.Infix Nothing 5 -> Doc.empty
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 Doc.empty docAssoc a <>
Doc.space <> Doc.bold (Doc.bluer (Doc.int p))
docFixityPrefix :: (Doc.Decorationable t, Doc.Colorable 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.Colorable 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.Colorable 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
-}