]> Git — Sourcephile - haskell/symantic.git/blob - symantic/Language/Symantic/Typing/Document.hs
Move symantic-document to its own Git repository.
[haskell/symantic.git] / symantic / Language / Symantic / Typing / Document.hs
1 {-# LANGUAGE PolyKinds #-}
2 {-# LANGUAGE TypeInType #-}
3 {-# OPTIONS_GHC -fno-warn-orphans #-}
4 module Language.Symantic.Typing.Document where
5
6 import Data.Function (id)
7 import Data.Map.Strict (Map)
8 import Data.Maybe (fromMaybe)
9 import Data.Monoid (Monoid(..))
10 import Data.Semigroup (Semigroup(..))
11 import Data.Set (Set)
12 import Data.Text (Text)
13 import Data.Typeable
14 import Symantic.Document (DocFrom(..))
15 import qualified Data.List as L
16 import qualified Data.Map.Strict as Map
17 import qualified Data.Set as Set
18 import qualified Data.Text as T
19 import qualified Symantic.Document as Doc
20
21 import Language.Symantic.Grammar
22 import Language.Symantic.Typing.Kind
23 import Language.Symantic.Typing.Variable
24 import Language.Symantic.Typing.Module
25 import Language.Symantic.Typing.Type
26
27 -- * Type 'Config_Doc_Type'
28 data Config_Doc_Type
29 = Config_Doc_Type
30 { config_Doc_Type_vars_numbering :: Bool
31 -- ^ Style of /type variables/:
32 --
33 -- * if 'True': use name and position (as in @a0@ then @a1@)
34 -- * if 'False': use name, and a counter when names collide (as in @a@ then @a1@)
35 --
36 -- NOTE: if the name is empty, a 'freshName' is taken from 'poolNames'.
37 , config_Doc_Type_imports :: Imports NameTy
38 }
39
40 config_Doc_Type :: Config_Doc_Type
41 config_Doc_Type =
42 Config_Doc_Type
43 { config_Doc_Type_vars_numbering = True
44 , config_Doc_Type_imports = mempty
45 }
46
47 -- * Document 'Type'
48 docType ::
49 forall src vs t d.
50 Semigroup d =>
51 DocFrom (Doc.Word Char) d =>
52 DocFrom (Doc.Word Text) d =>
53 Doc.Spaceable d =>
54 Doc.Colorable16 d =>
55 Config_Doc_Type ->
56 Precedence ->
57 Type src vs t -> d
58 docType conf@Config_Doc_Type{config_Doc_Type_imports=imps} pr ty =
59 let (v2n, _) = var2Name conf mempty ty in
60 go v2n (infixB SideL pr, SideL) ty
61 where
62 go ::
63 forall kx (x::kx).
64 Map IndexVar Name -> -- names of variables
65 (Infix, Side) ->
66 Type src vs x -> d
67 -- Var
68 go v2n _po (TyVar _src _n v) =
69 let iv = indexVar v in
70 case Map.lookup iv v2n of
71 Nothing -> error "[BUG] docType: variable name missing"
72 Just n -> docFrom (Doc.Word n)
73 -- Const
74 go _v2n _po (TyConst _src _vs c@Const{}) =
75 (if isNameTyOp c then docParen else id) $
76 docConst imps c
77 -- [] Const
78 go v2n _po (TyApp _ (TyConst _ _ f@Const{}) a)
79 | Just HRefl <- proj_ConstKi @(K []) @[] f =
80 Doc.between (docFrom (Doc.Word '[')) (docFrom (Doc.Word ']')) $
81 go v2n (infixB SideL 0, SideL) a
82 -- Infix Const
83 go v2n po (TyApp _ (TyApp _ (TyConst _ _ f@Const{}) a) b)
84 -- () =>
85 | Just HRefl <- proj_ConstKiTy @Constraint @(()::Constraint) a
86 , Just HRefl <- proj_ConstKi @(K (#>)) @(#>) f =
87 go v2n po b
88 | Just (Fixity2 op) <- fixityOf f =
89 (if isPairNeeded po op then docParen else id) $
90 go v2n (op, SideL) a <>
91 prettyConst f <>
92 go v2n (op, SideR) b
93 where
94 d_op :: Text -> d
95 d_op = Doc.yellower . docFrom . Doc.Word
96 prettyConst :: forall k c. Const src (c::k) -> d
97 prettyConst c | Just HRefl <- proj_ConstKi @(K (#>)) @(#>) c = Doc.space <> d_op "=>" <> Doc.space
98 prettyConst c | Just HRefl <- proj_ConstKi @(K (#)) @(#) c = d_op "," <> Doc.space
99 prettyConst c | Just HRefl <- proj_ConstKi @(K (,)) @(,) c = d_op "," <> Doc.space
100 prettyConst c@Const{}
101 | r <- typeRepTyCon (typeRep c)
102 , tyConName r =="#~"
103 , tyConModule r =="Language.Symantic.Typing.Type"
104 -- XXX: module name must be in sync with where (#~) is defined.
105 -- NOTE: cannot use 'proj_ConstKi' here
106 -- because (#~) has a polymorphic kind.
107 = Doc.space <> d_op "~" <> Doc.space
108 | otherwise = Doc.space <> d_op (docConst imps c) <> Doc.space
109 -- TyApp
110 go v2n po (TyApp _src f a) =
111 let op = infixL 11 in
112 (if isPairNeeded po op then docParen else id) $
113 go v2n (op, SideL) f <>
114 Doc.space <>
115 go v2n (op, SideR) a
116 -- TyFam
117 go v2n po (TyFam _src _len fam args) =
118 let op = infixL 11 in
119 (if isPairNeeded po op then docParen else id) $
120 docConst imps fam <>
121 foldlTys (\t acc ->
122 Doc.space <> go v2n (op, SideL) t <> acc
123 ) args mempty
124
125 -- | Return a 'Map' associating a distinct 'Name'
126 -- for all the variables of the given 'Type'.
127 var2Name ::
128 Config_Doc_Type ->
129 (Map IndexVar Name, Names) ->
130 Type src vs t ->
131 (Map IndexVar Name, Names)
132 var2Name _cfg st TyConst{} = st
133 var2Name cfg st@(v2n, ns) (TyVar _src (NameVar n) v) =
134 let iv = indexVar v in
135 case Map.lookup iv v2n of
136 Just{} -> st
137 Nothing ->
138 let n' =
139 if config_Doc_Type_vars_numbering cfg && not (T.null n)
140 then n <> T.pack (show iv)
141 else freshifyName ns n in
142 let v2n' = Map.insert iv n' v2n in
143 let ns' = Set.insert n' ns in
144 (v2n', ns')
145 var2Name cfg st (TyApp _src f a) = var2Name cfg (var2Name cfg st f) a
146 var2Name cfg st (TyFam _src _len _fam as) = foldlTys (flip $ var2Name cfg) as st
147
148 -- ** Type 'Names'
149 type Names = Set Name
150
151 -- | Return given 'Name' renamed a bit to avoid
152 -- conflicting with any given 'Names'.
153 freshifyName :: Names -> Name -> Name
154 freshifyName ns "" = freshName ns
155 freshifyName ns n =
156 let ints = [1..] :: [Int] in
157 L.head
158 [ fresh
159 | suffix <- "" : (show <$> ints)
160 , fresh <- [n <> T.pack suffix]
161 , fresh `Set.notMember` ns
162 ]
163
164 freshName :: Names -> Name
165 freshName ns = L.head $ poolNames L.\\ Set.toList ns
166
167 -- | Infinite list of unique 'Name's:
168 -- @a, b, .., z, a1, b1 .., z1, a2, ..@
169 poolNames :: [Name]
170 poolNames =
171 [ T.singleton n | n <- ['a'..'z'] ] <>
172 [ T.pack (n:show i) | n <- ['a'..'z']
173 , i <- [1 :: Int ..] ]
174
175 -- * Document 'Types'
176 docTypes ::
177 forall src vs ts d.
178 Semigroup d =>
179 DocFrom (Doc.Word Char) d =>
180 DocFrom (Doc.Word Text) d =>
181 Doc.Spaceable d =>
182 Doc.Colorable16 d =>
183 Config_Doc_Type ->
184 Types src vs ts -> d
185 docTypes conf tys =
186 d_op (docFrom (Doc.Word '[')) <> go tys <> d_op (docFrom (Doc.Word ']'))
187 where
188 d_op = Doc.yellower
189 go :: forall xs. Types src vs xs -> d
190 go TypesZ = mempty
191 go (TypesS t0 (TypesS t1 ts)) =
192 docType conf 10 t0 <>
193 d_op (docFrom (Doc.Word ',')) <> Doc.space <>
194 docType conf 10 t1 <>
195 go ts
196 go (TypesS t ts) = docType conf 10 t <> go ts
197
198 -- * Document 'Const'
199 docConst ::
200 Monoid d =>
201 DocFrom (Doc.Word Char) d =>
202 DocFrom (Doc.Word Text) d =>
203 Imports NameTy -> Const src c -> d
204 docConst imps c@Const{} =
205 docMod docNameTy $
206 maybe mn (`Mod` n) $
207 revlookupImports f (m `Mod` n) imps
208 where
209 f = fixyOfFixity $ Fixity2 infixN5 `fromMaybe` fixityOf c
210 mn@(m `Mod` n) = nameTyOf c
211
212 -- * Document 'NameTy'
213 docNameTy :: DocFrom (Doc.Word Text) d => NameTy -> d
214 docNameTy (NameTy t) = docFrom (Doc.Word t)
215
216 -- * Document 'Mod'
217 docMod ::
218 Monoid d =>
219 DocFrom (Doc.Word Char) d =>
220 DocFrom (Doc.Word Text) d =>
221 (a -> d) -> Mod a -> d
222 docMod a2d ([] `Mod` a) = a2d a
223 docMod a2d (m `Mod` a) = docPathMod m <> (docFrom (Doc.Word '.')) <> a2d a
224
225 -- * Document 'PathMod'
226 docPathMod ::
227 Monoid d =>
228 DocFrom (Doc.Word Char) d =>
229 DocFrom (Doc.Word Text) d =>
230 PathMod -> d
231 docPathMod (p::PathMod) =
232 mconcat $
233 L.intersperse (docFrom (Doc.Word '.')) $
234 (\(NameMod n) -> docFrom (Doc.Word n)) <$> p
235
236 docParen :: Doc.Spaceable d => DocFrom (Doc.Word Char) d => d -> d
237 docParen = Doc.between (docFrom (Doc.Word '(')) (docFrom (Doc.Word ')'))
238
239 {-
240 docModules ::
241 Source src =>
242 Doc.Textable d =>
243 Doc.Colorable16 d =>
244 Doc.Decorationable d =>
245 ReadTe src ss =>
246 Sym.Modules src ss -> d
247 docModules (Sym.Modules mods) =
248 Map.foldrWithKey
249 (\p m doc -> docModule p m <> doc)
250 mempty
251 mods
252
253 docModule ::
254 forall src ss d.
255 Source src =>
256 Doc.Textable d =>
257 Doc.Colorable16 d =>
258 Doc.Decorationable d =>
259 ReadTe src ss =>
260 Sym.PathMod -> Sym.Module src ss -> d
261 docModule m Sym.Module
262 { Sym.module_infix
263 , Sym.module_prefix
264 , Sym.module_postfix
265 } =
266 go docFixityInfix module_infix <>
267 go docFixityPrefix module_prefix <>
268 go docFixityPostfix module_postfix
269 where
270 go :: (fixy -> d) -> ModuleFixy src ss fixy -> d
271 go docFixy =
272 Map.foldrWithKey
273 (\n Sym.Tokenizer
274 { Sym.token_fixity
275 , Sym.token_term = t
276 } doc ->
277 docPathTe m n <>
278 docFixy token_fixity <>
279 Doc.space <> Doc.bold (Doc.yellower "::") <> Doc.space <>
280 docTokenTerm (t Sym.noSource) <>
281 Doc.eol <> doc)
282 mempty
283
284 docTokenTerm ::
285 forall src ss d.
286 Source src =>
287 Doc.Textable d =>
288 Doc.Colorable16 d =>
289 ReadTe src ss =>
290 Sym.Token_Term src ss -> d
291 docTokenTerm t =
292 let n2t = name2typeInj @ss in
293 case Sym.readTerm n2t CtxTyZ (G.BinTree0 t) of
294 Left{} -> error "[BUG] docTokenTerm: readTerm failed"
295 Right (Sym.TermVT te) ->
296 Sym.docType Sym.config_doc_type
297 { config_Doc_Type_vars_numbering = False
298 } 0 $ Sym.typeOfTerm te
299
300 docFixityInfix :: (Doc.Decorationable t, Doc.Colorable16 t, Doc.Textable t) => Infix -> t
301 docFixityInfix = \case
302 Sym.Infix Nothing 5 -> mempty
303 Sym.Infix a p ->
304 let docAssoc = \case
305 Sym.AssocL -> "l"
306 Sym.AssocR -> "r"
307 Sym.AssocB Sym.SideL -> "l"
308 Sym.AssocB Sym.SideR -> "r" in
309 Doc.magenta $ " infix" <> maybe mempty docAssoc a <>
310 Doc.space <> Doc.bold (Doc.bluer (Doc.int p))
311 docFixityPrefix :: (Doc.Decorationable t, Doc.Colorable16 t, Doc.Textable t) => Unifix -> t
312 docFixityPrefix p = Doc.magenta $ " prefix " <> Doc.bold (Doc.bluer (Doc.int $ Sym.unifix_prece p))
313 docFixityPostfix :: (Doc.Decorationable t, Doc.Colorable16 t, Doc.Textable t) => Unifix -> t
314 docFixityPostfix p = Doc.magenta $ " postfix " <> Doc.bold (Doc.bluer (Doc.int $ Sym.unifix_prece p))
315
316 docPathTe ::
317 Doc.Textable d =>
318 Doc.Colorable16 d =>
319 Sym.PathMod -> Sym.NameTe -> d
320 docPathTe (ms::Sym.PathMod) (Sym.NameTe n) =
321 Doc.catH $
322 L.intersperse (Doc.charH '.') $
323 ((\(Sym.NameMod m) -> Doc.textH m) <$> ms) <>
324 [(if isOp n then id else Doc.yellower) $ Doc.text n]
325 where
326 isOp = T.all $ \case '_' -> True; '\'' -> True; c -> Char.isAlphaNum c
327 -}