Skip to content

Commit 895fc55

Browse files
committed
Handle multiple hs-source-dirs and paths
1 parent a71069c commit 895fc55

File tree

3 files changed

+149
-52
lines changed

3 files changed

+149
-52
lines changed

src/Hie/Cabal/Parser.hs

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Hie.Cabal.Parser where
55
import Control.Applicative
66
import Control.Monad
77
import Data.Attoparsec.Text
8-
import Data.Char (isSpace)
8+
import Data.Char
99
import Data.Text (Text)
1010
import qualified Data.Text as T
1111

@@ -31,117 +31,138 @@ parsePackage' = parseOnly parsePackage
3131
parsePackage :: Parser Package
3232
parsePackage =
3333
( do
34-
n <- field 0 "name"
34+
n <- field 0 "name" $ const parseString
3535
(Package _ t) <- parsePackage
3636
pure $ Package n t
3737
)
3838
<|> ( do
3939
h <- parseComponent 0
4040
(Package n t) <- parsePackage
41-
pure $ Package n (h : t)
41+
pure $ Package n (h <> t)
4242
)
4343
<|> (skipToNextLine >> parsePackage)
4444
<|> pure (Package "" [])
4545

4646
componentHeader :: Indent -> Text -> Parser Name
4747
componentHeader i t = do
48-
indent i
48+
_ <- indent i
4949
_ <- asciiCI t
5050
skipMany tabOrSpace
5151
n <- parseString <|> pure ""
5252
skipToNextLine
5353
pure n
5454

55-
parseComponent :: Indent -> Parser Component
55+
parseComponent :: Indent -> Parser [Component]
5656
parseComponent i =
5757
parseExe i
5858
<|> parseLib i
5959
<|> parseBench i
6060
<|> parseTestSuite i
6161

62-
parseLib :: Indent -> Parser Component
62+
parseLib :: Indent -> Parser [Component]
6363
parseLib i = parseSec i "library" $ Comp Lib
6464

65-
parseTestSuite :: Indent -> Parser Component
65+
parseTestSuite :: Indent -> Parser [Component]
6666
parseTestSuite i = parseSec i "test-suite" $ Comp Test
6767

68-
parseExe :: Indent -> Parser Component
68+
parseExe :: Indent -> Parser [Component]
6969
parseExe = parseSecMain (Comp Exe) "executable"
7070

71-
parseBench :: Indent -> Parser Component
71+
parseBench :: Indent -> Parser [Component]
7272
parseBench = parseSecMain (Comp Bench) "benchmark"
7373

74-
parseSecMain :: (Name -> Path -> Component) -> Text -> Indent -> Parser Component
74+
parseSecMain :: (Name -> Path -> Component) -> Text -> Indent -> Parser [Component]
7575
parseSecMain c s i = do
7676
n <- componentHeader i s
77-
c n <$> pathMain (i + 1) "./" ""
77+
p <- pathMain (i + 1) ["./"] ""
78+
pure $ map (c n) p
7879

7980
parseQuoted :: Parser Text
8081
parseQuoted = do
8182
q <- char '"' <|> char '\''
8283
takeTill (== q)
8384

8485
parseString :: Parser Name
85-
parseString = parseQuoted <|> takeWhile1 (not . (\c -> isSpace c || c == ','))
86-
87-
pathMain :: Indent -> Text -> Text -> Parser Text
86+
parseString = parseQuoted <|> unqualName
87+
88+
unqualName :: Parser Text
89+
unqualName = takeWhile1 (\c -> isAlphaNum c || c `elem` ("-_./" :: String))
90+
91+
parseList :: Indent -> Parser [Text]
92+
parseList i = items <|> (emptyOrComLine >> indent i >> items)
93+
where
94+
items = do
95+
skipMany tabOrSpace
96+
h <- parseString
97+
skipMany tabOrSpace
98+
skipMany (char ',')
99+
t <-
100+
items
101+
<|> (skipToNextLine >> indent i >> parseList i)
102+
<|> pure []
103+
pure $ h : t
104+
105+
pathMain :: Indent -> [Text] -> Text -> Parser [Text]
88106
pathMain i p m =
89-
(field i "hs-source-dirs" >>= (\p' -> pathMain i p' m))
90-
<|> (field i "main-is" >>= pathMain i p)
107+
(hsSourceDir i >>= (\p' -> pathMain i p' m))
108+
<|> (field i "main-is" (const parseString) >>= pathMain i p)
91109
<|> (skipBlockLine i >> pathMain i p m)
92-
<|> pure (p <> "/" <> m)
110+
<|> pure (map (<> "/" <> m) p)
93111

