change license to AGPL-3.0-or-later
[haskell/symantic-parser.git] / src / Symantic / Parser / Grammar / Combinators.hs
index 5196ed2bf8431545a092d66e6a630946f26b7609..3c6cd8b1030a6569dccecc834eba59c07ba61b9c 100644 (file)
@@ -8,6 +8,8 @@
 {-# LANGUAGE DeriveLift #-} -- For TH.Lift (ErrorItem tok)
 {-# LANGUAGE StandaloneDeriving #-} -- For Show (ErrorItem (InputToken inp))
 {-# LANGUAGE TemplateHaskell #-}
+-- | Semantic of the grammar combinators used to express parsers,
+-- in the convenient tagless-final encoding.
 module Symantic.Parser.Grammar.Combinators where
 
 import Data.Bool (Bool(..), not, (||))
@@ -19,45 +21,48 @@ import Data.Int (Int)
 import Data.Maybe (Maybe(..))
 import Data.Ord (Ord)
 import Data.String (String)
-import Language.Haskell.TH (CodeQ)
 import Text.Show (Show(..))
 import qualified Data.Functor as Functor
 import qualified Data.List as List
+import qualified Language.Haskell.TH as TH
 import qualified Language.Haskell.TH.Syntax as TH
 
 import qualified Symantic.Univariant.Trans as Sym
-import qualified Symantic.Parser.Staging as H
+import qualified Symantic.Parser.Haskell as H
+
+-- * Type 'TermGrammar'
+type TermGrammar = H.Term H.ValueCode
 
 -- * Class 'Applicable'
 -- | This is like the usual 'Functor' and 'Applicative' type classes
--- from the @base@ package, but using @('H.Haskell' a)@ instead of just @(a)@
--- to be able to use and pattern match on some usual terms of type @(a)@ (like
--- 'H.id') and thus apply some optimizations.
--- @(repr)@ , for "representation", is the usual tagless-final abstraction
+-- from the @base@ package, but using @('TermGrammar' a)@ instead of just @(a)@
+-- to be able to use and pattern match on some usual terms of type @(a)@ (like 'H.id')
+-- and thus apply some optimizations.
+-- @(repr)@, for "representation", is the usual tagless-final abstraction
 -- over the many semantics that this syntax (formed by the methods
 -- of type class like this one) will be interpreted.
 class Applicable repr where
   -- | @(a2b '<$>' ra)@ parses like @(ra)@ but maps its returned value with @(a2b)@.
-  (<$>) :: H.Haskell (a -> b) -> repr a -> repr b
+  (<$>) :: TermGrammar (a -> b) -> repr a -> repr b
   (<$>) f = (pure f <*>)
 
   -- | Like '<$>' but with its arguments 'flip'-ped.
-  (<&>) :: repr a -> H.Haskell (a -> b) -> repr b
+  (<&>) :: repr a -> TermGrammar (a -> b) -> repr b
   (<&>) = flip (<$>)
 
   -- | @(a '<$' rb)@ parses like @(rb)@ but discards its returned value by replacing it with @(a)@.
-  (<$) :: H.Haskell a -> repr b -> repr a
+  (<$) :: TermGrammar a -> repr b -> repr a
   (<$) x = (pure x <*)
 
   -- | @(ra '$>' b)@ parses like @(ra)@ but discards its returned value by replacing it with @(b)@.
-  ($>) :: repr a -> H.Haskell b -> repr b
+  ($>) :: repr a -> TermGrammar b -> repr b
   ($>) = flip (<$)
 
   -- | @('pure' a)@ parses the empty string, always succeeding in returning @(a)@.
-  pure :: H.Haskell a -> repr a
+  pure :: TermGrammar a -> repr a
   default pure ::
     Sym.Liftable repr => Applicable (Sym.Output repr) =>
-    H.Haskell a -> repr a
+    TermGrammar a -> repr a
   pure = Sym.lift . pure
 
   -- | @(ra2b '<*>' ra)@ parses sequentially @(ra2b)@ and then @(ra)@,
@@ -71,7 +76,7 @@ class Applicable repr where
 
   -- | @('liftA2' a2b2c ra rb)@ parses sequentially @(ra)@ and then @(rb)@,
   -- and returns the application of @(a2b2c)@ to the values returned by those parsers.
-  liftA2 :: H.Haskell (a -> b -> c) -> repr a -> repr b -> repr c
+  liftA2 :: TermGrammar (a -> b -> c) -> repr a -> repr b -> repr c
   liftA2 f x = (<*>) (f <$> x)
 
   -- | @(ra '<*' rb)@ parses sequentially @(ra)@ and then @(rb)@,
@@ -124,13 +129,13 @@ class Alternable repr where
   p <+> q = H.left <$> p <|> H.right <$> q
 infixl 3 <|>, <+>
 
-optionally :: Applicable repr => Alternable repr => repr a -> H.Haskell b -> repr b
+optionally :: Applicable repr => Alternable repr => repr a -> TermGrammar b -> repr b
 optionally p x = p $> x <|> pure x
 
 optional :: Applicable repr => Alternable repr => repr a -> repr ()
 optional = flip optionally H.unit
 
-option :: Applicable repr => Alternable repr => H.Haskell a -> repr a -> repr a
+option :: Applicable repr => Alternable repr => TermGrammar a -> repr a -> repr a
 option x p = p <|> pure x
 
 choice :: Alternable repr => [repr a] -> repr a
@@ -155,14 +160,15 @@ class Selectable repr where
 -- * Class 'Matchable'
 class Matchable repr where
   conditional ::
-    Eq a => [H.Haskell (a -> Bool)] -> [repr b] -> repr a -> repr b -> repr b
+    Eq a => repr a -> [TermGrammar (a -> Bool)] -> [repr b] -> repr b -> repr b
   default conditional ::
-    Sym.Unliftable repr => Sym.Liftable2 repr => Matchable (Sym.Output repr) =>
-    Eq a => [H.Haskell (a -> Bool)] -> [repr b] -> repr a -> repr b -> repr b
-  conditional cs bs = Sym.lift2 (conditional cs (Sym.trans Functor.<$> bs))
+    Sym.Unliftable repr => Sym.Liftable1 repr => Matchable (Sym.Output repr) =>
+    Eq a => repr a -> [TermGrammar (a -> Bool)] -> [repr b] -> repr b -> repr b
+  conditional a ps bs = Sym.lift1 (conditional (Sym.trans a) ps (Sym.trans Functor.<$> bs))
 
-  match :: Eq a => [H.Haskell a] -> repr a -> (H.Haskell a -> repr b) -> repr b -> repr b
-  match as a a2b = conditional (H.eq Functor.<$> as) (a2b Functor.<$> as) a
+  match :: Eq a => repr a -> [TermGrammar a] -> (TermGrammar a -> repr b) -> repr b -> repr b
+  match a as a2b = conditional a ((H.eq H..@) Functor.<$> as) (a2b Functor.<$> as)
+  -- match a as a2b = conditional a (((H.eq H..@ H.qual) H..@) Functor.<$> as) (a2b Functor.<$> as)
 
 -- * Class 'Foldable'
 class Foldable repr where
@@ -192,24 +198,28 @@ class Foldable repr where
     where go = (H..) <$> op <*> go <|> pure H.id
 
 {-
-conditional :: Selectable repr => [(H.Haskell (a -> Bool), repr b)] -> repr a -> repr b -> repr b
+conditional :: Selectable repr => [(TermGrammar (a -> Bool), repr b)] -> repr a -> repr b -> repr b
 conditional cs p def = match p fs qs def
   where (fs, qs) = List.unzip cs
 -}
 
 -- * Class 'Satisfiable'
-class Satisfiable repr tok where
-  satisfy :: [ErrorItem tok] -> H.Haskell (tok -> Bool) -> repr tok
+class Satisfiable tok repr where
+  satisfy :: [ErrorItem tok] -> TermGrammar (tok -> Bool) -> repr tok
   default satisfy ::
-    Sym.Liftable repr => Satisfiable (Sym.Output repr) tok =>
+    Sym.Liftable repr => Satisfiable tok (Sym.Output repr) =>
     [ErrorItem tok] ->
-    H.Haskell (tok -> Bool) -> repr tok
+    TermGrammar (tok -> Bool) -> repr tok
   satisfy es = Sym.lift . satisfy es
 
+  item :: repr tok
+  item = satisfy [] (H.const H..@ H.bool True)
+
 -- ** Type 'ErrorItem'
 data ErrorItem tok
   =  ErrorItemToken tok
   |  ErrorItemLabel String
+  |  ErrorItemHorizon Int
   |  ErrorItemEnd
 deriving instance Eq tok => Eq (ErrorItem tok)
 deriving instance Ord tok => Ord (ErrorItem tok)
@@ -228,8 +238,8 @@ class Lookable repr where
   eof :: repr ()
   eof = Sym.lift eof
   default eof :: Sym.Liftable repr => Lookable (Sym.Output repr) => repr ()
-  -- eof = negLook (satisfy @_ @Char [ErrorItemAny] (H.const H..@ H.bool True))
-             -- (item @_ @Char)
+  -- eof = negLook (satisfy @Char [ErrorItemAny] (H.const H..@ H.bool True))
+             -- (item @Char)
 
 {-# INLINE (<:>) #-}
 infixl 4 <:>
@@ -250,43 +260,64 @@ repeat n p = traverse (const p) [1..n]
 between :: Applicable repr => repr o -> repr c -> repr a -> repr a
 between open close p = open *> p <* close
 
-string :: Applicable repr => Satisfiable repr Char => [Char] -> repr [Char]
-string = traverse char
-
--- oneOf :: [Char] -> repr Char
--- oneOf cs = satisfy [] (makeQ (flip elem cs) [||\c -> $$(ofChars cs [||c||])||])
-
-noneOf :: TH.Lift tok => Eq tok => Satisfiable repr tok => [tok] -> repr tok
-noneOf cs = satisfy (ErrorItemToken Functor.<$> cs) (H.Haskell H.ValueCode{..})
-  where
-  value = H.Value (not . flip List.elem cs)
-  code = [||\c -> not $$(ofChars cs [||c||])||]
-
-ofChars :: TH.Lift tok => Eq tok => [tok] -> CodeQ tok -> CodeQ Bool
-ofChars = List.foldr (\c rest qc -> [|| c == $$qc || $$(rest qc) ||]) (const [||False||])
-
-more :: Applicable repr => Satisfiable repr Char => Lookable repr => repr ()
-more = look (void (item @_ @Char))
-
-char :: Applicable repr => Satisfiable repr Char => Char -> repr Char
-char c = satisfy [ErrorItemToken c] (H.eq (H.char c)) $> H.char c
-
-anyChar :: Satisfiable repr Char => repr Char
+string ::
+  Applicable repr => Alternable repr =>
+  Satisfiable Char repr =>
+  [Char] -> repr [Char]
+string = try . traverse char
+
+oneOf ::
+  TH.Lift tok => Eq tok =>
+  Satisfiable tok repr =>
+  [tok] -> repr tok
+oneOf ts = satisfy [ErrorItemLabel "oneOf"]
+  (Sym.trans H.ValueCode
+    { value = (`List.elem` ts)
+    , code = [||\t -> $$(ofChars ts [||t||])||] })
+
+noneOf ::
+  TH.Lift tok => Eq tok =>
+  Satisfiable tok repr =>
+  [tok] -> repr tok
+noneOf cs = satisfy (ErrorItemToken Functor.<$> cs) (Sym.trans H.ValueCode
+  { value = not . (`List.elem` cs)
+  , code = [||\c -> not $$(ofChars cs [||c||])||]
+  })
+
+ofChars ::
+  TH.Lift tok => Eq tok =>
+  {-alternatives-}[tok] ->
+  {-input-}TH.CodeQ tok ->
+  TH.CodeQ Bool
+ofChars = List.foldr (\alt acc ->
+  \inp -> [|| alt == $$inp || $$(acc inp) ||])
+  (const [||False||])
+
+more :: Applicable repr => Satisfiable Char repr => Lookable repr => repr ()
+more = look (void (item @Char))
+
+char ::
+  Applicable repr => Satisfiable Char repr =>
+  Char -> repr Char
+char c = satisfy [ErrorItemToken c] (H.eq H..@ H.char c) $> H.char c
+-- char c = satisfy [ErrorItemToken c] (H.eq H..@ H.qual H..@ H.char c) $> H.char c
+
+anyChar :: Satisfiable Char repr => repr Char
 anyChar = satisfy [] (H.const H..@ H.bool True)
 
 token ::
-  TH.Lift tok => Eq tok => Applicable repr =>
-  Satisfiable repr tok => tok -> repr tok
-token tok = satisfy [ErrorItemToken tok] (H.eq (H.char tok)) $> H.char tok
+  TH.Lift tok => Show tok => Eq tok =>
+  Applicable repr => Satisfiable tok repr =>
+  tok -> repr tok
+token tok = satisfy [ErrorItemToken tok] (H.eq H..@ H.char tok) $> H.char tok
+-- token tok = satisfy [ErrorItemToken tok] (H.eq H..@ H.qual H..@ H.char tok) $> H.char tok
 
 tokens ::
-  TH.Lift tok => Eq tok => Applicable repr => Alternable repr =>
-  Satisfiable repr tok => [tok] -> repr [tok]
+  TH.Lift tok => Eq tok => Show tok =>
+  Applicable repr => Alternable repr =>
+  Satisfiable tok repr => [tok] -> repr [tok]
 tokens = try . traverse token
 
-item :: Satisfiable repr tok => repr tok
-item = satisfy [] (H.const H..@ H.bool True)
-
 -- Composite Combinators
 -- someTill :: repr a -> repr b -> repr [a]
 -- someTill p end = negLook end *> (p <:> manyTill p end)
@@ -324,12 +355,12 @@ infixl 4 ~>
 -- Lift Operations
 liftA2 ::
  Applicable repr =>
H.Haskell (a -> b -> c) -> repr a -> repr b -> repr c
TermGrammar (a -> b -> c) -> repr a -> repr b -> repr c
 liftA2 f x = (<*>) (fmap f x)
 
 liftA3 ::
  Applicable repr =>
H.Haskell (a -> b -> c -> d) -> repr a -> repr b -> repr c -> repr d
TermGrammar (a -> b -> c -> d) -> repr a -> repr b -> repr c -> repr d
 liftA3 f a b c = liftA2 f a b <*> c
 
 -}
@@ -337,28 +368,28 @@ liftA3 f a b c = liftA2 f a b <*> c
 -- Parser Folds
 pfoldr ::
  Applicable repr => Foldable repr =>
H.Haskell (a -> b -> b) -> H.Haskell b -> repr a -> repr b
TermGrammar (a -> b -> b) -> TermGrammar b -> repr a -> repr b
 pfoldr f k p = chainPre (f <$> p) (pure k)
 
 pfoldr1 ::
  Applicable repr => Foldable repr =>
H.Haskell (a -> b -> b) -> H.Haskell b -> repr a -> repr b
TermGrammar (a -> b -> b) -> TermGrammar b -> repr a -> repr b
 pfoldr1 f k p = f <$> p <*> pfoldr f k p
 
 pfoldl ::
  Applicable repr => Foldable repr =>
H.Haskell (b -> a -> b) -> H.Haskell b -> repr a -> repr b
TermGrammar (b -> a -> b) -> TermGrammar b -> repr a -> repr b
 pfoldl f k p = chainPost (pure k) ((H.flip <$> pure f) <*> p)
 
 pfoldl1 ::
  Applicable repr => Foldable repr =>
H.Haskell (b -> a -> b) -> H.Haskell b -> repr a -> repr b
TermGrammar (b -> a -> b) -> TermGrammar b -> repr a -> repr b
 pfoldl1 f k p = chainPost (f <$> pure k <*> p) ((H.flip <$> pure f) <*> p)
 
 -- Chain Combinators
 chainl1' ::
  Applicable repr => Foldable repr =>
H.Haskell (a -> b) -> repr a -> repr (b -> a -> b) -> repr b
TermGrammar (a -> b) -> repr a -> repr (b -> a -> b) -> repr b
 chainl1' f p op = chainPost (f <$> p) (H.flip <$> op <*> p)
 
 chainl1 ::
@@ -377,13 +408,13 @@ chainr1' f p op = newRegister_ H.id $ \acc ->
 chainr1 :: repr a -> repr (a -> a -> a) -> repr a
 chainr1 = chainr1' H.id
 
-chainr :: repr a -> repr (a -> a -> a) -> H.Haskell a -> repr a
+chainr :: repr a -> repr (a -> a -> a) -> TermGrammar a -> repr a
 chainr p op x = option x (chainr1 p op)
 -}
 
 chainl ::
  Applicable repr => Alternable repr => Foldable repr =>
- repr a -> repr (a -> a -> a) -> H.Haskell a -> repr a
+ repr a -> repr (a -> a -> a) -> TermGrammar a -> repr a
 chainl p op x = option x (chainl1 p op)
 
 -- Derived Combinators