1 {-# LANGUAGE FlexibleContexts #-}
2 module Hcompta.Lib.Parsec where
4 import qualified Data.Char
5 import qualified Data.List
6 import qualified Text.Parsec as R
7 import Text.Parsec (Stream, ParsecT, (<|>), (<?>))
11 -- | Like 'Text.Parsec.choice' but with 'Text.Parsec.try' on each case.
12 choice_try :: Stream s m t => [ParsecT s u m a] -> ParsecT s u m a
13 choice_try = Data.List.foldr (\a -> (<|>) (R.try a)) R.parserZero
14 -- choice_try = R.choice . Data.List.map R.try
16 -- | Like 'Text.Parsec.sepBy' but without parsing an ending separator.
22 many_separated p sep =
23 many1_separated p sep <|> return []
25 -- | Like 'Text.Parsec.sepBy1' but without parsing an ending separator.
31 many1_separated p sep = do
33 xs <- R.many (R.try (sep >> p))
35 -- (:) <$> p <*> R.many (R.try (sep >> p))
37 -- | Make a 'Text.Parsec.ParsecT' also return its user state.
41 -> ParsecT s u m (a, u)
49 -- | Return the 'Integer' obtained by multiplying the given digits
50 -- with the power of the given base respective to their rank.
53 -> [Char] -- ^ Digits (MUST be recognised by 'Data.Char.digitToInt').
55 integer_of_digits base =
56 Data.List.foldl (\x d ->
57 base*x + toInteger (Data.Char.digitToInt d)) 0
59 decimal :: Stream s m Char => ParsecT s u m Integer
60 decimal = integer 10 R.digit
61 hexadecimal :: Stream s m Char => ParsecT s u m Integer
62 hexadecimal = R.oneOf "xX" >> integer 16 R.hexDigit
63 octal :: Stream s m Char => ParsecT s u m Integer
64 octal = R.oneOf "oO" >> integer 8 R.octDigit
66 -- | Parse an 'Integer'.
67 integer :: Stream s m t
70 -> ParsecT s u m Integer
71 integer base digit = do
72 digits <- R.many1 digit
73 let n = integer_of_digits base digits
78 -- | Return 'True' if and only if the given 'Char' is an horizontal space.
79 is_space_horizontal :: Char -> Bool
80 is_space_horizontal c = c /= '\n' && c /= '\r' && Data.Char.isSpace c
82 space_horizontal :: Stream s m Char => ParsecT s u m Char
83 {-# INLINEABLE space_horizontal #-}
84 space_horizontal = R.satisfy is_space_horizontal <?> "horizontal-space"
86 new_line :: Stream s m Char => ParsecT s u m ()
87 {-# INLINEABLE new_line #-}
88 new_line = ((R.try (R.string "\r\n") <|> R.try (R.string "\n")) >> return ()) <?> "newline"