Skip to content

Commit 9781f0a

Browse files
authored
Merge pull request #24 from purescript-contrib/monadrec-many
Use MonadRec for many
2 parents 277bc41 + c43b0a4 commit 9781f0a

File tree

3 files changed

+74
-41
lines changed

3 files changed

+74
-41
lines changed

bower.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@
2525
"purescript-strings": "^2.0.2",
2626
"purescript-foldable-traversable": "^2.0.0",
2727
"purescript-either": "^2.0.0",
28-
"purescript-lists": "^3.0.1",
28+
"purescript-lists": "^3.1.0",
2929
"purescript-tailrec": "^2.0.0"
3030
},
3131
"devDependencies": {
3232
"purescript-math": "^2.0.0",
33-
"purescript-console": "^2.0.0"
33+
"purescript-console": "^2.0.0",
34+
"purescript-assert": "^2.0.0"
3435
}
3536
}

src/Text/Parsing/StringParser/Combinators.purs

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,42 @@
11
-- | This module defines combinators for building string parsers.
22

3-
module Text.Parsing.StringParser.Combinators where
3+
module Text.Parsing.StringParser.Combinators
4+
( lookAhead
5+
, many
6+
, many1
7+
, withError, (<?>)
8+
, between
9+
, option
10+
, optional
11+
, optionMaybe
12+
, sepBy
13+
, sepBy1
14+
, sepEndBy
15+
, sepEndBy1
16+
, endBy1
17+
, endBy
18+
, chainr
19+
, chainl
20+
, chainl1
21+
, chainl1'
22+
, chainr1
23+
, chainr1'
24+
, choice
25+
, manyTill
26+
, module Control.Lazy
27+
) where
428

529
import Prelude
630

31+
import Control.Alt ((<|>))
32+
import Control.Lazy (fix)
33+
734
import Data.Either (Either(..))
8-
import Data.Maybe (Maybe(..))
9-
import Data.List (List(..), singleton)
1035
import Data.Foldable (class Foldable, foldl)
36+
import Data.List (List(..), singleton, manyRec)
37+
import Data.Maybe (Maybe(..))
1138

12-
import Control.Alt ((<|>))
13-
14-
import Text.Parsing.StringParser (Parser(..), fail, unParser)
39+
import Text.Parsing.StringParser (Parser(..), fail)
1540

1641
-- | Read ahead without consuming input.
1742
lookAhead :: forall a. Parser a -> Parser a
@@ -22,25 +47,18 @@ lookAhead (Parser p) = Parser \s ->
2247

2348
-- | Match zero or more times.
2449
many :: forall a. Parser a -> Parser (List a)
25-
many p = many1 p <|> pure Nil
50+
many = manyRec
2651

2752
-- | Match one or more times.
2853
many1 :: forall a. Parser a -> Parser (List a)
29-
many1 p = do
30-
a <- p
31-
as <- many p
32-
pure (Cons a as)
54+
many1 p = Cons <$> p <*> many p
3355

3456
-- | Provide an error message in case of failure.
3557
withError :: forall a. Parser a -> String -> Parser a
3658
withError p msg = p <|> fail msg
3759

3860
infixl 3 withError as <?>
3961

40-
-- | Take the fixed point of a parser function. This function is sometimes useful when building recursive parsers.
41-
fix :: forall a. (Parser a -> Parser a) -> Parser a
42-
fix f = Parser \s -> unParser (f (fix f)) s
43-
4462
-- | Parse a string between opening and closing markers.
4563
between :: forall a open close. Parser open -> Parser close -> Parser a -> Parser a
4664
between open close p = do

test/Main.purs

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
module Test.Main where
22

