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
215 where go = (H..) <$> op <*> go <|> pure H.id
216 chainPost p op = p <**> go
217 where go = (H..) <$> op <*> go <|> pure H.id
220 conditional :: Selectable repr => [(TermGrammar (a -> Bool), repr b)] -> repr a -> repr b -> repr b
221 conditional cs p def = match p fs qs def
222 where (fs, qs) = List.unzip cs
225 -- * Class 'Satisfiable'
226 class Satisfiable tok repr where
227 satisfy :: [ErrorItem tok] -> TermGrammar (tok -> Bool) -> repr tok
229 Sym.Liftable repr => Satisfiable tok (Sym.Output repr) =>
231 TermGrammar (tok -> Bool) -> repr tok
232 satisfy es = Sym.lift . satisfy es
235 item = satisfy [] (H.const H..@ H.bool True)
237 -- ** Type 'ErrorItem'
240 | ErrorItemLabel String
241 | ErrorItemHorizon Int
243 deriving instance Eq tok => Eq (ErrorItem tok)
244 deriving instance Ord tok => Ord (ErrorItem tok)
245 deriving instance Show tok => Show (ErrorItem tok)
246 deriving instance TH.Lift tok => TH.Lift (ErrorItem tok)
248 -- * Class 'Lookable'
249 class Lookable repr where
250 look :: repr a -> repr a
251 negLook :: repr a -> repr ()
252 default look :: Sym.Liftable1 repr => Lookable (Sym.Output repr) => repr a -> repr a
253 default negLook :: Sym.Liftable1 repr => Lookable (Sym.Output repr) => repr a -> repr ()
254 look = Sym.lift1 look
255 negLook = Sym.lift1 negLook
259 default eof :: Sym.Liftable repr => Lookable (Sym.Output repr) => repr ()
260 -- eof = negLook (satisfy @Char [ErrorItemAny] (H.const H..@ H.bool True))
265 (<:>) :: Applicable repr => repr a -> repr [a] -> repr [a]
266 (<:>) = liftA2 H.cons
268 sequence :: Applicable repr => [repr a] -> repr [a]
269 sequence = List.foldr (<:>) (pure H.nil)
271 traverse :: Applicable repr => (a -> repr b) -> [a] -> repr [b]
272 traverse f = sequence . List.map f
273 -- FIXME: Here hlint suggests to use Control.Monad.mapM,
274 -- but at this point there is no mapM for our own sequence
276 repeat :: Applicable repr => Int -> repr a -> repr [a]
277 repeat n p = traverse (const p) [1..n]
279 between :: Applicable repr => repr o -> repr c -> repr a -> repr a
280 between open close p = open *> p <* close
283 Applicable repr => Alternable repr =>
284 Satisfiable Char repr =>
285 [Char] -> repr [Char]
286 string = try . traverse char
289 TH.Lift tok => Eq tok =>
290 Satisfiable tok repr =>
292 oneOf ts = satisfy [ErrorItemLabel "oneOf"]
293 (Sym.trans H.ValueCode
294 { value = (`List.elem` ts)
295 , code = [||\t -> $$(ofChars ts [||t||])||] })
298 TH.Lift tok => Eq tok =>
299 Satisfiable tok repr =>
301 noneOf cs = satisfy (ErrorItemToken Functor.<$> cs) (Sym.trans H.ValueCode
302 { value = not . (`List.elem` cs)
303 , code = [||\c -> not $$(ofChars cs [||c||])||]
307 TH.Lift tok => Eq tok =>
308 {-alternatives-}[tok] ->
309 {-input-}TH.CodeQ tok ->
311 ofChars = List.foldr (\alt acc ->
312 \inp -> [|| alt == $$inp || $$(acc inp) ||])
315 more :: Applicable repr => Satisfiable Char repr => Lookable repr => repr ()
316 more = look (void (item @Char))
319 Applicable repr => Satisfiable Char repr =>
321 char c = satisfy [ErrorItemToken c] (H.eq H..@ H.char c) $> H.char c
322 -- char c = satisfy [ErrorItemToken c] (H.eq H..@ H.qual H..@ H.char c) $> H.char c
324 anyChar :: Satisfiable Char repr => repr Char
325 anyChar = satisfy [] (H.const H..@ H.bool True)
328 TH.Lift tok => Show tok => Eq tok =>
329 Applicable repr => Satisfiable tok repr =>
331 token tok = satisfy [ErrorItemToken tok] (H.eq H..@ H.char tok) $> H.char tok
332 -- token tok = satisfy [ErrorItemToken tok] (H.eq H..@ H.qual H..@ H.char tok) $> H.char tok
335 TH.Lift tok => Eq tok => Show tok =>
336 Applicable repr => Alternable repr =>
337 Satisfiable tok repr => [tok] -> repr [tok]
338 tokens = try . traverse token
340 -- Composite Combinators
341 -- someTill :: repr a -> repr b -> repr [a]
342 -- someTill p end = negLook end *> (p <:> manyTill p end)
344 void :: Applicable repr => repr a -> repr ()
347 unit :: Applicable repr => repr ()
351 constp :: Applicable repr => repr a -> repr (b -> a)
352 constp = (H.const <$>)
357 (>>) :: Applicable repr => repr a -> repr b -> repr b
360 -- Monoidal Operations
363 (<~>) :: Applicable repr => repr a -> repr b -> repr (a, b)
364 (<~>) = liftA2 (H.runtime (,))
367 (<~) :: Applicable repr => repr a -> repr b -> repr a
371 (~>) :: Applicable repr => repr a -> repr b -> repr b
377 TermGrammar (a -> b -> c) -> repr a -> repr b -> repr c
378 liftA2 f x = (<*>) (fmap f x)
382 TermGrammar (a -> b -> c -> d) -> repr a -> repr b -> repr c -> repr d
383 liftA3 f a b c = liftA2 f a b <*> c
389 Applicable repr => Foldable repr =>
390 TermGrammar (a -> b -> b) -> TermGrammar b -> repr a -> repr b
391 pfoldr f k p = chainPre (f <$> p) (pure k)
394 Applicable repr => Foldable repr =>
395 TermGrammar (a -> b -> b) -> TermGrammar b -> repr a -> repr b
396 pfoldr1 f k p = f <$> p <*> pfoldr f k p
399 Applicable repr => Foldable repr =>
400 TermGrammar (b -> a -> b) -> TermGrammar b -> repr a -> repr b
401 pfoldl f k p = chainPost (pure k) ((H.flip <$> pure f) <*> p)
404 Applicable repr => Foldable repr =>
405 TermGrammar (b -> a -> b) -> TermGrammar b -> repr a -> repr b
406 pfoldl1 f k p = chainPost (f <$> pure k <*> p) ((H.flip <$> pure f) <*> p)
410 Applicable repr => Foldable repr =>
411 TermGrammar (a -> b) -> repr a -> repr (b -> a -> b) -> repr b
412 chainl1' f p op = chainPost (f <$> p) (H.flip <$> op <*> p)
415 Applicable repr => Foldable repr =>
416 repr a -> repr (a -> a -> a) -> repr a
417 chainl1 = chainl1' H.id
420 chainr1' :: ParserOps rep => rep (a -> b) -> repr a -> repr (a -> b -> b) -> repr b
421 chainr1' f p op = newRegister_ H.id $ \acc ->
422 let go = bind p $ \x ->
423 modify acc (H.flip (H..@) <$> (op <*> x)) *> go
427 chainr1 :: repr a -> repr (a -> a -> a) -> repr a
428 chainr1 = chainr1' H.id
430 chainr :: repr a -> repr (a -> a -> a) -> TermGrammar a -> repr a
431 chainr p op x = option x (chainr1 p op)
435 Applicable repr => Alternable repr => Foldable repr =>
436 repr a -> repr (a -> a -> a) -> TermGrammar a -> repr a
437 chainl p op x = option x (chainl1 p op)
439 -- Derived Combinators
441 Applicable repr => Foldable repr =>
443 many = pfoldr H.cons H.nil
446 Applicable repr => Foldable repr =>
447 Int -> repr a -> repr [a]
448 manyN n p = List.foldr (const (p <:>)) (many p) [1..n]
451 Applicable repr => Foldable repr =>
456 Applicable repr => Foldable repr =>
458 --skipMany p = let skipManyp = p *> skipManyp <|> unit in skipManyp
459 skipMany = void . pfoldl H.const H.unit -- the void here will encourage the optimiser to recognise that the register is unused
462 Applicable repr => Foldable repr =>
463 Int -> repr a -> repr ()
464 skipManyN n p = List.foldr (const (p *>)) (skipMany p) [1..n]
467 Applicable repr => Foldable repr =>
469 skipSome = skipManyN 1
472 Applicable repr => Alternable repr => Foldable repr =>
473 repr a -> repr b -> repr [a]
474 sepBy p sep = option H.nil (sepBy1 p sep)
477 Applicable repr => Alternable repr => Foldable repr =>
478 repr a -> repr b -> repr [a]
479 sepBy1 p sep = p <:> many (sep *> p)
482 Applicable repr => Alternable repr => Foldable repr =>
483 repr a -> repr b -> repr [a]
484 endBy p sep = many (p <* sep)
487 Applicable repr => Alternable repr => Foldable repr =>
488 repr a -> repr b -> repr [a]
489 endBy1 p sep = some (p <* sep)
492 Applicable repr => Alternable repr => Foldable repr =>
493 repr a -> repr b -> repr [a]
494 sepEndBy p sep = option H.nil (sepEndBy1 p sep)
497 Applicable repr => Alternable repr => Foldable repr =>
498 repr a -> repr b -> repr [a]
500 let seb1 = p <**> (sep *> (H.flip H..@ H.cons <$> option H.nil seb1)
501 <|> pure (H.flip H..@ H.cons H..@ H.nil))
505 sepEndBy1 :: repr a -> repr b -> repr [a]
506 sepEndBy1 p sep = newRegister_ H.id $ \acc ->
507 let go = modify acc ((H.flip (H..)) H..@ H.cons <$> p)
508 *> (sep *> (go <|> get acc) <|> get acc)
513 -- Combinators interpreters for 'Sym.Any'.
514 instance Applicable repr => Applicable (Sym.Any repr)
515 instance Satisfiable repr => Satisfiable (Sym.Any repr)
516 instance Alternable repr => Alternable (Sym.Any repr)
517 instance Selectable repr => Selectable (Sym.Any repr)
518 instance Matchable repr => Matchable (Sym.Any repr)
519 instance Lookable repr => Lookable (Sym.Any repr)
520 instance Foldable repr => Foldable (Sym.Any repr)