Improve list parser

Current parser for lists, like `packages:` in `cabal.project.local`,
fails to process some input, for example:

packages:
    foo/foo.cabal
  , bar/bar.cabal
  , baz/baz.cabal

This commit simplifies the parser by using `sepBy` directly and by
handling indented fields more uniformly.
This commit is contained in:
Maxim Koltsov 2020-06-04 23:05:16 +03:00
parent 5f2ed1c9cf
commit 7ae66425ab
No known key found for this signature in database
GPG Key ID: D591672EDC20D794
2 changed files with 20 additions and 10 deletions

View File

@ -6,6 +6,7 @@ import Control.Applicative
import Control.Monad
import Data.Attoparsec.Text
import Data.Char
import Data.Functor
import Data.Maybe
import Data.Text (Text)
import qualified Data.Text as T
@ -92,19 +93,24 @@ parseString = parseQuoted <|> unqualName
unqualName :: Parser Text
unqualName = takeWhile1 (not . (\c -> isSpace c || c == ','))
-- | Skip spaces and if enf of line is reached, skip it as well and require that
-- next one starts with indent.
--
-- Used for parsing fields.
optSkipToNextLine :: Indent -> Parser ()
optSkipToNextLine i = do
skipMany $ satisfy (\c -> isSpace c && not (isEndOfLine c))
mChar <- peekChar
case mChar of
Just c | isEndOfLine c ->
char c *> indent i $> ()
_ -> pure ()
-- | Comma or space separated list, with optional new lines.
parseList :: Indent -> Parser [Text]
parseList i = items <|> (emptyOrComLine >> indent i >> items)
where
items = do
skipMany tabOrSpace
h <- parseString
skipMany tabOrSpace
skipMany (char ',')
t <-
items
<|> (skipToNextLine >> indent i >> parseList i)
<|> pure []
pure $ h : t
items = sepBy parseString (optSkipToNextLine i *> skipMany (char ',') *> optSkipToNextLine i)
pathMain :: Indent -> [Text] -> Text -> [Text] -> [Text] -> Parser [Text]
pathMain i p m o a =

View File

@ -86,6 +86,10 @@ spec = do
$ it "quoted list"
$ ("\"one\"\n two\n three3" :: Text) ~> parseList 1
`shouldParse` ["one", "two", "three3"]
describe "Should Succeed"
$ it "list with leading commas"
$ ("one\n , two\n , three3" :: Text) ~> parseList 1
`shouldParse` ["one", "two", "three3"]
exeSection :: Text
exeSection =