1 -- The default type signature of type class methods are changed
2 -- to introduce a Liftable constraint and the same type class but on the 'Output' repr,
3 -- this setup avoids to define the method with boilerplate code when its default
4 -- definition with lift* and 'trans' does what is expected by an instance
5 -- of the type class. This is almost as explained in:
6 -- https://ro-che.info/articles/2016-02-03-finally-tagless-boilerplate
7 {-# LANGUAGE DefaultSignatures #-}
8 {-# LANGUAGE TemplateHaskell #-}
9 module Symantic.Parser.Grammar.Combinators where
11 import Data.Bool (Bool(..), not, (||))
12 import Data.Char (Char)
13 import Data.Either (Either(..))
14 import Data.Eq (Eq(..))
15 import Data.Function ((.), flip, const)
17 import Data.Maybe (Maybe(..))
18 import Data.String (String)
19 import Language.Haskell.TH (TExpQ)
20 import qualified Data.Functor as Functor
21 import qualified Data.List as List
23 import qualified Symantic.Univariant.Trans as Sym
24 import qualified Symantic.Parser.Staging as Hask
26 -- * Class 'Applicable'
27 -- | This is like the usual 'Functor' and 'Applicative' type classes
28 -- from the @base@ package, but using @('Hask.Haskell' a)@ instead of just @(a)@
29 -- to be able to use and pattern match on some usual terms of type @(a)@ (like
30 -- 'Hask.id') and thus apply some optimizations.
31 -- @(repr)@ , for "representation", is the usual tagless-final abstraction
32 -- over the many semantics that this syntax (formed by the methods
33 -- of type class like this one) will be interpreted.
34 class Applicable repr where
35 -- | @(a2b '<$>' ra)@ parses like @(ra)@ but maps its returned value with @(a2b)@.
36 (<$>) :: Hask.Haskell (a -> b) -> repr a -> repr b
37 (<$>) f = (pure f <*>)
39 -- | Like '<$>' but with its arguments 'flip'-ped.
40 (<&>) :: repr a -> Hask.Haskell (a -> b) -> repr b
43 -- | @(a '<$' rb)@ parses like @(rb)@ but discards its returned value by replacing it with @(a)@.
44 (<$) :: Hask.Haskell a -> repr b -> repr a
47 -- | @(ra '$>' b)@ parses like @(ra)@ but discards its returned value by replacing it with @(b)@.
48 ($>) :: repr a -> Hask.Haskell b -> repr b
51 -- | @('pure' a)@ parses the empty string, always succeeding in returning @(a)@.
52 pure :: Hask.Haskell a -> repr a
54 Sym.Liftable repr => Applicable (Sym.Output repr) =>
55 Hask.Haskell a -> repr a
56 pure = Sym.lift . pure
58 -- | @(ra2b '<*>' ra)@ parses sequentially @(ra2b)@ and then @(ra)@,
59 -- and returns the application of the function returned by @(ra2b)@
60 -- to the value returned by @(ra)@.
61 (<*>) :: repr (a -> b) -> repr a -> repr b
63 Sym.Liftable2 repr => Applicable (Sym.Output repr) =>
64 repr (a -> b) -> repr a -> repr b
65 (<*>) = Sym.lift2 (<*>)
67 -- | @('liftA2' a2b2c ra rb)@ parses sequentially @(ra)@ and then @(rb)@,
68 -- and returns the application of @(a2b2c)@ to the values returned by those parsers.
69 liftA2 :: Hask.Haskell (a -> b -> c) -> repr a -> repr b -> repr c
70 liftA2 f x = (<*>) (f <$> x)
72 -- | @(ra '<*' rb)@ parses sequentially @(ra)@ and then @(rb)@,
73 -- and returns like @(ra)@, discarding the return value of @(rb)@.
74 (<*) :: repr a -> repr b -> repr a
75 (<*) = liftA2 Hask.const
77 -- | @(ra '*>' rb)@ parses sequentially @(ra)@ and then @(rb)@,
78 -- and returns like @(rb)@, discarding the return value of @(ra)@.
79 (*>) :: repr a -> repr b -> repr b
80 x *> y = (Hask.id <$ x) <*> y
82 -- | Like '<*>' but with its arguments 'flip'-ped.
83 (<**>) :: repr a -> repr (a -> b) -> repr b
84 (<**>) = liftA2 (Hask.flip Hask..@ (Hask.$))
86 (<**>) :: repr a -> repr (a -> b) -> repr b
87 (<**>) = liftA2 (\a f -> f a)
89 infixl 4 <$>, <&>, <$, $>, <*>, <*, *>, <**>
91 -- * Class 'Alternable'
92 class Alternable repr where
93 -- | @(rl '<|>' rr)@ parses @(rl)@ and return its return value or,
94 -- if it fails, parses @(rr)@ from where @(rl)@ has left the input stream,
95 -- and returns its return value.
96 (<|>) :: repr a -> repr a -> repr a
97 -- | @(empty)@ parses nothing, always failing to return a value.
99 -- | @('try' ra)@ records the input stream position,
100 -- then parses like @(ra)@ and either returns its value it it succeeds or fails
101 -- if it fails but with a reset of the input stream to the recorded position.
102 -- Generally used on the first alternative: @('try' rl '<|>' rr)@.
103 try :: repr a -> repr a
105 Sym.Liftable2 repr => Alternable (Sym.Output repr) =>
106 repr a -> repr a -> repr a
108 Sym.Liftable repr => Alternable (Sym.Output repr) =>
111 Sym.Liftable1 repr => Alternable (Sym.Output repr) =>
113 (<|>) = Sym.lift2 (<|>)
114 empty = Sym.lift empty
116 -- | Like @('<|>')@ but with different returning types for the alternatives,
117 -- and a return value wrapped in an 'Either' accordingly.
118 (<+>) :: Applicable repr => Alternable repr => repr a -> repr b -> repr (Either a b)
119 p <+> q = Hask.left <$> p <|> Hask.right <$> q
122 optionally :: Applicable repr => Alternable repr => repr a -> Hask.Haskell b -> repr b
123 optionally p x = p $> x <|> pure x
125 optional :: Applicable repr => Alternable repr => repr a -> repr ()
126 optional = flip optionally Hask.unit
128 option :: Applicable repr => Alternable repr => Hask.Haskell a -> repr a -> repr a
129 option x p = p <|> pure x
131 choice :: Alternable repr => [repr a] -> repr a
132 choice = List.foldr (<|>) empty
133 -- FIXME: Here hlint suggests to use Data.Foldable.asum,
134 -- but at this point there is no asum for our own (<|>)
136 maybeP :: Applicable repr => Alternable repr => repr a -> repr (Maybe a)
137 maybeP p = option Hask.nothing (Hask.just <$> p)
139 manyTill :: Applicable repr => Alternable repr => repr a -> repr b -> repr [a]
140 manyTill p end = let go = end $> Hask.nil <|> p <:> go in go
142 -- * Class 'Selectable'
143 class Selectable repr where
144 branch :: repr (Either a b) -> repr (a -> c) -> repr (b -> c) -> repr c
146 Sym.Liftable3 repr => Selectable (Sym.Output repr) =>
147 repr (Either a b) -> repr (a -> c) -> repr (b -> c) -> repr c
148 branch = Sym.lift3 branch
150 -- * Class 'Matchable'
151 class Matchable repr where
153 Eq a => [Hask.Haskell (a -> Bool)] -> [repr b] -> repr a -> repr b -> repr b
154 default conditional ::
155 Sym.Unliftable repr => Sym.Liftable2 repr => Matchable (Sym.Output repr) =>
156 Eq a => [Hask.Haskell (a -> Bool)] -> [repr b] -> repr a -> repr b -> repr b
157 conditional cs bs = Sym.lift2 (conditional cs (Sym.trans Functor.<$> bs))
159 match :: Eq a => [Hask.Haskell a] -> repr a -> (Hask.Haskell a -> repr b) -> repr b -> repr b
160 match as a a2b = conditional (Hask.eq Functor.<$> as) (a2b Functor.<$> as) a
162 -- * Class 'Foldable'
163 class Foldable repr where
164 chainPre :: repr (a -> a) -> repr a -> repr a
165 chainPost :: repr a -> repr (a -> a) -> repr a
168 Sym.Liftable2 repr => Foldable (Sym.Output repr) =>
169 repr (a -> a) -> repr a -> repr a
171 Sym.Liftable2 repr => Foldable (Sym.Output repr) =>
172 repr a -> repr (a -> a) -> repr a
173 chainPre = Sym.lift2 chainPre
174 chainPost = Sym.lift2 chainPost
179 repr (a -> a) -> repr a -> repr a
183 repr a -> repr (a -> a) -> repr a
184 chainPre op p = go <*> p
185 where go = (Hask..) <$> op <*> go <|> pure Hask.id
186 chainPost p op = p <**> go
187 where go = (Hask..) <$> op <*> go <|> pure Hask.id
190 conditional :: Selectable repr => [(Hask.Haskell (a -> Bool), repr b)] -> repr a -> repr b -> repr b
191 conditional cs p def = match p fs qs def
192 where (fs, qs) = List.unzip cs
195 -- * Class 'Charable'
196 class Charable repr where
197 satisfy :: Hask.Haskell (Char -> Bool) -> repr Char
199 Sym.Liftable repr => Charable (Sym.Output repr) =>
200 Hask.Haskell (Char -> Bool) -> repr Char
201 satisfy = Sym.lift . satisfy
203 -- * Class 'Lookable'
204 class Lookable repr where
205 look :: repr a -> repr a
206 negLook :: repr a -> repr ()
207 default look :: Sym.Liftable1 repr => Lookable (Sym.Output repr) => repr a -> repr a
208 default negLook :: Sym.Liftable1 repr => Lookable (Sym.Output repr) => repr a -> repr ()
209 look = Sym.lift1 look
210 negLook = Sym.lift1 negLook
214 (<:>) :: Applicable repr => repr a -> repr [a] -> repr [a]
215 (<:>) = liftA2 Hask.cons
217 sequence :: Applicable repr => [repr a] -> repr [a]
218 sequence = List.foldr (<:>) (pure Hask.nil)
220 traverse :: Applicable repr => (a -> repr b) -> [a] -> repr [b]
221 traverse f = sequence . List.map f
222 -- FIXME: Here hlint suggests to use Control.Monad.mapM,
223 -- but at this point there is no mapM for our own sequence
225 repeat :: Applicable repr => Int -> repr a -> repr [a]
226 repeat n p = traverse (const p) [1..n]
228 between :: Applicable repr => repr o -> repr c -> repr a -> repr a
229 between open close p = open *> p <* close
231 string :: Applicable repr => Charable repr => String -> repr String
232 string = traverse char
234 -- oneOf :: [Char] -> repr Char
235 -- oneOf cs = satisfy (makeQ (flip elem cs) [||\c -> $$(ofChars cs [||c||])||])
237 noneOf :: Charable repr => String -> repr Char
238 noneOf cs = satisfy (Hask.Haskell Hask.ValueCode{..})
240 value = Hask.Value (not . flip List.elem cs)
241 code = Hask.Code [||\c -> not $$(ofChars cs [||c||])||]
243 ofChars :: String -> TExpQ Char -> TExpQ Bool
244 ofChars = List.foldr (\c rest qc -> [|| c == $$qc || $$(rest qc) ||]) (const [||False||])
246 token :: Applicable repr => Alternable repr => Charable repr => String -> repr String
249 eof :: Charable repr => Lookable repr => repr ()
252 more :: Applicable repr => Charable repr => Lookable repr => repr ()
253 more = look (void item)
255 char :: Applicable repr => Charable repr => Char -> repr Char
256 char c = satisfy (Hask.eq (Hask.char c)) $> Hask.char c
258 item :: Charable repr => repr Char
259 item = satisfy (Hask.const Hask..@ Hask.bool True)
261 -- Composite Combinators
262 -- someTill :: repr a -> repr b -> repr [a]
263 -- someTill p end = negLook end *> (p <:> manyTill p end)
265 void :: Applicable repr => repr a -> repr ()
268 unit :: Applicable repr => repr ()
269 unit = pure Hask.unit
272 constp :: Applicable repr => repr a -> repr (b -> a)
273 constp = (Hask.const <$>)
278 (>>) :: Applicable repr => repr a -> repr b -> repr b
281 -- Monoidal Operations
284 (<~>) :: Applicable repr => repr a -> repr b -> repr (a, b)
285 (<~>) = liftA2 (Hask.runtime (,))
288 (<~) :: Applicable repr => repr a -> repr b -> repr a
292 (~>) :: Applicable repr => repr a -> repr b -> repr b
298 Hask.Haskell (a -> b -> c) -> repr a -> repr b -> repr c
299 liftA2 f x = (<*>) (fmap f x)
303 Hask.Haskell (a -> b -> c -> d) -> repr a -> repr b -> repr c -> repr d
304 liftA3 f a b c = liftA2 f a b <*> c
310 Applicable repr => Foldable repr =>
311 Hask.Haskell (a -> b -> b) -> Hask.Haskell b -> repr a -> repr b
312 pfoldr f k p = chainPre (f <$> p) (pure k)
315 Applicable repr => Foldable repr =>
316 Hask.Haskell (a -> b -> b) -> Hask.Haskell b -> repr a -> repr b
317 pfoldr1 f k p = f <$> p <*> pfoldr f k p
320 Applicable repr => Foldable repr =>
321 Hask.Haskell (b -> a -> b) -> Hask.Haskell b -> repr a -> repr b
322 pfoldl f k p = chainPost (pure k) ((Hask.flip <$> pure f) <*> p)
325 Applicable repr => Foldable repr =>
326 Hask.Haskell (b -> a -> b) -> Hask.Haskell b -> repr a -> repr b
327 pfoldl1 f k p = chainPost (f <$> pure k <*> p) ((Hask.flip <$> pure f) <*> p)
331 Applicable repr => Foldable repr =>
332 Hask.Haskell (a -> b) -> repr a -> repr (b -> a -> b) -> repr b
333 chainl1' f p op = chainPost (f <$> p) (Hask.flip <$> op <*> p)
336 Applicable repr => Foldable repr =>
337 repr a -> repr (a -> a -> a) -> repr a
338 chainl1 = chainl1' Hask.id
341 chainr1' :: ParserOps rep => rep (a -> b) -> repr a -> repr (a -> b -> b) -> repr b
342 chainr1' f p op = newRegister_ Hask.id $ \acc ->
343 let go = bind p $ \x ->
344 modify acc (Hask.flip (Hask..@) <$> (op <*> x)) *> go
348 chainr1 :: repr a -> repr (a -> a -> a) -> repr a
349 chainr1 = chainr1' Hask.id
351 chainr :: repr a -> repr (a -> a -> a) -> Hask.Haskell a -> repr a
352 chainr p op x = option x (chainr1 p op)
356 Applicable repr => Alternable repr => Foldable repr =>
357 repr a -> repr (a -> a -> a) -> Hask.Haskell a -> repr a
358 chainl p op x = option x (chainl1 p op)
360 -- Derived Combinators
362 Applicable repr => Foldable repr =>
364 many = pfoldr Hask.cons Hask.nil
367 Applicable repr => Foldable repr =>
368 Int -> repr a -> repr [a]
369 manyN n p = List.foldr (const (p <:>)) (many p) [1..n]
372 Applicable repr => Foldable repr =>
377 Applicable repr => Foldable repr =>
379 --skipMany p = let skipManyp = p *> skipManyp <|> unit in skipManyp
380 skipMany = void . pfoldl Hask.const Hask.unit -- the void here will encourage the optimiser to recognise that the register is unused
383 Applicable repr => Foldable repr =>
384 Int -> repr a -> repr ()
385 skipManyN n p = List.foldr (const (p *>)) (skipMany p) [1..n]
388 Applicable repr => Foldable repr =>
390 skipSome = skipManyN 1
393 Applicable repr => Alternable repr => Foldable repr =>
394 repr a -> repr b -> repr [a]
395 sepBy p sep = option Hask.nil (sepBy1 p sep)
398 Applicable repr => Alternable repr => Foldable repr =>
399 repr a -> repr b -> repr [a]
400 sepBy1 p sep = p <:> many (sep *> p)
403 Applicable repr => Alternable repr => Foldable repr =>
404 repr a -> repr b -> repr [a]
405 endBy p sep = many (p <* sep)
408 Applicable repr => Alternable repr => Foldable repr =>
409 repr a -> repr b -> repr [a]
410 endBy1 p sep = some (p <* sep)
413 Applicable repr => Alternable repr => Foldable repr =>
414 repr a -> repr b -> repr [a]
415 sepEndBy p sep = option Hask.nil (sepEndBy1 p sep)
418 Applicable repr => Alternable repr => Foldable repr =>
419 repr a -> repr b -> repr [a]
421 let seb1 = p <**> (sep *> (Hask.flip Hask..@ Hask.cons <$> option Hask.nil seb1)
422 <|> pure (Hask.flip Hask..@ Hask.cons Hask..@ Hask.nil))
426 sepEndBy1 :: repr a -> repr b -> repr [a]
427 sepEndBy1 p sep = newRegister_ Hask.id $ \acc ->
428 let go = modify acc ((Hask.flip (Hask..)) Hask..@ Hask.cons <$> p)
429 *> (sep *> (go <|> get acc) <|> get acc)
430 in go <*> pure Hask.nil
434 -- Combinators interpreters for 'Sym.Any'.
435 instance Applicable repr => Applicable (Sym.Any repr)
436 instance Charable repr => Charable (Sym.Any repr)
437 instance Alternable repr => Alternable (Sym.Any repr)
438 instance Selectable repr => Selectable (Sym.Any repr)
439 instance Matchable repr => Matchable (Sym.Any repr)
440 instance Lookable repr => Lookable (Sym.Any repr)
441 instance Foldable repr => Foldable (Sym.Any repr)