Skip to content

Commit a1e22ec

Browse files
hdgarroodpaf31
authored andcommitted
Add Prim docs to the library, resolves purescript#2494 (purescript#2498)
* Add Prim docs to the library, resolves purescript#2494 * Use 'a' in the Char example * Link to placeholder documentation page
1 parent c2bd953 commit a1e22ec

File tree

7 files changed

+259
-18
lines changed

7 files changed

+259
-18
lines changed

purescript.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ library
250250
Language.PureScript.Docs.Convert
251251
Language.PureScript.Docs.Convert.Single
252252
Language.PureScript.Docs.Convert.ReExports
253+
Language.PureScript.Docs.Prim
253254
Language.PureScript.Docs.Render
254255
Language.PureScript.Docs.Types
255256
Language.PureScript.Docs.RenderedCode
@@ -539,6 +540,7 @@ test-suite tests
539540
other-modules: TestUtils
540541
TestCompiler
541542
TestDocs
543+
TestPrimDocs
542544
TestPscPublish
543545
TestPsci
544546
TestPscIde

src/Language/PureScript/Docs.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module Language.PureScript.Docs (
77
) where
88

99
import Language.PureScript.Docs.Convert as Docs
10+
import Language.PureScript.Docs.Prim as Docs
1011
import Language.PureScript.Docs.ParseAndBookmark as Docs
1112
import Language.PureScript.Docs.Render as Docs
1213
import Language.PureScript.Docs.RenderedCode.Render as Docs

src/Language/PureScript/Docs/Convert/Single.hs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ import Control.Arrow (first)
99
import Control.Category ((>>>))
1010
import Control.Monad
1111

12-
import Data.Bifunctor (bimap)
1312
import Data.Either
1413
import Data.List (nub)
1514
import Data.Maybe (mapMaybe)
1615
import Data.Monoid ((<>))
1716
import Data.Text (Text)
1817
import qualified Data.Text as T
19-
import qualified Data.Vector as V
2018

