Skip to content

Commit fe969c3

Browse files
authored
Non-empty combinators return NonEmptyList (#102)
* Non-empty combinators return NonEmptyList * Updated change log * Separate 0+ and 1+ combinator implementations * Removed `toList` from imports
1 parent 77df9c7 commit fe969c3

File tree

4 files changed

+38
-23
lines changed

4 files changed

+38
-23
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Notable changes to this project are documented in this file. The format is based
55
## [Unreleased]
66

77
Breaking changes (😱!!!):
8+
- Non-empty combinators return `NonEmptyList` ([#102](https://github.com/purescript-contrib/purescript-parsing/pull/102))
89

910
New features:
1011

src/Text/Parsing/Parser/Combinators.purs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ import Control.Monad.State (StateT(..), runStateT)
2929
import Control.Plus (empty, (<|>))
3030
import Data.Either (Either(..))
3131
import Data.Foldable (class Foldable, foldl)
32-
import Data.List (List(..), (:), many, some, singleton)
32+
import Data.List (List(..), (:), many)
33+
import Data.List.NonEmpty (NonEmptyList, cons', singleton)
3334
import Data.Maybe (Maybe(..))
3435
import Data.Newtype (unwrap)
3536
import Data.Tuple (Tuple(..))
@@ -99,30 +100,40 @@ lookAhead p = (ParserT <<< ExceptT <<< StateT) \s -> do
99100
-- | digit `sepBy` string ","
100101
-- | ```
101102
sepBy :: forall m s a sep. Monad m => ParserT s m a -> ParserT s m sep -> ParserT s m (List a)
102-
sepBy p sep = sepBy1 p sep <|> pure Nil
103+
sepBy p sep =
104+
(do a <- p
105+
as <- many $ sep *> p
106+
pure (a : as)) <|> pure Nil
103107

104108
-- | Parse phrases delimited by a separator, requiring at least one match.
105-
sepBy1 :: forall m s a sep. Monad m => ParserT s m a -> ParserT s m sep -> ParserT s m (List a)
109+
sepBy1 :: forall m s a sep. Monad m => ParserT s m a -> ParserT s m sep -> ParserT s m (NonEmptyList a)
106110
sepBy1 p sep = do
107111
a <- p
108112
as <- many $ sep *> p
109-
pure (a : as)
113+
pure (cons' a as)
110114

111115
-- | Parse phrases delimited and optionally terminated by a separator.
112116
sepEndBy :: forall m s a sep. Monad m => ParserT s m a -> ParserT s m sep -> ParserT s m (List a)
113-
sepEndBy p sep = sepEndBy1 p sep <|> pure Nil
117+
sepEndBy p sep =
118+
(do a <- p
119+
as <- many $ sep *> p
120+
optional sep
121+
pure (a : as)) <|> pure Nil
114122

115123
-- | Parse phrases delimited and optionally terminated by a separator, requiring at least one match.
116-
sepEndBy1 :: forall m s a sep. Monad m => ParserT s m a -> ParserT s m sep -> ParserT s m (List a)
124+
sepEndBy1 :: forall m s a sep. Monad m => ParserT s m a -> ParserT s m sep -> ParserT s m (NonEmptyList a)
117125
sepEndBy1 p sep = do
118126
a <- p
119-
(do _ <- sep
120-
as <- sepEndBy p sep
121-
pure (a : as)) <|> pure (singleton a)
127+
(do as <- many $ sep *> p
128+
optional sep
129+
pure (cons' a as)) <|> pure (singleton a)
122130

123131
-- | Parse phrases delimited and terminated by a separator, requiring at least one match.
124-
endBy1 :: forall m s a sep. Monad m => ParserT s m a -> ParserT s m sep -> ParserT s m (List a)
125-
endBy1 p sep = some $ p <* sep
132+
endBy1 :: forall m s a sep. Monad m => ParserT s m a -> ParserT s m sep -> ParserT s m (NonEmptyList a)
133+
endBy1 p sep = do
134+
a <- p <* sep
135+
as <- many $ p <* sep
136+
pure (cons' a as)
126137

127138
-- | Parse phrases delimited and terminated by a separator.
128139
endBy :: forall m s a sep. Monad m => ParserT s m a -> ParserT s m sep -> ParserT s m (List a)
@@ -193,8 +204,9 @@ manyTill p end = scan
193204
pure (x:xs)
194205

195206
-- | Parse several phrases until the specified terminator matches, requiring at least one match.
196-
many1Till :: forall s a m e. Monad m => ParserT s m a -> ParserT s m e -> ParserT s m (List a)
207+
many1Till :: forall s a m e. Monad m => ParserT s m a -> ParserT s m e -> ParserT s m (NonEmptyList a)
197208
many1Till p end = do
198209
x <- p
199210
xs <- manyTill p end
200-
pure (x:xs)
211+
pure (cons' x xs)
212+

src/Text/Parsing/Parser/Token.purs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import Data.Identity (Identity)
3636
import Data.Int (toNumber)
3737
import Data.List (List(..))
3838
import Data.List as List
39+
import Data.List.NonEmpty (NonEmptyList)
3940
import Data.Maybe (Maybe(..), maybe)
4041
import Data.String (null, toLower)
4142
import Data.String.CodeUnits as SCU
@@ -258,15 +259,15 @@ type GenTokenParser s m
258259
semiSep :: forall a . ParserT s m a -> ParserT s m (List a),
259260
-- | Lexeme parser `semiSep1 p` parses *one* or more occurrences of `p`
260261
-- | separated by `semi`. Returns a list of values pureed by `p`.
261-
semiSep1 :: forall a . ParserT s m a -> ParserT s m (List a),
262+
semiSep1 :: forall a . ParserT s m a -> ParserT s m (NonEmptyList a),
262263
-- | Lexeme parser `commaSep p` parses *zero* or more occurrences of
263264
-- | `p` separated by `comma`. Returns a list of values pureed
264265
-- | by `p`.
265266
commaSep :: forall a . ParserT s m a -> ParserT s m (List a),
266267
-- | Lexeme parser `commaSep1 p` parses *one* or more occurrences of
267268
-- | `p` separated by `comma`. Returns a list of values pureed
268269
-- | by `p`.
269-
commaSep1 :: forall a . ParserT s m a -> ParserT s m (List a)
270+
commaSep1 :: forall a . ParserT s m a -> ParserT s m (NonEmptyList a)
270271
}
271272

272273
-----------------------------------------------------------
@@ -369,10 +370,10 @@ makeTokenParser (LanguageDef languageDef)
369370
semiSep :: forall a . ParserT String m a -> ParserT String m (List a)
370371
semiSep p = sepBy p semi
371372

372-
commaSep1 :: forall a . ParserT String m a -> ParserT String m (List a)
373+
commaSep1 :: forall a . ParserT String m a -> ParserT String m (NonEmptyList a)
373374
commaSep1 p = sepBy1 p comma
374375

375-
semiSep1 :: forall a . ParserT String m a -> ParserT String m (List a)
376+
semiSep1 :: forall a . ParserT String m a -> ParserT String m (NonEmptyList a)
376377
semiSep1 p = sepBy1 p semi
377378

378379
-----------------------------------------------------------

test/Main.purs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Control.Lazy (fix)
77
import Data.Array (some)
88
import Data.Either (Either(..))
99
import Data.List (List(..), fromFoldable, many)
10+
import Data.List.NonEmpty (cons, cons')
1011
import Data.Maybe (Maybe(..))
1112
import Data.String.CodeUnits (fromCharArray, singleton)
1213
import Data.Tuple (Tuple(..))
@@ -357,10 +358,10 @@ tokenParserSemiSepTest = do
357358
tokenParserSemiSep1Test :: TestM
358359
tokenParserSemiSep1Test = do
359360
-- parse semi sep1
360-
parseTest "foo; foo" (fromFoldable ["foo", "foo"]) $ testTokenParser.semiSep1 $ string "foo"
361+
parseTest "foo; foo" (cons "foo" (cons' "foo" Nil)) $ testTokenParser.semiSep1 $ string "foo"
361362

362363
-- parse semi sep1 with newline
363-
parseTest "foo; \nfoo" (fromFoldable ["foo", "foo"]) $ testTokenParser.semiSep1 $ string "foo"
364+
parseTest "foo; \nfoo" (cons "foo" (cons' "foo" Nil)) $ testTokenParser.semiSep1 $ string "foo"
364365

365366
-- no parse on empty string
366367
parseErrorTestPosition (testTokenParser.semiSep1 $ string "foo") "" $ mkPos 1
@@ -379,10 +380,10 @@ tokenParserCommaSepTest = do
379380
tokenParserCommaSep1Test :: TestM
380381
tokenParserCommaSep1Test = do
381382
-- parse comma sep1
382-
parseTest "foo, foo" (fromFoldable ["foo", "foo"]) $ testTokenParser.commaSep1 $ string "foo"
383+
parseTest "foo, foo" (cons "foo" (cons' "foo" Nil)) $ testTokenParser.commaSep1 $ string "foo"
383384

384385
-- parse comma sep1 with newline
385-
parseTest "foo, \nfoo" (fromFoldable ["foo", "foo"]) $ testTokenParser.commaSep1 $ string "foo"
386+
parseTest "foo, \nfoo" (cons "foo" (cons' "foo" Nil)) $ testTokenParser.commaSep1 $ string "foo"
386387

387388
-- no parse on empty string
388389
parseErrorTestPosition (testTokenParser.commaSep1 $ string "foo") "" $ mkPos 1
@@ -438,8 +439,8 @@ main = do
438439
parseTest "(ab)" (Just "b") $ parens do
439440
_ <- string "a"
440441
optionMaybe $ string "b"
441-
parseTest "a,a,a" (Cons "a" (Cons "a" (Cons "a" Nil))) $ string "a" `sepBy1` string ","
442-
parseTest "a,a,a," (Cons "a" (Cons "a" (Cons "a" Nil))) $ do
442+
parseTest "a,a,a" (cons "a" (cons "a" (cons' "a" Nil))) $ string "a" `sepBy1` string ","
443+
parseTest "a,a,a," (cons "a" (cons "a" (cons' "a" Nil))) $ do
443444
as <- string "a" `endBy1` string ","
444445
eof
445446
pure as

0 commit comments

Comments
 (0)