{-# 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
data Config_Doc_Type
= Config_Doc_Type
{ config_Doc_Type_vars_numbering :: Bool
, 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
}
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 ->
(Infix, Side) ->
Type src vs x -> d
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)
go _v2n _po (TyConst _src _vs c@Const{}) =
(if isNameTyOp c then docParen else id) $
docConst imps c
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
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"
= Doc.space <> d_op "~" <> Doc.space
| otherwise = Doc.space <> d_op (docConst imps c) <> Doc.space
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
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
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 = Set Name
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
poolNames :: [Name]
poolNames =
[ T.singleton n | n <- ['a'..'z'] ] <>
[ T.pack (n:show i) | n <- ['a'..'z']
, i <- [1 :: Int ..] ]
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
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
docNameTy :: DocFrom (Doc.Word Text) d => NameTy -> d
docNameTy (NameTy t) = docFrom (Doc.Word t)
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
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 ')'))