Skip to content

Commit 6ba0595

Browse files
committed
[Parsers] End ReadP chapter
1 parent 967c0bd commit 6ba0595

File tree

1 file changed

+120
-1
lines changed

1 file changed

+120
-1
lines changed

en/lessons/parsers/readp.md

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,123 @@ title: ReadP
55

66
{% include toc.html %}
77

8-
https://hackage.haskell.org/package/base-4.12.0.0/docs/Text-ParserCombinators-ReadP.html
8+
`base` embeds a parser combinators library ([`ReadP`](https://hackage.haskell.org/package/base-4.16.0.0/docs/Text-ParserCombinators-ReadP.html))
9+
as seen in the introduction.
10+
11+
# Definition
12+
13+
Right from the introduction:
14+
15+
> It parses all alternatives in parallel, so it never keeps hold of the beginning of the input string, a common source of space leaks with other parsers.
16+
17+
Let's proceed with `ReadP` definition:
18+
19+
```haskell
20+
newtype ReadP a = R (forall b . (a -> P b) -> P b)
21+
22+
instance Functor ReadP where
23+
fmap h (R f) = R (\k -> f (k . h))
24+
25+
instance Applicative ReadP where
26+
pure x = R (\k -> k x)
27+
(<*>) = ap
28+
29+
instance Monad ReadP where
30+
R m >>= f = R (\k -> m (\a -> let R m' = f a in m' k))
31+
32+
instance Alternative ReadP where
33+
empty = R (\_ -> Fail)
34+
R f1 <|> R f2 = R (\k -> f1 k <|> f2 k)
35+
```
36+
37+
That's minimalist, let see how `P` is defined:
38+
39+
```haskell
40+
data P a
41+
= Get (Char -> P a)
42+
| Look (String -> P a)
43+
| Fail
44+
| Result a (P a)
45+
| Final (NonEmpty (a,String))
46+
```
47+
48+
It looks like `P` is the current parser's instruction.
49+
50+
Now, we can have a look at the associated instances:
51+
52+
```haskell
53+
instance Applicative P where
54+
pure x = Result x Fail
55+
(<*>) = ap
56+
57+
instance Monad P where
58+
(Get f) >>= k = Get (\c -> f c >>= k)
59+
(Look f) >>= k = Look (\s -> f s >>= k)
60+
Fail >>= _ = Fail
61+
(Result x p) >>= k = k x <|> (p >>= k)
62+
(Final (r:|rs)) >>= k = final [ys' | (x,s) <- (r:rs), ys' <- run (k x) s]
63+
64+
instance Alternative P where
65+
empty = Fail
66+
67+
-- most common case: two gets are combined
68+
Get f1 <|> Get f2 = Get (\c -> f1 c <|> f2 c)
69+
70+
-- results are delivered as soon as possible
71+
Result x p <|> q = Result x (p <|> q)
72+
p <|> Result x q = Result x (p <|> q)
73+
74+
-- fail disappears
75+
Fail <|> p = p
76+
p <|> Fail = p
77+
78+
-- two finals are combined
79+
-- final + look becomes one look and one final (=optimization)
80+
-- final + sthg else becomes one look and one final
81+
Final r <|> Final t = Final (r <> t)
82+
Final (r:|rs) <|> Look f = Look (\s -> Final (r:|(rs ++ run (f s) s)))
83+
Final (r:|rs) <|> p = Look (\s -> Final (r:|(rs ++ run p s)))
84+
Look f <|> Final r = Look (\s -> Final (case run (f s) s of
85+
[] -> r
86+
(x:xs) -> (x:|xs) <> r))
87+
p <|> Final r = Look (\s -> Final (case run p s of
88+
[] -> r
89+
(x:xs) -> (x:|xs) <> r))
90+
91+
-- two looks are combined (=optimization)
92+
-- look + sthg else floats upwards
93+
Look f <|> Look g = Look (\s -> f s <|> g s)
94+
Look f <|> p = Look (\s -> f s <|> p)
95+
p <|> Look f = Look (\s -> p <|> f s)
96+
```
97+
98+
All the logic is here, it makes more sense.
99+
100+
# Running the parser
101+
102+
There's no direct to get a result from a `ReadP`, instead we have:
103+
104+
```haskell
105+
type ReadS a = String -> [(a, String)]
106+
107+
readP_to_S :: ReadP a -> ReadS a
108+
readP_to_S (R f) = run (f return)
109+
110+
run :: P a -> ReadS a
111+
run (Get f) (c:s) = run (f c) s
112+
run (Look f) s = run (f s) s
113+
run (Result x p) s = (x,s) : run p s
114+
run (Final (r:|rs)) _ = (r:rs)
115+
run _ _ = []
116+
```
117+
118+
Simple, looks like a basic expression interpreter, and it explains why we
119+
might get multiple results.
120+
121+
We also see that error handling is implicit, you only know that parsing failed,
122+
because there's still characters to consume.
123+
124+
# Conclusion
125+
126+
`ReadP` is a really simple parser combinators library, it's a trade off which
127+
costs error handling and performance.

0 commit comments

Comments
 (0)