Skip to content

Commit eaf90fc

Browse files
committed
Add some basic combinators
1 parent 742803d commit eaf90fc

File tree

2 files changed

+159
-36
lines changed

2 files changed

+159
-36
lines changed

parsing.purs

Lines changed: 136 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ module Parsing where
33
import Prelude
44
import Either
55
import String
6+
import Arrays
7+
import Maybe
68

79
data ParseError = ParseError
810
{ message :: String
@@ -15,41 +17,153 @@ parseError message = ParseError
1517

1618
data ParseResult s a = ParseResult
1719
{ leftover :: s
18-
, result :: a
20+
, consumed :: Boolean
21+
, result :: Either ParseError a
1922
}
2023

21-
parseResult :: forall s a. s -> a -> ParseResult s a
22-
parseResult leftover result = ParseResult
24+
parseResult :: forall s a. s -> Boolean -> Either ParseError a -> ParseResult s a
25+
parseResult leftover consumed result = ParseResult
2326
{ leftover: leftover
27+
, consumed: consumed
2428
, result: result
2529
}
2630

27-
data Parser s a = Parser (s -> Either ParseError (ParseResult s a))
31+
successResult :: forall s a. s -> Boolean -> a -> ParseResult s a
32+
successResult leftover consumed result = parseResult leftover consumed (Right result)
2833

29-
runParser :: forall s a. Parser s a -> s -> Either ParseError (ParseResult s a)
30-
runParser (Parser p) s = p s
34+
failureResult :: forall s a. s -> Boolean -> ParseError -> ParseResult s a
35+
failureResult leftover consumed err = parseResult leftover consumed (Left err)
3136

32-
fail :: String -> forall s a. Parser s a
33-
fail message = Parser $ \s -> Left (parseError message)
37+
instance Prelude.Functor (ParseResult s) where
38+
(<$>) f (ParseResult o) = parseResult o.leftover o.consumed (f <$> o.result)
3439

35-
string :: String -> Parser String String
36-
string s = Parser $ \s' -> case indexOfS s' s of
37-
0 -> Right $ parseResult (substring (lengthS s) (lengthS s') s') s
38-
_ -> Left $ parseError $ "Expected \"" ++ s ++ "\""
40+
data Parser s a = Parser (s -> ParseResult s a)
3941

40-
char :: Parser String String
41-
char = Parser $ \s -> case s of
42-
"" -> Left $ parseError $ "Unexpected EOF"
43-
_ -> Right $ parseResult (substring 1 (lengthS s) s) (substr 0 1 s)
42+
runParser :: forall s a. Parser s a -> s -> ParseResult s a
43+
runParser (Parser p) s = p s
4444

4545
instance Prelude.Monad (Parser s) where
46-
return a = Parser $ \s -> Right (parseResult s a)
47-
(>>=) (Parser p) f = Parser $ \s -> case p s of
48-
Left err -> Left err
49-
Right (ParseResult { leftover = s, result = a }) -> runParser (f a) s
46+
return a = Parser $ \s -> successResult s false a
47+
(>>=) p f = Parser $ \s -> case runParser p s of
48+
ParseResult ({ leftover = s', consumed = consumed, result = Left err }) -> failureResult s' consumed err
49+
ParseResult ({ leftover = s', consumed = consumed, result = Right a }) -> runParser (f a) s' -- TODO
5050

5151
instance Prelude.Alternative (Parser s) where
5252
empty = fail "No alternative"
5353
(<|>) p1 p2 = Parser $ \s -> case runParser p1 s of
54-
Left _ -> runParser p2 s
55-
Right res -> Right res
54+
ParseResult ({ leftover = s', consumed = false, result = Left _ }) -> runParser p2 s
55+
res -> res
56+
57+
-- Polymorphic Parser Combinators
58+
59+
fail :: forall s a. String -> Parser s a
60+
fail message = Parser $ \s -> failureResult s false (parseError message)
61+
62+
(<?>) :: forall s a. Parser s a -> String -> Parser s a
63+
(<?>) p msg = p <|> fail msg
64+
65+
many :: forall s a. Parser s a -> Parser s [a]
66+
many p = many1 p <|> return []
67+
68+
many1 :: forall s a. Parser s a -> Parser s [a]
69+
many1 p = do a <- p
70+
as <- many p
71+
return (a : as)
72+
73+
between :: forall s a open close. Parser s open -> Parser s close -> ({} -> Parser s a) -> Parser s a
74+
between open close p = do
75+
open
76+
a <- p {}
77+
close
78+
return a
79+
80+
option :: forall s a. a -> Parser s a -> Parser s a
81+
option a p = p <|> return a
82+
83+
optional :: forall s a. Parser s a -> Parser s {}
84+
optional p = (do p
85+
return {}) <|> return {}
86+
87+
optionMaybe :: forall s a. Parser s a -> Parser s (Maybe a)
88+
optionMaybe p = option Nothing (Just <$> p)
89+
90+
try :: forall s a. Parser s a -> Parser s a
91+
try p = Parser $ \s -> case runParser p s of
92+
ParseResult ({ consumed = true, result = Left err }) -> failureResult s false err
93+
res -> res
94+
95+
sepBy :: forall s a sep. Parser s a -> Parser s sep -> Parser s [a]
96+
sepBy p sep = sepBy1 p sep <|> return []
97+
98+
sepBy1 :: forall s a sep. Parser s a -> Parser s sep -> Parser s [a]
99+
sepBy1 p sep = do
100+
a <- p
101+
as <- many $ do
102+
sep
103+
p
104+
return (a : as)
105+
106+
sepEndBy :: forall s a sep. Parser s a -> Parser s sep -> Parser s [a]
107+
sepEndBy p sep = sepEndBy1 p sep <|> return []
108+
109+
sepEndBy1 :: forall s a sep. Parser s a -> Parser s sep -> Parser s [a]
110+
sepEndBy1 p sep = do
111+
a <- p
112+
(do sep
113+
as <- sepEndBy p sep
114+
return (a : as)) <|> return [a]
115+
116+
endBy1 :: forall s a sep. Parser s a -> Parser s sep -> Parser s [a]
117+
endBy1 p sep = many1 $ do
118+
a <- p
119+
sep
120+
return a
121+
122+
endBy :: forall s a sep. Parser s a -> Parser s sep -> Parser s [a]
123+
endBy p sep = many $ do
124+
a <- p
125+
sep
126+
return a
127+
128+
chainr :: forall s a. Parser s a -> Parser s (a -> a -> a) -> a -> Parser s a
129+
chainr p f a = chainr1 p f <|> return a
130+
131+
chainl :: forall s a. Parser s a -> Parser s (a -> a -> a) -> a -> Parser s a
132+
chainl p f a = chainl1 p f <|> return a
133+
134+
chainl1 :: forall s a. Parser s a -> Parser s (a -> a -> a) -> Parser s a
135+
chainl1 p f = do
136+
a <- p
137+
chainl1' p f a
138+
139+
chainl1' :: forall s a. Parser s a -> Parser s (a -> a -> a) -> a -> Parser s a
140+
chainl1' p f a = (do f' <- f
141+
a' <- p
142+
chainl1' p f (f' a a')) <|> return a
143+
144+
chainr1 :: forall s a. Parser s a -> Parser s (a -> a -> a) -> Parser s a
145+
chainr1 p f = do
146+
a <- p
147+
chainr1' p f a
148+
149+
chainr1' :: forall s a. Parser s a -> Parser s (a -> a -> a) -> a -> Parser s a
150+
chainr1' p f a = (do f' <- f
151+
a' <- chainr1 p f
152+
return $ f' a a') <|> return a
153+
154+
-- String Parsers
155+
156+
eof :: Parser String {}
157+
eof = Parser $ \s -> case s of
158+
"" -> successResult s false {}
159+
_ -> failureResult s false $ parseError "Expected EOF"
160+
161+
string :: String -> Parser String String
162+
string s = Parser $ \s' -> case indexOfS s' s of
163+
0 -> successResult (substring (lengthS s) (lengthS s') s') true s
164+
_ -> failureResult s' false $ parseError $ "Expected \"" ++ s ++ "\""
165+
166+
char :: Parser String String
167+
char = Parser $ \s -> case s of
168+
"" -> failureResult s false $ parseError "Unexpected EOF"
169+
_ -> successResult (substring 1 (lengthS s) s) true (substr 0 1 s)

test.purs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,35 @@ import Prelude
44
import Either
55
import Eff
66
import Parsing
7-
8-
parser :: Parser String String
9-
parser = do
10-
s1 <- string "a" <|> string "b"
11-
s2 <- string s1
12-
return $ s1 ++ s2
7+
import Trace
8+
import Arrays
9+
import Maybe
1310

1411
parens :: forall a. ({} -> Parser String a) -> Parser String a
15-
parens p = do
16-
string "("
17-
a <- p {}
18-
string ")"
19-
return a
12+
parens = between (string "(") (string ")")
2013

2114
nested :: {} -> Parser String Number
2215
nested _ = (do
2316
string "a"
2417
return 0) <|> ((+) 1) <$> parens nested
2518

19+
parseTest :: forall s a eff. (Show a) => Parser s a -> s -> Eff (trace :: Trace | eff) {}
20+
parseTest p input = case runParser p input of
21+
ParseResult { result = Left (ParseError err) } -> Trace.print err.message
22+
ParseResult { result = Right result } -> Trace.print result
23+
24+
opTest = chainl char (do string "+"
25+
return (++)) ""
26+
2627
main = do
27-
case runParser (nested {}) "(((a)))" of
28-
Left (ParseError err) -> Trace.print err.message
29-
Right (ParseResult res) -> Trace.print res.result
28+
parseTest (nested {}) "(((a)))"
29+
parseTest (many (string "a")) "aaa"
30+
parseTest (parens (const $ do
31+
string "a"
32+
optionMaybe $ string "b")) "(ab)"
33+
parseTest (string "a" `sepBy1` string ",") "a,a,a"
34+
parseTest (do
35+
as <- string "a" `endBy1` string ","
36+
eof
37+
return as) "a,a,a,"
38+
parseTest opTest "a+b+c"

0 commit comments

Comments
 (0)