From 9860ade97c5922dba44f229f8a83264bf0a0d345 Mon Sep 17 00:00:00 2001
From: Hans-Peter Deifel <hpd@hpdeifel.de>
Date: Tue, 17 Jul 2018 17:43:03 +0200
Subject: [PATCH] Generalize functor expression parser

This allows to parser more complex expressions in a single
functor (like the polynomial functor), instead of only unary and
binary parsers.

The downside is that implementing a functor parser got more complex:
Each functor now has to call the `inner` parser by itself and also
handle the case where the inner parser succeeds and the outer parser
fails separately.

This design doesn't sound particularly elegant, but there is no
obvious other solution.
---
 src/MA/FunctorExpression/Parser.hs       | 91 ++++++++++++------------
 src/MA/Functors/Bag.hs                   |  2 +-
 src/MA/Functors/Distribution.hs          |  2 +-
 src/MA/Functors/FixedProduct.hs          |  2 +-
 src/MA/Functors/MonoidValued.hs          |  4 +-
 src/MA/Functors/Powerset.hs              |  2 +-
 src/MA/Functors/Product.hs               |  2 +-
 src/MA/Parser.hs                         |  4 +-
 tests/MA/FunctorExpression/ParserSpec.hs |  9 +--
 9 files changed, 58 insertions(+), 60 deletions(-)

diff --git a/src/MA/FunctorExpression/Parser.hs b/src/MA/FunctorExpression/Parser.hs
index e305491..b786df0 100644
--- a/src/MA/FunctorExpression/Parser.hs
+++ b/src/MA/FunctorExpression/Parser.hs
@@ -1,6 +1,11 @@
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 module MA.FunctorExpression.Parser
   ( FunctorParser(..)
+  , prefix
+  , postfix
+  , infixR
   , transParser
   , Precedence(..)
   , functorsParser
@@ -8,6 +13,7 @@ module MA.FunctorExpression.Parser
   ) where
 
 import           Data.List
+import           Control.Monad
 
 import           Text.Megaparsec
 import qualified Text.Megaparsec.Expr as Expr
@@ -18,60 +24,51 @@ import           MA.Parser.Lexer
 import           MA.Parser.Types
 
 data FunctorParser f where
-  Prefix :: (forall a m. ParserT m (a -> f a)) -> FunctorParser f
-  Postfix :: (forall a m. ParserT m (a -> f a)) -> FunctorParser f
-  InfixN :: (forall  a m. ParserT m (a -> a -> f a )) -> FunctorParser f
-  InfixL :: (forall  a m. ParserT m (a -> a -> f a )) -> FunctorParser f
-  InfixR :: (forall  a m. ParserT m (a -> a -> f a )) -> FunctorParser f
+  FunctorParser :: (forall a m. MonadParser m => m a -> m (Either a (f a))) -> FunctorParser f
+
+prefix :: (forall a m. MonadParser m => m (a -> f a)) -> FunctorParser f
+prefix parser = FunctorParser $ \inner -> do
+  (parser >>= \f -> Right . f <$> inner) <|> (Left <$> inner)
+
+postfix :: (forall a m. MonadParser m => m (a -> f a)) -> FunctorParser f
+postfix parser = FunctorParser $ \inner -> do
+  i <- inner
+  (parser >>= \f -> return (Right (f i))) <|> return (Left i)
+
+infixR :: (forall a m. MonadParser m => m (a -> a -> f a)) -> FunctorParser f
+infixR parser = FunctorParser $ \inner -> do
+  l <- inner
+  let p = do
+        f <- parser
+        r <- inner
+        return (Right (f l r))
+
+  p <|> return (Left l)
 
 transParser :: (forall a. f a -> g a) -> FunctorParser f -> FunctorParser g
-transParser natTrans (Prefix p) = Prefix (fmap natTrans <$> p)
-transParser natTrans (Postfix p) = Postfix (fmap natTrans <$> p)
-transParser natTrans (InfixN p) = InfixN ((\f a b -> natTrans (f a b)) <$> p)
-transParser natTrans (InfixL p) = InfixL ((\f a b -> natTrans (f a b)) <$> p)
-transParser natTrans (InfixR p) = InfixR ((\f a b -> natTrans (f a b)) <$> p)
+transParser natTrans (FunctorParser p) =
+  -- yeah! Tripple fmap
+  FunctorParser (fmap (fmap (fmap natTrans))  p)
 
 newtype Precedence = Precedence Int
   deriving (Num, Bounded, Enum, Integral, Real, Ord, Eq, Show)
 
-functorsParser :: [[FunctorParser f]] -> ParserT m (FunctorExpression f Precedence)
-functorsParser functors =
-  try spaceConsumer *> Expr.makeExprParser term (makeOpTable functors)
-  >>= checkForFunction
+functorsParser :: forall f m. [FunctorParser f] -> ParserT m (FunctorExpression f Precedence)
+functorsParser functors = try spaceConsumer *> parseLevel (zip (reverse functors) [1..]) >>= checkForFunctor
   where
