1 {-# LANGUAGE OverloadedStrings #-}
2 {-# LANGUAGE ScopedTypeVariables #-}
3 module Distribution
.Described
(
6 -- * Regular expressions
16 -- * Special expressions
23 describeFlagAssignmentNonEmpty
,
41 (Bool (..), Char, Either (..), Enum
(..), Eq
(..), Ord
(..), Show (..), String, elem, fmap, foldr, id, map, maybe, otherwise, return, undefined, ($),
44 import Data
.Functor
.Identity
(Identity
(..))
45 import Data
.Maybe (fromMaybe)
46 import Data
.Proxy
(Proxy
(..))
47 import Data
.String (IsString
(..))
48 import Data
.Typeable
(Typeable
, typeOf
)
49 import Data
.Void
(Void
, vacuous
)
50 import Test
.QuickCheck
(Arbitrary
(..), Property
, counterexample
)
51 import Test
.Tasty
(TestTree
, testGroup
)
52 import Test
.Tasty
.QuickCheck
(testProperty
)
54 import Distribution
.Compat
.Semigroup
(Semigroup
(..))
55 import Distribution
.Parsec
(Parsec
, eitherParsec
)
56 import Distribution
.Pretty
(Pretty
, prettyShow
)
58 import qualified Distribution
.Utils
.CharSet
as CS
59 import qualified RERE
as RE
60 import qualified RERE
.CharSet
as RE
61 import qualified Text
.PrettyPrint
as PP
63 import Distribution
.Utils
.GrammarRegex
66 import Distribution
.Compat
.Newtype
67 import Distribution
.Compiler
(CompilerFlavor
, CompilerId
, knownCompilerFlavors
)
68 import Distribution
.FieldGrammar
.Newtypes
69 import Distribution
.ModuleName
(ModuleName
)
70 import Distribution
.System
(Arch
, OS
, knownArches
, knownOSs
)
71 import Distribution
.Types
.AbiDependency
(AbiDependency
)
72 import Distribution
.Types
.AbiHash
(AbiHash
)
73 import Distribution
.Types
.BenchmarkType
(BenchmarkType
)
74 import Distribution
.Types
.BuildType
(BuildType
)
75 import Distribution
.Types
.Dependency
(Dependency
)
76 import Distribution
.Types
.ExecutableScope
(ExecutableScope
)
77 import Distribution
.Types
.ExeDependency
(ExeDependency
)
78 import Distribution
.Types
.ExposedModule
(ExposedModule
)
79 import Distribution
.Types
.Flag
(FlagAssignment
, FlagName
)
80 import Distribution
.Types
.ForeignLib
(LibVersionInfo
)
81 import Distribution
.Types
.ForeignLibOption
(ForeignLibOption
)
82 import Distribution
.Types
.ForeignLibType
(ForeignLibType
)
83 import Distribution
.Types
.IncludeRenaming
(IncludeRenaming
)
84 import Distribution
.Types
.LegacyExeDependency
(LegacyExeDependency
)
85 import Distribution
.Types
.LibraryVisibility
(LibraryVisibility
)
86 import Distribution
.Types
.Mixin
(Mixin
)
87 import Distribution
.Types
.ModuleReexport
(ModuleReexport
)
88 import Distribution
.Types
.ModuleRenaming
(ModuleRenaming
)
89 import Distribution
.Types
.MungedPackageName
(MungedPackageName
)
90 import Distribution
.Types
.PackageId
(PackageIdentifier
)
91 import Distribution
.Types
.PackageName
(PackageName
)
92 import Distribution
.Types
.PackageVersionConstraint
(PackageVersionConstraint
)
93 import Distribution
.Types
.PkgconfigDependency
(PkgconfigDependency
)
94 import Distribution
.Types
.SourceRepo
(RepoType
)
95 import Distribution
.Types
.TestType
(TestType
)
96 import Distribution
.Types
.UnitId
(UnitId
)
97 import Distribution
.Types
.UnqualComponentName
(UnqualComponentName
)
98 import Distribution
.Verbosity
(Verbosity
)
99 import Distribution
.Version
(Version
, VersionRange
)
100 import Language
.Haskell
.Extension
(Extension
, Language
)
102 -- | Class describing the pretty/parsec format of a.
103 class (Pretty a
, Parsec a
) => Described a
where
104 -- | A pretty document of "regex" describing the field format
105 describe
:: proxy a
-> GrammarRegex void
107 -- | Pretty-print description.
109 -- >>> describeDoc ([] :: [Bool])
110 -- \left\{ \mathop{\mathord{``}\mathtt{True}\mathord{"}}\mid\mathop{\mathord{``}\mathtt{False}\mathord{"}} \right\}
112 describeDoc
:: Described a
=> proxy a
-> PP
.Doc
113 describeDoc p
= regexDoc
(describe p
)
115 instance Described
Bool where
116 describe _
= REUnion
["True", "False"]
118 instance Described a
=> Described
(Identity a
) where
119 describe _
= describe
([] :: [a
])
121 -------------------------------------------------------------------------------
123 ------------------------------------------------------------------------------
125 reSpacedList
:: GrammarRegex a
-> GrammarRegex a
126 reSpacedList
= REMunch RESpaces1
128 reCommaList
:: GrammarRegex a
-> GrammarRegex a
129 reCommaList
= RECommaList
131 reCommaNonEmpty
:: GrammarRegex a
-> GrammarRegex a
132 reCommaNonEmpty
= RECommaNonEmpty
134 reOptCommaList
:: GrammarRegex a
-> GrammarRegex a
135 reOptCommaList
= REOptCommaList
137 -------------------------------------------------------------------------------
139 -------------------------------------------------------------------------------
141 reHsString
:: GrammarRegex a
142 reHsString
= RENamed
"hs-string" impl
where
143 impl
= reChar
'"' <> REMunch reEps (REUnion [strChar, escChar]) <> reChar '"'
144 strChar
= RECharSet
$ CS
.difference CS
.universe
(CS
.fromList
"\"\\")
149 , REUnion
["\\n", RENamed
"escapes" "\\n"] -- TODO
150 , "\\" <> RECharSet
"0123456789"
151 , "\\o" <> RECharSet
"01234567"
152 , "\\x" <> RECharSet
"0123456789abcdefABCDEF"
153 , REUnion
["\\^@", RENamed
"control" "\\^@"] -- TODO
154 , REUnion
["\\NUL", RENamed
"ascii" "\\NUL"] -- TODO
157 reUnqualComponent
:: GrammarRegex a
158 reUnqualComponent
= RENamed
"unqual-name" $
159 REMunch1
(reChar
'-') component
162 = REMunch reEps
(RECharSet csAlphaNum
)
163 -- currently the parser accepts "csAlphaNum `difference` "0123456789"
164 -- which is larger set than CS.alpha
166 -- Hackage rejects non ANSI names, so it's not so relevant.
167 <> RECharSet CS
.alpha
168 <> REMunch reEps
(RECharSet csAlphaNum
)
170 reDot
:: GrammarRegex a
173 reComma
:: GrammarRegex a
176 reSpacedComma
:: GrammarRegex a
177 reSpacedComma
= RESpaces
<> reComma
<> RESpaces
179 -------------------------------------------------------------------------------
181 -------------------------------------------------------------------------------
183 csChar
:: Char -> CS
.CharSet
184 csChar
= CS
.singleton
186 csAlpha
:: CS
.CharSet
189 csAlphaNum
:: CS
.CharSet
190 csAlphaNum
= CS
.alphanum
192 csUpper
:: CS
.CharSet
195 csNotSpace
:: CS
.CharSet
196 csNotSpace
= CS
.difference CS
.universe
$ CS
.singleton
' '
198 csNotSpaceOrComma
:: CS
.CharSet
199 csNotSpaceOrComma
= CS
.difference csNotSpace
$ CS
.singleton
','
201 -------------------------------------------------------------------------------
203 -------------------------------------------------------------------------------
205 describeFlagAssignmentNonEmpty
:: GrammarRegex void
206 describeFlagAssignmentNonEmpty
= REMunch1 RESpaces1
$
207 REUnion
[fromString
"+", fromString
"-"] <> describe
(Proxy
:: Proxy FlagName
)
209 -------------------------------------------------------------------------------
211 -------------------------------------------------------------------------------
213 convert
:: GrammarRegex Void
-> RE
.RE Void
214 convert
= go
id . vacuous
where
215 go
:: Ord b
=> (a
-> b
) -> GrammarRegex a
-> RE
.RE b
216 go f
(REAppend rs
) = foldr (\r acc
-> go f r
<> acc
) RE
.Eps rs
217 go f
(REUnion rs
) = foldr (\r acc
-> go f r RE
.\/ acc
) RE
.Null rs
218 go _
(RECharSet cs
) = RE
.Ch
(convertCS cs
)
219 go _
(REString str
) = RE
.string_ str
221 go f
(REMunch sep r
) = RE
.Eps RE
.\/ r
' <> RE
.star_
(sep
' <> r
') where
224 go f
(REMunch1 sep r
) = r
' <> RE
.star_
(sep
' <> r
') where
227 go f
(REMunchR n sep r
)
229 |
otherwise = RE
.Eps RE
.\/ r
' <> go
' (pred n
)
234 go
' m | m
<= 0 = RE
.Eps
235 |
otherwise = RE
.Eps RE
.\/ sep
' <> r
' <> go
' (pred m
)
237 go f
(REOpt r
) = RE
.Eps RE
.\/ go f r
239 go f
(REVar a
) = RE
.Var
(f a
)
240 go f
(RENamed _ r
) = go f r
241 go f
(RERec n r
) = RE
.fix_
(fromString n
)
242 (go
(maybe RE
.B
(RE
.F
. f
)) r
)
244 go _ RESpaces
= RE
.Eps RE
.\/ RE
.ch_
' ' RE
.\/ " " RE
.\/ "\n"
245 go _ RESpaces1
= RE
.ch_
' ' RE
.\/ " " RE
.\/ "\n"
247 go f
(RECommaList r
) = go f
(expandedCommaList r
)
248 go f
(RECommaNonEmpty r
)= go f
(expandedCommaNonEmpty r
)
249 go f
(REOptCommaList r
) = go f
(expandedOptCommaList r
)
251 go _ RETodo
= RE
.Null
253 expandedCommaList
:: GrammarRegex a
-> GrammarRegex a
254 expandedCommaList
= REUnion
. expandedCommaList
'
256 expandedCommaNonEmpty
:: GrammarRegex a
-> GrammarRegex a
257 expandedCommaNonEmpty r
= REUnion
258 [ REMunch1 reSpacedComma r
259 , reComma
<> RESpaces
<> REMunch1 reSpacedComma r
260 , REMunch1 reSpacedComma r
<> RESpaces
<> reComma
263 expandedCommaList
' :: GrammarRegex a
-> [GrammarRegex a
]
264 expandedCommaList
' r
=
265 [ REMunch reSpacedComma r
266 , reComma
<> RESpaces
<> REMunch1 reSpacedComma r
267 , REMunch1 reSpacedComma r
<> RESpaces
<> reComma
270 expandedOptCommaList
:: GrammarRegex a
-> GrammarRegex a
271 expandedOptCommaList r
= REUnion
$ reSpacedList r
: expandedCommaList
' r
273 convertCS
:: CS
.CharSet
-> RE
.CharSet
274 convertCS
= RE
.fromIntervalList
. CS
.toIntervalList
276 -------------------------------------------------------------------------------
278 -------------------------------------------------------------------------------
281 :: forall a
. (Arbitrary a
, Described a
, Typeable a
, Eq a
, Show a
)
284 testDescribed _
= testGroup name
285 [ testProperty
"parsec" propParsec
286 , testProperty
"pretty" propPretty
287 , testProperty
"roundtrip" propRoundtrip
290 name
= show (typeOf
(undefined :: a
))
292 propParsec
:: Ex a
-> Property
293 propParsec
(Example str
) = counterexample
(show res
) $ case res
of
297 res
:: Either String a
298 res
= eitherParsec str
301 rr
= convert
$ describe
(Proxy
:: Proxy a
)
303 propPretty
:: a
-> Property
304 propPretty x
= counterexample str
$ RE
.matchR rr str
308 propRoundtrip
:: a
-> Property
309 propRoundtrip x
= counterexample
(show (res
, str
)) $ case res
of
314 res
= eitherParsec str
316 newtype Ex a
= Example
String
319 instance Described a
=> Arbitrary
(Ex a
) where
322 $ fromMaybe (return "")
324 $ convert
$ describe
(Proxy
:: Proxy a
)
327 |
'\n' `
elem` s
= [ Example
$ map (\c
-> if c
== '\n' then ' ' else c
) s
]
330 -------------------------------------------------------------------------------
332 -------------------------------------------------------------------------------
334 instance Described AbiDependency
where
336 describe
(Proxy
:: Proxy UnitId
) <>
338 describe
(Proxy
:: Proxy AbiHash
)
340 instance Described AbiHash
where
341 describe _
= reMunchCS csAlphaNum
343 instance Described Arch
where
345 [ fromString
(prettyShow arch
)
346 | arch
<- knownArches
349 instance Described BenchmarkType
where
350 describe _
= "exitcode-stdio-1.0"
352 instance Described BuildType
where
353 describe _
= REUnion
["Simple","Configure","Custom","Make","Default"]
355 instance Described CompilerFlavor
where
357 [ fromString
(prettyShow c
)
358 | c
<- knownCompilerFlavors
361 instance Described CompilerId
where
363 describe
(Proxy
:: Proxy CompilerFlavor
)
365 <> describe
(Proxy
:: Proxy Version
)
367 instance Described Dependency
where
368 describe _
= REAppend
369 [ RENamed
"pkg-name" (describe
(Proxy
:: Proxy PackageName
))
377 -- no leading or trailing comma
378 , REMunch1 reSpacedComma reUnqualComponent
384 , REOpt
$ RESpaces
<> vr
387 vr
= RENamed
"version-range" (describe
(Proxy
:: Proxy VersionRange
))
389 instance Described ExecutableScope
where
390 describe _
= REUnion
["public","private"]
392 instance Described ExeDependency
where
395 instance Described ExposedModule
where
398 instance Described Extension
where
401 instance Described FlagAssignment
where
402 describe _
= REMunch RESpaces1
$
403 REUnion
[fromString
"+", fromString
"-"] <> describe
(Proxy
:: Proxy FlagName
)
405 instance Described FlagName
where
406 describe _
= lead
<> rest
where
407 lead
= RECharSet
$ csAlphaNum
<> fromString
"_"
408 rest
= reMunchCS
$ csAlphaNum
<> fromString
"_-"
410 instance Described ForeignLibOption
where
411 describe _
= "standalone"
413 instance Described ForeignLibType
where
414 describe _
= REUnion
["native-shared","native-static"]
416 instance Described IncludeRenaming
where
417 describe _
= mr
<> REOpt
(RESpaces
<> "requires" <> RESpaces1
<> mr
)
419 mr
= describe
(Proxy
:: Proxy ModuleRenaming
)
421 instance Described Language
where
422 describe _
= REUnion
["Haskell98", "Haskell2010"]
424 instance Described LegacyExeDependency
where
427 instance Described LibraryVisibility
where
428 describe _
= REUnion
["public","private"]
430 instance Described LibVersionInfo
where
431 describe _
= reDigits
<> REOpt
(reChar
':' <> reDigits
<> REOpt
(reChar
':' <> reDigits
)) where
432 reDigits
= reChars
['0'..'9']
434 instance Described Mixin
where
436 RENamed
"package-name" (describe
(Proxy
:: Proxy PackageName
)) <>
437 REOpt
(reChar
':' <> RENamed
"library-name" (describe
(Proxy
:: Proxy UnqualComponentName
))) <>
438 REOpt
(RESpaces1
<> describe
(Proxy
:: Proxy IncludeRenaming
))
440 instance Described ModuleName
where
441 describe _
= REMunch1
(reChar
'.') component
where
442 component
= RECharSet csUpper
<> REMunch reEps
(REUnion
[RECharSet csAlphaNum
, RECharSet
(fromString
"_'")])
444 instance Described ModuleReexport
where
447 instance Described ModuleRenaming
where
450 , "hiding" <> RESpaces
<> bp
(REMunch reSpacedComma mn
)
451 , bp
(REMunch reSpacedComma entry
)
454 bp r
= "(" <> RESpaces
<> r
<> RESpaces
<> ")"
455 mn
= RENamed
"module-name" $ describe
(Proxy
:: Proxy ModuleName
)
457 entry
= mn
<> REOpt
(RESpaces1
<> "as" <> RESpaces1
<> mn
)
459 instance Described MungedPackageName
where
462 instance Described OS
where
464 [ fromString
(prettyShow os
)
468 instance Described PackageIdentifier
where
469 describe _
= describe
(Proxy
:: Proxy PackageName
) <> fromString
"-" <> describe
(Proxy
:: Proxy Version
)
471 instance Described PackageName
where
472 describe _
= reUnqualComponent
474 instance Described PackageVersionConstraint
where
475 describe _
= describe
(Proxy
:: Proxy PackageName
) <> REUnion
476 [ fromString
"-" <> describe
(Proxy
:: Proxy Version
)
477 , RESpaces
<> describe
(Proxy
:: Proxy VersionRange
)
480 instance Described PkgconfigDependency
where
483 instance Described RepoType
where
484 describe _
= reMunch1CS
$ csAlphaNum
<> csChar
'_
' <> csChar
'-'
486 instance Described TestType
where
487 describe _
= REUnion
["exitcode-stdio-1.0", "detailed-0.9"]
489 instance Described Verbosity
where
491 [ REUnion
["0", "1", "2", "3"]
492 , REUnion
["silent", "normal", "verbose", "debug", "deafening"]
493 <> REMunch reEps
(RESpaces
<> "+" <>
494 -- markoutput is left out on purpose
495 REUnion
["callsite", "callstack", "nowrap", "timestamp", "stderr", "stdout" ])
498 instance Described Version
where
499 describe _
= REMunch1 reDot reDigits
where
502 , reChars
['1'..'9'] <> REMunchR
8 reEps
(reChars
['0'..'9'])
505 instance Described VersionRange
where
506 describe _
= RERec
"version-range" $ REUnion
507 [ "==" <> RESpaces
<> ver
508 , ">" <> RESpaces
<> ver
509 , "<" <> RESpaces
<> ver
510 , "<=" <> RESpaces
<> ver
511 , ">=" <> RESpaces
<> ver
512 , "^>=" <> RESpaces
<> ver
515 , "==" <> RESpaces
<> wildVer
517 , reVar0
<> RESpaces
<> "||" <> RESpaces
<> reVar0
518 , reVar0
<> RESpaces
<> "&&" <> RESpaces
<> reVar0
519 , "(" <> RESpaces
<> reVar0
<> RESpaces
<> ")"
522 -- silly haddock: ^>= { 0.1.2, 3.4.5 }
523 , "==" <> RESpaces
<> verSet
524 , "^>=" <> RESpaces
<> verSet
527 ver
' = describe
(Proxy
:: Proxy Version
)
528 ver
= RENamed
"version" ver
'
529 wildVer
= ver
' <> ".*"
530 verSet
= "{" <> RESpaces
<> REMunch1 reSpacedComma ver
<> RESpaces
<> "}"
532 instance Described UnitId
where
533 describe _
= reMunch1CS
$ csAlphaNum
<> csChar
'-' <> csChar
'_
' <> csChar
'.' <> csChar
'+'
535 instance Described UnqualComponentName
where
536 describe _
= reUnqualComponent
538 -------------------------------------------------------------------------------
539 -- Instances: Newtypes
540 -------------------------------------------------------------------------------
542 class Sep sep
=> DescribeSep sep
where
543 describeSep
:: Proxy sep
-> GrammarRegex a
-> GrammarRegex a
545 instance DescribeSep CommaVCat
where describeSep _
= reCommaList
546 instance DescribeSep CommaFSep
where describeSep _
= reCommaList
547 instance DescribeSep VCat
where describeSep _
= reCommaList
548 instance DescribeSep FSep
where describeSep _
= reOptCommaList
549 instance DescribeSep NoCommaFSep
where describeSep _
= reSpacedList
551 instance (Newtype a b
, DescribeSep sep
, Described b
) => Described
(List sep b a
) where
552 describe _
= describeSep
(Proxy
:: Proxy sep
) (describe
(Proxy
:: Proxy b
))
554 instance (Newtype a b
, Ord a
, DescribeSep sep
, Described b
) => Described
(Set
' sep b a
) where
555 describe _
= describeSep
(Proxy
:: Proxy sep
) (describe
(Proxy
:: Proxy b
))
557 instance Described Token
where
558 describe _
= REUnion
[reHsString
, reMunch1CS csNotSpaceOrComma
]
560 instance Described Token
' where
561 describe _
= REUnion
[reHsString
, reMunch1CS csNotSpace
]
563 instance Described a
=> Described
(MQuoted a
) where
564 -- TODO: this is simplification
565 describe _
= describe
([] :: [a
])
567 instance Described SpecVersion
where
568 describe _
= "3.4" -- :)
570 instance Described SpecLicense
where
573 instance Described TestedWith
where
576 instance Described FilePathNT
where
577 describe _
= describe
([] :: [Token
])