You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`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
+
newtypeReadPa=R (forallb. (a->Pb) ->Pb)
21
+
22
+
instanceFunctorReadPwhere
23
+
fmap h (R f) =R (\k -> f (k . h))
24
+
25
+
instanceApplicativeReadPwhere
26
+
pure x =R (\k -> k x)
27
+
(<*>)= ap
28
+
29
+
instanceMonadReadPwhere
30
+
R m >>= f =R (\k -> m (\a ->letR m' = f a in m' k))
31
+
32
+
instanceAlternativeReadPwhere
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
+
dataPa
41
+
=Get (Char->Pa)
42
+
| Look (String->Pa)
43
+
| Fail
44
+
| Resulta (Pa)
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
+
instanceApplicativePwhere
54
+
pure x =Result x Fail
55
+
(<*>)= ap
56
+
57
+
instanceMonadPwhere
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
+
instanceAlternativePwhere
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
+
typeReadSa=String-> [(a, String)]
106
+
107
+
readP_to_S::ReadPa->ReadSa
108
+
readP_to_S (R f) = run (f return)
109
+
110
+
run::Pa->ReadSa
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
0 commit comments