@@ -2,30 +2,50 @@
22{-# LANGUAGE OverloadedStrings #-}
33module Elm.Docs
44 ( Documentation
5− , Module(..)
65 , toDict
7− , decoder
8− , encode
6+ , Module(..)
7+ , fromCanonical
98 , Union(..)
109 , Alias(..)
1110 , Value(..)
1211 , Binop(..)
1312 , Binop.Associativity(..)
1413 , Binop.Precedence(..)
14+ , decoder
15+ , encode
1516 )
1617 where
1718
1819
20+import Control.Monad (foldM)
21+import qualified Data.ByteString as B
22+import qualified Data.List as List
23+import Data.Map ((!))
1924import qualified Data.Map as Map
20−import Data.Text (Text)
25+import qualified Data.Text as Text
26+import qualified Data.Text.Encoding as Text
2127
28+import qualified AST.Canonical as Can
29+import qualified AST.Module.Name as ModuleName
2230import qualified AST.Utils.Binop as Binop
31+import qualified Data.OneOrMore as OneOrMore
2332import qualified Elm.Compiler.Module as Module
2433import qualified Elm.Compiler.Type as Type
34+import qualified Elm.Compiler.Type.Extract as Extract
2535import qualified Elm.Name as N
2636import qualified Json.Decode as Decode
2737import qualified Json.Encode as Encode
2838import Json.Encode ((==>))
39+import qualified Reporting.Annotation as A
40+import qualified Reporting.Error as Error
41+import qualified Reporting.Error.Docs as E
42+import qualified Reporting.Region as R
43+import qualified Reporting.Result as Result
44+import Parse.Primitives (Parser)
45+import qualified Parse.Primitives as P
46+import qualified Parse.Primitives.Symbol as Symbol
47+import qualified Parse.Primitives.Variable as Var
48+import qualified Parse.Primitives.Whitespace as W
2949
3050
3151
@@ -54,7 +74,7 @@ toPair modul@(Module name _ _ _ _ _) =
5474data Module =
5575 Module
5676 { _name :: N.Name
57− , _comment :: Text
77+ , _comment :: Comment
5878 , _unions :: Map.Map N.Name Union
5979 , _aliases :: Map.Map N.Name Alias
6080 , _values :: Map.Map N.Name Value
@@ -62,14 +82,17 @@ data Module =
6282 }
6383
6484
65−data Alias = Alias Text [N.Name] Type.Type
66−data Union = Union Text [N.Name] [(N.Name, [Type.Type])]
67−data Value = Value Text Type.Type
68−data Binop = Binop Text Type.Type Binop.Associativity Binop.Precedence
85+type Comment = Text.Text
6986
7087
88+data Alias = Alias Comment [N.Name] Type.Type
89+data Union = Union Comment [N.Name] [(N.Name, [Type.Type])]
90+data Value = Value Comment Type.Type
91+data Binop = Binop Comment Type.Type Binop.Associativity Binop.Precedence
7192
72−-- ENCODE
93+
94+
95+-- JSON ENCODE / DECODE
7396
7497
7598encode :: Module -> Encode.Value
@@ -84,10 +107,6 @@ encode (Module name comment unions aliases values binops) =
84107 ]
85108
86109
87−
88−-- DECODE
89−
90−
91110decoder :: Decode.Decoder Module
92111decoder =
93112 Module
@@ -112,7 +131,7 @@ named entryDecoder =
112131
113132
114133
115−-- UNION
134+-- UNION JSON
116135
117136
118137encodeUnion :: (N.Name, Union) -> Encode.Value
@@ -133,10 +152,6 @@ union =
133152 <*> Decode.field "cases" (Decode.list caseDecoder)
134153
135154
136−
137−-- UNION CASE
138−
139−
140155encodeCase :: ( N.Name, [Type.Type] ) -> Encode.Value
141156encodeCase ( tag, args ) =
142157 Encode.list id [ Encode.text tag, Encode.list Type.encode args ]
@@ -150,7 +165,7 @@ caseDecoder =
150165
151166
152167
153−-- ALIAS
168+-- ALIAS JSON
154169
155170
156171encodeAlias :: (N.Name, Alias) -> Encode.Value
@@ -172,7 +187,7 @@ alias =
172187
173188
174189
175−-- ENCODE VALUE
190+-- VALUE JSON
176191
177192
178193encodeValue :: (N.Name, Value) -> Encode.Value
@@ -192,7 +207,7 @@ value =
192207
193208
194209
195−-- BINOP
210+-- BINOP JSON
196211
197212
198213encodeBinop :: (N.Name, Binop) -> Encode.Value
@@ -216,7 +231,7 @@ binop =
216231
217232
218233
219−-- ASSOCIATIVITY
234+-- ASSOCIATIVITY JSON
220235
221236
222237encodeAssoc :: Binop.Associativity -> Encode.Value
@@ -238,7 +253,7 @@ assocDecoder =
238253
239254
240255
241−-- PRECEDENCE
256+-- PRECEDENCE JSON
242257
243258
244259encodePrec :: Binop.Precedence -> Encode.Value
@@ -249,3 +264,257 @@ encodePrec (Binop.Precedence n) =
249264precDecoder :: Decode.Decoder Binop.Precedence
250265precDecoder =
251266 Binop.Precedence <$> Decode.int
267+
268+
269+
270+-- FROM MODULE
271+
272+
273+fromCanonical :: Can.Module -> Result.Result i w Error.Error Module
274+fromCanonical (Can.Module name docs exports decls unions aliases binops effects) =
275+ case exports of
276+ Can.ExportEverything ->
277+ Result.throw $ Error.Docs $ E.ImplicitExposing
278+
279+ Can.Export exportDict ->
280+ case docs of
281+ Nothing ->
282+ Result.throw $ Error.Docs $ E.NoDocs
283+
284+ Just (A.At region (Can.Docs overview comments)) ->
285+ do names <- parseOverview region overview
286+ Result.mapError Error.Docs $
287+ do checkNames exportDict names
288+ let types = gatherTypes decls Map.empty
289+ foldM
290+ (addExport (Info comments types unions aliases binops effects))
291+ (emptyModule name overview)
292+ (Map.toList exportDict)
293+
294+
295+emptyModule :: ModuleName.Canonical -> B.ByteString -> Module
296+emptyModule (ModuleName.Canonical _ name) overview =
297+ Module name (Text.decodeUtf8 overview) Map.empty Map.empty Map.empty Map.empty
298+
299+
300+
301+-- PARSE OVERVIEW
302+
303+
304+type LName = A.Located N.Name
305+
306+
307+parseOverview :: R.Region -> B.ByteString -> Result.Result i w Error.Error [LName]
308+parseOverview (R.Region (R.Position row col) _) overview =
309+ case P.runAt row (col + 3) (chompOverview []) overview of
310+ Left err ->
311+ Result.throw (Error.Syntax err)
312+
313+ Right names ->
314+ Result.ok names
315+
316+
317+chompOverview :: [LName] -> Parser [LName]
318+chompOverview names =
319+ do isDocs <- W.chompUntilDocs
320+ if isDocs
321+ then chompOverviewHelp names
322+ else return names
323+
324+
325+chompOverviewHelp :: [LName] -> Parser [LName]
326+chompOverviewHelp names =
327+ do pos <- P.getPosition
328+ (W.SPos spos) <- W.whitespace
329+ if pos == spos
330+ then chompOverview names
331+ else chompOverview =<< chompDocs names
332+
333+
334+chompDocs :: [LName] -> Parser [LName]
335+chompDocs names =
336+ do name <- P.addLocation (P.oneOf [ Var.lower, Var.upper, chompBinop ])
337+ spos <- W.whitespace
338+ P.oneOf
339+ [ do P.checkSpace spos
340+ Symbol.comma
341+ P.spaces
342+ chompDocs (name:names)
343+ , return (name:names)
344+ ]
345+
346+
347+chompBinop :: Parser N.Name
348+chompBinop =
349+ do Symbol.leftParen
350+ name <- Symbol.binop
351+ Symbol.rightParen
352+ return name
353+
354+
355+
356+-- CHECK NAMES
357+
358+
359+type Result i w a =
360+ Result.Result i w E.Error a
361+
362+
363+type Dups =
364+ Map.Map N.Name (OneOrMore.OneOrMore R.Region)
365+
366+
367+checkNames :: Map.Map N.Name (A.Located Can.Export) -> [LName] -> Result i w ()
368+checkNames exports names =
369+ do docs <- Map.traverseWithKey isUnique (List.foldl' addName Map.empty names)
370+ let overlap = Map.size (Map.intersection docs exports)
371+ if Map.size exports == overlap && overlap == Map.size docs
372+ then Result.ok ()
373+ else
374+ do _ <- Map.traverseWithKey onlyInDocs (Map.difference docs exports)
375+ _ <- Map.traverseWithKey onlyInExports (Map.difference exports docs)
376+ Result.ok ()
377+
378+
379+addName :: Dups -> LName -> Dups
380+addName dict (A.At region name) =
381+ Map.insertWith OneOrMore.more name (OneOrMore.one region) dict
382+
383+
384+isUnique :: N.Name -> OneOrMore.OneOrMore R.Region -> Result i w R.Region
385+isUnique name regions =
386+ case regions of
387+ OneOrMore.One region ->
388+ Result.ok region
389+
390+ OneOrMore.More _ _ ->
391+ let (r1:r2:_) = OneOrMore.toList regions in
392+ Result.throw (E.Duplicate name r1 r2)
393+
394+
395+onlyInDocs :: N.Name -> R.Region -> Result i w a
396+onlyInDocs name region =
397+ Result.throw $ E.OnlyInDocs name region
398+
399+
400+onlyInExports :: N.Name -> A.Located Can.Export -> Result i w a
401+onlyInExports name (A.At region _) =
402+ Result.throw $ E.OnlyInExports name region
403+
404+
405+
406+-- CHECK EXPORTS
407+
408+
409+data Info =
410+ Info
411+ { _iComments :: Map.Map N.Name Comment
412+ , _iValues :: Types
413+ , _iUnions :: Map.Map N.Name Can.Union
414+ , _iAliases :: Map.Map N.Name Can.Alias
415+ , _iBinops :: Map.Map N.Name Can.Binop
416+ , _iEffects :: Can.Effects
417+ }
418+
419+
420+addExport :: Info -> Module -> (N.Name, A.Located Can.Export) -> Result i w Module
421+addExport info (Module n c us as_ vs bs) (name, A.At region export) =
422+ case export of
423+ Can.ExportValue ->
424+ do tipe <- getType region name info
425+ comment <- getComment region name info
426+
427+ let newValues = Map.insert name (Value comment tipe) vs
428+ Result.ok $ Module n c us as_ newValues bs
429+
430+ Can.ExportBinop ->
431+ do let (Can.Binop_ assoc prec realName) = _iBinops info ! name
432+ tipe <- getType region realName info
433+ comment <- getComment region realName info
434+
435+ let newBinops = Map.insert name (Binop comment tipe assoc prec) bs
436+ Result.ok $ Module n c us as_ vs newBinops
437+
438+ Can.ExportAlias ->
439+ do let (Can.Alias tvars tipe _) = _iAliases info ! name
440+ comment <- getComment region name info
441+
442+ let newAliases = Map.insert name (Alias comment tvars (Extract.fromType tipe)) as_
443+ Result.ok $ Module n c us newAliases vs bs
444+
445+ Can.ExportUnionOpen ->
446+ do let (Can.Union tvars ctors) = _iUnions info ! name
447+ comment <- getComment region name info
448+
449+ let newUnions = Map.insert name (Union comment tvars (map dector ctors)) us
450+ Result.ok $ Module n c newUnions as_ vs bs
451+
452+ Can.ExportUnionClosed ->
453+ do let (Can.Union tvars _) = _iUnions info ! name
454+ comment <- getComment region name info
455+
456+ let newUnions = Map.insert name (Union comment tvars []) us
457+ Result.ok $ Module n c newUnions as_ vs bs
458+
459+ Can.ExportPort ->
460+ error "TODO"
461+
462+
463+getComment :: R.Region -> N.Name -> Info -> Result i w Comment
464+getComment region name info =
465+ case Map.lookup name (_iComments info) of
466+ Nothing ->
467+ Result.throw (E.NoComment name region)
468+
469+ Just comment ->
470+ Result.ok comment
471+
472+
473+getType :: R.Region -> N.Name -> Info -> Result i w Type.Type
474+getType exportRegion name info =
475+ case _iValues info ! name of
476+ A.At defRegion Nothing ->
477+ Result.throw (E.NoAnnotation name exportRegion defRegion)
478+
479+ A.At _ (Just tipe) ->
480+ Result.ok (Extract.fromType tipe)
481+
482+
483+dector :: (N.Name, [Can.Type]) -> (N.Name, [Type.Type])
484+dector (name, args) =
485+ ( name, map Extract.fromType args )
486+
487+
488+
489+-- GATHER TYPES
490+
491+
492+type Types =
493+ Map.Map N.Name (A.Located (Maybe Can.Type))
494+
495+
496+gatherTypes :: Can.Decls -> Types -> Types
497+gatherTypes decls types =
498+ case decls of
499+ Can.Declare def subDecls ->
500+ gatherTypes subDecls (addDef types def)
501+
502+ Can.DeclareRec defs subDecls ->
503+ gatherTypes subDecls (List.foldl' addDef types defs)
504+
505+ Can.SaveTheEnvironment ->
506+ types
507+
508+
509+addDef :: Types -> Can.Def -> Types
510+addDef types def =
511+ case def of
512+ Can.Def (A.At region name) _ _ ->
513+ Map.insert name (A.At region Nothing) types
514+
515+ Can.TypedDef (A.At region name) _ typedArgs _ resultType ->
516+ let
517+ tipe =
518+ foldr Can.TLambda resultType (map snd typedArgs)
519+ in
520+ Map.insert name (A.At region (Just tipe)) types