Correction : Filter : [Filter_Path_Section_Many].
[comptalang.git] / lib / Hcompta / Lib / Parsec.hs
index 8456c6500de3d63c87b4a693ff3978e28ae1caa4..b09318891cbedd0570469d4a17aa0b2bcddefd3b 100644 (file)
@@ -1,22 +1,42 @@
 {-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE Rank2Types #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 module Hcompta.Lib.Parsec where
 
+import           Control.Monad (void)
+import           Control.Monad.Trans.Class (lift)
 import           Control.Monad.Trans.State (StateT(..), get, put)
-import           Control.Monad.Trans.Class (lift, MonadTrans(..))
+import           Data.Bool
+import           Data.Char (Char)
 import qualified Data.Char
+import           Data.Either
+import           Data.Eq (Eq(..))
+import           Data.Foldable (elem, notElem, foldl')
+import           Data.Functor (Functor(..))
 import           Data.Functor.Identity (Identity(..))
 import qualified Data.List
-import qualified Text.Parsec as R hiding (satisfy, string, char, space, newline, crlf)
+import           Data.Maybe (Maybe(..))
+import           Data.Ord (Ord(..))
+import           Data.String (String)
+import           Prelude ( ($), (.)
+                         , Integer
+                         , Integral(..)
+                         , Monad(..)
+                         , Num(..)
+                         , Show(..)
+                         , const
+                         , seq
+                         )
 import           Text.Parsec (Stream, ParsecT, (<|>), (<?>))
+import qualified Text.Parsec as R hiding (satisfy, string, char, space, newline, crlf)
 import qualified Text.Parsec.Pos as R
 
 -- * Combinators
 
 -- | Like 'Text.Parsec.choice' but with 'Text.Parsec.try' on each case.
 choice_try :: Stream s m t => [ParsecT s u m a] -> ParsecT s u m a
-choice_try = Data.List.foldr (\a -> (<|>) (R.try a)) R.parserZero
+choice_try = Data.List.foldr ((<|>) . R.try) R.parserZero
 -- choice_try = R.choice . Data.List.map R.try
 
 -- | Like 'Text.Parsec.sepBy' but without parsing an ending separator.
@@ -61,7 +81,7 @@ updatePosChar pos c =
 
 -- | Like 'R.updatePosString' but using fixed 'updatePosChar'.
 updatePosString :: R.SourcePos -> String -> R.SourcePos
-updatePosString pos s = foldl updatePosChar pos s
+updatePosString = foldl' updatePosChar
 
 -- | Like 'R.updatePosChar' but using fixed 'updatePosChar'.
 satisfy :: (Stream s m Char) => (Char -> Bool) -> ParsecT s u m Char
@@ -71,7 +91,7 @@ satisfy f = R.tokenPrim (\c -> show [c])
 
 -- | Like 'R.string' but using fixed 'updatePosString'.
 string :: (Stream s m Char) => String -> ParsecT s u m String
-string s = R.tokens show updatePosString s
+string = R.tokens show updatePosString
 
 -- | Like 'R.char' but using fixed 'satisfy'.
 char :: (Stream s m Char) => Char -> ParsecT s u m Char
@@ -83,11 +103,11 @@ anyChar = satisfy (const True)
 
 -- | Like 'R.oneOf' but using fixed 'satisfy'.
 oneOf :: (Stream s m Char) => [Char] -> ParsecT s u m Char
-oneOf cs = satisfy (\c -> elem c cs)
+oneOf cs = satisfy (`elem` cs)
 
 -- | Like 'R.noneOf' but using fixed 'satisfy'.
 noneOf :: (Stream s m Char) => [Char] -> ParsecT s u m Char
-noneOf cs = satisfy (\c -> not (elem c cs))
+noneOf cs = satisfy (`notElem` cs)
 
 -- ** Custom 'ParsecT' errors
 
@@ -102,6 +122,11 @@ data Error e
  |   Error_At     R.SourcePos [Error e] -- ^ 'Error' raised by 'runParserT_with_Error_fail'.
  deriving (Show)
 
+instance Functor Error where
+       fmap _ (Error_Parser e)     = Error_Parser e
+       fmap f (Error_Custom pos e) = Error_Custom pos (f e)
+       fmap f (Error_At pos es)    = Error_At pos $ fmap (fmap f) es
+
 -- | Like 'R.parserFail'
 --   but fail with given custom error.
 fail_with :: (Stream s (Error_State e m) Char, Monad m)
@@ -109,14 +134,14 @@ fail_with :: (Stream s (Error_State e m) Char, Monad m)
 fail_with msg err = do
        (sp, se) <- lift get
        rp <- R.getPosition -- NOTE: reported position
-       _ <- ((R.anyChar >> return ()) <|> R.eof)
+       _ <- (void R.anyChar <|> R.eof)
         -- NOTE: somehow commits that this character has an error
        p <- R.getPosition -- NOTE: compared position
        case () of
         _ | R.sourceLine   p  > R.sourceLine   sp ||
            (R.sourceLine   p == R.sourceLine   sp &&
             R.sourceColumn p  > R.sourceColumn sp)
-          -> lift $ put (p, Error_Custom rp err:[])
+          -> lift $ put (p, [Error_Custom rp err])
         _ | R.sourceLine   p == R.sourceLine   sp &&
             R.sourceColumn p == R.sourceColumn sp
           -> lift $ put (p, Error_Custom rp err:se)
@@ -125,7 +150,7 @@ fail_with msg err = do
 
 -- | Like 'R.runParserT' but return as error an 'Error' list
 runParserT_with_Error
- :: (Stream s (Error_State e m) Char, Monad m, Show r, Show e)
+ :: (Stream s (Error_State e m) Char, Monad m)
  => ParsecT s u (Error_State e m) r
  -> u -> R.SourceName -> s
  -> m (Either [Error e] r)
@@ -141,13 +166,13 @@ runParserT_with_Error p u sn s = do
         Left pe | R.sourceLine   (R.errorPos pe) == R.sourceLine   sp &&
                   R.sourceColumn (R.errorPos pe) == R.sourceColumn sp
                 -> return $ Left $ se -- NOTE: prefer custom errors
-        Left pe -> return $ Left $ Error_Parser pe:[]
+        Left pe -> return $ Left $ [Error_Parser pe]
         Right x -> return $ Right x
 
 -- | Like 'R.runParserT_with_Error'
 --   but applied on the 'Identity' monad.
 runParser_with_Error
- :: (Stream s (Error_State e Identity) Char, Show r, Show e)
+ :: (Stream s (Error_State e Identity) Char)
  => ParsecT s u (Error_State e Identity) r
  -> u -> R.SourceName -> s
  -> Either [Error e] r
@@ -158,14 +183,15 @@ runParser_with_Error p u sn s =
 -- | Like 'R.runParserT_with_Error'
 --   but propagate any failure to a calling 'ParsecT' monad.
 runParserT_with_Error_fail ::
- ( Stream s1 (Error_State e m) Char
- , Stream s (Error_State e (ParsecT s1 u1 (Error_State e m))) Char
- , Monad m, Show r, Show e )
+ ( Stream s1 (Error_State ee m) Char
+ , Stream s (Error_State e (ParsecT s1 u1 (Error_State ee m))) Char
+ , Monad m )
  => String
- -> ParsecT s u (Error_State e (ParsecT s1 u1 (Error_State e m))) r
+ -> (Error e -> Error ee)
+ -> ParsecT s u (Error_State e (ParsecT s1 u1 (Error_State ee m))) r
  -> u -> R.SourceName -> s
- -> ParsecT s1 u1 (Error_State e m) r
-runParserT_with_Error_fail msg p u sn s = do
+ -> ParsecT s1 u1 (Error_State ee m) r
+runParserT_with_Error_fail msg map_ko p u sn s = do
        r <- runParserT_with_Error p u sn s
        case r of
         Right ok -> return ok
@@ -173,9 +199,36 @@ runParserT_with_Error_fail msg p u sn s = do
                rpos <- R.getPosition
                _ <- commit_position
                pos <- R.getPosition
-               lift $ put (pos, Error_At rpos ko:[])
+               lift $ put (pos, [Error_At rpos (fmap map_ko ko)])
                fail msg
-       where commit_position = (R.anyChar >> return ()) <|> R.eof
+       where commit_position = (void R.anyChar) <|> R.eof
+
+-- ** Mapping inner monad
+
+-- | Like an instance Control.Functor.Morph.'MFunctor' of @R.ParsecT s u@
+--   but also requiring a second 'Monad' constraint
+--   on the returned base container.
+--
+-- NOTE: This code is not used by Hcompta, still is left here
+-- because it was not trivial to write it,
+-- so eventually it may help others.
+hoist :: (Monad m, Monad n) => (forall a. m a -> n a) -> ParsecT s u m b -> ParsecT s u n b
+hoist nat m = R.mkPT $ \s -> do
+       c <- nat $ R.runParsecT m s
+       return $ case c of
+                R.Consumed mrep -> R.Consumed $ nat mrep
+                R.Empty    mrep -> R.Empty    $ nat mrep
+
+-- | Map the type of a 'StateT'.
+--
+-- NOTE: This code is not used by Hcompta, still is left here
+-- because it was not trivial to write it,
+-- so eventually it may help others.
+smap :: Monad m => (s1 -> s0) -> (s0 -> s1) -> StateT s0 m a -> StateT s1 m a
+smap s1_to_s0 s0_to_s1 st =
+       StateT (\s1_begin -> do
+               (a, s0_end) <- runStateT st (s1_to_s0 s1_begin)
+               return (a, s0_to_s1 s0_end))
 
 -- * Numbers
 
@@ -186,7 +239,7 @@ integer_of_digits
  -> [Char]  -- ^ Digits (MUST be recognised by 'Data.Char.digitToInt').
  -> Integer
 integer_of_digits base =
-       Data.List.foldl (\x d ->
+       foldl' (\x d ->
                base*x + toInteger (Data.Char.digitToInt d)) 0
 
 decimal :: Stream s m Char => ParsecT s u m Integer
@@ -234,4 +287,4 @@ space_horizontal = satisfy is_space_horizontal <?> "horizontal-space"
 
 new_line :: Stream s m Char => ParsecT s u m ()
 {-# INLINEABLE new_line #-}
-new_line = ((R.try (string "\r\n") <|> R.try (string "\n")) >> return ()) <?> "newline"
+new_line = (void (R.try (string "\r\n") <|> R.try (string "\n"))) <?> "newline"