]> Git — Sourcephile - haskell/symantic-parser.git/blob - src/Symantic/Parser/Grammar/Combinators.hs
grammar: sort symantics by name
[haskell/symantic-parser.git] / src / Symantic / Parser / Grammar / Combinators.hs
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 DeriveLift #-} -- For TH.Lift (ErrorItem tok)
9 {-# LANGUAGE StandaloneDeriving #-} -- For Show (ErrorItem (InputToken inp))
10 {-# LANGUAGE TemplateHaskell #-}
11 -- | Semantic of the grammar combinators used to express parsers,
12 -- in the convenient tagless-final encoding.
13 module Symantic.Parser.Grammar.Combinators where
14
15 import Data.Bool (Bool(..), not, (||))
16 import Data.Char (Char)
17 import Data.Either (Either(..))
18 import Data.Eq (Eq(..))
19 import Data.Function ((.), flip, const)
20 import Data.Int (Int)
21 import Data.Maybe (Maybe(..))
22 import Data.Ord (Ord)
23 import Data.Proxy (Proxy(..))
24 import Data.String (String)
25 import GHC.TypeLits (KnownSymbol)
26 import Text.Show (Show(..))
27 import qualified Data.Functor as Functor
28 import qualified Data.List as List
29 import qualified Language.Haskell.TH as TH
30 import qualified Language.Haskell.TH.Syntax as TH
31
32 import qualified Symantic.Univariant.Trans as Sym
33 import qualified Symantic.Parser.Haskell as H
34
35 -- * Type 'TermGrammar'
36 type TermGrammar = H.Term H.ValueCode
37
38 -- * Class 'CombAlternable'
39 class CombAlternable repr where
40 -- | @(rl '<|>' rr)@ parses @(rl)@ and return its return value or,
41 -- if it fails, parses @(rr)@ from where @(rl)@ has left the input stream,
42 -- and returns its return value.
43 (<|>) :: repr a -> repr a -> repr a
44 -- | @(empty)@ parses nothing, always failing to return a value.
45 empty :: repr a
46 -- | @('try' ra)@ records the input stream position,
47 -- then parses like @(ra)@ and either returns its value it it succeeds or fails
48 -- if it fails but with a reset of the input stream to the recorded position.
49 -- Generally used on the first alternative: @('try' rl '<|>' rr)@.
50 try :: repr a -> repr a
51 default (<|>) ::
52 Sym.Liftable2 repr => CombAlternable (Sym.Output repr) =>
53 repr a -> repr a -> repr a
54 default empty ::
55 Sym.Liftable repr => CombAlternable (Sym.Output repr) =>
56 repr a
57 default try ::
58 Sym.Liftable1 repr => CombAlternable (Sym.Output repr) =>
59 repr a -> repr a
60 (<|>) = Sym.lift2 (<|>)
61 empty = Sym.lift empty
62 try = Sym.lift1 try
63 -- | Like @('<|>')@ but with different returning types for the alternatives,
64 -- and a return value wrapped in an 'Either' accordingly.
65 (<+>) :: CombApplicable repr => CombAlternable repr => repr a -> repr b -> repr (Either a b)
66 p <+> q = H.left <$> p <|> H.right <$> q
67 infixl 3 <|>, <+>
68
69 optionally :: CombApplicable repr => CombAlternable repr => repr a -> TermGrammar b -> repr b
70 optionally p x = p $> x <|> pure x
71
72 optional :: CombApplicable repr => CombAlternable repr => repr a -> repr ()
73 optional = flip optionally H.unit
74
75 option :: CombApplicable repr => CombAlternable repr => TermGrammar a -> repr a -> repr a
76 option x p = p <|> pure x
77
78 choice :: CombAlternable repr => [repr a] -> repr a
79 choice = List.foldr (<|>) empty
80 -- FIXME: Here hlint suggests to use Data.Foldable.asum,
81 -- but at this point there is no asum for our own (<|>)
82
83 maybeP :: CombApplicable repr => CombAlternable repr => repr a -> repr (Maybe a)
84 maybeP p = option H.nothing (H.just <$> p)
85
86 manyTill :: CombApplicable repr => CombAlternable repr => repr a -> repr b -> repr [a]
87 manyTill p end = let go = end $> H.nil <|> p <:> go in go
88
89
90 -- * Class 'CombApplicable'
91 -- | This is like the usual 'Functor' and 'Applicative' type classes
92 -- from the @base@ package, but using @('TermGrammar' a)@ instead of just @(a)@
93 -- to be able to use and pattern match on some usual terms of type @(a)@ (like 'H.id')
94 -- and thus apply some optimizations.
95 -- @(repr)@, for "representation", is the usual tagless-final abstraction
96 -- over the many semantics that this syntax (formed by the methods
97 -- of type class like this one) will be interpreted.
98 class CombApplicable repr where
99 -- | @(a2b '<$>' ra)@ parses like @(ra)@ but maps its returned value with @(a2b)@.
100 (<$>) :: TermGrammar (a -> b) -> repr a -> repr b
101 (<$>) f = (pure f <*>)
102
103 -- | Like '<$>' but with its arguments 'flip'-ped.
104 (<&>) :: repr a -> TermGrammar (a -> b) -> repr b
105 (<&>) = flip (<$>)
106
107 -- | @(a '<$' rb)@ parses like @(rb)@ but discards its returned value by replacing it with @(a)@.
108 (<$) :: TermGrammar a -> repr b -> repr a
109 (<$) x = (pure x <*)
110
111 -- | @(ra '$>' b)@ parses like @(ra)@ but discards its returned value by replacing it with @(b)@.
112 ($>) :: repr a -> TermGrammar b -> repr b
113 ($>) = flip (<$)
114
115 -- | @('pure' a)@ parses the empty string, always succeeding in returning @(a)@.
116 pure :: TermGrammar a -> repr a
117 default pure ::
118 Sym.Liftable repr => CombApplicable (Sym.Output repr) =>
119 TermGrammar a -> repr a
120 pure = Sym.lift . pure
121
122 -- | @(ra2b '<*>' ra)@ parses sequentially @(ra2b)@ and then @(ra)@,
123 -- and returns the application of the function returned by @(ra2b)@
124 -- to the value returned by @(ra)@.
125 (<*>) :: repr (a -> b) -> repr a -> repr b
126 default (<*>) ::
127 Sym.Liftable2 repr => CombApplicable (Sym.Output repr) =>
128 repr (a -> b) -> repr a -> repr b
129 (<*>) = Sym.lift2 (<*>)
130
131 -- | @('liftA2' a2b2c ra rb)@ parses sequentially @(ra)@ and then @(rb)@,
132 -- and returns the application of @(a2b2c)@ to the values returned by those parsers.
133 liftA2 :: TermGrammar (a -> b -> c) -> repr a -> repr b -> repr c
134 liftA2 f x = (<*>) (f <$> x)
135
136 -- | @(ra '<*' rb)@ parses sequentially @(ra)@ and then @(rb)@,
137 -- and returns like @(ra)@, discarding the return value of @(rb)@.
138 (<*) :: repr a -> repr b -> repr a
139 (<*) = liftA2 H.const
140
141 -- | @(ra '*>' rb)@ parses sequentially @(ra)@ and then @(rb)@,
142 -- and returns like @(rb)@, discarding the return value of @(ra)@.
143 (*>) :: repr a -> repr b -> repr b
144 x *> y = (H.id <$ x) <*> y
145
146 -- | Like '<*>' but with its arguments 'flip'-ped.
147 (<**>) :: repr a -> repr (a -> b) -> repr b
148 (<**>) = liftA2 (H.flip H..@ (H.$))
149 {-
150 (<**>) :: repr a -> repr (a -> b) -> repr b
151 (<**>) = liftA2 (\a f -> f a)
152 -}
153 infixl 4 <$>, <&>, <$, $>, <*>, <*, *>, <**>
154
155 {-# INLINE (<:>) #-}
156 infixl 4 <:>
157 (<:>) :: CombApplicable repr => repr a -> repr [a] -> repr [a]
158 (<:>) = liftA2 H.cons
159
160 sequence :: CombApplicable repr => [repr a] -> repr [a]
161 sequence = List.foldr (<:>) (pure H.nil)
162
163 traverse :: CombApplicable repr => (a -> repr b) -> [a] -> repr [b]
164 traverse f = sequence . List.map f
165 -- FIXME: Here hlint suggests to use Control.Monad.mapM,
166 -- but at this point there is no mapM for our own sequence
167
168 repeat :: CombApplicable repr => Int -> repr a -> repr [a]
169 repeat n p = traverse (const p) [1..n]
170
171 between :: CombApplicable repr => repr o -> repr c -> repr a -> repr a
172 between open close p = open *> p <* close
173
174 void :: CombApplicable repr => repr a -> repr ()
175 void p = p *> unit
176
177 unit :: CombApplicable repr => repr ()
178 unit = pure H.unit
179
180 -- * Class 'CombFoldable'
181 class CombFoldable repr where
182 chainPre :: repr (a -> a) -> repr a -> repr a
183 chainPost :: repr a -> repr (a -> a) -> repr a
184 {-
185 default chainPre ::
186 Sym.Liftable2 repr => CombFoldable (Sym.Output repr) =>
187 repr (a -> a) -> repr a -> repr a
188 default chainPost ::
189 Sym.Liftable2 repr => CombFoldable (Sym.Output repr) =>
190 repr a -> repr (a -> a) -> repr a
191 chainPre = Sym.lift2 chainPre
192 chainPost = Sym.lift2 chainPost
193 -}
194 default chainPre ::
195 CombApplicable repr =>
196 CombAlternable repr =>
197 repr (a -> a) -> repr a -> repr a
198 default chainPost ::
199 CombApplicable repr =>
200 CombAlternable repr =>
201 repr a -> repr (a -> a) -> repr a
202 chainPre op p = go <*> p where go = (H..) <$> op <*> go <|> pure H.id
203 chainPost p op = p <**> go where go = (H..) <$> op <*> go <|> pure H.id
204 {-
205 chainPre op p = flip (foldr ($)) <$> many op <*> p
206 chainPost p op = foldl' (flip ($)) <$> p <*> many op
207 -}
208
209 {-
210 conditional :: CombSelectable repr => [(TermGrammar (a -> Bool), repr b)] -> repr a -> repr b -> repr b
211 conditional cs p def = match p fs qs def
212 where (fs, qs) = List.unzip cs
213 -}
214
215 -- Parser Folds
216 pfoldr ::
217 CombApplicable repr => CombFoldable repr =>
218 TermGrammar (a -> b -> b) -> TermGrammar b -> repr a -> repr b
219 pfoldr f k p = chainPre (f <$> p) (pure k)
220
221 pfoldr1 ::
222 CombApplicable repr => CombFoldable repr =>
223 TermGrammar (a -> b -> b) -> TermGrammar b -> repr a -> repr b
224 pfoldr1 f k p = f <$> p <*> pfoldr f k p
225
226 pfoldl ::
227 CombApplicable repr => CombFoldable repr =>
228 TermGrammar (b -> a -> b) -> TermGrammar b -> repr a -> repr b
229 pfoldl f k p = chainPost (pure k) ((H.flip <$> pure f) <*> p)
230
231 pfoldl1 ::
232 CombApplicable repr => CombFoldable repr =>
233 TermGrammar (b -> a -> b) -> TermGrammar b -> repr a -> repr b
234 pfoldl1 f k p = chainPost (f <$> pure k <*> p) ((H.flip <$> pure f) <*> p)
235
236 -- Chain Combinators
237 chainl1' ::
238 CombApplicable repr => CombFoldable repr =>
239 TermGrammar (a -> b) -> repr a -> repr (b -> a -> b) -> repr b
240 chainl1' f p op = chainPost (f <$> p) (H.flip <$> op <*> p)
241
242 chainl1 ::
243 CombApplicable repr => CombFoldable repr =>
244 repr a -> repr (a -> a -> a) -> repr a
245 chainl1 = chainl1' H.id
246
247 {-
248 chainr1' :: ParserOps rep => rep (a -> b) -> repr a -> repr (a -> b -> b) -> repr b
249 chainr1' f p op = newRegister_ H.id $ \acc ->
250 let go = bind p $ \x ->
251 modify acc (H.flip (H..@) <$> (op <*> x)) *> go
252 <|> f <$> x
253 in go <**> get acc
254
255 chainr1 :: repr a -> repr (a -> a -> a) -> repr a
256 chainr1 = chainr1' H.id
257
258 chainr :: repr a -> repr (a -> a -> a) -> TermGrammar a -> repr a
259 chainr p op x = option x (chainr1 p op)
260 -}
261
262 chainl ::
263 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
264 repr a -> repr (a -> a -> a) -> TermGrammar a -> repr a
265 chainl p op x = option x (chainl1 p op)
266
267 -- Derived Combinators
268 many ::
269 CombApplicable repr => CombFoldable repr =>
270 repr a -> repr [a]
271 many = pfoldr H.cons H.nil
272
273 manyN ::
274 CombApplicable repr => CombFoldable repr =>
275 Int -> repr a -> repr [a]
276 manyN n p = List.foldr (const (p <:>)) (many p) [1..n]
277
278 some ::
279 CombApplicable repr => CombFoldable repr =>
280 repr a -> repr [a]
281 some = manyN 1
282
283 skipMany ::
284 CombApplicable repr => CombFoldable repr =>
285 repr a -> repr ()
286 --skipMany p = let skipManyp = p *> skipManyp <|> unit in skipManyp
287 skipMany = void . pfoldl H.const H.unit -- the void here will encourage the optimiser to recognise that the register is unused
288
289 skipManyN ::
290 CombApplicable repr => CombFoldable repr =>
291 Int -> repr a -> repr ()
292 skipManyN n p = List.foldr (const (p *>)) (skipMany p) [1..n]
293
294 skipSome ::
295 CombApplicable repr => CombFoldable repr =>
296 repr a -> repr ()
297 skipSome = skipManyN 1
298
299 sepBy ::
300 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
301 repr a -> repr b -> repr [a]
302 sepBy p sep = option H.nil (sepBy1 p sep)
303
304 sepBy1 ::
305 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
306 repr a -> repr b -> repr [a]
307 sepBy1 p sep = p <:> many (sep *> p)
308
309 endBy ::
310 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
311 repr a -> repr b -> repr [a]
312 endBy p sep = many (p <* sep)
313
314 endBy1 ::
315 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
316 repr a -> repr b -> repr [a]
317 endBy1 p sep = some (p <* sep)
318
319 sepEndBy ::
320 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
321 repr a -> repr b -> repr [a]
322 sepEndBy p sep = option H.nil (sepEndBy1 p sep)
323
324 sepEndBy1 ::
325 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
326 repr a -> repr b -> repr [a]
327 sepEndBy1 p sep =
328 let seb1 = p <**> (sep *> (H.flip H..@ H.cons <$> option H.nil seb1)
329 <|> pure (H.flip H..@ H.cons H..@ H.nil))
330 in seb1
331
332 {-
333 sepEndBy1 :: repr a -> repr b -> repr [a]
334 sepEndBy1 p sep = newRegister_ H.id $ \acc ->
335 let go = modify acc ((H.flip (H..)) H..@ H.cons <$> p)
336 *> (sep *> (go <|> get acc) <|> get acc)
337 in go <*> pure H.nil
338 -}
339
340 -- * Class 'CombMatchable'
341 class CombMatchable repr where
342 conditional ::
343 Eq a => repr a -> [TermGrammar (a -> Bool)] -> [repr b] -> repr b -> repr b
344 default conditional ::
345 Sym.Unliftable repr => Sym.Liftable1 repr => CombMatchable (Sym.Output repr) =>
346 Eq a => repr a -> [TermGrammar (a -> Bool)] -> [repr b] -> repr b -> repr b
347 conditional a ps bs = Sym.lift1 (conditional (Sym.trans a) ps (Sym.trans Functor.<$> bs))
348
349 match :: Eq a => repr a -> [TermGrammar a] -> (TermGrammar a -> repr b) -> repr b -> repr b
350 match a as a2b = conditional a ((H.eq H..@) Functor.<$> as) (a2b Functor.<$> as)
351 -- match a as a2b = conditional a (((H.eq H..@ H.qual) H..@) Functor.<$> as) (a2b Functor.<$> as)
352
353 -- * Class 'CombSatisfiable'
354 class CombSatisfiable tok repr where
355 satisfy :: [ErrorItem tok] -> TermGrammar (tok -> Bool) -> repr tok
356 default satisfy ::
357 Sym.Liftable repr => CombSatisfiable tok (Sym.Output repr) =>
358 [ErrorItem tok] ->
359 TermGrammar (tok -> Bool) -> repr tok
360 satisfy es = Sym.lift . satisfy es
361
362 item :: repr tok
363 item = satisfy [] (H.const H..@ H.bool True)
364
365 string ::
366 CombApplicable repr => CombAlternable repr =>
367 CombSatisfiable Char repr =>
368 [Char] -> repr [Char]
369 string = try . traverse char
370
371 oneOf ::
372 TH.Lift tok => Eq tok =>
373 CombSatisfiable tok repr =>
374 [tok] -> repr tok
375 oneOf ts = satisfy [ErrorItemLabel "oneOf"]
376 (Sym.trans H.ValueCode
377 { value = (`List.elem` ts)
378 , code = [||\t -> $$(ofChars ts [||t||])||] })
379
380 noneOf ::
381 TH.Lift tok => Eq tok =>
382 CombSatisfiable tok repr =>
383 [tok] -> repr tok
384 noneOf cs = satisfy (ErrorItemToken Functor.<$> cs) (Sym.trans H.ValueCode
385 { value = not . (`List.elem` cs)
386 , code = [||\c -> not $$(ofChars cs [||c||])||]
387 })
388
389 ofChars ::
390 TH.Lift tok => Eq tok =>
391 {-alternatives-}[tok] ->
392 {-input-}TH.CodeQ tok ->
393 TH.CodeQ Bool
394 ofChars = List.foldr (\alt acc ->
395 \inp -> [|| alt == $$inp || $$(acc inp) ||])
396 (const [||False||])
397
398 more :: CombApplicable repr => CombSatisfiable Char repr => CombLookable repr => repr ()
399 more = look (void (item @Char))
400
401 char ::
402 CombApplicable repr => CombSatisfiable Char repr =>
403 Char -> repr Char
404 char c = satisfy [ErrorItemToken c] (H.eq H..@ H.char c) $> H.char c
405 -- char c = satisfy [ErrorItemToken c] (H.eq H..@ H.qual H..@ H.char c) $> H.char c
406
407 anyChar :: CombSatisfiable Char repr => repr Char
408 anyChar = satisfy [] (H.const H..@ H.bool True)
409
410 token ::
411 TH.Lift tok => Show tok => Eq tok =>
412 CombApplicable repr => CombSatisfiable tok repr =>
413 tok -> repr tok
414 token tok = satisfy [ErrorItemToken tok] (H.eq H..@ H.char tok) $> H.char tok
415 -- token tok = satisfy [ErrorItemToken tok] (H.eq H..@ H.qual H..@ H.char tok) $> H.char tok
416
417 tokens ::
418 TH.Lift tok => Eq tok => Show tok =>
419 CombApplicable repr => CombAlternable repr =>
420 CombSatisfiable tok repr => [tok] -> repr [tok]
421 tokens = try . traverse token
422
423 -- * Class 'CombSelectable'
424 class CombSelectable repr where
425 branch :: repr (Either a b) -> repr (a -> c) -> repr (b -> c) -> repr c
426 default branch ::
427 Sym.Liftable3 repr => CombSelectable (Sym.Output repr) =>
428 repr (Either a b) -> repr (a -> c) -> repr (b -> c) -> repr c
429 branch = Sym.lift3 branch
430
431 -- * Class 'CombThrowable'
432 class CombThrowable repr where
433 throw :: KnownSymbol lbl => Proxy lbl -> repr a
434 default throw ::
435 forall lbl a.
436 Sym.Liftable repr => CombThrowable (Sym.Output repr) =>
437 KnownSymbol lbl => Proxy lbl -> repr a
438 throw lbl = Sym.lift (throw lbl)
439
440 -- ** Type 'ErrorItem'
441 data ErrorItem tok
442 = ErrorItemToken tok
443 | ErrorItemLabel String
444 | ErrorItemHorizon Int
445 | ErrorItemEnd
446 deriving instance Eq tok => Eq (ErrorItem tok)
447 deriving instance Ord tok => Ord (ErrorItem tok)
448 deriving instance Show tok => Show (ErrorItem tok)
449 deriving instance TH.Lift tok => TH.Lift (ErrorItem tok)
450
451 -- * Class 'CombLookable'
452 class CombLookable repr where
453 look :: repr a -> repr a
454 negLook :: repr a -> repr ()
455 default look :: Sym.Liftable1 repr => CombLookable (Sym.Output repr) => repr a -> repr a
456 default negLook :: Sym.Liftable1 repr => CombLookable (Sym.Output repr) => repr a -> repr ()
457 look = Sym.lift1 look
458 negLook = Sym.lift1 negLook
459
460 eof :: repr ()
461 eof = Sym.lift eof
462 default eof :: Sym.Liftable repr => CombLookable (Sym.Output repr) => repr ()
463 -- eof = negLook (satisfy @Char [ErrorItemAny] (H.const H..@ H.bool True))
464 -- (item @Char)
465
466 -- Composite Combinators
467 -- someTill :: repr a -> repr b -> repr [a]
468 -- someTill p end = negLook end *> (p <:> manyTill p end)
469
470 {-
471 constp :: CombApplicable repr => repr a -> repr (b -> a)
472 constp = (H.const <$>)
473
474
475 -- Alias Operations
476 infixl 1 >>
477 (>>) :: CombApplicable repr => repr a -> repr b -> repr b
478 (>>) = (*>)
479
480 -- Monoidal Operations
481
482 infixl 4 <~>
483 (<~>) :: CombApplicable repr => repr a -> repr b -> repr (a, b)
484 (<~>) = liftA2 (H.runtime (,))
485
486 infixl 4 <~
487 (<~) :: CombApplicable repr => repr a -> repr b -> repr a
488 (<~) = (<*)
489
490 infixl 4 ~>
491 (~>) :: CombApplicable repr => repr a -> repr b -> repr b
492 (~>) = (*>)
493
494 -- Lift Operations
495 liftA2 ::
496 CombApplicable repr =>
497 TermGrammar (a -> b -> c) -> repr a -> repr b -> repr c
498 liftA2 f x = (<*>) (fmap f x)
499
500 liftA3 ::
501 CombApplicable repr =>
502 TermGrammar (a -> b -> c -> d) -> repr a -> repr b -> repr c -> repr d
503 liftA3 f a b c = liftA2 f a b <*> c
504
505 -}
506
507 {-
508 -- Combinators interpreters for 'Sym.Any'.
509 instance CombApplicable repr => CombApplicable (Sym.Any repr)
510 instance CombSatisfiable repr => CombSatisfiable (Sym.Any repr)
511 instance CombAlternable repr => CombAlternable (Sym.Any repr)
512 instance CombSelectable repr => CombSelectable (Sym.Any repr)
513 instance CombMatchable repr => CombMatchable (Sym.Any repr)
514 instance CombLookable repr => CombLookable (Sym.Any repr)
515 instance CombFoldable repr => CombFoldable (Sym.Any repr)
516 -}