94-
parseSec :: Indent -> Text -> (Name -> Path -> Component) -> Parser Component
112+
parseSec :: Indent -> Text -> (Name -> Path -> Component) -> Parser [Component]
95113
parseSec i compType compCon = do
96114
n <- componentHeader i compType
97-
compCon n <$> extractPath (i + 1)
115+
p <- extractPath (i + 1) []
116+
let p' = if null p then ["./"] else p
117+
pure $ map (compCon n) p'
98118

99119
skipToNextLine :: Parser ()
100120
skipToNextLine = skipWhile (not . isEndOfLine) >> endOfLine
101121

102122
skipBlock :: Indent -> Parser ()
103123
skipBlock i = skipMany $ skipBlockLine i
104124

125+
comment :: Parser ()
126+
comment = skipMany tabOrSpace >> "--" >> skipToNextLine
127+
105128
skipBlockLine :: Indent -> Parser ()
106-
skipBlockLine i =
107-
(indent i >> skipToNextLine)
108-
<|> (skipMany tabOrSpace >> endOfLine)
109-
<|> (skipSpace >> "--" >> skipToNextLine)
129+
skipBlockLine i = (indent i >> skipToNextLine) <|> emptyOrComLine
130+
131+
emptyOrComLine :: Parser ()
132+
emptyOrComLine = skipMany tabOrSpace >> endOfLine <|> comment
110133

111134
tabOrSpace :: Parser Char
112135
tabOrSpace = char ' ' <|> char '\t'
113136

114-
field :: Indent -> Text -> Parser Text
115-
field i f =
137+
hsSourceDir :: Indent -> Parser [Text]
138+
hsSourceDir i = field i "hs-source-dirs" parseList
139+
140+
-- field :: Indent -> Text -> Parser Text
141+
field ::
142+
Indent ->
143+
Text ->
144+
(Indent -> Parser a) ->
145+
Parser a
146+
field i f p =
116147
do
117-
indent i
148+
i' <- indent i
118149
_ <- asciiCI f
119150
skipSpace
120151
_ <- char ':'
121152
skipSpace
122-
p <- parseString
153+
p' <- p $ i' + 1
123154
skipToNextLine
124-
pure p
155+
pure p'
125156

126-
parseMainIs :: Indent -> Parser Path
127-
parseMainIs i =
128-
do
129-
p <- field i "main-is"
130-
skipBlock i
131-
pure p
132-
<?> "hs-source-dirs"
133-
134-
extractPath :: Indent -> Parser Path
135-
extractPath i =
136-
( do
137-
p <- field i "hs-source-dirs"
138-
skipBlock i
139-
pure p
140-
)
141-
<|> (skipBlockLine i >> extractPath i <?> "skip line")
142-
<|> (pure "./" <?> "not found") <?> "extractPath"
157+
extractPath :: Indent -> [Path] -> Parser [Path]
158+
extractPath i ps =
159+
(field i "hs-source-dirs" parseList >>= (\p -> extractPath i $ ps <> p))
160+
<|> (skipBlockLine i >> extractPath i ps)
161+
<|> (comment >> extractPath i ps)
162+
<|> pure ps
143163

144164
-- | Skip at least n spaces
145-
indent :: Indent -> Parser ()
146-
indent 0 = skipMany tabOrSpace <?> "indent 0"
147-
indent i = tabOrSpace >> indent (i - 1) <?> "indent 0"
165+
indent :: Indent -> Parser Int
166+
indent i = do
167+
c <- length <$> many' tabOrSpace
168+
if c >= i then pure c else fail "insufficient indent"

