gigaparsec-0.2.4.0: src/Text/Gigaparsec/Internal/Token/Generic.hs
{-# LANGUAGE Safe #-}
{-# LANGUAGE OverloadedLists, NamedFieldPuns #-}
{-# OPTIONS_HADDOCK hide #-}
module Text.Gigaparsec.Internal.Token.Generic (module Text.Gigaparsec.Internal.Token.Generic) where
import Text.Gigaparsec ((<|>), many, Parsec, ($>), (<:>))
import Text.Gigaparsec.Char (satisfy, char, digit, hexDigit, octDigit, bit)
import Text.Gigaparsec.Combinator (optional)
import Text.Gigaparsec.Errors.Combinator ((<?>), hide)
import Text.Gigaparsec.Token.Descriptions (
BreakCharDesc(BreakCharSupported, NoBreakChar),
NumericDesc(NumericDesc, literalBreakChar, leadingZerosAllowed)
)
import Data.Char (isDigit, isHexDigit, isOctDigit, digitToInt)
import Data.List (foldl')
import Text.Gigaparsec.Token.Errors (ErrorConfig (labelNumericBreakChar))
import Text.Gigaparsec.Internal.Token.Errors (annotate, LabelConfig)
type GenericNumeric :: *
data GenericNumeric = Generic { zeroAllowedDecimal :: LabelConfig -> Parsec Integer
, zeroAllowedHexadecimal :: LabelConfig -> Parsec Integer
, zeroAllowedOctal :: LabelConfig -> Parsec Integer
, zeroAllowedBinary :: LabelConfig -> Parsec Integer
, zeroNotAllowedDecimal :: LabelConfig -> Parsec Integer
, zeroNotAllowedHexadecimal :: LabelConfig -> Parsec Integer
, zeroNotAllowedOctal :: LabelConfig -> Parsec Integer
, zeroNotAllowedBinary :: LabelConfig -> Parsec Integer
, plainDecimal :: NumericDesc -> LabelConfig -> Parsec Integer
, plainHexadecimal :: NumericDesc -> LabelConfig -> Parsec Integer
, plainOctal :: NumericDesc -> LabelConfig -> Parsec Integer
, plainBinary :: NumericDesc -> LabelConfig -> Parsec Integer
}
mkGeneric :: ErrorConfig -> GenericNumeric
mkGeneric !err = Generic {..}
where ofRadix1 :: Integer -> Parsec Char -> LabelConfig -> Parsec Integer
ofRadix1 radix dig = ofRadix2 radix dig dig
ofRadix2 :: Integer -> Parsec Char -> Parsec Char -> LabelConfig -> Parsec Integer
ofRadix2 radix startDig dig label =
foldl' (withDigit radix) 0 <$> (startDig <:> many (annotate label dig)) --TODO: improve
ofRadixBreak1 :: Integer -> Parsec Char -> Char -> LabelConfig -> Parsec Integer
ofRadixBreak1 radix dig = ofRadixBreak2 radix dig dig
ofRadixBreak2 :: Integer -> Parsec Char -> Parsec Char -> Char -> LabelConfig -> Parsec Integer
ofRadixBreak2 radix startDig dig breakChar label =
foldl' (withDigit radix) 0 <$> (startDig <:> many (optional (annotate (labelNumericBreakChar err) (annotate label (char breakChar))) *> annotate label dig)) --TODO: improve
nonZeroDigit = satisfy (\c -> isDigit c && c /= '0') <?> ["digit"]
nonZeroHexDigit = satisfy (\c -> isHexDigit c && c /= '0') <?> ["hexadecimal digit"]
nonZeroOctDigit = satisfy (\c -> isOctDigit c && c /= '0') <?> ["octal digit"]
nonZeroBit = char '1' <?> ["bit"]
-- why secret? so that the above digits can be marked as digits without "non-zero or zero digit"
secretZero :: Parsec Integer
secretZero = hide (char '0') $> 0
zeroAllowedDecimal = ofRadix1 10 digit
zeroAllowedHexadecimal = ofRadix1 16 hexDigit
zeroAllowedOctal = ofRadix1 8 octDigit
zeroAllowedBinary = ofRadix1 2 bit
zeroNotAllowedDecimal label = ofRadix2 10 nonZeroDigit digit label <|> secretZero
zeroNotAllowedHexadecimal label = ofRadix2 16 nonZeroHexDigit hexDigit label <|> secretZero
zeroNotAllowedOctal label = ofRadix2 8 nonZeroOctDigit octDigit label <|> secretZero
zeroNotAllowedBinary label = ofRadix2 2 nonZeroBit bit label <|> secretZero
plainDecimal NumericDesc{leadingZerosAllowed, literalBreakChar} label = case literalBreakChar of
NoBreakChar | leadingZerosAllowed -> zeroAllowedDecimal label
NoBreakChar -> zeroNotAllowedDecimal label
BreakCharSupported c _ | leadingZerosAllowed -> ofRadixBreak1 10 digit c label
BreakCharSupported c _ -> ofRadixBreak2 10 nonZeroDigit digit c label <|> secretZero
plainHexadecimal NumericDesc{leadingZerosAllowed, literalBreakChar} label = case literalBreakChar of
NoBreakChar | leadingZerosAllowed -> zeroAllowedDecimal label
NoBreakChar -> zeroNotAllowedDecimal label
BreakCharSupported c _ | leadingZerosAllowed -> ofRadixBreak1 16 hexDigit c label
BreakCharSupported c _ -> ofRadixBreak2 16 nonZeroHexDigit hexDigit c label <|> secretZero
plainOctal NumericDesc{leadingZerosAllowed, literalBreakChar} label = case literalBreakChar of
NoBreakChar | leadingZerosAllowed -> zeroAllowedDecimal label
NoBreakChar -> zeroNotAllowedDecimal label
BreakCharSupported c _ | leadingZerosAllowed -> ofRadixBreak1 8 octDigit c label
BreakCharSupported c _ -> ofRadixBreak2 8 nonZeroOctDigit octDigit c label <|> secretZero
plainBinary NumericDesc{leadingZerosAllowed, literalBreakChar} label = case literalBreakChar of
NoBreakChar | leadingZerosAllowed -> zeroAllowedDecimal label
NoBreakChar -> zeroNotAllowedDecimal label
BreakCharSupported c _ | leadingZerosAllowed -> ofRadixBreak1 2 bit c label
BreakCharSupported c _ -> ofRadixBreak2 2 nonZeroBit bit c label <|> secretZero
withDigit :: Integer -> Integer -> Char -> Integer
withDigit radix n d = n * radix + fromIntegral (digitToInt d)