1 {-# LANGUAGE FlexibleInstances #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE ScopedTypeVariables #-}
4 module Distribution
.Described
(
7 -- * Regular expressions
17 -- * Special expressions
24 describeFlagAssignmentNonEmpty
,
42 ( Bool (..), Char, Either (..), Enum
(..), Eq
(..), Ord
(..), Show (..), String
43 , elem, fmap, foldr, id, map, maybe, otherwise, return, reverse, undefined
47 import Data
.Functor
.Identity
(Identity
(..))
48 import Data
.Maybe (fromMaybe)
49 import Data
.Proxy
(Proxy
(..))
50 import Data
.String (IsString
(..))
51 import Data
.Typeable
(Typeable
, typeOf
)
52 import Data
.Void
(Void
, vacuous
)
53 import Test
.QuickCheck
(Arbitrary
(..), Property
, counterexample
)
54 import Test
.Tasty
(TestTree
, testGroup
)
55 import Test
.Tasty
.QuickCheck
(testProperty
)
57 import Distribution
.Compat
.Semigroup
(Semigroup
(..))
58 import Distribution
.Parsec
(Parsec
, eitherParsec
)
59 import Distribution
.Pretty
(Pretty
, prettyShow
)
61 import qualified Distribution
.Utils
.CharSet
as CS
62 import qualified RERE
as RE
63 import qualified RERE
.CharSet
as RE
64 import qualified Text
.PrettyPrint
as PP
66 import Distribution
.Utils
.GrammarRegex
69 import Distribution
.Compat
.Newtype
70 import Distribution
.Compiler
(CompilerFlavor
, CompilerId
, knownCompilerFlavors
)
71 import Distribution
.PackageDescription
.FieldGrammar
(CompatLicenseFile
, CompatDataDir
)
72 import Distribution
.FieldGrammar
.Newtypes
73 import Distribution
.ModuleName
(ModuleName
)
74 import Distribution
.System
(Arch
, OS
, knownArches
, knownOSs
)
75 import Distribution
.Types
.AbiDependency
(AbiDependency
)
76 import Distribution
.Types
.AbiHash
(AbiHash
)
77 import Distribution
.Types
.BenchmarkType
(BenchmarkType
)
78 import Distribution
.Types
.BuildType
(BuildType
)
79 import Distribution
.Types
.Dependency
(Dependency
)
80 import Distribution
.Types
.ExecutableScope
(ExecutableScope
)
81 import Distribution
.Types
.ExeDependency
(ExeDependency
)
82 import Distribution
.Types
.ExposedModule
(ExposedModule
)
83 import Distribution
.Types
.Flag
(FlagAssignment
, FlagName
)
84 import Distribution
.Types
.ForeignLib
(LibVersionInfo
)
85 import Distribution
.Types
.ForeignLibOption
(ForeignLibOption
)
86 import Distribution
.Types
.ForeignLibType
(ForeignLibType
)
87 import Distribution
.Types
.IncludeRenaming
(IncludeRenaming
)
88 import Distribution
.Types
.LegacyExeDependency
(LegacyExeDependency
)
89 import Distribution
.Types
.LibraryVisibility
(LibraryVisibility
)
90 import Distribution
.Types
.Mixin
(Mixin
)
91 import Distribution
.Types
.ModuleReexport
(ModuleReexport
)
92 import Distribution
.Types
.ModuleRenaming
(ModuleRenaming
)
93 import Distribution
.Types
.MungedPackageName
(MungedPackageName
)
94 import Distribution
.Types
.PackageId
(PackageIdentifier
)
95 import Distribution
.Types
.PackageName
(PackageName
)
96 import Distribution
.Types
.PackageVersionConstraint
(PackageVersionConstraint
)
97 import Distribution
.Types
.PkgconfigDependency
(PkgconfigDependency
)
98 import Distribution
.Types
.SourceRepo
(RepoType
)
99 import Distribution
.Types
.TestType
(TestType
)
100 import Distribution
.Types
.UnitId
(UnitId
)
101 import Distribution
.Types
.UnqualComponentName
(UnqualComponentName
)
102 import Distribution
.Utils
.Path
(SymbolicPath
, RelativePath
)
103 import Distribution
.Verbosity
(Verbosity
)
104 import Distribution
.Version
(Version
, VersionRange
)
105 import Language
.Haskell
.Extension
(Extension
, Language
, knownLanguages
)
107 -- | Class describing the pretty/parsec format of a.
108 class (Pretty a
, Parsec a
) => Described a
where
109 -- | A pretty document of "regex" describing the field format
110 describe
:: proxy a
-> GrammarRegex void
112 -- | Pretty-print description.
114 -- >>> describeDoc ([] :: [Bool])
115 -- \left\{ \mathop{\mathord{``}\mathtt{True}\mathord{"}}\mid\mathop{\mathord{``}\mathtt{False}\mathord{"}} \right\}
117 describeDoc
:: Described a
=> proxy a
-> PP
.Doc
118 describeDoc p
= regexDoc
(describe p
)
120 instance Described
Bool where
121 describe _
= REUnion
["True", "False"]
123 instance Described a
=> Described
(Identity a
) where
124 describe _
= describe
([] :: [a
])
126 -------------------------------------------------------------------------------
128 ------------------------------------------------------------------------------
130 reSpacedList
:: GrammarRegex a
-> GrammarRegex a
131 reSpacedList
= REMunch RESpaces1
133 reCommaList
:: GrammarRegex a
-> GrammarRegex a
134 reCommaList
= RECommaList
136 reCommaNonEmpty
:: GrammarRegex a
-> GrammarRegex a
137 reCommaNonEmpty
= RECommaNonEmpty
139 reOptCommaList
:: GrammarRegex a
-> GrammarRegex a
140 reOptCommaList
= REOptCommaList
142 -------------------------------------------------------------------------------
144 -------------------------------------------------------------------------------
146 reHsString
:: GrammarRegex a
147 reHsString
= RENamed
"hs-string" impl
where
148 impl
= reChar
'"' <> REMunch reEps (REUnion [strChar, escChar]) <> reChar '"'
149 strChar
= RECharSet
$ CS
.difference CS
.universe
(CS
.fromList
"\"\\")
154 , REUnion
["\\n", RENamed
"escapes" "\\n"] -- TODO
155 , "\\" <> RECharSet
"0123456789"
156 , "\\o" <> RECharSet
"01234567"
157 , "\\x" <> RECharSet
"0123456789abcdefABCDEF"
158 , REUnion
["\\^@", RENamed
"control" "\\^@"] -- TODO
159 , REUnion
["\\NUL", RENamed
"ascii" "\\NUL"] -- TODO
162 reUnqualComponent
:: GrammarRegex a
163 reUnqualComponent
= RENamed
"unqual-name" $
164 REMunch1
(reChar
'-') component
167 = REMunch reEps
(RECharSet csAlphaNum
)
168 -- currently the parser accepts "csAlphaNum `difference` "0123456789"
169 -- which is larger set than CS.alpha
171 -- Hackage rejects non ANSI names, so it's not so relevant.
172 <> RECharSet CS
.alpha
173 <> REMunch reEps
(RECharSet csAlphaNum
)
175 reDot
:: GrammarRegex a
178 reComma
:: GrammarRegex a
181 reSpacedComma
:: GrammarRegex a
182 reSpacedComma
= RESpaces
<> reComma
<> RESpaces
184 -------------------------------------------------------------------------------
186 -------------------------------------------------------------------------------
188 csChar
:: Char -> CS
.CharSet
189 csChar
= CS
.singleton
191 csAlpha
:: CS
.CharSet
194 csAlphaNum
:: CS
.CharSet
195 csAlphaNum
= CS
.alphanum
197 csUpper
:: CS
.CharSet
200 csNotSpace
:: CS
.CharSet
201 csNotSpace
= CS
.difference CS
.universe
$ CS
.singleton
' '
203 csNotSpaceOrComma
:: CS
.CharSet
204 csNotSpaceOrComma
= CS
.difference csNotSpace
$ CS
.singleton
','
206 -------------------------------------------------------------------------------
208 -------------------------------------------------------------------------------
210 describeFlagAssignmentNonEmpty
:: GrammarRegex void
211 describeFlagAssignmentNonEmpty
= REMunch1 RESpaces1
$
212 REUnion
[fromString
"+", fromString
"-"] <> describe
(Proxy
:: Proxy FlagName
)
214 -------------------------------------------------------------------------------
216 -------------------------------------------------------------------------------
218 convert
:: GrammarRegex Void
-> RE
.RE Void
219 convert
= go
id . vacuous
where
220 go
:: Ord b
=> (a
-> b
) -> GrammarRegex a
-> RE
.RE b
221 go f
(REAppend rs
) = foldr (\r acc
-> go f r
<> acc
) RE
.Eps rs
222 go f
(REUnion rs
) = foldr (\r acc
-> go f r RE
.\/ acc
) RE
.Null rs
223 go _
(RECharSet cs
) = RE
.Ch
(convertCS cs
)
224 go _
(REString str
) = RE
.string_ str
226 go f
(REMunch sep r
) = RE
.Eps RE
.\/ r
' <> RE
.star_
(sep
' <> r
') where
229 go f
(REMunch1 sep r
) = r
' <> RE
.star_
(sep
' <> r
') where
232 go f
(REMunchR n sep r
)
234 |
otherwise = RE
.Eps RE
.\/ r
' <> go
' (pred n
)
239 go
' m | m
<= 0 = RE
.Eps
240 |
otherwise = RE
.Eps RE
.\/ sep
' <> r
' <> go
' (pred m
)
242 go f
(REOpt r
) = RE
.Eps RE
.\/ go f r
244 go f
(REVar a
) = RE
.Var
(f a
)
245 go f
(RENamed _ r
) = go f r
246 go f
(RERec n r
) = RE
.fix_
(fromString n
)
247 (go
(maybe RE
.B
(RE
.F
. f
)) r
)
249 go _ RESpaces
= RE
.Eps RE
.\/ RE
.ch_
' ' RE
.\/ " " RE
.\/ "\n"
250 go _ RESpaces1
= RE
.ch_
' ' RE
.\/ " " RE
.\/ "\n"
252 go f
(RECommaList r
) = go f
(expandedCommaList r
)
253 go f
(RECommaNonEmpty r
)= go f
(expandedCommaNonEmpty r
)
254 go f
(REOptCommaList r
) = go f
(expandedOptCommaList r
)
256 go _ RETodo
= RE
.Null
258 expandedCommaList
:: GrammarRegex a
-> GrammarRegex a
259 expandedCommaList
= REUnion
. expandedCommaList
'
261 expandedCommaNonEmpty
:: GrammarRegex a
-> GrammarRegex a
262 expandedCommaNonEmpty r
= REUnion
263 [ REMunch1 reSpacedComma r
264 , reComma
<> RESpaces
<> REMunch1 reSpacedComma r
265 , REMunch1 reSpacedComma r
<> RESpaces
<> reComma
268 expandedCommaList
' :: GrammarRegex a
-> [GrammarRegex a
]
269 expandedCommaList
' r
=
270 [ REMunch reSpacedComma r
271 , reComma
<> RESpaces
<> REMunch1 reSpacedComma r
272 , REMunch1 reSpacedComma r
<> RESpaces
<> reComma
275 expandedOptCommaList
:: GrammarRegex a
-> GrammarRegex a
276 expandedOptCommaList r
= REUnion
$ reSpacedList r
: expandedCommaList
' r
278 convertCS
:: CS
.CharSet
-> RE
.CharSet
279 convertCS
= RE
.fromIntervalList
. CS
.toIntervalList
281 -------------------------------------------------------------------------------
283 -------------------------------------------------------------------------------
286 :: forall a
. (Arbitrary a
, Described a
, Typeable a
, Eq a
, Show a
)
289 testDescribed _
= testGroup name
290 [ testProperty
"parsec" propParsec
291 , testProperty
"pretty" propPretty
292 , testProperty
"roundtrip" propRoundtrip
295 name
= show (typeOf
(undefined :: a
))
297 propParsec
:: Ex a
-> Property
298 propParsec
(Example str
) = counterexample
(show res
) $ case res
of
302 res
:: Either String a
303 res
= eitherParsec str
306 rr
= convert
$ describe
(Proxy
:: Proxy a
)
308 propPretty
:: a
-> Property
309 propPretty x
= counterexample str
$ RE
.matchR rr str
313 propRoundtrip
:: a
-> Property
314 propRoundtrip x
= counterexample
(show (res
, str
)) $ case res
of
319 res
= eitherParsec str
321 newtype Ex a
= Example
String
324 instance Described a
=> Arbitrary
(Ex a
) where
327 $ fromMaybe (return "")
329 $ convert
$ describe
(Proxy
:: Proxy a
)
332 |
'\n' `
elem` s
= [ Example
$ map (\c
-> if c
== '\n' then ' ' else c
) s
]
335 -------------------------------------------------------------------------------
337 -------------------------------------------------------------------------------
339 instance Described AbiDependency
where
341 describe
(Proxy
:: Proxy UnitId
) <>
343 describe
(Proxy
:: Proxy AbiHash
)
345 instance Described AbiHash
where
346 describe _
= reMunchCS csAlphaNum
348 instance Described Arch
where
350 [ fromString
(prettyShow arch
)
351 | arch
<- knownArches
354 instance Described BenchmarkType
where
355 describe _
= "exitcode-stdio-1.0"
357 instance Described BuildType
where
358 describe _
= REUnion
["Simple","Configure","Custom","Hooks","Make","Default"]
360 instance Described CompilerFlavor
where
362 [ fromString
(prettyShow c
)
363 | c
<- knownCompilerFlavors
366 instance Described CompilerId
where
368 describe
(Proxy
:: Proxy CompilerFlavor
)
370 <> describe
(Proxy
:: Proxy Version
)
372 instance Described Dependency
where
373 describe _
= REAppend
374 [ RENamed
"pkg-name" (describe
(Proxy
:: Proxy PackageName
))
382 -- no leading or trailing comma
383 , REMunch1 reSpacedComma reUnqualComponent
389 , REOpt
$ RESpaces
<> vr
392 vr
= RENamed
"version-range" (describe
(Proxy
:: Proxy VersionRange
))
394 instance Described ExecutableScope
where
395 describe _
= REUnion
["public","private"]
397 instance Described ExeDependency
where
400 instance Described ExposedModule
where
403 instance Described Extension
where
406 instance Described FlagAssignment
where
407 describe _
= REMunch RESpaces1
$
408 REUnion
[fromString
"+", fromString
"-"] <> describe
(Proxy
:: Proxy FlagName
)
410 instance Described FlagName
where
411 describe _
= lead
<> rest
where
412 lead
= RECharSet
$ csAlphaNum
<> fromString
"_"
413 rest
= reMunchCS
$ csAlphaNum
<> fromString
"_-"
415 instance Described ForeignLibOption
where
416 describe _
= "standalone"
418 instance Described ForeignLibType
where
419 describe _
= REUnion
["native-shared","native-static"]
421 instance Described IncludeRenaming
where
422 describe _
= mr
<> REOpt
(RESpaces
<> "requires" <> RESpaces1
<> mr
)
424 mr
= describe
(Proxy
:: Proxy ModuleRenaming
)
426 instance Described Language
where
427 describe _
= REUnion
$ (REString
. show) <$> reverse knownLanguages
429 instance Described LegacyExeDependency
where
432 instance Described LibraryVisibility
where
433 describe _
= REUnion
["public","private"]
435 instance Described LibVersionInfo
where
436 describe _
= reDigits
<> REOpt
(reChar
':' <> reDigits
<> REOpt
(reChar
':' <> reDigits
)) where
437 reDigits
= reChars
['0'..'9']
439 instance Described Mixin
where
441 RENamed
"package-name" (describe
(Proxy
:: Proxy PackageName
)) <>
442 REOpt
(reChar
':' <> RENamed
"library-name" (describe
(Proxy
:: Proxy UnqualComponentName
))) <>
443 REOpt
(RESpaces1
<> describe
(Proxy
:: Proxy IncludeRenaming
))
445 instance Described ModuleName
where
446 describe _
= REMunch1
(reChar
'.') component
where
447 component
= RECharSet csUpper
<> REMunch reEps
(REUnion
[RECharSet csAlphaNum
, RECharSet
(fromString
"_'")])
449 instance Described ModuleReexport
where
452 instance Described ModuleRenaming
where
455 , "hiding" <> RESpaces
<> bp
(REMunch reSpacedComma mn
)
456 , bp
(REMunch reSpacedComma entry
)
459 bp r
= "(" <> RESpaces
<> r
<> RESpaces
<> ")"
460 mn
= RENamed
"module-name" $ describe
(Proxy
:: Proxy ModuleName
)
462 entry
= mn
<> REOpt
(RESpaces1
<> "as" <> RESpaces1
<> mn
)
464 instance Described MungedPackageName
where
467 instance Described OS
where
469 [ fromString
(prettyShow os
)
473 instance Described PackageIdentifier
where
474 describe _
= describe
(Proxy
:: Proxy PackageName
) <> fromString
"-" <> describe
(Proxy
:: Proxy Version
)
476 instance Described PackageName
where
477 describe _
= reUnqualComponent
479 instance Described PackageVersionConstraint
where
480 describe _
= describe
(Proxy
:: Proxy PackageName
) <> REUnion
481 [ fromString
"-" <> describe
(Proxy
:: Proxy Version
)
482 , RESpaces
<> describe
(Proxy
:: Proxy VersionRange
)
485 instance Described PkgconfigDependency
where
488 instance Described RepoType
where
489 describe _
= reMunch1CS
$ csAlphaNum
<> csChar
'_
' <> csChar
'-'
491 instance Described TestType
where
492 describe _
= REUnion
["exitcode-stdio-1.0", "detailed-0.9"]
494 instance Described Verbosity
where
496 [ REUnion
["0", "1", "2", "3"]
497 , REUnion
["silent", "normal", "verbose", "debug", "deafening"]
498 <> REMunch reEps
(RESpaces
<> "+" <>
499 -- markoutput is left out on purpose
500 REUnion
["callsite", "callstack", "nowrap", "timestamp", "stderr", "stdout" ])
503 instance Described Version
where
504 describe _
= REMunch1 reDot reDigits
where
507 , reChars
['1'..'9'] <> REMunchR
8 reEps
(reChars
['0'..'9'])
510 instance Described VersionRange
where
511 describe _
= RERec
"version-range" $ REUnion
512 [ "==" <> RESpaces
<> ver
513 , ">" <> RESpaces
<> ver
514 , "<" <> RESpaces
<> ver
515 , "<=" <> RESpaces
<> ver
516 , ">=" <> RESpaces
<> ver
517 , "^>=" <> RESpaces
<> ver
520 , "==" <> RESpaces
<> wildVer
522 , reVar0
<> RESpaces
<> "||" <> RESpaces
<> reVar0
523 , reVar0
<> RESpaces
<> "&&" <> RESpaces
<> reVar0
524 , "(" <> RESpaces
<> reVar0
<> RESpaces
<> ")"
527 -- silly haddock: ^>= { 0.1.2, 3.4.5 }
528 , "==" <> RESpaces
<> verSet
529 , "^>=" <> RESpaces
<> verSet
532 ver
' = describe
(Proxy
:: Proxy Version
)
533 ver
= RENamed
"version" ver
'
534 wildVer
= ver
' <> ".*"
535 verSet
= "{" <> RESpaces
<> REMunch1 reSpacedComma ver
<> RESpaces
<> "}"
537 instance Described UnitId
where
538 describe _
= reMunch1CS
$ csAlphaNum
<> csChar
'-' <> csChar
'_
' <> csChar
'.' <> csChar
'+'
540 instance Described UnqualComponentName
where
541 describe _
= reUnqualComponent
543 -------------------------------------------------------------------------------
544 -- Instances: Newtypes
545 -------------------------------------------------------------------------------
547 class Sep sep
=> DescribeSep sep
where
548 describeSep
:: Proxy sep
-> GrammarRegex a
-> GrammarRegex a
550 instance DescribeSep CommaVCat
where describeSep _
= reCommaList
551 instance DescribeSep CommaFSep
where describeSep _
= reCommaList
552 instance DescribeSep VCat
where describeSep _
= reCommaList
553 instance DescribeSep FSep
where describeSep _
= reOptCommaList
554 instance DescribeSep NoCommaFSep
where describeSep _
= reSpacedList
556 instance (Newtype a b
, DescribeSep sep
, Described b
) => Described
(List sep b a
) where
557 describe _
= describeSep
(Proxy
:: Proxy sep
) (describe
(Proxy
:: Proxy b
))
559 instance (Newtype a b
, Ord a
, DescribeSep sep
, Described b
) => Described
(Set
' sep b a
) where
560 describe _
= describeSep
(Proxy
:: Proxy sep
) (describe
(Proxy
:: Proxy b
))
562 instance Described Token
where
563 describe _
= REUnion
[reHsString
, reMunch1CS csNotSpaceOrComma
]
565 instance Described Token
' where
566 describe _
= REUnion
[reHsString
, reMunch1CS csNotSpace
]
568 instance Described a
=> Described
(MQuoted a
) where
569 -- TODO: this is simplification
570 describe _
= describe
([] :: [a
])
572 instance Described SpecVersion
where
573 describe _
= "3.4" -- :)
575 instance Described SpecLicense
where
578 instance Described TestedWith
where
582 instance Described
(SymbolicPath from to
) where
583 describe _
= describe
([] :: [Token
])
585 instance Described
(RelativePath from to
) where
586 describe _
= describe
([] :: [Token
])
588 instance Described
(SymbolicPathNT from to
) where
589 describe _
= describe
([] :: [Token
])
591 instance Described
(RelativePathNT from to
) where
592 describe _
= describe
([] :: [Token
])
594 instance Described CompatLicenseFile
where
595 describe _
= describe
([] :: [Token
])
597 instance Described CompatDataDir
where
598 describe _
= describe
([] :: [Token
])
600 instance Described FilePathNT
where
601 describe _
= describe
([] :: [Token
])