Skip to content

Commit 80c19b4

Browse files
committed
Introduce FIeldGrammar.
This commit reworks how GenericPackageDescription is parsed from `[Field Position]` and pretty-printed to `Doc`. This also fixes few issues: - Fix haskell#4697: `cabal format` doesn't output custom-setup stanza (nor foreign-lib stanzas) - Fix haskell#4719: `parse . pretty . parse = parse` for all Hackage cabal files. - `parser-hackage-tests roundtrip` is the test program. The handling of `license-file` and `license-files` is changed. Now they behave the same.
1 parent 6083225 commit 80c19b4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+3619
-2019
lines changed

Cabal/Cabal.cabal

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@ extra-source-files:
3333
-- Do NOT edit this section manually; instead, run the script.
3434
-- BEGIN gen-extra-source-files
3535
tests/ParserTests/regressions/Octree-0.5.cabal
36+
tests/ParserTests/regressions/elif.cabal
3637
tests/ParserTests/regressions/encoding-0.8.cabal
38+
tests/ParserTests/regressions/generics-sop.cabal
3739
tests/ParserTests/regressions/issue-774.cabal
3840
tests/ParserTests/regressions/nothing-unicode.cabal
41+
tests/ParserTests/regressions/shake.cabal
3942
tests/ParserTests/warnings/bom.cabal
4043
tests/ParserTests/warnings/bool.cabal
4144
tests/ParserTests/warnings/deprecatedfield.cabal
@@ -274,18 +277,22 @@ library
274277
parsec >= 3.1.9 && <3.2
275278
exposed-modules:
276279
Distribution.Compat.Parsec
280+
Distribution.FieldGrammar
281+
Distribution.FieldGrammar.Class
282+
Distribution.FieldGrammar.Parsec
283+
Distribution.FieldGrammar.Pretty
284+
Distribution.PackageDescription.FieldGrammar
277285
Distribution.PackageDescription.Parsec
278-
Distribution.PackageDescription.Parsec.FieldDescr
279-
Distribution.PackageDescription.Parsec.Quirks
286+
Distribution.PackageDescription.Quirks
280287
Distribution.Parsec.Class
288+
Distribution.Parsec.Common
281289
Distribution.Parsec.ConfVar
290+
Distribution.Parsec.Field
282291
Distribution.Parsec.Lexer
283292
Distribution.Parsec.LexerMonad
293+
Distribution.Parsec.Newtypes
294+
Distribution.Parsec.ParseResult
284295
Distribution.Parsec.Parser
285-
Distribution.Parsec.Types.Common
286-
Distribution.Parsec.Types.Field
287-
Distribution.Parsec.Types.FieldDescr
288-
Distribution.Parsec.Types.ParseResult
289296

290297
-- Lens functionality
291298
exposed-modules:
@@ -400,6 +407,7 @@ test-suite parser-tests
400407
type: exitcode-stdio-1.0
401408
hs-source-dirs: tests
402409
main-is: ParserTests.hs
410+
build-depends: containers
403411
build-depends:
404412
base,
405413
bytestring,
@@ -450,7 +458,7 @@ test-suite parser-hackage-tests
450458

451459
if flag(parsec-struct-diff)
452460
build-depends:
453-
generics-sop >= 0.2.5 && <0.3,
461+
generics-sop >= 0.3.1.0 && <0.4,
454462
these >=0.7.1 && <0.8,
455463
singleton-bool >=0.1.1.0 && <0.2,
456464
keys

Cabal/Distribution/Compat/Map/Strict.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Distribution.Compat.Map.Strict
1212
#ifdef HAVE_containers_050
1313
#else
1414
, insertWith
15+
, fromSet
1516
#endif
1617
) where
1718

