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
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.Kind (Constraint)
22 import Data.Maybe (Maybe(..))
24 import Data.Proxy (Proxy(..))
25 import Data.String (String)
26 import GHC.TypeLits (KnownSymbol, Symbol)
27 import Text.Show (Show(..))
28 import qualified Data.Functor as Functor
29 import qualified Data.List as List
30 import qualified Language.Haskell.TH as TH
31 import qualified Language.Haskell.TH.Syntax as TH
33 import qualified Symantic.Univariant.Trans as Sym
34 import qualified Symantic.Parser.Haskell as H
36 -- * Type 'TermGrammar'
37 type TermGrammar = H.Term H.ValueCode
39 -- * Class 'Applicable'
40 -- | This is like the usual 'Functor' and 'Applicative' type classes
41 -- from the @base@ package, but using @('TermGrammar' a)@ instead of just @(a)@
42 -- to be able to use and pattern match on some usual terms of type @(a)@ (like 'H.id')
43 -- and thus apply some optimizations.
44 -- @(repr)@, for "representation", is the usual tagless-final abstraction
45 -- over the many semantics that this syntax (formed by the methods
46 -- of type class like this one) will be interpreted.
47 class Applicable repr where
48 -- | @(a2b '<$>' ra)@ parses like @(ra)@ but maps its returned value with @(a2b)@.
49 (<$>) :: TermGrammar (a -> b) -> repr a -> repr b
50 (<$>) f = (pure f <*>)
52 -- | Like '<$>' but with its arguments 'flip'-ped.
53 (<&>) :: repr a -> TermGrammar (a -> b) -> repr b
56 -- | @(a '<$' rb)@ parses like @(rb)@ but discards its returned value by replacing it with @(a)@.
57 (<$) :: TermGrammar a -> repr b -> repr a
60 -- | @(ra '$>' b)@ parses like @(ra)@ but discards its returned value by replacing it with @(b)@.
61 ($>) :: repr a -> TermGrammar b -> repr b
64 -- | @('pure' a)@ parses the empty string, always succeeding in returning @(a)@.
65 pure :: TermGrammar a -> repr a
67 Sym.Liftable repr => Applicable (Sym.Output repr) =>
68 TermGrammar a -> repr a
69 pure = Sym.lift . pure
71 -- | @(ra2b '<*>' ra)@ parses sequentially @(ra2b)@ and then @(ra)@,
72 -- and returns the application of the function returned by @(ra2b)@
73 -- to the value returned by @(ra)@.
74 (<*>) :: repr (a -> b) -> repr a -> repr b
76 Sym.Liftable2 repr => Applicable (Sym.Output repr) =>
77 repr (a -> b) -> repr a -> repr b
78 (<*>) = Sym.lift2 (<*>)
80 -- | @('liftA2' a2b2c ra rb)@ parses sequentially @(ra)@ and then @(rb)@,
81 -- and returns the application of @(a2b2c)@ to the values returned by those parsers.
82 liftA2 :: TermGrammar (a -> b -> c) -> repr a -> repr b -> repr c
83 liftA2 f x = (<*>) (f <$> x)
85 -- | @(ra '<*' rb)@ parses sequentially @(ra)@ and then @(rb)@,
86 -- and returns like @(ra)@, discarding the return value of @(rb)@.
87 (<*) :: repr a -> repr b -> repr a
90 -- | @(ra '*>' rb)@ parses sequentially @(ra)@ and then @(rb)@,
91 -- and returns like @(rb)@, discarding the return value of @(ra)@.
92 (*>) :: repr a -> repr b -> repr b
93 x *> y = (H.id <$ x) <*> y
95 -- | Like '<*>' but with its arguments 'flip'-ped.
96 (<**>) :: repr a -> repr (a -> b) -> repr b
97 (<**>) = liftA2 (H.flip H..@ (H.$))
99 (<**>) :: repr a -> repr (a -> b) -> repr b
100 (<**>) = liftA2 (\a f -> f a)
102 infixl 4 <$>, <&>, <$, $>, <*>, <*, *>, <**>
104 -- * Class 'Alternable'
105 class Alternable repr where
106 -- | @(rl '<|>' rr)@ parses @(rl)@ and return its return value or,
107 -- if it fails, parses @(rr)@ from where @(rl)@ has left the input stream,
108 -- and returns its return value.
109 (<|>) :: repr a -> repr a -> repr a
110 -- | @(empty)@ parses nothing, always failing to return a value.
112 -- | @('try' ra)@ records the input stream position,
113 -- then parses like @(ra)@ and either returns its value it it succeeds or fails
114 -- if it fails but with a reset of the input stream to the recorded position.
115 -- Generally used on the first alternative: @('try' rl '<|>' rr)@.
116 try :: repr a -> repr a
118 Sym.Liftable2 repr => Alternable (Sym.Output repr) =>
119 repr a -> repr a -> repr a
121 Sym.Liftable repr => Alternable (Sym.Output repr) =>
124 Sym.Liftable1 repr => Alternable (Sym.Output repr) =>
126 (<|>) = Sym.lift2 (<|>)
127 empty = Sym.lift empty
129 -- | Like @('<|>')@ but with different returning types for the alternatives,
130 -- and a return value wrapped in an 'Either' accordingly.
131 (<+>) :: Applicable repr => Alternable repr => repr a -> repr b -> repr (Either a b)
132 p <+> q = H.left <$> p <|> H.right <$> q
135 class Throwable repr where
136 type ThrowableLabel repr (lbl::Symbol) :: Constraint
137 --type ThrowableLabel repr lbl = ThrowableLabel (Sym.Output repr) lbl
140 ThrowableLabel repr lbl =>
144 Sym.Liftable repr => Alternable (Sym.Output repr) =>
146 Throwable (Sym.Output repr) =>
147 ThrowableLabel (Sym.Output repr) lbl =>
149 throw lbl = Sym.lift (throw lbl)
151 optionally :: Applicable repr => Alternable repr => repr a -> TermGrammar b -> repr b
152 optionally p x = p $> x <|> pure x
154 optional :: Applicable repr => Alternable repr => repr a -> repr ()
155 optional = flip optionally H.unit
157 option :: Applicable repr => Alternable repr => TermGrammar a -> repr a -> repr a
158 option x p = p <|> pure x
160 choice :: Alternable repr => [repr a] -> repr a
161 choice = List.foldr (<|>) empty
162 -- FIXME: Here hlint suggests to use Data.Foldable.asum,
163 -- but at this point there is no asum for our own (<|>)
165 maybeP :: Applicable repr => Alternable repr => repr a -> repr (Maybe a)
166 maybeP p = option H.nothing (H.just <$> p)
168 manyTill :: Applicable repr => Alternable repr => repr a -> repr b -> repr [a]
169 manyTill p end = let go = end $> H.nil <|> p <:> go in go
171 -- * Class 'Selectable'
172 class Selectable repr where
173 branch :: repr (Either a b) -> repr (a -> c) -> repr (b -> c) -> repr c
175 Sym.Liftable3 repr => Selectable (Sym.Output repr) =>
176 repr (Either a b) -> repr (a -> c) -> repr (b -> c) -> repr c
177 branch = Sym.lift3 branch
179 -- * Class 'Matchable'
180 class Matchable repr where
182 Eq a => repr a -> [TermGrammar (a -> Bool)] -> [repr b] -> repr b -> repr b
183 default conditional ::
184 Sym.Unliftable repr => Sym.Liftable1 repr => Matchable (Sym.Output repr) =>
185 Eq a => repr a -> [TermGrammar (a -> Bool)] -> [repr b] -> repr b -> repr b
186 conditional a ps bs = Sym.lift1 (conditional (Sym.trans a) ps (Sym.trans Functor.<$> bs))
188 match :: Eq a => repr a -> [TermGrammar a] -> (TermGrammar a -> repr b) -> repr b -> repr b
189 match a as a2b = conditional a ((H.eq H..@) Functor.<$> as) (a2b Functor.<$> as)
190 -- match a as a2b = conditional a (((H.eq H..@ H.qual) H..@) Functor.<$> as) (a2b Functor.<$> as)
192 -- * Class 'Foldable'
193 class Foldable repr where
194 chainPre :: repr (a -> a) -> repr a -> repr a
195 chainPost :: repr a -> repr (a -> a) -> repr a
198 Sym.Liftable2 repr => Foldable (Sym.Output repr) =>
199 repr (a -> a) -> repr a -> repr a
201 Sym.Liftable2 repr => Foldable (Sym.Output repr) =>
202 repr a -> repr (a -> a) -> repr a
203 chainPre = Sym.lift2 chainPre
204 chainPost = Sym.lift2 chainPost
209 repr (a -> a) -> repr a -> repr a
213 repr a -> repr (a -> a) -> repr a
214 chainPre op p = go <*> p where go = (H..) <$> op <*> go <|> pure H.id
215 chainPost p op = p <**> go where go = (H..) <$> op <*> go <|> pure H.id
217 chainPre op p = flip (foldr ($)) <$> many op <*> p
218 chainPost p op = foldl' (flip ($)) <$> p <*> many op
222 conditional :: Selectable repr => [(TermGrammar (a -> Bool), repr b)] -> repr a -> repr b -> repr b
223 conditional cs p def = match p fs qs def
224 where (fs, qs) = List.unzip cs
227 -- * Class 'Satisfiable'
228 class Satisfiable tok repr where
229 satisfy :: [ErrorItem tok] -> TermGrammar (tok -> Bool) -> repr tok
231 Sym.Liftable repr => Satisfiable tok (Sym.Output repr) =>
233 TermGrammar (tok -> Bool) -> repr tok
234 satisfy es = Sym.lift . satisfy es
237 item = satisfy [] (H.const H..@ H.bool True)
239 -- ** Type 'ErrorItem'
242 | ErrorItemLabel String
243 | ErrorItemHorizon Int
245 deriving instance Eq tok => Eq (ErrorItem tok)
246 deriving instance Ord tok => Ord (ErrorItem tok)
247 deriving instance Show tok => Show (ErrorItem tok)
248 deriving instance TH.Lift tok => TH.Lift (ErrorItem tok)
250 -- * Class 'Lookable'
251 class Lookable repr where
252 look :: repr a -> repr a
253 negLook :: repr a -> repr ()
254 default look :: Sym.Liftable1 repr => Lookable (Sym.Output repr) => repr a -> repr a
255 default negLook :: Sym.Liftable1 repr => Lookable (Sym.Output repr) => repr a -> repr ()
256 look = Sym.lift1 look
257 negLook = Sym.lift1 negLook
261 default eof :: Sym.Liftable repr => Lookable (Sym.Output repr) => repr ()
262 -- eof = negLook (satisfy @Char [ErrorItemAny] (H.const H..@ H.bool True))
267 (<:>) :: Applicable repr => repr a -> repr [a] -> repr [a]
268 (<:>) = liftA2 H.cons
270 sequence :: Applicable repr => [repr a] -> repr [a]
271 sequence = List.foldr (<:>) (pure H.nil)
273 traverse :: Applicable repr => (a -> repr b) -> [a] -> repr [b]
274 traverse f = sequence . List.map f
275 -- FIXME: Here hlint suggests to use Control.Monad.mapM,
276 -- but at this point there is no mapM for our own sequence
278 repeat :: Applicable repr => Int -> repr a -> repr [a]
279 repeat n p = traverse (const p) [1..n]
281 between :: Applicable repr => repr o -> repr c -> repr a -> repr a
282 between open close p = open *> p <* close
285 Applicable repr => Alternable repr =>
286 Satisfiable Char repr =>
287 [Char] -> repr [Char]
288 string = try . traverse char
291 TH.Lift tok => Eq tok =>
292 Satisfiable tok repr =>
294 oneOf ts = satisfy [ErrorItemLabel "oneOf"]
295 (Sym.trans H.ValueCode
296 { value = (`List.elem` ts)
297 , code = [||\t -> $$(ofChars ts [||t||])||] })
300 TH.Lift tok => Eq tok =>
301 Satisfiable tok repr =>
303 noneOf cs = satisfy (ErrorItemToken Functor.<$> cs) (Sym.trans H.ValueCode
304 { value = not . (`List.elem` cs)
305 , code = [||\c -> not $$(ofChars cs [||c||])||]
309 TH.Lift tok => Eq tok =>
310 {-alternatives-}[tok] ->
311 {-input-}TH.CodeQ tok ->
313 ofChars = List.foldr (\alt acc ->
314 \inp -> [|| alt == $$inp || $$(acc inp) ||])
317 more :: Applicable repr => Satisfiable Char repr => Lookable repr => repr ()
318 more = look (void (item @Char))
321 Applicable repr => Satisfiable Char repr =>
323 char c = satisfy [ErrorItemToken c] (H.eq H..@ H.char c) $> H.char c
324 -- char c = satisfy [ErrorItemToken c] (H.eq H..@ H.qual H..@ H.char c) $> H.char c
326 anyChar :: Satisfiable Char repr => repr Char
327 anyChar = satisfy [] (H.const H..@ H.bool True)
330 TH.Lift tok => Show tok => Eq tok =>
331 Applicable repr => Satisfiable tok repr =>
333 token tok = satisfy [ErrorItemToken tok] (H.eq H..@ H.char tok) $> H.char tok
334 -- token tok = satisfy [ErrorItemToken tok] (H.eq H..@ H.qual H..@ H.char tok) $> H.char tok
337 TH.Lift tok => Eq tok => Show tok =>
338 Applicable repr => Alternable repr =>
339 Satisfiable tok repr => [tok] -> repr [tok]
340 tokens = try . traverse token
342 -- Composite Combinators
343 -- someTill :: repr a -> repr b -> repr [a]
344 -- someTill p end = negLook end *> (p <:> manyTill p end)
346 void :: Applicable repr => repr a -> repr ()
349 unit :: Applicable repr => repr ()
353 constp :: Applicable repr => repr a -> repr (b -> a)
354 constp = (H.const <$>)
359 (>>) :: Applicable repr => repr a -> repr b -> repr b
362 -- Monoidal Operations
365 (<~>) :: Applicable repr => repr a -> repr b -> repr (a, b)
366 (<~>) = liftA2 (H.runtime (,))
369 (<~) :: Applicable repr => repr a -> repr b -> repr a
373 (~>) :: Applicable repr => repr a -> repr b -> repr b
379 TermGrammar (a -> b -> c) -> repr a -> repr b -> repr c
380 liftA2 f x = (<*>) (fmap f x)
384 TermGrammar (a -> b -> c -> d) -> repr a -> repr b -> repr c -> repr d
385 liftA3 f a b c = liftA2 f a b <*> c
391 Applicable repr => Foldable repr =>
392 TermGrammar (a -> b -> b) -> TermGrammar b -> repr a -> repr b
393 pfoldr f k p = chainPre (f <$> p) (pure k)
396 Applicable repr => Foldable repr =>
397 TermGrammar (a -> b -> b) -> TermGrammar b -> repr a -> repr b
398 pfoldr1 f k p = f <$> p <*> pfoldr f k p
401 Applicable repr => Foldable repr =>
402 TermGrammar (b -> a -> b) -> TermGrammar b -> repr a -> repr b
403 pfoldl f k p = chainPost (pure k) ((H.flip <$> pure f) <*> p)
406 Applicable repr => Foldable repr =>
407 TermGrammar (b -> a -> b) -> TermGrammar b -> repr a -> repr b
408 pfoldl1 f k p = chainPost (f <$> pure k <*> p) ((H.flip <$> pure f) <*> p)
412 Applicable repr => Foldable repr =>
413 TermGrammar (a -> b) -> repr a -> repr (b -> a -> b) -> repr b
414 chainl1' f p op = chainPost (f <$> p) (H.flip <$> op <*> p)
417 Applicable repr => Foldable repr =>
418 repr a -> repr (a -> a -> a) -> repr a
419 chainl1 = chainl1' H.id
422 chainr1' :: ParserOps rep => rep (a -> b) -> repr a -> repr (a -> b -> b) -> repr b
423 chainr1' f p op = newRegister_ H.id $ \acc ->
424 let go = bind p $ \x ->
425 modify acc (H.flip (H..@) <$> (op <*> x)) *> go
429 chainr1 :: repr a -> repr (a -> a -> a) -> repr a
430 chainr1 = chainr1' H.id
432 chainr :: repr a -> repr (a -> a -> a) -> TermGrammar a -> repr a
433 chainr p op x = option x (chainr1 p op)
437 Applicable repr => Alternable repr => Foldable repr =>
438 repr a -> repr (a -> a -> a) -> TermGrammar a -> repr a
439 chainl p op x = option x (chainl1 p op)
441 -- Derived Combinators
443 Applicable repr => Foldable repr =>
445 many = pfoldr H.cons H.nil
448 Applicable repr => Foldable repr =>
449 Int -> repr a -> repr [a]
450 manyN n p = List.foldr (const (p <:>)) (many p) [1..n]
453 Applicable repr => Foldable repr =>
458 Applicable repr => Foldable repr =>
460 --skipMany p = let skipManyp = p *> skipManyp <|> unit in skipManyp
461 skipMany = void . pfoldl H.const H.unit -- the void here will encourage the optimiser to recognise that the register is unused
464 Applicable repr => Foldable repr =>
465 Int -> repr a -> repr ()
466 skipManyN n p = List.foldr (const (p *>)) (skipMany p) [1..n]
469 Applicable repr => Foldable repr =>
471 skipSome = skipManyN 1
474 Applicable repr => Alternable repr => Foldable repr =>
475 repr a -> repr b -> repr [a]
476 sepBy p sep = option H.nil (sepBy1 p sep)
479 Applicable repr => Alternable repr => Foldable repr =>
480 repr a -> repr b -> repr [a]
481 sepBy1 p sep = p <:> many (sep *> p)
484 Applicable repr => Alternable repr => Foldable repr =>
485 repr a -> repr b -> repr [a]
486 endBy p sep = many (p <* sep)
489 Applicable repr => Alternable repr => Foldable repr =>
490 repr a -> repr b -> repr [a]
491 endBy1 p sep = some (p <* sep)
494 Applicable repr => Alternable repr => Foldable repr =>
495 repr a -> repr b -> repr [a]
496 sepEndBy p sep = option H.nil (sepEndBy1 p sep)
499 Applicable repr => Alternable repr => Foldable repr =>
500 repr a -> repr b -> repr [a]
502 let seb1 = p <**> (sep *> (H.flip H..@ H.cons <$> option H.nil seb1)
503 <|> pure (H.flip H..@ H.cons H..@ H.nil))
507 sepEndBy1 :: repr a -> repr b -> repr [a]
508 sepEndBy1 p sep = newRegister_ H.id $ \acc ->
509 let go = modify acc ((H.flip (H..)) H..@ H.cons <$> p)
510 *> (sep *> (go <|> get acc) <|> get acc)
515 -- Combinators interpreters for 'Sym.Any'.
516 instance Applicable repr => Applicable (Sym.Any repr)
517 instance Satisfiable repr => Satisfiable (Sym.Any repr)
518 instance Alternable repr => Alternable (Sym.Any repr)
519 instance Selectable repr => Selectable (Sym.Any repr)
520 instance Matchable repr => Matchable (Sym.Any repr)
521 instance Lookable repr => Lookable (Sym.Any repr)
522 instance Foldable repr => Foldable (Sym.Any repr)