2119
import Language.PureScript.Docs.Types
2220
import qualified Language.PureScript as P
@@ -132,29 +130,14 @@ convertDeclaration (P.TypeSynonymDeclaration _ args ty) title =
132130
convertDeclaration (P.TypeClassDeclaration _ args implies fundeps ds) title =
133131
Just (Right (mkDeclaration title info) { declChildren = children })
134132
where
135-
info = TypeClassDeclaration (map (first T.unpack) args) implies (map (bimap (map T.unpack) (map T.unpack)) fundeps')
133+
info = TypeClassDeclaration (map (first T.unpack) args) implies (convertFundepsToStrings args fundeps)
136134
children = map convertClassMember ds
137135
convertClassMember (P.PositionedDeclaration _ _ d) =
138136
convertClassMember d
139137
convertClassMember (P.TypeDeclaration ident' ty) =
140138
ChildDeclaration (T.unpack (P.showIdent ident')) Nothing Nothing (ChildTypeClassMember ty)
141139
convertClassMember _ =
142140
P.internalError "convertDeclaration: Invalid argument to convertClassMember."
143-
fundeps' = map (\(P.FunctionalDependency from to) -> toArgs from to) fundeps
144-
where
145-
argsVec = V.fromList (map fst args)
146-
getArg i =
147-
maybe
148-
(P.internalError $ unlines
149-
[ "convertDeclaration: Functional dependency index"
150-
, show i
151-
, "is bigger than arguments list"
152-
, show (map fst args)
153-
, "Functional dependencies are"
154-
, show fundeps
155-
]
156-
) id $ argsVec V.!? i
157-
toArgs from to = (map getArg from, map getArg to)
158141
convertDeclaration (P.TypeInstanceDeclaration _ constraints className tys _) title =
159142
Just (Left (T.unpack classNameString : map T.unpack typeNameStrings, AugmentChild childDecl))
160143
where
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
-- | This module provides documentation for the builtin Prim module.
2+
module Language.PureScript.Docs.Prim (primDocsModule) where
3+
4+
import Prelude.Compat hiding (fail)
5+
import Control.Arrow (first)
6+
import qualified Data.Text as T
7+
import qualified Data.Map as Map
8+
import Language.PureScript.Docs.Types
9+
import qualified Language.PureScript as P
10+
11+
primDocsModule :: Module
12+
primDocsModule = Module
13+
{ modName = P.moduleNameFromString "Prim"
14+
, modComments = Just "The Prim module is embedded in the PureScript compiler in order to provide compiler support for certain types &mdash; for example, value literals, or syntax sugar."
15+
, modDeclarations =
16+
[ function
17+
, array
18+
, record
19+
, number
20+
, int
21+
, string
22+
, char
23+
, boolean
24+
, partial
25+
, fail
26+
, typeConcat
27+
, typeString
28+
]
29+
, modReExports = []
30+
}
31+
32+
unsafeLookup :: forall v (a :: P.ProperNameType).
33+
Map.Map (P.Qualified (P.ProperName a)) v -> String -> String -> v
34+
unsafeLookup m errorMsg ty = go ty
35+
where
36+
go = fromJust' . flip Map.lookup m . P.primName . T.pack
37+
38+
fromJust' (Just x) = x
39+
fromJust' _ = P.internalError $ errorMsg ++ ty
40+
41+
lookupPrimKind :: String -> P.Kind
42+
lookupPrimKind = fst . unsafeLookup P.primTypes "Docs.Prim: No such Prim type: "
43+
44+
primType :: String -> String -> Declaration
45+
primType title comments = Declaration
46+
{ declTitle = title
47+
, declComments = Just comments
48+
, declSourceSpan = Nothing
49+
, declChildren = []
50+
, declInfo = ExternDataDeclaration (lookupPrimKind title)
51+
}
52+
53+
-- | Lookup the TypeClassData of a Prim class. This function is specifically
54+
-- not exported because it is partial.
55+
lookupPrimClass :: String -> P.TypeClassData
56+
lookupPrimClass = unsafeLookup P.primClasses "Docs.Prim: No such Prim class: "
57+
58+
primClass :: String -> String -> Declaration
59+
primClass title comments = Declaration
60+
{ declTitle = title
61+
, declComments = Just comments
62+
, declSourceSpan = Nothing
63+
, declChildren = []
64+
, declInfo =
65+
let
66+
tcd = lookupPrimClass title
67+
args = P.typeClassArguments tcd
68+
superclasses = P.typeClassSuperclasses tcd
69+
fundeps = convertFundepsToStrings args (P.typeClassDependencies tcd)
70+
in
71+
TypeClassDeclaration (map (first T.unpack) args) superclasses fundeps
72+
}
73+
74+
function :: Declaration
75+
function = primType "Function" $ unlines
76+
[ "A function, which takes values of the type specified by the first type"
77+
, "parameter, and returns values of the type specified by the second."
78+
, "In the JavaScript backend, this is a standard JavaScript Function."
79+
, ""
80+
, "The type constructor `(->)` is syntactic sugar for this type constructor."
81+
, "It is recommended to use `(->)` rather than `Function`, where possible."
82+
, ""
83+
, "That is, prefer this:"
84+
, ""
85+
, " f :: Number -> Number"
86+
, ""
87+
, "to either of these:"
88+
, ""
89+
, " f :: Function Number Number"
90+
, " f :: (->) Number Number"
91+
]
92+
93+
array :: Declaration
94+
array = primType "Array" $ unlines
95+
[ "An Array: a data structure supporting efficient random access. In"
96+
, "the JavaScript backend, values of this type are represented as JavaScript"
97+
, "Arrays at runtime."
98+
, ""
99+
, "Construct values using literals:"
100+
, ""
101+
, " x = [1,2,3,4,5] :: Array Int"
102+
]
103+
104+
record :: Declaration
105+
record = primType "Record" $ unlines
106+
[ "The type of records whose fields are known at compile time. In the"
107+
, "JavaScript backend, values of this type are represented as JavaScript"
108+
, "Objects at runtime."
109+
, ""
110+
, "The type signature here means that the `Record` type constructor takes"
111+
, "a row of concrete types. For example:"
112+
, ""
113+
, " type Person = Record (name :: String, age :: Number)"
114+
, ""
115+
, "The syntactic sugar with curly braces `{ }` is generally preferred, though:"
116+
, ""
117+
, " type Person = { name :: String, age :: Number }"
118+
]
119+
120+
number :: Declaration
121+
number = primType "Number" $ unlines
122+
[ "A double precision floating point number (IEEE 754)."
123+
, ""
124+
, "Construct values of this type with literals:"
125+
, ""
126+
, " y = 35.23 :: Number"
127+
, " z = 1.224e6 :: Number"
128+
]
129+
130+
int :: Declaration
131+
int = primType "Int" $ unlines
132+
[ "A 32-bit signed integer. See the purescript-integers package for details"
133+
, "of how this is accomplished when compiling to JavaScript."
134+
, ""
135+
, "Construct values of this type with literals:"
136+
, ""
137+
, " x = 23 :: Int"
138+
]
139+
140+
string :: Declaration
141+
string = primType "String" $ unlines
142+
[ "A String. As in JavaScript, String values represent sequences of UTF-16"
143+
, "code units, which are not required to form a valid encoding of Unicode"
144+
, "text (for example, lone surrogates are permitted)."
145+
, ""
146+
, "Construct values of this type with literals, using double quotes `\"`:"
147+
, ""
148+
, " x = \"hello, world\" :: String"
149+
, ""
150+
, "Multi-line string literals are also supported with triple quotes (`\"\"\"`)."
151+
]
152+
153+
char :: Declaration
154+
char = primType "Char" $ unlines
155+
[ "A single character (UTF-16 code unit). The JavaScript representation is a"
156+
, "normal String, which is guaranteed to contain one code unit. This means"
157+
, "that astral plane characters (i.e. those with code point values greater"
158+
, "than 0xFFFF) cannot be represented as Char values."
159+
, ""
160+
, "Construct values of this type with literals, using single quotes `'`:"
161+
, ""
162+
, " x = 'a' :: Char"
163+
]
164+
165+
boolean :: Declaration
166+
boolean = primType "Boolean" $ unlines
167+
[ "A JavaScript Boolean value."
168+
, ""
169+
, "Construct values of this type with the literals `true` and `false`."
170+
]
171+
172+
partial :: Declaration
173+
partial = primClass "Partial" $ unlines
174+
[ "The Partial type class is used to indicate that a function is *partial,*"
175+
, "that is, it will throw an error for some inputs. For more information,"
176+
, "see [the Partial type class guide](https://github.com/purescript/documentation/blob/master/guides/The-Partial-type-class.md)."
177+
]
178+
179+
fail :: Declaration
180+
fail = primClass "Fail" $ unlines
181+
[ "The Fail type class is part of the custom type errors feature. To provide"
182+
, "a custom type error when someone tries to use a particular instance,"
183+
, "write that instance out with a Fail constraint."
184+
, ""
185+
, "For more information, see"
186+
, "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)."
187+
]
188+
189+
typeConcat :: Declaration
190+
typeConcat = primType "TypeConcat" $ unlines
191+
[ "The TypeConcat type constructor concatenates two Symbols in a custom type"
192+
, "error."
193+
, ""
194+
, "For more information, see"
195+
, "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)."
196+
]
197+
198+
typeString :: Declaration
199+
typeString = primType "TypeString" $ unlines
200+
[ "The TypeString type constructor renders any concrete type into a Symbol"
201+
, "in a custom type error."
202+
, ""
203+
, "For more information, see"
204+
, "[the Custom Type Errors guide](https://github.com/purescript/documentation/blob/master/guides/Custom-Type-Errors.md)."
205+
]

src/Language/PureScript/Docs/Types.hs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ import Prelude.Compat
99
import Control.Arrow (first, (***))
1010
import Control.Monad (when)
1111

12+
import Data.Bifunctor (bimap)
1213
import Data.Aeson ((.=))
1314
import Data.Aeson.BetterErrors
1415
import Data.ByteString.Lazy (ByteString)
1516
import Data.Either (isLeft, isRight)
1617
import Data.Maybe (mapMaybe)
1718
import Data.Text (Text)
1819
import Data.Version
20+
import qualified Data.Vector as V
1921
import qualified Data.Aeson as A
2022
import qualified Data.Text as T
2123

@@ -132,6 +134,25 @@ data DeclarationInfo
132134
| AliasDeclaration P.Fixity FixityAlias
133135
deriving (Show, Eq, Ord)
134136

137+
convertFundepsToStrings :: [(Text, Maybe P.Kind)] -> [P.FunctionalDependency] -> [([String], [String])]
138+
convertFundepsToStrings args fundeps =
139+
map (bimap (map T.unpack) (map T.unpack)) fundeps'
140+
where
141+
fundeps' = map (\(P.FunctionalDependency from to) -> toArgs from to) fundeps
142+
argsVec = V.fromList (map fst args)
143+
getArg i =
144+
maybe
145+
(P.internalError $ unlines
146+
[ "convertDeclaration: Functional dependency index"
147+
, show i
148+
, "is bigger than arguments list"
149+
, show (map fst args)
150+
, "Functional dependencies are"
151+
, show fundeps
152+
]
153+
) id $ argsVec V.!? i
154+
toArgs from to = (map getArg from, map getArg to)
155+
135156
type FixityAlias = P.Qualified (Either (P.ProperName 'P.TypeName) (Either P.Ident (P.ProperName 'P.ConstructorName)))
136157

137158
declInfoToString :: DeclarationInfo -> String

tests/Main.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import qualified TestDocs
1313
import qualified TestPsci
1414
import qualified TestPscIde
1515
import qualified TestPscPublish
16+
import qualified TestPrimDocs
1617
import qualified TestUtils
1718

1819
import System.IO (hSetEncoding, stdout, stderr, utf8)
@@ -28,6 +29,7 @@ main = do
2829
TestCompiler.main
2930
heading "Documentation test suite"
3031
TestDocs.main
32+
TestPrimDocs.main
3133
heading "psc-publish test suite"
3234
TestPscPublish.main
3335
heading "psci test suite"

tests/TestPrimDocs.hs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
module TestPrimDocs where
2+
3+
import Control.Monad
4+
import Data.List ((\\))
5+
import qualified Data.Map as Map
6+
import qualified Data.Text as T
7+
import qualified Language.PureScript as P
8+
import qualified Language.PureScript.Docs as D
9+
import qualified Language.PureScript.Docs.AsMarkdown as D
10+
11+
main :: IO ()
12+
main = do
13+
putStrLn "Test that there are no bottoms hiding in primDocsModule"
14+
seq (T.pack (D.runDocs (D.modulesAsMarkdown [D.primDocsModule]))) (return ())
15+
16+
putStrLn "Test that Prim is fully documented"
17+
let actualPrimTypes = map (P.runProperName . P.disqualify . fst) $ Map.toList P.primTypes
18+
let documentedPrimTypes = map (T.pack . D.declTitle) (D.modDeclarations D.primDocsModule)
19+
20+
let undocumentedTypes = actualPrimTypes \\ documentedPrimTypes
21+
let extraTypes = documentedPrimTypes \\ actualPrimTypes
22+
23+
when (not (null undocumentedTypes)) $
24+
error $ "Undocumented Prim types: " ++ show undocumentedTypes
25+
26+
when (not (null extraTypes)) $
27+
error $ "Extra Prim types: " ++ show undocumentedTypes

0 commit comments

Comments
 (0)