diff --git a/src/Toml/Parser/Core.hs b/src/Toml/Parser/Core.hs index 02589ddc..1d8cc51c 100644 --- a/src/Toml/Parser/Core.hs +++ b/src/Toml/Parser/Core.hs @@ -26,7 +26,7 @@ import Data.Void (Void) import Text.Megaparsec (Parsec, anySingle, eof, errorBundlePretty, match, parse, satisfy, try, ()) -import Text.Megaparsec.Char (alphaNumChar, char, digitChar, eol, hexDigitChar, space, space1, +import Text.Megaparsec.Char (alphaNumChar, char, digitChar, eol, hexDigitChar, octDigitChar, binDigitChar, space, space1, string, tab) import Text.Megaparsec.Char.Lexer (binary, float, hexadecimal, octal, signed, skipLineComment, symbol) diff --git a/src/Toml/Parser/Value.hs b/src/Toml/Parser/Value.hs index f76985ef..901d9694 100644 --- a/src/Toml/Parser/Value.hs +++ b/src/Toml/Parser/Value.hs @@ -22,9 +22,12 @@ import Control.Applicative.Combinators (between, count, option, optional, sepBy1 import Data.Fixed (Pico) import Data.Time (Day, LocalTime (..), TimeOfDay, ZonedTime (..), fromGregorianValid, makeTimeOfDayValid, minutesToTimeZone) +import Data.String (fromString) + import Text.Read (readMaybe) +import Text.Megaparsec (parseMaybe) -import Toml.Parser.Core (Parser, binary, char, digitChar, hexadecimal, lexeme, octal, sc, signed, +import Toml.Parser.Core (Parser, char, digitChar, hexDigitChar, octDigitChar, binDigitChar, hexadecimal, octal, binary, lexeme, sc, signed, string, text, try, ()) import Toml.Parser.String (textP) import Toml.Type (AnyValue, UValue (..), typeCheck) @@ -41,15 +44,33 @@ decimalP = zero <|> more check :: Maybe Integer -> Parser Integer check = maybe (fail "Not an integer") pure +-- | Parser for hexadecimal, octal and binary numbers : included parsing +numberP :: Parser Integer -> Parser Char -> String -> Parser Integer +numberP parseInteger parseDigit errorMessage = more + where + more :: Parser Integer + more = check =<< intValueMaybe . concat <$> sepBy1 (some parseDigit) (char '_') + + intValueMaybe :: String -> Maybe Integer + intValueMaybe = parseMaybe parseInteger . fromString + + check :: Maybe Integer -> Parser Integer + check = maybe (fail errorMessage) pure + + + -- | Parser for 'Integer' value. integerP :: Parser Integer integerP = lexeme (bin <|> oct <|> hex <|> dec) "integer" where bin, oct, hex, dec :: Parser Integer - bin = try (char '0' *> char 'b') *> binary "bin" - oct = try (char '0' *> char 'o') *> octal "oct" - hex = try (char '0' *> char 'x') *> hexadecimal "hex" + bin = try (char '0' *> char 'b') *> binaryP "bin" + oct = try (char '0' *> char 'o') *> octalP "oct" + hex = try (char '0' *> char 'x') *> hexadecimalP "hex" dec = signed sc decimalP "dec" + binaryP = numberP binary binDigitChar "Invalid binary number" + octalP = numberP octal octDigitChar "Invalid ocatl number" + hexadecimalP = numberP hexadecimal hexDigitChar "Invalid hexadecimal number" -- | Parser for 'Double' value. doubleP :: Parser Double diff --git a/test/Test/Toml/Parser/Integer.hs b/test/Test/Toml/Parser/Integer.hs index 3bc6a678..ad300145 100644 --- a/test/Test/Toml/Parser/Integer.hs +++ b/test/Test/Toml/Parser/Integer.hs @@ -81,3 +81,41 @@ integerSpecs = describe "integerP" $ do it "can parse numbers when hex digits are in both lowercase and uppercase" $ do parseInteger "0xAbCdEf" 0xAbCdEf parseInteger "0xaBcDeF" 0xaBcDeF + context "when there is underscore in hexadecimal, octal and binary representation" $ do + it "can parse numbers with underscore in hexadecimal representation" $ do + parseInteger "0xAb_Cd_Ef" 0xabcdef + parseInteger "0xA_bcd_ef" 0xabcdef + parseInteger "0x123_abc" 0x123abc + parseInteger "0xa_b_c_1_2_3" 0xabc123 + it "can't parse when underscore is between hexadecimal prefix and suffix" $ do + integerFailOn "0x_Abab_ca" + integerFailOn "0x_ababbac" + it "can parse numbers with underscore in octal representation" $ do + parseInteger "0o12_34_56" 0o123456 + parseInteger "0o1_2345_6" 0o123456 + parseInteger "0o76_54_21" 0o765421 + parseInteger "0o4_5_3_2_6" 0o45326 + it "can't parse when underscore is between octal prefix and suffix" $ do + integerFailOn "0o_123_4567" + integerFailOn "0o_1234567" + it "can parse numbers with underscore in binary representation" $ do + parseInteger "0b10_101_0" 42 + parseInteger "0b10_10_10" 42 + parseInteger "0b1_0_1" 5 + parseInteger "0b1_0" 2 + it "can't parse numbers when underscore is between binary prefix and suffix" $ do + integerFailOn "0b_10101_0" + integerFailOn "0b_101010" + it "doesn't parse underscore not followed by any numbers" $ do + integerFailOn "0b_" + integerFailOn "0o_" + integerFailOn "0x_" + it "doesn't parse when number is ending with underscore" $ do + integerFailOn "0b101_110_" + integerFailOn "0b10101_" + integerFailOn "0x1_23_daf_" + integerFailOn "0x1214adf_" + integerFailOn "0o1_15_41_" + integerFailOn "0o1215147_" + +