-    term = parens (functorsParser functors) <|> (symbol "X" >> return Variable)
+    parseLevel :: [(FunctorParser f, Precedence)] -> ParserT m (FunctorExpression f Precedence)
+    parseLevel ((FunctorParser f, i):functors) = f (parseLevel functors) >>= \case
+      Left a -> return a
+      Right fa -> return (Functor i fa)
+    parseLevel [] = variable <|> parens (functorsParser functors)
 
-checkForFunction ::
-     FunctorExpression f Precedence -> ParserT m (FunctorExpression f Precedence)
-checkForFunction (Functor a b) = return (Functor a b)
-checkForFunction Variable = fail "Functor expression needs at least one function symbol"
-
-makeOpTable ::
-     [[FunctorParser f]]
-  -> [[Expr.Operator (ParserT m) (FunctorExpression f Precedence)]]
-makeOpTable table =
-  let tableWithPrecedence = zip (reverse [1 .. genericLength table]) table
-   in map
-        (\(precedence, functors) -> map (funcToOp precedence) functors)
-        tableWithPrecedence
-  where
-    funcToOp ::
-         Precedence
-      -> FunctorParser f
-      -> Expr.Operator (ParserT m) (FunctorExpression f Precedence)
-    funcToOp precedence funcParser =
-      case funcParser of
-        Prefix p -> Expr.Prefix ((Functor precedence .) <$> p)
-        Postfix p -> Expr.Postfix ((Functor precedence .) <$> p)
-        InfixN p -> Expr.InfixN (mkInfix precedence <$> p)
-        InfixL p -> Expr.InfixL (mkInfix precedence <$> p)
-        InfixR p -> Expr.InfixR (mkInfix precedence <$> p)
+    variable :: ParserT m (FunctorExpression f Precedence)
+    variable = symbol "X" >> return Variable
 
-    mkInfix precedence f a b = Functor precedence (f a b)
+checkForFunctor ::
+     FunctorExpression f Precedence -> ParserT m (FunctorExpression f Precedence)
+checkForFunctor (Functor a b) = return (Functor a b)
+checkForFunctor Variable = fail "Functor expression needs at least one function symbol"
 
-parseFunctorExpression ::
-     [[FunctorParser f]]
-  -> String
-  -> Text
-  -> Either ParseErr (FunctorExpression f Precedence)
-parseFunctorExpression functors = parse (functorsParser functors)
+parseFunctorExpression :: [[FunctorParser f]] -> String -> Text -> Either ParseErr (FunctorExpression f Precedence)
+parseFunctorExpression = parse . functorsParser . concat
diff --git a/src/MA/Functors/Bag.hs b/src/MA/Functors/Bag.hs
index bce4dc6..55cc667 100644
--- a/src/MA/Functors/Bag.hs
+++ b/src/MA/Functors/Bag.hs
@@ -32,7 +32,7 @@ newtype Bag a = Bag a
   deriving (Functor,Foldable,Traversable)
 
 bag :: FunctorParser Bag
-bag = Prefix ((L.symbol "B" <|> L.symbol "Ɓ") >> pure Bag)
+bag = prefix ((L.symbol "B" <|> L.symbol "Ɓ") >> pure Bag)
 
 type instance Label Bag = Label (MonoidValued Int)
 type instance Weight Bag = Weight (MonoidValued Int)
diff --git a/src/MA/Functors/Distribution.hs b/src/MA/Functors/Distribution.hs
index f748a8a..46b3157 100644
--- a/src/MA/Functors/Distribution.hs
+++ b/src/MA/Functors/Distribution.hs
@@ -25,7 +25,7 @@ newtype Distribution x = Distribution x
   deriving (Functor, Foldable, Traversable)
 
 distribution :: FunctorParser Distribution
-distribution = Prefix ((L.symbol "D" <|> L.symbol "Ɗ") >> pure Distribution)
+distribution = prefix ((L.symbol "D" <|> L.symbol "Ɗ") >> pure Distribution)
 
 type instance Label Distribution = Label (MonoidValued Double)
 type instance Weight Distribution = Weight (MonoidValued Double)
diff --git a/src/MA/Functors/FixedProduct.hs b/src/MA/Functors/FixedProduct.hs
index 6f6126e..98d4649 100644
--- a/src/MA/Functors/FixedProduct.hs
+++ b/src/MA/Functors/FixedProduct.hs
@@ -23,7 +23,7 @@ data FixedProduct a = FixedProduct (Vector Text) a
   deriving (Show,Functor,Foldable,Traversable)
 
 fixedproduct :: FunctorParser FixedProduct
-fixedproduct = Prefix $ do
+fixedproduct = prefix $ do
   labels <- L.braces (L.name `sepBy` L.comma)
   void $ L.symbol "x"
   return (FixedProduct (V.fromList labels))
