3030
3131import Control.Lens qualified as Lens
3232import Control.Monad.State qualified as S
33- import Data.Char (isAlphaNum , isDigit , isSpace , ord , toLower )
33+ import Data.Char (digitToInt , isAlphaNum , isDigit , isHexDigit , isOctDigit , isSpace , ord , toLower )
3434import Data.Foldable qualified as Foldable
3535import Data.Functor.Classes (Show1 (.. ), showsPrec1 )
3636import Data.List qualified as List
@@ -492,21 +492,21 @@ lexemes eof =
492492
493493 numeric = bytes <|> otherbase <|> float <|> intOrNat
494494 where
495- intOrNat = P. try $ num <$> sign <*> LP. decimal
495+ intOrNat = P. try $ num <$> sign <*> (digitsToInteger 10 <$> digitsWithUnderscores " decimal digit " isDigit)
496496 float = do
497- _ <- P. try (P. lookAhead (sign >> ( LP. decimal :: P Int ) >> (char ' .' <|> char ' e' <|> char ' E' ))) -- commit after this
497+ _ <- P. try (P. lookAhead (sign >> digitsWithUnderscores " decimal digit " isDigit >> (char ' .' <|> char ' e' <|> char ' E' ))) -- commit after this
498498 start <- posP
499499 sign <- fromMaybe " " <$> sign
500- base <- P. takeWhile1P ( Just " base" ) isDigit
500+ base <- digitsWithUnderscores " base" isDigit
501501 decimals <-
502502 P. optional $
503503 let missingFractional = err start (MissingFractional $ base <> " ." )
504- in liftA2 (<>) (lit " ." ) (P. takeWhile1P ( Just " decimals" ) isDigit <|> missingFractional)
504+ in liftA2 (<>) (lit " ." ) (digitsWithUnderscores " decimals" isDigit <|> missingFractional)
505505 exp <- P. optional $ do
506506 e <- map toLower <$> (lit " e" <|> lit " E" )
507507 sign <- fromMaybe " " <$> optional (lit " +" <|> lit " -" )
508508 let missingExp = err start (MissingExponent $ base <> fromMaybe " " decimals <> e <> sign)
509- exp <- P. takeWhile1P ( Just " exponent" ) isDigit <|> missingExp
509+ exp <- digitsWithUnderscores " exponent" isDigit <|> missingExp
510510 pure $ e <> sign <> exp
511511 pure $ Numeric (sign <> base <> fromMaybe " " decimals <> fromMaybe " " exp )
512512
@@ -518,23 +518,33 @@ lexemes eof =
518518 Left _ -> err start (InvalidBytesLiteral $ " 0xs" <> s)
519519 Right bs -> pure (Bytes bs)
520520 otherbase = octal <|> hex <|> binary
521- octal = do
522- start <- posP
523- commitAfter2 sign (lit " 0o" ) $ \ sign _ ->
524- fmap (num sign) LP. octal <|> err start InvalidOctalLiteral
525- hex = do
526- start <- posP
527- commitAfter2 sign (lit " 0x" ) $ \ sign _ ->
528- fmap (num sign) LP. hexadecimal <|> err start InvalidHexLiteral
529- binary = do
521+ octal = baseWithPrefix " 0o" 8 " octal digit" isOctDigit InvalidOctalLiteral
522+ hex = baseWithPrefix " 0x" 16 " hexadecimal digit" isHexDigit InvalidHexLiteral
523+ binary = baseWithPrefix " 0b" 2 " binary digit" isBinDigit InvalidBinaryLiteral
524+
525+ baseWithPrefix :: String -> Int -> String -> (Char -> Bool ) -> Err -> P Lexeme
526+ baseWithPrefix prefix base label isValidDigit errType = do
530527 start <- posP
531- commitAfter2 sign (lit " 0b" ) $ \ sign _ ->
532- fmap (num sign) LP. binary <|> err start InvalidBinaryLiteral
528+ commitAfter2 sign (lit prefix) $ \ sign _ ->
529+ fmap (num sign) (P. try $ digitsToInteger base <$> digitsWithUnderscores label isValidDigit)
530+ <|> err start errType
533531
534532 num :: Maybe String -> Integer -> Lexeme
535533 num sign n = Numeric (fromMaybe " " sign <> show n)
536534 sign = P. optional (lit " +" <|> lit " -" )
537535
536+ isBinDigit :: Char -> Bool
537+ isBinDigit c = c == ' 0' || c == ' 1'
538+
539+ digitsWithUnderscores :: String -> (Char -> Bool ) -> P String
540+ digitsWithUnderscores label isValidDigit = do
541+ first <- P. takeWhile1P (Just label) isValidDigit
542+ rest <- many (char ' _' *> P. takeWhile1P (Just label) isValidDigit)
543+ pure $ mconcat $ first : rest
544+
545+ digitsToInteger :: Int -> String -> Integer
546+ digitsToInteger base = foldl' (\ acc c -> acc * toInteger base + toInteger (digitToInt c)) 0
547+
538548 hash = Hash <$> P. try shortHashP
539549
540550 reserved :: P [Token Lexeme ]
0 commit comments