1 -- The default type signature of type class methods are changed
2 -- to introduce a 'LiftDerived'* constraint and the same type class but on the 'Derived' repr,
3 -- this setup avoids to define the method with boilerplate code when its default
4 -- definition with 'liftDerived'* and 'derive' 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 DeriveGeneric #-} -- For NFData instances
9 {-# LANGUAGE DeriveAnyClass #-} -- For NFData instances
10 {-# LANGUAGE DeriveLift #-} -- For TH.Lift (Exception tok)
11 {-# LANGUAGE DerivingStrategies #-} -- For UnscopedRegister
12 {-# LANGUAGE PatternSynonyms #-} -- For Failure
13 {-# LANGUAGE StandaloneDeriving #-} -- For Show (Exception (InputToken inp))
14 {-# LANGUAGE InstanceSigs #-}
15 {-# LANGUAGE TemplateHaskell #-}
16 {-# LANGUAGE ViewPatterns #-} -- For unSomeFailure
17 -- | Semantic of the grammar combinators used to express parsers,
18 -- in the convenient tagless-final encoding.
19 module Symantic.Parser.Grammar.Combinators where
21 import Data.Proxy (Proxy(..))
22 import Control.Monad (Monad(..))
23 import Control.DeepSeq (NFData(..))
24 import GHC.Generics (Generic)
25 -- import Data.Set (Set)
26 -- import GHC.TypeLits (KnownSymbol)
27 import Data.Bifunctor (second)
28 import Data.Bool (Bool(..), not, (||))
29 import Data.Char (Char)
30 import Data.Either (Either(..))
31 import Data.Eq (Eq(..))
32 import Data.Function ((.), flip, const, fix)
33 import Data.Ord (Ord(..), Ordering(..))
35 import Data.Kind (Type, Constraint)
36 import Data.Maybe (Maybe(..))
38 import Data.String (String)
39 import Text.Show (Show(..))
40 import Type.Reflection (Typeable, typeRep, eqTypeRep, (:~~:)(..), SomeTypeRep(..))
41 import qualified Data.Functor as Functor
42 import qualified Data.List as List
43 import qualified Data.Set as Set
44 import qualified Language.Haskell.TH as TH
45 import qualified Language.Haskell.TH.Syntax as TH
47 import Symantic.Derive
48 import qualified Symantic.Class as Prod
49 import Symantic.Parser.Grammar.Production
52 type ReprComb = Type -> Type
54 -- * Class 'CombAlternable'
55 class CombAlternable repr where
56 -- | @('alt' es l r)@ parses @(l)@ and return its return value or,
57 -- if it fails with an 'Exception' within @(es)@,
58 -- parses @(r)@ from where @(l)@ has left the input stream,
59 -- and returns its return value,
60 -- otherwise throw the 'Exception' again.
61 alt :: Exception -> repr a -> repr a -> repr a
62 throw :: ExceptionLabel -> repr a
63 -- | @('try' ra)@ records the input stream position,
64 -- then parses like @(ra)@ and either returns its value it it succeeds or fails
65 -- if it fails but with a reset of the input stream to the recorded position.
66 -- Generally used on the first alternative: @('try' rl '<|>' rr)@.
67 try :: repr a -> repr a
69 FromDerived2 CombAlternable repr =>
70 Exception -> repr a -> repr a -> repr a
72 FromDerived CombAlternable repr =>
73 ExceptionLabel -> repr a
75 FromDerived1 CombAlternable repr =>
77 alt = liftDerived2 . alt
78 throw = liftDerived . throw
79 try = liftDerived1 try
81 failure :: SomeFailure -> repr a
83 FromDerived CombAlternable repr =>
85 failure = liftDerived . failure
87 -- | @(empty)@ parses nothing, always failing to return a value.
89 empty = failure (SomeFailure FailureEmpty)
91 data instance Failure CombAlternable
93 deriving (Eq, Ord, Show, TH.Lift, Generic, NFData)
95 -- ** Data family 'Failure'
96 -- | 'Failure's of the 'Grammar'.
97 -- This is an extensible data-type.
99 (comb :: ReprComb -> Constraint)
103 -- | Convenient utility to pattern-match a 'SomeFailure'.
104 pattern Failure :: Typeable comb => Failure comb -> SomeFailure
105 pattern Failure x <- (unSomeFailure -> Just x)
108 -- ** Type 'SomeFailure'
113 , Show (Failure comb)
114 , TH.Lift (Failure comb)
115 , NFData (Failure comb)
117 ) => SomeFailure (Failure comb {-repr a-})
118 instance Eq SomeFailure where
119 SomeFailure (x::Failure x) == SomeFailure (y::Failure y) =
120 case typeRep @x `eqTypeRep` typeRep @y of
123 instance Ord SomeFailure where
124 SomeFailure (x::Failure x) `compare` SomeFailure (y::Failure y) =
125 -- WARNING: this ordering is convenient to make a 'Set' of 'SomeFailure's
126 -- but it is based upon a hash which changes with packages' ABI
127 -- and also if the install is "inplace" or not.
128 -- Therefore this 'Ord' is not stable enough to put 'SomeFailure'
130 let xT = typeRep @x in
131 let yT = typeRep @y in
132 case SomeTypeRep xT `compare` SomeTypeRep yT of
133 EQ | Just HRefl <- xT `eqTypeRep` yT -> compare x y
135 instance Show SomeFailure where
136 showsPrec p (SomeFailure x) = showsPrec p x
137 instance TH.Lift SomeFailure where
138 liftTyped (SomeFailure x) = [|| SomeFailure $$(TH.liftTyped x) ||]
139 instance NFData SomeFailure where
140 rnf (SomeFailure x) = rnf x
143 instance Derivable (SomeFailure repr) where
144 derive (SomeFailure x) = derive x
147 -- | @(unSomeFailure c :: 'Maybe' ('Failure' comb repr a))@
148 -- extract the data-constructor from the given 'SomeFailure'
149 -- iif. it belongs to the @('Failure' comb repr a)@ data-instance.
150 unSomeFailure :: forall comb. Typeable comb => SomeFailure -> Maybe (Failure comb)
151 unSomeFailure (SomeFailure (c::Failure c)) =
152 case typeRep @comb `eqTypeRep` typeRep @c of
156 -- ** Type 'Exception'
158 = ExceptionLabel ExceptionLabel
161 deriving (Eq, Ord, Show, TH.Lift, Generic, NFData)
162 type ExceptionLabel = String
163 -- type Exceptions = Set Exception
165 -- | Like @('<|>')@ but with different returning types for the alternatives,
166 -- and a return value wrapped in an 'Either' accordingly.
167 (<+>) :: CombApplicable repr => CombAlternable repr => repr a -> repr b -> repr (Either a b)
168 p <+> q = Prod.left <$> p <|> Prod.right <$> q
170 (<|>) :: CombAlternable repr => repr a -> repr a -> repr a
171 (<|>) = alt ExceptionFailure
175 optionally :: CombApplicable repr => CombAlternable repr => repr a -> Production b -> repr b
176 optionally p x = p $> x <|> pure x
178 optional :: CombApplicable repr => CombAlternable repr => repr a -> repr ()
179 optional = flip optionally (Prod.constant ())
181 option :: CombApplicable repr => CombAlternable repr => Production a -> repr a -> repr a
182 option x p = p <|> pure x
184 choice :: CombAlternable repr => [repr a] -> repr a
185 choice = List.foldr (<|>) empty
186 -- FIXME: Here hlint suggests to use Data.Foldable.asum,
187 -- but at this point there is no asum for our own (<|>)
189 maybeP :: CombApplicable repr => CombAlternable repr => repr a -> repr (Maybe a)
190 maybeP p = option Prod.nothing (Prod.just <$> p)
192 manyTill :: CombApplicable repr => CombAlternable repr => repr a -> repr b -> repr [a]
193 manyTill p end = let go = end $> Prod.nil <|> p <:> go in go
195 -- * Class 'CombApplicable'
196 -- | This is like the usual 'Functor' and 'Applicative' type classes
197 -- from the @base@ package, but using @('Production' a)@ instead of just @(a)@
198 -- to be able to use and pattern match on some usual terms of type @(a)@ (like 'Prod.id')
199 -- and thus apply some optimizations.
200 -- @(repr)@, for "representation", is the usual tagless-final abstraction
201 -- over the many semantics that this syntax (formed by the methods
202 -- of type class like this one) will be interpreted.
203 class CombApplicable repr where
204 -- | @(a2b '<$>' ra)@ parses like @(ra)@ but maps its returned value with @(a2b)@.
205 (<$>) :: Production (a -> b) -> repr a -> repr b
206 (<$>) f = (pure f <*>)
207 (<$>%) :: (Production a -> Production b) -> repr a -> repr b
208 a2b <$>% ma = Prod.lam a2b <$> ma
210 -- | Like '<$>' but with its arguments 'flip'-ped.
211 (<&>) :: repr a -> Production (a -> b) -> repr b
214 -- | @(a '<$' rb)@ parses like @(rb)@ but discards its returned value by replacing it with @(a)@.
215 (<$) :: Production a -> repr b -> repr a
218 -- | @(ra '$>' b)@ parses like @(ra)@ but discards its returned value by replacing it with @(b)@.
219 ($>) :: repr a -> Production b -> repr b
222 -- | @('pure' a)@ parses the empty string, always succeeding in returning @(a)@.
223 pure :: Production a -> repr a
225 FromDerived CombApplicable repr =>
226 Production a -> repr a
227 pure = liftDerived . pure
229 -- | @(ra2b '<*>' ra)@ parses sequentially @(ra2b)@ and then @(ra)@,
230 -- and returns the application of the function returned by @(ra2b)@
231 -- to the value returned by @(ra)@.
232 (<*>) :: repr (a -> b) -> repr a -> repr b
234 FromDerived2 CombApplicable repr =>
235 repr (a -> b) -> repr a -> repr b
236 (<*>) = liftDerived2 (<*>)
238 -- | @(ra '<*' rb)@ parses sequentially @(ra)@ and then @(rb)@,
239 -- and returns like @(ra)@, discarding the return value of @(rb)@.
240 (<*) :: repr a -> repr b -> repr a
241 (<*) = liftA2 Prod.const
243 -- | @(ra '*>' rb)@ parses sequentially @(ra)@ and then @(rb)@,
244 -- and returns like @(rb)@, discarding the return value of @(ra)@.
245 (*>) :: repr a -> repr b -> repr b
246 x *> y = (Prod.id <$ x) <*> y
248 -- | Like '<*>' but with its arguments 'flip'-ped.
249 (<**>) :: repr a -> repr (a -> b) -> repr b
250 (<**>) = liftA2 (Prod.flip Prod..@ (Prod.$))
252 (<**>) :: repr a -> repr (a -> b) -> repr b
253 (<**>) = liftA2 (\a f -> f a)
255 -- | @('liftA2' a2b2c ra rb)@ parses sequentially @(ra)@ and then @(rb)@,
256 -- and returns the application of @(a2b2c)@ to the values returned by those parsers.
257 liftA2 :: Production (a -> b -> c) -> repr a -> repr b -> repr c
258 liftA2 f x = (<*>) (f <$> x)
260 infixl 4 <*>, <*, *>, <**>
261 data instance Failure CombApplicable
266 (<:>) :: CombApplicable repr => repr a -> repr [a] -> repr [a]
267 (<:>) = liftA2 Prod.cons
269 sequence :: CombApplicable repr => [repr a] -> repr [a]
270 sequence = List.foldr (<:>) (pure Prod.nil)
272 traverse :: CombApplicable repr => (a -> repr b) -> [a] -> repr [b]
273 traverse f = sequence . List.map f
274 -- FIXME: Here hlint suggests to use Control.Monad.mapM,
275 -- but at this point there is no mapM for our own sequence
277 repeat :: CombApplicable repr => Int -> repr a -> repr [a]
278 repeat n p = traverse (const p) [1..n]
280 between :: CombApplicable repr => repr o -> repr c -> repr a -> repr a
281 between open close p = open *> p <* close
283 void :: CombApplicable repr => repr a -> repr ()
286 unit :: CombApplicable repr => repr ()
287 unit = pure (Prod.constant ())
289 -- * Class 'CombFoldable'
290 class CombFoldable repr where
291 chainPre :: repr (a -> a) -> repr a -> repr a
292 chainPost :: repr a -> repr (a -> a) -> repr a
293 chainPre = liftDerived2 chainPre
294 chainPost = liftDerived2 chainPost
296 FromDerived2 CombFoldable repr =>
297 repr (a -> a) -> repr a -> repr a
299 FromDerived2 CombFoldable repr =>
300 repr a -> repr (a -> a) -> repr a
303 CombApplicable repr =>
304 CombAlternable repr =>
305 repr (a -> a) -> repr a -> repr a
307 CombApplicable repr =>
308 CombAlternable repr =>
309 repr a -> repr (a -> a) -> repr a
310 chainPre op p = go <*> p where go = (Prod..) <$> op <*> go <|> pure Prod.id
311 chainPost p op = p <**> go where go = (Prod..) <$> op <*> go <|> pure Prod.id
314 chainPre op p = flip (foldr ($)) <$> many op <*> p
315 chainPost p op = foldl' (flip ($)) <$> p <*> many op
317 data instance Failure CombFoldable
320 conditional :: CombSelectable repr => [(Production (a -> Bool), repr b)] -> repr a -> repr b -> repr b
321 conditional cs p def = match p fs qs def
322 where (fs, qs) = List.unzip cs
327 CombApplicable repr => CombFoldable repr =>
328 Production (a -> b -> b) -> Production b -> repr a -> repr b
329 pfoldr f k p = chainPre (f <$> p) (pure k)
332 CombApplicable repr => CombFoldable repr =>
333 Production (a -> b -> b) -> Production b -> repr a -> repr b
334 pfoldr1 f k p = f <$> p <*> pfoldr f k p
337 CombApplicable repr => CombFoldable repr =>
338 Production (b -> a -> b) -> Production b -> repr a -> repr b
339 pfoldl f k p = chainPost (pure k) ((Prod.flip <$> pure f) <*> p)
342 CombApplicable repr => CombFoldable repr =>
343 Production (b -> a -> b) -> Production b -> repr a -> repr b
344 pfoldl1 f k p = chainPost (f <$> pure k <*> p) ((Prod.flip <$> pure f) <*> p)
348 CombApplicable repr => CombFoldable repr =>
349 Production (a -> b) -> repr a -> repr (b -> a -> b) -> repr b
350 chainl1' f p op = chainPost (f <$> p) (Prod.flip <$> op <*> p)
353 CombApplicable repr => CombFoldable repr =>
354 repr a -> repr (a -> a -> a) -> repr a
355 chainl1 = chainl1' Prod.id
358 chainr1' :: ParserOps rep => rep (a -> b) -> repr a -> repr (a -> b -> b) -> repr b
359 chainr1' f p op = newRegister_ Prod.id $ \acc ->
360 let go = bind p $ \x ->
361 modify acc (Prod.flip (Prod..@) <$> (op <*> x)) *> go
365 chainr1 :: repr a -> repr (a -> a -> a) -> repr a
366 chainr1 = chainr1' Prod.id
368 chainr :: repr a -> repr (a -> a -> a) -> Production a -> repr a
369 chainr p op x = option x (chainr1 p op)
373 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
374 repr a -> repr (a -> a -> a) -> Production a -> repr a
375 chainl p op x = option x (chainl1 p op)
377 -- Derived Combinators
379 CombApplicable repr => CombFoldable repr =>
381 many = pfoldr Prod.cons Prod.nil
384 CombApplicable repr => CombFoldable repr =>
385 Int -> repr a -> repr [a]
386 manyN n p = List.foldr (const (p <:>)) (many p) [1..n]
389 CombApplicable repr => CombFoldable repr =>
394 CombApplicable repr => CombFoldable repr =>
396 --skipMany p = let skipManyp = p *> skipManyp <|> unit in skipManyp
397 skipMany = void . pfoldl Prod.const Prod.unit -- the void here will encourage the optimiser to recognise that the register is unused
400 CombApplicable repr => CombFoldable repr =>
401 Int -> repr a -> repr ()
402 skipManyN n p = List.foldr (const (p *>)) (skipMany p) [1..n]
405 CombApplicable repr => CombFoldable repr =>
407 skipSome = skipManyN 1
410 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
411 repr a -> repr b -> repr [a]
412 sepBy p sep = option Prod.nil (sepBy1 p sep)
415 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
416 repr a -> repr b -> repr [a]
417 sepBy1 p sep = p <:> many (sep *> p)
420 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
421 repr a -> repr b -> repr [a]
422 endBy p sep = many (p <* sep)
425 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
426 repr a -> repr b -> repr [a]
427 endBy1 p sep = some (p <* sep)
430 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
431 repr a -> repr b -> repr [a]
432 sepEndBy p sep = option Prod.nil (sepEndBy1 p sep)
435 CombApplicable repr => CombAlternable repr => CombFoldable repr =>
436 repr a -> repr b -> repr [a]
438 let seb1 = p <**> (sep *> (Prod.flip Prod..@ Prod.cons <$> option Prod.nil seb1)
439 <|> pure (Prod.flip Prod..@ Prod.cons Prod..@ Prod.nil))
443 sepEndBy1 :: repr a -> repr b -> repr [a]
444 sepEndBy1 p sep = newRegister_ Prod.id $ \acc ->
445 let go = modify acc ((Prod.flip (Prod..)) Prod..@ Prod.cons <$> p)
446 *> (sep *> (go <|> get acc) <|> get acc)
447 in go <*> pure Prod.nil
450 -- * Class 'CombMatchable'
451 class CombMatchable repr where
453 repr a -> [(Production (a -> Bool), repr b)] -> repr b -> repr b
454 conditional a bs = liftDerived1
455 (conditional (derive a) (second derive Functor.<$> bs))
456 default conditional ::
457 FromDerived1 CombMatchable repr => Derivable repr =>
458 repr a -> [(Production (a -> Bool), repr b)] -> repr b -> repr b
459 data instance Failure CombMatchable
462 CombMatchable repr =>
464 repr a -> [Production a] -> (Production a -> repr b) -> repr b -> repr b
465 match a as p = conditional a
467 ( Prod.lam (v Prod.==)
473 CombMatchable repr =>
474 Production (a -> Bool) -> repr a -> repr b -> repr b -> repr b
475 predicate p a b = conditional a [(p, b)]
479 CombMatchable repr =>
480 repr Bool -> (repr a, repr a) -> repr a
481 cond <?:> (p, q) = predicate Prod.id cond p q
483 -- * Class 'CombSatisfiable'
484 class CombSatisfiable tok repr where
485 -- | Like 'satisfyOrFail' but with no custom failure.
486 satisfy :: Production (tok -> Bool) -> repr tok
487 satisfy = satisfyOrFail Set.empty
488 -- | Like 'satisfy' but with a custom set of 'SomeFailure's.
491 Production (tok -> Bool) -> repr tok
492 default satisfyOrFail ::
493 FromDerived (CombSatisfiable tok) repr =>
495 Production (tok -> Bool) -> repr tok
496 satisfyOrFail fs = liftDerived . satisfyOrFail fs
498 data instance Failure (CombSatisfiable tok)
500 -- FIXME: this 'Failure' is a bit special since multiple ones
501 -- with different 'Horizon's makes no sense.
502 -- This should likely be treated separately in 'ParsingError'.
503 | FailureHorizon Int -- FIXME: use Natural?
504 | FailureLabel String
506 deriving (Eq, Ord, Show, Typeable, Generic, NFData)
507 -- | Global 'TH.Name' to refer to the @(InputToken inp)@ type
508 -- from TemplateHaskell code.
509 inputTokenProxy :: TH.Name
510 inputTokenProxy = TH.mkName "inputToken"
511 instance TH.Lift tok => TH.Lift (Failure (CombSatisfiable tok)) where
512 liftTyped :: forall m. TH.Quote m => Failure (CombSatisfiable tok) -> TH.Code m (Failure (CombSatisfiable tok))
515 $$(let inputToken :: TH.Code m (Proxy tok) =
516 TH.unsafeCodeCoerce (return (TH.VarE inputTokenProxy))
518 (Proxy :: Proxy tok') ->
520 FailureAny -> [|| FailureAny @tok' ||]
521 FailureHorizon h -> [|| FailureHorizon @tok' h ||]
522 FailureLabel lbl -> [|| FailureLabel @tok' lbl ||]
523 FailureToken tok -> [|| FailureToken $$(TH.liftTyped tok) ||]
528 CombApplicable repr =>
529 CombSatisfiable Char repr =>
531 char c = satisfyOrFail
532 (Set.singleton (SomeFailure (FailureToken c)))
533 (Prod.equal Prod..@ Prod.constant c)
536 item :: forall tok repr.
537 Ord tok => Show tok => Typeable tok => TH.Lift tok => NFData tok =>
538 CombSatisfiable tok repr => repr tok
540 (Set.singleton (SomeFailure (FailureAny @tok)))
541 (Prod.const Prod..@ Prod.constant True)
544 CombAlternable repr =>
545 CombSatisfiable Char repr =>
550 CombApplicable repr => CombAlternable repr =>
551 CombSatisfiable Char repr =>
552 [Char] -> repr [Char]
553 string = try . traverse char
556 Ord tok => Show tok => Typeable tok => TH.Lift tok => NFData tok =>
557 CombSatisfiable tok repr =>
559 oneOf ts = satisfyOrFail
560 (Set.fromList (SomeFailure . FailureToken Functor.<$> ts))
563 [||\t -> $$(ofChars ts [||t||])||])
566 TH.Lift tok => Eq tok =>
567 CombSatisfiable tok repr =>
569 noneOf cs = satisfy (production
570 (not . (`List.elem` cs))
571 [||\c -> not $$(ofChars cs [||c||])||])
574 TH.Lift tok => Eq tok =>
575 {-alternatives-}[tok] ->
576 {-input-}TH.CodeQ tok ->
579 (\tok acc inp -> [|| tok == $$inp || $$(acc inp) ||])
583 CombAlternable repr =>
584 CombApplicable repr =>
585 CombSatisfiable Char repr =>
586 CombLookable repr => repr ()
587 more = look (void (item @Char))
590 TH.Lift tok => Show tok => Eq tok => Typeable tok =>
591 CombAlternable repr =>
592 CombApplicable repr =>
593 CombSatisfiable tok repr =>
595 token tok = satisfy (Prod.equal Prod..@ Prod.constant tok) $> Prod.constant tok
596 -- token tok = satisfy [ExceptionToken tok] (Prod.eq Prod..@ Prod.qual Prod..@ Prod.char tok) $> Prod.char tok
599 TH.Lift tok => Eq tok => Show tok => Typeable tok =>
600 CombApplicable repr => CombAlternable repr =>
601 CombSatisfiable tok repr => [tok] -> repr [tok]
602 tokens = try . traverse token
604 -- * Class 'CombSelectable'
605 class CombSelectable repr where
606 branch :: repr (Either a b) -> repr (a -> c) -> repr (b -> c) -> repr c
608 FromDerived3 CombSelectable repr =>
609 repr (Either a b) -> repr (a -> c) -> repr (b -> c) -> repr c
610 branch = liftDerived3 branch
611 data instance Failure CombSelectable
614 CombMatchable repr =>
615 CombSelectable repr =>
616 Prod.Constantable () repr =>
617 repr Bool -> repr () -> repr ()
618 when p q = p <?:> (q, Prod.constant ())
621 CombMatchable repr =>
622 CombSelectable repr =>
623 Prod.Constantable () repr =>
625 while x = fix (when x)
627 -- * Class 'CombLookable'
628 class CombLookable repr where
629 look :: repr a -> repr a
630 negLook :: repr a -> repr ()
632 FromDerived1 CombLookable repr =>
635 FromDerived1 CombLookable repr =>
637 look = liftDerived1 look
638 negLook = liftDerived1 negLook
641 eof = liftDerived eof
643 FromDerived CombLookable repr =>
645 -- eof = negLook (satisfy @Char (Prod.const Prod..@ Prod.bool True))
647 data instance Failure CombLookable
649 deriving (Eq, Ord, Show, Typeable, TH.Lift, Generic, NFData)
651 -- Composite Combinators
652 -- someTill :: repr a -> repr b -> repr [a]
653 -- someTill p end = negLook end *> (p <:> manyTill p end)
656 constp :: CombApplicable repr => repr a -> repr (b -> a)
657 constp = (Prod.const <$>)
662 (>>) :: CombApplicable repr => repr a -> repr b -> repr b
665 -- Monoidal Operations
668 (<~>) :: CombApplicable repr => repr a -> repr b -> repr (a, b)
669 (<~>) = liftA2 (Prod.runtime (,))
672 (<~) :: CombApplicable repr => repr a -> repr b -> repr a
676 (~>) :: CombApplicable repr => repr a -> repr b -> repr b
681 CombApplicable repr =>
682 Production (a -> b -> c) -> repr a -> repr b -> repr c
683 liftA2 f x = (<*>) (fmap f x)
686 CombApplicable repr =>
687 Production (a -> b -> c -> d) -> repr a -> repr b -> repr c -> repr d
688 liftA3 f a b c = liftA2 f a b <*> c
693 -- Combinators interpreters for 'Sym.Any'.
694 instance CombApplicable repr => CombApplicable (Sym.Any repr)
695 instance CombSatisfiable repr => CombSatisfiable (Sym.Any repr)
696 instance CombAlternable repr => CombAlternable (Sym.Any repr)
697 instance CombSelectable repr => CombSelectable (Sym.Any repr)
698 instance CombMatchable repr => CombMatchable (Sym.Any repr)
699 instance CombLookable repr => CombLookable (Sym.Any repr)
700 instance CombFoldable repr => CombFoldable (Sym.Any repr)
704 newtype Register r a = Register { unRegister :: UnscopedRegister a }
707 -- ** Type 'UnscopedRegister'
708 newtype UnscopedRegister r = UnscopedRegister { unUnscopedRegister :: TH.Name }
710 deriving newtype Show
714 put_ :: ParserOps rep => Register r a -> rep a -> Parser ()
715 put_ r = put r . pure
717 gets_ :: ParserOps rep => Register r a -> rep (a -> b) -> Parser b
718 gets_ r = gets r . pure
720 modify_ :: ParserOps rep => Register r a -> rep (a -> a) -> Parser ()
721 modify_ r = modify r . pure
725 CombApplicable repr =>
726 CombRegisterable repr =>
727 Register r a -> repr (a -> b) -> repr b
728 gets r p = p <*> get r
731 CombApplicable repr =>
732 CombRegisterable repr =>
733 Register r a -> repr (a -> a) -> repr ()
734 modify r p = put r (gets r p)
737 CombRegisterable repr =>
738 Register r1 a -> Register r2 a -> repr ()
739 move dst src = put dst (get src)
742 CombRegisterable repr =>
743 repr a -> (repr a -> repr b) -> repr b
744 bind p f = new p (f . get)
747 CombApplicable repr =>
748 CombRegisterable repr =>
749 Register r a -> repr a -> repr b -> repr b
750 local r p q = bind (get r) (\x -> put r p *> q <* put r x)
753 CombApplicable repr =>
754 CombRegisterable repr =>
755 Register r1 a -> Register r2 a -> repr ()
756 swap r1 r2 = bind (get r1) (\x -> move r1 r2 *> put r2 x)
759 CombAlternable repr =>
760 CombApplicable repr =>
761 CombRegisterable repr =>
762 Register r a -> repr b -> repr b
763 rollback r p = bind (get r) (\x -> p <|> put r x *> empty)
766 CombApplicable repr =>
767 CombMatchable repr =>
768 CombSelectable repr =>
769 CombRegisterable repr =>
770 Prod.Constantable () repr =>
771 repr a -> repr (a -> Bool) -> repr (a -> a) -> repr () -> repr ()
772 for init cond step body =
774 let cond' = gets i cond in
775 when cond' (while (body *> modify i step *> cond'))
779 -- ** Class 'CombRegisterable'
780 class CombRegisterable (repr::ReprComb) where
781 new :: repr a -> (forall r. Register r a -> repr b) -> repr b
782 get :: Register r a -> repr a
783 put :: Register r a -> repr a -> repr ()
785 FromDerived CombRegisterable repr => Derivable repr =>
786 repr a -> (forall r. Register r a -> repr b) -> repr b
788 FromDerived CombRegisterable repr =>
789 Register r a -> repr a
791 FromDerived1 CombRegisterable repr =>
792 Register r a -> repr a -> repr ()
793 new ini f = liftDerived (new (derive ini) (derive . f))
794 get = liftDerived . get
795 put = liftDerived1 . put