test/Spec.hs

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,29 @@ spec = do
1717
describe "Should Succeed"
1818
$ it "successfully parses executable section"
1919
$ exeSection ~> parseExe 0
20-
`shouldParse` Comp Exe "implicit-hie-exe" "app/Main.hs"
20+
`shouldParse` [Comp Exe "implicit-hie-exe" "app/Main.hs"]
2121
describe "Should Succeed"
2222
$ it "successfully parses test section"
2323
$ testSection ~> parseTestSuite 0
24-
`shouldParse` Comp Test "implicit-hie-test" "test"
24+
`shouldParse` [Comp Test "implicit-hie-test" "test"]
2525
describe "Should Succeed"
2626
$ it "successfully parses library section"
2727
$ libSection ~> parseLib 0
28-
`shouldParse` Comp Lib "" "src"
28+
`shouldParse` [Comp Lib "" "src"]
29+
describe "Should Succeed"
30+
$ it "successfully parses library section with 2 hs-source-dirs"
31+
$ libSection2 ~> parseLib 0
32+
`shouldParse` [Comp Lib "" "src", Comp Lib "" "src2"]
33+
describe "Should Succeed"
34+
$ it "successfully parses library section with 2 paths under hs-source-dirs"
35+
$ libSection3 ~> parseLib 0
36+
`shouldParse` [Comp Lib "" "src", Comp Lib "" "src2"]
2937
describe "Should Succeed"
3038
$ it "successfully parses bench section"
3139
$ do
3240
bs <- T.readFile "test/benchSection"
3341
bs ~> parseBench 0
34-
`shouldParse` Comp Bench "folds" "benchmarks/folds.hs"
42+
`shouldParse` [Comp Bench "folds" "benchmarks/folds.hs"]
3543
describe "Should Succeed"
3644
$ it "successfully parses package"
3745
$ fullFile ~> parsePackage
@@ -58,6 +66,17 @@ spec = do
5866
o <- readFile "test/hie.yaml.cbl"
5967
(hieYaml "cabal" . fmtPkgs "cabal" . (: []) <$> parseOnly parsePackage f)
6068
`shouldBe` Right o
69+
describe "Should Succeed"
70+
$ it "successfully parses comma list"
71+
$ ("one, two" :: Text) ~> parseList 1 `shouldParse` ["one", "two"]
72+
describe "Should Succeed"
73+
$ it "successfully parses newline list"
74+
$ ("one\n two \n three3" :: Text) ~> parseList 1
75+
`shouldParse` ["one", "two", "three3"]
76+
describe "Should Succeed"
77+
$ it "successfully parses newline list"
78+
$ ("one\n two, three3" :: Text) ~> parseList 1
79+
`shouldParse` ["one", "two", "three3"]
6180

6281
fullFile :: Text
6382
fullFile = "name: implicit-hie\n" <> libSection <> exeSection <> testSection
@@ -108,6 +127,43 @@ libSection =
108127
\ default-language: Haskell2010\n\
109128
\"
110129

130+
libSection2 :: Text
131+
libSection2 =
132+
"library\n\
133+
\ exposed-modules:\n\
134+
\ Lib\n\
135+
\ other-modules:\n\
136+
\ Paths_implicit_hie\n\
137+
\ hs-source-dirs:\n\
138+
\ src\n\
139+
\ hs-source-dirs:\n\
140+
\ src2\n\
141+
\ ghc-options: -fspecialize-aggressively -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns -fno-warn-unused-imports -fno-warn-unused-binds -fno-warn-name-shadowing -fwarn-redundant-constraints\n\
142+
\ build-depends:\n\
143+
\ attoparsec\n\
144+
\ , base >=4.7 && <5\n\
145+
\ , text\n\
146+
\ default-language: Haskell2010\n\
147+
\"
148+
149+
libSection3 :: Text
150+
libSection3 =
151+
"library\n\
152+
\ exposed-modules:\n\
153+
\ Lib\n\
154+
\ other-modules:\n\
155+
\ Paths_implicit_hie\n\
156+
\ hs-source-dirs:\n\
157+
\ src,\n\
158+
\ src2\n\
159+
\ ghc-options: -fspecialize-aggressively -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns -fno-warn-unused-imports -fno-warn-unused-binds -fno-warn-name-shadowing -fwarn-redundant-constraints\n\
160+
\ build-depends:\n\
161+
\ attoparsec\n\
162+
\ , base >=4.7 && <5\n\
163+
\ , text\n\
164+
\ default-language: Haskell2010\n\
165+
\"
166+
111167
stackHie :: String
112168
stackHie =
113169
"cradle:\n\

test/benchSection

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
benchmark folds
2+
default-language: Haskell2010
3+
hs-source-dirs: benchmarks
4+
ghc-options: -Wall -threaded
5+
6+
-- GHCJS takes forever to compile dependencies
7+
if impl(ghcjs)
8+
buildable: False
9+
10+
build-depends: base
11+
, bytestring
12+
, containers
13+
, criterion
14+
, lens
15+
, optics
16+
, unordered-containers
17+
, vector
18+
19+
type: exitcode-stdio-1.0
20+
main-is: folds.hs

0 commit comments

Comments
 (0)