gigaparsec-0.2.2.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')
type GenericNumeric :: *
data GenericNumeric = Generic { zeroAllowedDecimal :: Parsec Integer
, zeroAllowedHexadecimal :: Parsec Integer
, zeroAllowedOctal :: Parsec Integer
, zeroAllowedBinary :: Parsec Integer
, zeroNotAllowedDecimal :: Parsec Integer
, zeroNotAllowedHexadecimal :: Parsec Integer
, zeroNotAllowedOctal :: Parsec Integer
, zeroNotAllowedBinary :: Parsec Integer
-- FIXME: labels are configurable here
, plainDecimal :: NumericDesc -> Parsec Integer
, plainHexadecimal :: NumericDesc -> Parsec Integer
, plainOctal :: NumericDesc -> Parsec Integer
, plainBinary :: NumericDesc -> Parsec Integer
}
mkGeneric :: GenericNumeric
mkGeneric = Generic {..}
where ofRadix1 :: Integer -> Parsec Char -> Parsec Integer
ofRadix1 radix dig = ofRadix2 radix dig dig
ofRadix2 :: Integer -> Parsec Char -> Parsec Char -> Parsec Integer
ofRadix2 radix startDig dig =
foldl' (withDigit radix) 0 <$> (startDig <:> many dig) --TODO: improve
ofRadixBreak1 :: Integer -> Parsec Char -> Char -> Parsec Integer
ofRadixBreak1 radix dig = ofRadixBreak2 radix dig dig
ofRadixBreak2 :: Integer -> Parsec Char -> Parsec Char -> Char -> Parsec Integer
ofRadixBreak2 radix startDig dig breakChar =
foldl' (withDigit radix) 0 <$> (startDig <:> many (optional (char breakChar) *> 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 = ofRadix2 10 nonZeroDigit digit <|> secretZero
zeroNotAllowedHexadecimal = ofRadix2 16 nonZeroHexDigit hexDigit <|> secretZero
zeroNotAllowedOctal = ofRadix2 8 nonZeroOctDigit octDigit <|> secretZero
zeroNotAllowedBinary = ofRadix2 2 nonZeroBit bit <|> secretZero
plainDecimal NumericDesc{leadingZerosAllowed, literalBreakChar} = case literalBreakChar of
NoBreakChar | leadingZerosAllowed -> zeroAllowedDecimal
NoBreakChar -> zeroNotAllowedDecimal
BreakCharSupported c _ | leadingZerosAllowed -> ofRadixBreak1 10 digit c
BreakCharSupported c _ -> ofRadixBreak2 10 nonZeroDigit digit c <|> secretZero
plainHexadecimal NumericDesc{leadingZerosAllowed, literalBreakChar} = case literalBreakChar of
NoBreakChar | leadingZerosAllowed -> zeroAllowedDecimal
NoBreakChar -> zeroNotAllowedDecimal
BreakCharSupported c _ | leadingZerosAllowed -> ofRadixBreak1 16 hexDigit c
BreakCharSupported c _ -> ofRadixBreak2 16 nonZeroHexDigit hexDigit c <|> secretZero
plainOctal NumericDesc{leadingZerosAllowed, literalBreakChar} = case literalBreakChar of
NoBreakChar | leadingZerosAllowed -> zeroAllowedDecimal
NoBreakChar -> zeroNotAllowedDecimal
BreakCharSupported c _ | leadingZerosAllowed -> ofRadixBreak1 8 octDigit c
BreakCharSupported c _ -> ofRadixBreak2 8 nonZeroOctDigit octDigit c <|> secretZero
plainBinary NumericDesc{leadingZerosAllowed, literalBreakChar} = case literalBreakChar of
NoBreakChar | leadingZerosAllowed -> zeroAllowedDecimal
NoBreakChar -> zeroNotAllowedDecimal
BreakCharSupported c _ | leadingZerosAllowed -> ofRadixBreak1 2 bit c
BreakCharSupported c _ -> ofRadixBreak2 2 nonZeroBit bit c <|> secretZero
withDigit :: Integer -> Integer -> Char -> Integer
withDigit radix n d = n * radix + fromIntegral (digitToInt d)