33
import Prelude hiding (between)
4+
45
import Control.Alt ((<|>))
56
import Control.Monad.Eff (Eff)
6-
import Control.Monad.Eff.Console (CONSOLE, logShow)
7-
import Data.Either (Either(..))
8-
import Data.String (singleton)
9-
import Text.Parsing.StringParser (Parser, ParseError(..), try, runParser)
7+
import Control.Monad.Eff.Console (CONSOLE)
8+
9+
import Data.Either (isLeft, isRight, Either(..))
10+
import Data.List (List(Nil), (:))
11+
import Data.String (joinWith, singleton)
12+
import Data.Unfoldable (replicate)
13+
14+
import Test.Assert (assert', ASSERT, assert)
15+
import Text.Parsing.StringParser (Parser, runParser, try)
1016
import Text.Parsing.StringParser.Combinators (many1, endBy1, sepBy1, optionMaybe, many, chainl, fix, between)
1117
import Text.Parsing.StringParser.Expr (Assoc(..), Operator(..), buildExprParser)
12-
import Text.Parsing.StringParser.String (anyDigit, eof, string, anyChar, anyLetter, alphaNum)
18+
import Text.Parsing.StringParser.String (anyDigit, eof, string, anyChar)
1319

1420
parens :: forall a. Parser a -> Parser a
1521
parens = between (string "(") (string ")")
@@ -19,12 +25,6 @@ nested = fix $ \p -> (do
1925
string "a"
2026
pure 0) <|> ((+) 1) <$> parens p
2127

22-
parseTest :: forall a eff. Show a => Parser a -> String -> Eff (console :: CONSOLE | eff) Unit
23-
parseTest p input =
24-
case runParser p input of
25-
Left (ParseError err) -> logShow err
26-
Right result -> logShow result
27-
2828
opTest :: Parser String
2929
opTest = chainl (singleton <$> anyChar) (string "+" $> append) ""
3030

@@ -51,22 +51,36 @@ tryTest :: Parser String
5151
tryTest = try ((<>) <$> string "aa" <*> string "bb") <|>
5252
(<>) <$> string "aa" <*> string "cc"
5353

54-
main :: forall e. Eff (console :: CONSOLE | e) Unit
54+
canParse :: forall a. Parser a -> String -> Boolean
55+
canParse p input = isRight $ runParser p input
56+
57+
parseFail :: forall a. Parser a -> String -> Boolean
58+
parseFail p input = isLeft $ runParser p input
59+
60+
expectResult :: forall a. (Eq a) => a -> Parser a -> String -> Boolean
61+
expectResult res p input = runParser p input == Right res
62+
63+
main :: forall e. Eff (console :: CONSOLE, assert :: ASSERT | e) Unit
5564
main = do
56-
parseTest nested "(((a)))"
57-
parseTest (many (string "a")) "aaa"
58-
parseTest (parens (do
65+
assert' "many should not blow the stack" $ canParse (many (string "a")) (joinWith "" $ replicate 100000 "a")
66+
assert' "many failing after" $ parseFail (do
67+
as <- many (string "a")
68+
eof
69+
pure as) (joinWith "" (replicate 100000 "a") <> "b" )
70+
71+
assert $ expectResult 3 nested "(((a)))"
72+
assert $ expectResult ("a":"a":"a":Nil) (many (string "a")) "aaa"
73+
assert $ parseFail (many1 (string "a")) ""
74+
assert $ canParse (parens (do
5975
string "a"
6076
optionMaybe $ string "b")) "(ab)"
61-
parseTest (string "a" `sepBy1` string ",") "a,a,a"
62-
parseTest (do
77+
assert $ expectResult ("a":"a":"a":Nil) (string "a" `sepBy1` string ",") "a,a,a"
78+
assert $ canParse (do
6379
as <- string "a" `endBy1` string ","
6480
eof
6581
pure as) "a,a,a,"
66-
parseTest opTest "a+b+c"
67-
parseTest exprTest "1*2+3/4-5"
68-
parseTest tryTest "aacc"
69-
parseTest (many1 anyDigit) "01234/"
70-
parseTest (many1 anyDigit) "56789:"
71-
parseTest (many anyLetter) "aB"
72-
parseTest (many alphaNum) "aB3"
82+
assert' "opTest" $ expectResult "abc" opTest "a+b+c"
83+
assert' "exprTest" $ expectResult (-3) exprTest "1*2+3/4-5"
84+
assert' "tryTest "$ canParse tryTest "aacc"
85+
assert $ expectResult ('0':'1':'2':'3':'4':Nil) (many1 anyDigit) "01234/"
86+
assert $ expectResult ('5':'6':'7':'8':'9':Nil) (many1 anyDigit) "56789:"

0 commit comments

Comments
 (0)