Skip to content

Commit 75d5d0d

Browse files
authored
Implement tryAhead and fix docs (#86)
* implement try ahead and fix docs * update changelog
1 parent 04fdd95 commit 75d5d0d

File tree

8 files changed

+34
-23
lines changed

8 files changed

+34
-23
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ New features:
1313
Bugfixes:
1414
- Do not export `chainl'` and `chainr'` helper functions (#84 by @chtenb)
1515
- Issue #69: Fix regex parser to always wrap pattern inside `^(..)` (#80 by @chtenb)
16+
- Issue #73: lookAhead consumes input on failure. Introduce `tryAhead` and correct documentation for `lookAhead` (#86 by @chtenb)
1617

1718
Other improvements:
1819
- Added `purs-tidy` formatter (#76 by @thomashoneyman)

src/Text/Parsing/StringParser.purs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,6 @@ instance lazyParser :: Lazy (Parser a) where
9696
fail :: forall a. String -> Parser a
9797
fail error = Parser \{ position } -> Left { pos: position, error }
9898

99-
-- | In case of error, the default behavior is to backtrack if no input was consumed.
100-
-- |
101-
-- | `try p` backtracks even if input was consumed.
102-
try :: forall a. Parser a -> Parser a
103-
try (Parser p) = Parser \s ->
104-
case p s of
105-
Left { error } -> Left { pos: s.position, error }
106-
right -> right
107-
10899
instance semigroupParser :: Semigroup a => Semigroup (Parser a) where
109100
append = lift2 append
110101

src/Text/Parsing/StringParser/CodePoints.purs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ import Data.String.CodePoints as SCP
3535
import Data.String.CodeUnits as SCU
3636
import Data.String.Regex as Regex
3737
import Data.String.Regex.Flags (noFlags)
38-
import Text.Parsing.StringParser (Parser(..), try, fail)
39-
import Text.Parsing.StringParser.Combinators (many, (<?>))
38+
import Text.Parsing.StringParser (Parser(..), fail)
39+
import Text.Parsing.StringParser.Combinators (try, many, (<?>))
4040
import Text.Parsing.StringParser.CodeUnits as CodeUnitsParser
4141

4242
-- | Match the end of the file.

src/Text/Parsing/StringParser/CodeUnits.purs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ import Data.String.CodeUnits (charAt, singleton)
3434
import Data.String.CodeUnits as SCU
3535
import Data.String.Regex as Regex
3636
import Data.String.Regex.Flags (noFlags)
37-
import Text.Parsing.StringParser (Parser(..), try, fail)
38-
import Text.Parsing.StringParser.Combinators (many, (<?>))
37+
import Text.Parsing.StringParser (Parser(..), fail)
38+
import Text.Parsing.StringParser.Combinators (try, many, (<?>))
3939

4040
-- | Match the end of the file.
4141
eof :: Parser Unit

src/Text/Parsing/StringParser/Combinators.purs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
-- | This module defines combinators for building string parsers.
22
module Text.Parsing.StringParser.Combinators
3-
( many
3+
( try
4+
, lookAhead
5+
, tryAhead
6+
, many
47
, many1
58
, withError
69
, (<?>)
@@ -21,7 +24,6 @@ module Text.Parsing.StringParser.Combinators
2124
, choice
2225
, manyTill
2326
, many1Till
24-
, lookAhead
2527
, module Control.Lazy
2628
) where
2729

@@ -39,13 +41,26 @@ import Data.Maybe (Maybe(..))
3941
import Data.NonEmpty ((:|))
4042
import Text.Parsing.StringParser (Parser(..), fail)
4143

42-
-- | Read ahead without consuming input.
44+
-- | `try p` means: run `p` but do not consume input in case of failure.
45+
try :: forall a. Parser a -> Parser a
46+
try (Parser p) = Parser \s ->
47+
case p s of
48+
Left { error } -> Left { pos: s.position, error }
49+
right -> right
50+
51+
-- | `lookAhead p` means: run `p` but do not consume input in case of success.
52+
-- | In most cases you will probably want to use `tryAhead` instead.
4353
lookAhead :: forall a. Parser a -> Parser a
4454
lookAhead (Parser p) = Parser \s ->
4555
case p s of
4656
Right { result } -> Right { result, suffix: s }
4757
left -> left
4858

59+
-- | Read ahead without consuming input.
60+
-- | `tryAhead p` means: succeed if what comes next is of the form `p`; fail otherwise.
61+
tryAhead :: forall a. Parser a -> Parser a
62+
tryAhead = try <<< lookAhead
63+
4964
-- | Match zero or more times.
5065
many :: forall a. Parser a -> Parser (List a)
5166
many = manyRec

test/BasicSpecs.purs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import Data.Traversable (traverse)
1212
import Effect (Effect)
1313
import Effect.Class.Console (log)
1414
import Test.Assert (assert')
15-
import Text.Parsing.StringParser (Parser, runParser, try)
15+
import Text.Parsing.StringParser (Parser, runParser)
1616
import Text.Parsing.StringParser.CodePoints (anyChar, anyDigit, anyLetter, char, eof, skipSpaces, string)
17-
import Text.Parsing.StringParser.Combinators (between, chainl, chainl1, endBy, endBy1, lookAhead, many, many1, manyTill, sepBy, sepBy1, sepEndBy, sepEndBy1)
17+
import Text.Parsing.StringParser.Combinators (try, tryAhead, between, chainl, chainl1, endBy, endBy1, lookAhead, many, many1, manyTill, sepBy, sepBy1, sepEndBy, sepEndBy1)
1818

1919
type TestInputs = { successes :: Array String, failures :: Array String }
2020
type TestCase = { name :: String, parser :: AnyParser, inputs :: TestInputs }
@@ -57,7 +57,11 @@ testCases =
5757
}
5858
, { name: "lookAhead"
5959
, parser: mkAnyParser $ lookAhead (char 'a') *> anyLetter
60-
, inputs: { successes: [ "a" ], failures: [ "", "b" ] }
60+
, inputs: { successes: [ "a" ], failures: [ "", "b", "ab" ] }
61+
}
62+
, { name: "tryAhead"
63+
, parser: mkAnyParser $ tryAhead (char 'a' *> anyDigit) *> (anyChar *> anyChar) <|> (anyDigit *> anyDigit)
64+
, inputs: { successes: [ "a6", "66" ], failures: [ "", "b", "-", "6", "aa", "a6aa", "aa66" ] }
6165
}
6266
, { name: "many"
6367
, parser: mkAnyParser $ many (char 'a')

test/CodePoints.purs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import Data.Unfoldable (replicate)
1515
import Effect (Effect)
1616
import Effect.Class.Console (log)
1717
import Test.Assert (assert', assert)
18-
import Text.Parsing.StringParser (ParseError, Parser(..), PosString, runParser, try)
18+
import Text.Parsing.StringParser (ParseError, Parser(..), PosString, runParser)
1919
import Text.Parsing.StringParser.CodePoints (anyDigit, char, eof, string, anyChar, regex)
20-
import Text.Parsing.StringParser.Combinators (many1, endBy1, sepBy1, optionMaybe, many, manyTill, many1Till, chainl, fix, between)
20+
import Text.Parsing.StringParser.Combinators (try, many1, endBy1, sepBy1, optionMaybe, many, manyTill, many1Till, chainl, fix, between)
2121
import Text.Parsing.StringParser.Expr (Assoc(..), Operator(..), buildExprParser)
2222

2323
parens :: forall a. Parser a -> Parser a

test/CodeUnits.purs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import Data.Unfoldable (replicate)
1515
import Effect (Effect)
1616
import Effect.Class.Console (log)
1717
import Test.Assert (assert', assert)
18-
import Text.Parsing.StringParser (Parser, runParser, try)
18+
import Text.Parsing.StringParser (Parser, runParser)
1919
import Text.Parsing.StringParser.CodeUnits (anyDigit, eof, string, anyChar, regex)
20-
import Text.Parsing.StringParser.Combinators (many1, endBy1, sepBy1, optionMaybe, many, manyTill, many1Till, chainl, fix, between)
20+
import Text.Parsing.StringParser.Combinators (try, many1, endBy1, sepBy1, optionMaybe, many, manyTill, many1Till, chainl, fix, between)
2121
import Text.Parsing.StringParser.Expr (Assoc(..), Operator(..), buildExprParser)
2222

2323
parens :: forall a. Parser a -> Parser a

0 commit comments

Comments
 (0)