@@ -20,7 +21,11 @@ import Data.Map.Strict as X
2021
#else
2122
import Data.Map as X hiding (insertWith, insertWith')
2223
import qualified Data.Map
24+
import qualified Data.Set
2325

2426
insertWith :: Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
2527
insertWith = Data.Map.insertWith'
28+
29+
fromSet :: (k -> a) -> Data.Set.Set k -> Map k a
30+
fromSet f = Data.Map.fromDistinctAscList . Prelude.map (\k -> (k, f k)) . Data.Set.toList
2631
#endif

Cabal/Distribution/Compat/Parsec.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ module Distribution.Compat.Parsec (
2525
P.satisfy,
2626
P.space,
2727
P.spaces,
28+
skipSpaces1,
2829
P.string,
2930
munch,
3031
munch1,
@@ -72,3 +73,6 @@ munch
7273
=> (Char -> Bool)
7374
-> P.ParsecT s u m String
7475
munch = many . P.satisfy
76+
77+
skipSpaces1 :: P.Stream s m Char => P.ParsecT s u m ()
78+
skipSpaces1 = P.skipMany1 P.space

Cabal/Distribution/Compat/ReadP.hs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,23 @@ module Distribution.Compat.ReadP
6767
-- * Running a parser
6868
ReadS, -- :: *; = String -> [(a,String)]
6969
readP_to_S, -- :: ReadP a -> ReadS a
70-
readS_to_P -- :: ReadS a -> ReadP a
70+
readS_to_P, -- :: ReadS a -> ReadP a
71+
72+
-- ** Parsec
73+
parsecToReadP,
7174
)
7275
where
7376

7477
import Prelude ()
7578
import Distribution.Compat.Prelude hiding (many, get)
79+
import Control.Applicative (liftA2)
7680

7781
import qualified Distribution.Compat.MonadFail as Fail
7882

7983
import Control.Monad( replicateM, (>=>) )
8084

85+
import qualified Text.Parsec as P
86+
8187
infixr 5 +++, <++
8288

8389
-- ---------------------------------------------------------------------------
@@ -414,3 +420,16 @@ readS_to_P :: ReadS a -> ReadP r a
414420
-- parser, and therefore a possible inefficiency.
415421
readS_to_P r =
416422
R (\k -> Look (\s -> final [bs'' | (a,s') <- r s, bs'' <- run (k a) s']))
423+
424+
-- ---------------------------------------------------------------------------
425+
-- Converting from Parsec to ReadP
426+
--
427+
-- | Convert @Parsec@ parser to 'ReadP'.
428+
parsecToReadP
429+
:: P.Parsec [Char] u a
430+
-> u -- ^ initial user state
431+
-> ReadP r a
432+
parsecToReadP p u = R $ \k -> Look $ \s ->
433+
case P.runParser (liftA2 (,) p P.getInput) u "<parsecToReadP>" s of
434+
Right (x, s') -> final (run (k x) s')
435+
Left _ -> Fail

Cabal/Distribution/Compiler.hs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,11 @@ import Language.Haskell.Extension
5151
import Distribution.Version (Version, mkVersion', nullVersion)
5252

5353
import qualified System.Info (compilerName, compilerVersion)
54+
import Distribution.Parsec.Class (Parsec (..))
55+
import Distribution.Pretty (Pretty (..))
5456
import Distribution.Text (Text(..), display)
5557
import qualified Distribution.Compat.ReadP as Parse
58+
import qualified Distribution.Compat.Parsec as P
5659
import qualified Text.PrettyPrint as Disp
5760

5861
data CompilerFlavor =
@@ -66,12 +69,20 @@ instance Binary CompilerFlavor
6669
knownCompilerFlavors :: [CompilerFlavor]
6770
knownCompilerFlavors = [GHC, GHCJS, NHC, YHC, Hugs, HBC, Helium, JHC, LHC, UHC]
6871

69-
instance Text CompilerFlavor where
70-
disp (OtherCompiler name) = Disp.text name
71-
disp (HaskellSuite name) = Disp.text name
72-
disp NHC = Disp.text "nhc98"
73-
disp other = Disp.text (lowercase (show other))
72+
instance Pretty CompilerFlavor where
73+
pretty (OtherCompiler name) = Disp.text name
74+
pretty (HaskellSuite name) = Disp.text name
75+
pretty NHC = Disp.text "nhc98"
76+
pretty other = Disp.text (lowercase (show other))
77+
78+
instance Parsec CompilerFlavor where
79+
parsec = classifyCompilerFlavor <$> component
80+
where
81+
component = do
82+
cs <- P.munch1 isAlphaNum
83+
if all isDigit cs then fail "all digits compiler name" else return cs
7484

85+
instance Text CompilerFlavor where
7586
parse = do
7687
comp <- Parse.munch1 isAlphaNum
7788
when (all isDigit comp) Parse.pfail
@@ -81,7 +92,7 @@ classifyCompilerFlavor :: String -> CompilerFlavor
8192
classifyCompilerFlavor s =
8293
fromMaybe (OtherCompiler s) $ lookup (lowercase s) compilerMap
8394
where
84-
compilerMap = [ (display compiler, compiler)
95+
compilerMap = [ (lowercase (display compiler), compiler)
8596
| compiler <- knownCompilerFlavors ]
8697

8798

Cabal/Distribution/FieldGrammar.hs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{-# LANGUAGE FlexibleContexts #-}
2+
{-# LANGUAGE FlexibleInstances #-}
3+
{-# LANGUAGE ScopedTypeVariables #-}
4+
-- | This module provides a way to specify a grammar of @.cabal@ -like files.
5+
module Distribution.FieldGrammar (
6+
-- * Field grammar type
7+
FieldGrammar (..),
8+
uniqueField,
9+
optionalField,
10+
optionalFieldDef,
11+
optionalFieldDefAla,
12+
monoidalField,
13+
deprecatedField',
14+
-- * Concrete grammar implementations
15+
ParsecFieldGrammar,
16+
ParsecFieldGrammar',
17+
parseFieldGrammar,
18+
fieldGrammarKnownFieldList,
19+
PrettyFieldGrammar,
20+
PrettyFieldGrammar',
21+
prettyFieldGrammar,
22+
-- * Auxlilary
23+
(^^^),
24+
Section(..),
25+
Fields,
26+
partitionFields,
27+
takeFields,
28+
runFieldParser,
29+
runFieldParser',
30+
) where
31+
32+
import Distribution.Compat.Prelude
33+
import Prelude ()
34+
35+
import qualified Distribution.Compat.Map.Strict as Map
36+
37+
import Distribution.FieldGrammar.Class
38+
import Distribution.FieldGrammar.Parsec
39+
import Distribution.FieldGrammar.Pretty
40+
import Distribution.Parsec.Field
41+
import Distribution.Utils.Generic (spanMaybe)
42+
43+
type ParsecFieldGrammar' a = ParsecFieldGrammar a a
44+
type PrettyFieldGrammar' a = PrettyFieldGrammar a a
45+
46+
infixl 5 ^^^
47+
48+
-- | Reverse function application which binds tighter than '<$>' and '<*>'.
49+
-- Useful for refining grammar specification.
50+
--
51+
-- @
52+
-- \<*\> 'monoidalFieldAla' "extensions" (alaList' FSep MQuoted) oldExtensions
53+
-- ^^^ 'deprecatedSince' [1,12] "Please use 'default-extensions' or 'other-extensions' fields."
54+
-- @
55+
(^^^) :: a -> (a -> b) -> b
56+
x ^^^ f = f x
57+
58+
-- | Partitionin state
59+
data PS ann = PS (Fields ann) [Section ann] [[Section ann]]
60+
61+
-- | Partition field list into field map and groups of sections.
62+
partitionFields :: [Field ann] -> (Fields ann, [[Section ann]])
63+
partitionFields = finalize . foldl' f (PS mempty mempty mempty)
64+
where
65+
finalize :: PS ann -> (Fields ann, [[Section ann]])
66+
finalize (PS fs s ss)
67+
| null s = (fs, reverse ss)
68+
| otherwise = (fs, reverse (reverse s : ss))
69+
70+
f :: PS ann -> Field ann -> PS ann
71+
f (PS fs s ss) (Field (Name ann name) fss) =
72+
PS (Map.insertWith (flip (++)) name [MkNamelessField ann fss] fs) [] ss'
73+
where
74+
ss' | null s = ss
75+
| otherwise = reverse s : ss
76+
f (PS fs s ss) (Section name sargs sfields) =
77+
PS fs (MkSection name sargs sfields : s) ss
78+
79+
-- | Take all fields from the front.
80+
takeFields :: [Field ann] -> (Fields ann, [Field ann])
81+
takeFields = finalize . spanMaybe match
82+
where
83+
finalize (fs, rest) = (Map.fromListWith (flip (++)) fs, rest)
84+
85+
match (Field (Name ann name) fs) = Just (name, [MkNamelessField ann fs])
86+
match _ = Nothing

0 commit comments

Comments
 (0)