diff --git a/src/MA/Functors/MonoidValued.hs b/src/MA/Functors/MonoidValued.hs
index cd9b5a3..f309038 100644
--- a/src/MA/Functors/MonoidValued.hs
+++ b/src/MA/Functors/MonoidValued.hs
@@ -35,11 +35,11 @@ deriving instance Foldable (MonoidValued m)
 deriving instance Traversable (MonoidValued m)
 
 intValued :: FunctorParser (MonoidValued Int)
-intValued = Prefix
+intValued = prefix
   ((L.symbol "Z" <|> L.symbol "ℤ") >> L.symbol "^" >> pure IntValued)
 
 realValued :: FunctorParser (MonoidValued Double)
-realValued = Prefix
+realValued = prefix
   ((L.symbol "R" <|> L.symbol "ℝ") >> L.symbol "^" >> pure RealValued)
 
 type instance Label (MonoidValued m) = m
diff --git a/src/MA/Functors/Powerset.hs b/src/MA/Functors/Powerset.hs
index 3ef7950..fb1ed9a 100644
--- a/src/MA/Functors/Powerset.hs
+++ b/src/MA/Functors/Powerset.hs
@@ -18,7 +18,7 @@ newtype Powerset a = Powerset a
   deriving (Show,Functor,Foldable,Traversable)
 
 powerset :: FunctorParser Powerset
-powerset = Prefix ((L.symbol "P" <|> L.symbol "Ƥ") >> pure Powerset)
+powerset = prefix ((L.symbol "P" <|> L.symbol "Ƥ") >> pure Powerset)
 
 -- | No edge labels
 type instance Label Powerset = ()
diff --git a/src/MA/Functors/Product.hs b/src/MA/Functors/Product.hs
index 29ee9d9..2c7430a 100644
--- a/src/MA/Functors/Product.hs
+++ b/src/MA/Functors/Product.hs
@@ -18,7 +18,7 @@ data Product a = Product a a
   deriving (Show, Functor, Foldable, Traversable)
 
 product :: FunctorParser Product
-product = InfixR $ do
+product = infixR $ do
   void $ L.symbol "×"
   return Product
 
diff --git a/src/MA/Parser.hs b/src/MA/Parser.hs
index 51b22c9..f8164cc 100644
--- a/src/MA/Parser.hs
+++ b/src/MA/Parser.hs
@@ -6,7 +6,6 @@ module MA.Parser
   , readCoalgebraFromStdin
   ) where
 
-import           Control.Monad
 import           Data.Bifunctor
 import           Data.Proxy
 
@@ -29,7 +28,8 @@ coalgebraParser ::
      (Traversable f, ParseMorphism f)
   => [[FunctorParser f]]
   -> Parser (SymbolTable, Encoding (Label (Desorted f)) (H1 (Desorted f)))
-coalgebraParser = fmap annotateSorts . functorsParser >=> morphismsParser
+coalgebraParser functors =
+  functorsParser (concat functors) >>= return . annotateSorts >>= morphismsParser
 
 -- TODO: Needs better name
 type TheFunctor = Desorted SomeFunctor
diff --git a/tests/MA/FunctorExpression/ParserSpec.hs b/tests/MA/FunctorExpression/ParserSpec.hs
index e0c3042..f651dad 100644
--- a/tests/MA/FunctorExpression/ParserSpec.hs
+++ b/tests/MA/FunctorExpression/ParserSpec.hs
@@ -63,6 +63,7 @@ parseFunctorExpressionSpec = describe "parseFunctorExpression" $ do
   it "handles whitespace at the begining" $
     parsing `shouldSucceedOn` "  Pre X"
 
+
 -- A simple prefix parser
 
 data PrefixFunctor a = PrefixFunctor a
@@ -73,11 +74,11 @@ instance Eq1 PrefixFunctor where
 
 instance Show1 PrefixFunctor where
   liftShowsPrec show' _ prec (PrefixFunctor x) rest
-    | prec > 10 = "(PrefixFunctor" ++ show' 11 x (rest ++ ")")
-    | otherwise = "PrefixFunctor" ++ show' 11 x rest
+    | prec > 10 = "(PrefixFunctor " ++ show' 11 x (rest ++ ")")
+    | otherwise = "PrefixFunctor " ++ show' 11 x rest
 
 prefixParser :: FunctorParser PrefixFunctor
-prefixParser = Prefix (symbol "Pre" >> return PrefixFunctor)
+prefixParser = prefix (symbol "Pre" >> return PrefixFunctor)
 
 -- A simple postfix parser
 
@@ -93,7 +94,7 @@ instance Show1 PostfixFunctor where
     | otherwise = "PostfixFunctor " ++ show' 11 x rest
 
 postfixParser :: FunctorParser PostfixFunctor
-postfixParser = Postfix (symbol "Post" >> return PostfixFunctor)
+postfixParser = postfix (symbol "Post" >> return PostfixFunctor)
 
 -- List of functors
 
-- 
GitLab