try Apple AArch64 again
[cabal.git] / Cabal-described / src / Distribution / Described.hs
blob717fd6a5c7a0ca482600a066062aebf32f13c6cb
1 {-# LANGUAGE FlexibleInstances #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE ScopedTypeVariables #-}
4 module Distribution.Described (
5 Described (..),
6 describeDoc,
7 -- * Regular expressions
8 GrammarRegex (..),
9 reEps,
10 reChar,
11 reChars,
12 reMunchCS,
13 reMunch1CS,
14 -- * Variables
15 reVar0,
16 reVar1,
17 -- * Special expressions
18 reDot,
19 reComma,
20 reSpacedComma,
21 reHsString,
22 reUnqualComponent,
23 -- *
24 describeFlagAssignmentNonEmpty,
25 -- * Lists
26 reSpacedList,
27 reCommaList,
28 reCommaNonEmpty,
29 reOptCommaList,
30 -- * Character Sets
31 csChar,
32 csAlpha,
33 csAlphaNum,
34 csUpper,
35 csNotSpace,
36 csNotSpaceOrComma,
37 -- * tasty
38 testDescribed,
39 ) where
41 import Prelude
42 ( Bool (..), Char, Either (..), Enum (..), Eq (..), Ord (..), Show (..), String
43 , elem, fmap, foldr, id, map, maybe, otherwise, return, reverse, undefined
44 , ($), (.), (<$>)
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
68 -- Types
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 -------------------------------------------------------------------------------
127 -- Lists
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 -------------------------------------------------------------------------------
143 -- Specific grammars
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 "\"\\")
151 escChar = REUnion
152 [ "\\&"
153 , "\\\\"
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
165 where
166 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
176 reDot = reChar '.'
178 reComma :: GrammarRegex a
179 reComma = reChar ','
181 reSpacedComma :: GrammarRegex a
182 reSpacedComma = RESpaces <> reComma <> RESpaces
184 -------------------------------------------------------------------------------
185 -- Character sets
186 -------------------------------------------------------------------------------
188 csChar :: Char -> CS.CharSet
189 csChar = CS.singleton
191 csAlpha :: CS.CharSet
192 csAlpha = CS.alpha
194 csAlphaNum :: CS.CharSet
195 csAlphaNum = CS.alphanum
197 csUpper :: CS.CharSet
198 csUpper = CS.upper
200 csNotSpace :: CS.CharSet
201 csNotSpace = CS.difference CS.universe $ CS.singleton ' '
203 csNotSpaceOrComma :: CS.CharSet
204 csNotSpaceOrComma = CS.difference csNotSpace $ CS.singleton ','
206 -------------------------------------------------------------------------------
207 -- Special
208 -------------------------------------------------------------------------------
210 describeFlagAssignmentNonEmpty :: GrammarRegex void
211 describeFlagAssignmentNonEmpty = REMunch1 RESpaces1 $
212 REUnion [fromString "+", fromString "-"] <> describe (Proxy :: Proxy FlagName)
214 -------------------------------------------------------------------------------
215 -- Conversion
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
227 sep' = go f sep
228 r' = go f r
229 go f (REMunch1 sep r) = r' <> RE.star_ (sep' <> r') where
230 sep' = go f sep
231 r' = go f r
232 go f (REMunchR n sep r)
233 | n <= 0 = RE.Eps
234 | otherwise = RE.Eps RE.\/ r' <> go' (pred n)
235 where
236 sep' = go f sep
237 r' = go f r
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 -------------------------------------------------------------------------------
282 -- tasty
283 -------------------------------------------------------------------------------
285 testDescribed
286 :: forall a. (Arbitrary a, Described a, Typeable a, Eq a, Show a)
287 => Proxy a
288 -> TestTree
289 testDescribed _ = testGroup name
290 [ testProperty "parsec" propParsec
291 , testProperty "pretty" propPretty
292 , testProperty "roundtrip" propRoundtrip
294 where
295 name = show (typeOf (undefined :: a))
297 propParsec :: Ex a -> Property
298 propParsec (Example str) = counterexample (show res) $ case res of
299 Right _ -> True
300 Left _ -> False
301 where
302 res :: Either String a
303 res = eitherParsec str
305 rr :: RE.RE Void
306 rr = convert $ describe (Proxy :: Proxy a)
308 propPretty :: a -> Property
309 propPretty x = counterexample str $ RE.matchR rr str
310 where
311 str = prettyShow x
313 propRoundtrip :: a -> Property
314 propRoundtrip x = counterexample (show (res, str)) $ case res of
315 Right y -> x == y
316 Left _ -> False
317 where
318 str = prettyShow x
319 res = eitherParsec str
321 newtype Ex a = Example String
322 deriving (Show)
324 instance Described a => Arbitrary (Ex a) where
325 arbitrary
326 = fmap Example
327 $ fromMaybe (return "")
328 $ RE.generate 10 5
329 $ convert $ describe (Proxy :: Proxy a)
331 shrink (Example s)
332 | '\n' `elem` s = [ Example $ map (\c -> if c == '\n' then ' ' else c) s ]
333 | otherwise = []
335 -------------------------------------------------------------------------------
336 -- Instances
337 -------------------------------------------------------------------------------
339 instance Described AbiDependency where
340 describe _ =
341 describe (Proxy :: Proxy UnitId) <>
342 reChar '=' <>
343 describe (Proxy :: Proxy AbiHash)
345 instance Described AbiHash where
346 describe _ = reMunchCS csAlphaNum
348 instance Described Arch where
349 describe _ = REUnion
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
361 describe _ = REUnion
362 [ fromString (prettyShow c)
363 | c <- knownCompilerFlavors
366 instance Described CompilerId where
367 describe _ =
368 describe (Proxy :: Proxy CompilerFlavor)
369 <> fromString "-"
370 <> describe (Proxy :: Proxy Version)
372 instance Described Dependency where
373 describe _ = REAppend
374 [ RENamed "pkg-name" (describe (Proxy :: Proxy PackageName))
375 , REOpt $
376 reChar ':'
377 <> REUnion
378 [ reUnqualComponent
379 , REAppend
380 [ reChar '{'
381 , RESpaces
382 -- no leading or trailing comma
383 , REMunch1 reSpacedComma reUnqualComponent
384 , RESpaces
385 , reChar '}'
389 , REOpt $ RESpaces <> vr
391 where
392 vr = RENamed "version-range" (describe (Proxy :: Proxy VersionRange))
394 instance Described ExecutableScope where
395 describe _ = REUnion ["public","private"]
397 instance Described ExeDependency where
398 describe _ = RETodo
400 instance Described ExposedModule where
401 describe _ = RETodo
403 instance Described Extension where
404 describe _ = RETodo
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)
423 where
424 mr = describe (Proxy :: Proxy ModuleRenaming)
426 instance Described Language where
427 describe _ = REUnion $ (REString . show) <$> reverse knownLanguages
429 instance Described LegacyExeDependency where
430 describe _ = RETodo
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
440 describe _ =
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
450 describe _ = RETodo
452 instance Described ModuleRenaming where
453 describe _ = REUnion
454 [ reEps
455 , "hiding" <> RESpaces <> bp (REMunch reSpacedComma mn)
456 , bp (REMunch reSpacedComma entry)
458 where
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
465 describe _ = RETodo
467 instance Described OS where
468 describe _ = REUnion
469 [ fromString (prettyShow os)
470 | os <- knownOSs
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
486 describe _ = RETodo
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
495 describe _ = REUnion
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
505 reDigits = REUnion
506 [ reChar '0'
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
519 -- ==0.1.*
520 , "==" <> RESpaces <> wildVer
522 , reVar0 <> RESpaces <> "||" <> RESpaces <> reVar0
523 , reVar0 <> RESpaces <> "&&" <> RESpaces <> reVar0
524 , "(" <> RESpaces <> reVar0 <> RESpaces <> ")"
526 -- == { 0.1.2 }
527 -- silly haddock: ^>= { 0.1.2, 3.4.5 }
528 , "==" <> RESpaces <> verSet
529 , "^>=" <> RESpaces <> verSet
531 where
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
576 describe _ = RETodo
578 instance Described TestedWith where
579 describe _ = RETodo
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])