Skip to content

Commit 581c909

Browse files
committed
Add combinators, port tests.
1 parent bc35426 commit 581c909

File tree

2 files changed

+138
-14
lines changed

2 files changed

+138
-14
lines changed

src/Text/Parsing/StringParser.purs

Lines changed: 105 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module Text.Parsing.StringParser where
22

3+
import Data.Maybe (Maybe(..))
34
import Data.String (charAt, length, take)
45
import Data.Either (Either(..))
56

@@ -64,15 +65,13 @@ instance alternativeParser :: Alternative Parser where
6465
unParser p1 s (\_ ->
6566
unParser p2 s fc sc) sc)
6667

67-
--
68-
-- Error handling combinator
69-
--
70-
(<?>) :: forall a. Parser a -> String -> Parser a
71-
(<?>) p msg = Parser (\s fc sc -> unParser p s (\_ -> fc (ParseError msg)) sc)
72-
7368
--
7469
-- Some elementary parsers
7570
--
71+
72+
fail :: forall a. String -> Parser a
73+
fail msg = Parser (\_ fc _ -> fc (ParseError msg))
74+
7675
eof :: Parser {}
7776
eof = Parser (\s fc sc -> case s of
7877
{ str = str, pos = i } | i < length str -> fc (ParseError "Expected EOF")
@@ -94,6 +93,104 @@ foreign import indexOf'
9493

9594
string :: String -> Parser String
9695
string nt = Parser (\s fc sc -> case s of
97-
{ str = str, pos = i } | indexOf' nt i str == 0 -> sc nt { str: str, pos: i + length nt }
98-
_ -> fc (ParseError $ "Expected '" ++ nt ++ "'"))
96+
{ str = str, pos = i } | indexOf' nt i str == i -> sc nt { str: str, pos: i + length nt }
97+
{ pos = i } -> fc (ParseError $ "Expected '" ++ nt ++ "' at position " ++ show i ++ "."))
9998

99+
--
100+
-- Parsing Combinators
101+
--
102+
103+
many :: forall a. Parser a -> Parser [a]
104+
many p = many1 p <|> return []
105+
106+
many1 :: forall a. Parser a -> Parser [a]
107+
many1 p = do
108+
a <- p
109+
as <- many p
110+
return (a : as)
111+
112+
(<?>) :: forall a. Parser a -> String -> Parser a
113+
(<?>) p msg = p <|> fail msg
114+
115+
fix :: forall a. (Parser a -> Parser a) -> Parser a
116+
fix f = Parser (\s fc sc -> unParser (f (fix f)) s fc sc)
117+
118+
between :: forall a open close. Parser open -> Parser close -> Parser a -> Parser a
119+
between open close p = do
120+
open
121+
a <- p
122+
close
123+
return a
124+
125+
option :: forall a. a -> Parser a -> Parser a
126+
option a p = p <|> return a
127+
128+
optional :: forall a. Parser a -> Parser {}
129+
optional p = (p >>= \_ -> return {}) <|> return {}
130+
131+
optionMaybe :: forall a. Parser a -> Parser (Maybe a)
132+
optionMaybe p = option Nothing (Just <$> p)
133+
134+
sepBy :: forall a sep. Parser a -> Parser sep -> Parser [a]
135+
sepBy p sep = sepBy1 p sep <|> return []
136+
137+
sepBy1 :: forall a sep. Parser a -> Parser sep -> Parser [a]
138+
sepBy1 p sep = do
139+
a <- p
140+
as <- many $ do
141+
sep
142+
p
143+
return (a : as)
144+
145+
sepEndBy :: forall a sep. Parser a -> Parser sep -> Parser [a]
146+
sepEndBy p sep = sepEndBy1 p sep <|> return []
147+
148+
sepEndBy1 :: forall a sep. Parser a -> Parser sep -> Parser [a]
149+
sepEndBy1 p sep = do
150+
a <- p
151+
(do sep
152+
as <- sepEndBy p sep
153+
return (a : as)) <|> return [a]
154+
155+
endBy1 :: forall a sep. Parser a -> Parser sep -> Parser [a]
156+
endBy1 p sep = many1 $ do
157+
a <- p
158+
sep
159+
return a
160+
161+
endBy :: forall a sep. Parser a -> Parser sep -> Parser [a]
162+
endBy p sep = many $ do
163+
a <- p
164+
sep
165+
return a
166+
167+
chainr :: forall a. Parser a -> Parser (a -> a -> a) -> a -> Parser a
168+
chainr p f a = chainr1 p f <|> return a
169+
170+
chainl :: forall a. Parser a -> Parser (a -> a -> a) -> a -> Parser a
171+
chainl p f a = chainl1 p f <|> return a
172+
173+
chainl1 :: forall a. Parser a -> Parser (a -> a -> a) -> Parser a
174+
chainl1 p f = do
175+
a <- p
176+
chainl1' p f a
177+
178+
chainl1' :: forall a. Parser a -> Parser (a -> a -> a) -> a -> Parser a
179+
chainl1' p f a = (do f' <- f
180+
a' <- p
181+
chainl1' p f (f' a a')) <|> return a
182+
183+
chainr1 :: forall a. Parser a -> Parser (a -> a -> a) -> Parser a
184+
chainr1 p f = do
185+
a <- p
186+
chainr1' p f a
187+
188+
chainr1' :: forall a. Parser a -> Parser (a -> a -> a) -> a -> Parser a
189+
chainr1' p f a = (do f' <- f
190+
a' <- chainr1 p f
191+
return $ f' a a') <|> return a
192+
193+
choice :: forall a. [Parser a] -> Parser a
194+
choice [] = fail "Nothing to parse"
195+
choice [x] = x
196+
choice (x:xs) = x <|> choice xs

tests/Tests.purs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,43 @@ module Main where
22

33
import Data.Array
44
import Data.Maybe
5+
import Data.Either
56

67
import Debug.Trace
78

8-
import qualified Text.Parsing.StringParser as P
9+
import Control.Monad.Eff
10+
11+
import Text.Parsing.StringParser
912

1013
import qualified Test.QuickCheck as QC
1114

12-
main = do
15+
parens :: forall a. Parser a -> Parser a
16+
parens = between (string "(") (string ")")
17+
18+
nested :: Parser Number
19+
nested = fix $ \p -> (do
20+
string "a"
21+
return 0) <|> ((+) 1) <$> parens p
1322

14-
let test1 = P.string "test" :: P.Parser String
15-
16-
print $ P.runParser test1 "testing"
17-
print $ P.runParser test1 "foo"
23+
parseTest :: forall a eff. (Show a) => Parser a -> String -> Eff (trace :: Trace | eff) {}
24+
parseTest p input = case runParser p input of
25+
Left (ParseError err) -> print err
26+
Right result -> print result
27+
28+
opTest :: Parser String
29+
opTest = chainl anyChar (do
30+
string "+"
31+
return (++)) ""
32+
33+
main = do
34+
parseTest nested "(((a)))"
35+
parseTest (many (string "a")) "aaa"
36+
parseTest (parens (do
37+
string "a"
38+
optionMaybe $ string "b")) "(ab)"
39+
parseTest (string "a" `sepBy1` string ",") "a,a,a"
40+
parseTest (do
41+
as <- string "a" `endBy1` string ","
42+
eof
43+
return as) "a,a,a,"
44+
parseTest opTest "a+b+c"

0 commit comments

Comments
 (0)