Commit 4a13c5b1 authored by Takenobu Tani's avatar Takenobu Tani Committed by Ben Gamari

Implement underscores in numeric literals (NumericUnderscores extension)

Implement the proposal of underscores in numeric literals.
Underscores in numeric literals are simply ignored.

The specification of the feature is available here:
https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/000
9-numeric-underscores.rst

For a discussion of the various choices:
https://github.com/ghc-proposals/ghc-proposals/pull/76

Implementation detail:

* Added dynamic flag
  * `NumericUnderscores` extension flag is added for this feature.

* Alex "Regular expression macros" in Lexer.x
  * Add `@numspc` (numeric spacer) macro to represent multiple
    underscores.
  * Modify `@decimal`, `@decimal`, `@binary`, `@octal`, `@hexadecimal`,
    `@exponent`, and `@bin_exponent` macros to include `@numspc`.

* Alex "Rules" in Lexer.x
  * To be simpler, we have only the definitions with underscores.
    And then we have a separate function (`tok_integral` and `tok_frac`)
    that validates the literals.

* Validation functions in Lexer.x
  * `tok_integral` and `tok_frac` functions validate
    whether contain underscores or not.
    If `NumericUnderscores` extensions are not enabled,
    check that there are no underscores.
  * `tok_frac` function is created by merging `strtoken` and
    `init_strtoken`.
  * `init_strtoken` is deleted. Because it is no longer used.

* Remove underscores from target literal string
  * `parseUnsignedInteger`, `readRational__`, and `readHexRational} use
    the customized `span'` function to remove underscores.

* Added Testcase
  * testcase for NumericUnderscores enabled.
      NumericUnderscores0.hs and NumericUnderscores1.hs
  * testcase for NumericUnderscores disabled.
      NoNumericUnderscores0.hs and NoNumericUnderscores1.hs
  * testcase to invalid pattern for NumericUnderscores enabled.
      NumericUnderscoresFail0.hs and NumericUnderscoresFail1.hs

Test Plan: `validate` including the above testcase

Reviewers: goldfire, bgamari

Reviewed By: bgamari

Subscribers: carter, rwbarton, thomie

GHC Trac Issues: #14473

Differential Revision: https://phabricator.haskell.org/D4235
parent 180ca65f
...@@ -4057,6 +4057,7 @@ xFlagsDeps = [ ...@@ -4057,6 +4057,7 @@ xFlagsDeps = [
flagSpec "MonomorphismRestriction" LangExt.MonomorphismRestriction, flagSpec "MonomorphismRestriction" LangExt.MonomorphismRestriction,
flagSpec "MultiParamTypeClasses" LangExt.MultiParamTypeClasses, flagSpec "MultiParamTypeClasses" LangExt.MultiParamTypeClasses,
flagSpec "MultiWayIf" LangExt.MultiWayIf, flagSpec "MultiWayIf" LangExt.MultiWayIf,
flagSpec "NumericUnderscores" LangExt.NumericUnderscores,
flagSpec "NPlusKPatterns" LangExt.NPlusKPatterns, flagSpec "NPlusKPatterns" LangExt.NPlusKPatterns,
flagSpec "NamedFieldPuns" LangExt.RecordPuns, flagSpec "NamedFieldPuns" LangExt.RecordPuns,
flagSpec "NamedWildCards" LangExt.NamedWildCards, flagSpec "NamedWildCards" LangExt.NamedWildCards,
......
...@@ -177,12 +177,14 @@ $docsym = [\| \^ \* \$] ...@@ -177,12 +177,14 @@ $docsym = [\| \^ \* \$]
@varsym = ($symbol # \:) $symbol* -- variable (operator) symbol @varsym = ($symbol # \:) $symbol* -- variable (operator) symbol
@consym = \: $symbol* -- constructor (operator) symbol @consym = \: $symbol* -- constructor (operator) symbol
@decimal = $decdigit+ -- See Note [Lexing NumericUnderscores extension] and #14473
@binary = $binit+ @numspc = _* -- numeric spacer (#14473)
@octal = $octit+ @decimal = $decdigit(@numspc $decdigit)*
@hexadecimal = $hexit+ @binary = $binit(@numspc $binit)*
@exponent = [eE] [\-\+]? @decimal @octal = $octit(@numspc $octit)*
@bin_exponent = [pP] [\-\+]? @decimal @hexadecimal = $hexit(@numspc $hexit)*
@exponent = @numspc [eE] [\-\+]? @decimal
@bin_exponent = @numspc [pP] [\-\+]? @decimal
@qual = (@conid \.)+ @qual = (@conid \.)+
@qvarid = @qual @varid @qvarid = @qual @varid
...@@ -190,8 +192,8 @@ $docsym = [\| \^ \* \$] ...@@ -190,8 +192,8 @@ $docsym = [\| \^ \* \$]
@qvarsym = @qual @varsym @qvarsym = @qual @varsym
@qconsym = @qual @consym @qconsym = @qual @consym
@floating_point = @decimal \. @decimal @exponent? | @decimal @exponent @floating_point = @numspc @decimal \. @decimal @exponent? | @numspc @decimal @exponent
@hex_floating_point = @hexadecimal \. @hexadecimal @bin_exponent? | @hexadecimal @bin_exponent @hex_floating_point = @numspc @hexadecimal \. @hexadecimal @bin_exponent? | @numspc @hexadecimal @bin_exponent
-- normal signed numerical literals can only be explicitly negative, -- normal signed numerical literals can only be explicitly negative,
-- not explicitly positive (contrast @exponent) -- not explicitly positive (contrast @exponent)
...@@ -485,24 +487,34 @@ $tab { warnTab } ...@@ -485,24 +487,34 @@ $tab { warnTab }
-- For the normal boxed literals we need to be careful -- For the normal boxed literals we need to be careful
-- when trying to be close to Haskell98 -- when trying to be close to Haskell98
-- Note [Lexing NumericUnderscores extension] (#14473)
--
-- NumericUnderscores extension allows underscores in numeric literals.
-- Multiple underscores are represented with @numspc macro.
-- To be simpler, we have only the definitions with underscores.
-- And then we have a separate function (tok_integral and tok_frac)
-- that validates the literals.
-- If extensions are not enabled, check that there are no underscores.
--
<0> { <0> {
-- Normal integral literals (:: Num a => a, from Integer) -- Normal integral literals (:: Num a => a, from Integer)
@decimal { tok_num positive 0 0 decimal } @decimal { tok_num positive 0 0 decimal }
0[bB] @binary / { ifExtension binaryLiteralsEnabled } { tok_num positive 2 2 binary } 0[bB] @numspc @binary / { ifExtension binaryLiteralsEnabled } { tok_num positive 2 2 binary }
0[oO] @octal { tok_num positive 2 2 octal } 0[oO] @numspc @octal { tok_num positive 2 2 octal }
0[xX] @hexadecimal { tok_num positive 2 2 hexadecimal } 0[xX] @numspc @hexadecimal { tok_num positive 2 2 hexadecimal }
@negative @decimal / { ifExtension negativeLiteralsEnabled } { tok_num negative 1 1 decimal } @negative @decimal / { ifExtension negativeLiteralsEnabled } { tok_num negative 1 1 decimal }
@negative 0[bB] @binary / { ifExtension negativeLiteralsEnabled `alexAndPred` @negative 0[bB] @numspc @binary / { ifExtension negativeLiteralsEnabled `alexAndPred`
ifExtension binaryLiteralsEnabled } { tok_num negative 3 3 binary } ifExtension binaryLiteralsEnabled } { tok_num negative 3 3 binary }
@negative 0[oO] @octal / { ifExtension negativeLiteralsEnabled } { tok_num negative 3 3 octal } @negative 0[oO] @numspc @octal / { ifExtension negativeLiteralsEnabled } { tok_num negative 3 3 octal }
@negative 0[xX] @hexadecimal / { ifExtension negativeLiteralsEnabled } { tok_num negative 3 3 hexadecimal } @negative 0[xX] @numspc @hexadecimal / { ifExtension negativeLiteralsEnabled } { tok_num negative 3 3 hexadecimal }
-- Normal rational literals (:: Fractional a => a, from Rational) -- Normal rational literals (:: Fractional a => a, from Rational)
@floating_point { strtoken tok_float } @floating_point { tok_frac 0 tok_float }
@negative @floating_point / { ifExtension negativeLiteralsEnabled } { strtoken tok_float } @negative @floating_point / { ifExtension negativeLiteralsEnabled } { tok_frac 0 tok_float }
0[xX] @hex_floating_point / { ifExtension hexFloatLiteralsEnabled } { strtoken tok_hex_float } 0[xX] @numspc @hex_floating_point / { ifExtension hexFloatLiteralsEnabled } { tok_frac 0 tok_hex_float }
@negative 0[xX]@hex_floating_point / { ifExtension hexFloatLiteralsEnabled `alexAndPred` @negative 0[xX] @numspc @hex_floating_point / { ifExtension hexFloatLiteralsEnabled `alexAndPred`
ifExtension negativeLiteralsEnabled } { strtoken tok_hex_float } ifExtension negativeLiteralsEnabled } { tok_frac 0 tok_hex_float }
} }
<0> { <0> {
...@@ -510,26 +522,26 @@ $tab { warnTab } ...@@ -510,26 +522,26 @@ $tab { warnTab }
-- It's simpler (and faster?) to give separate cases to the negatives, -- It's simpler (and faster?) to give separate cases to the negatives,
-- especially considering octal/hexadecimal prefixes. -- especially considering octal/hexadecimal prefixes.
@decimal \# / { ifExtension magicHashEnabled } { tok_primint positive 0 1 decimal } @decimal \# / { ifExtension magicHashEnabled } { tok_primint positive 0 1 decimal }
0[bB] @binary \# / { ifExtension magicHashEnabled `alexAndPred` 0[bB] @numspc @binary \# / { ifExtension magicHashEnabled `alexAndPred`
ifExtension binaryLiteralsEnabled } { tok_primint positive 2 3 binary } ifExtension binaryLiteralsEnabled } { tok_primint positive 2 3 binary }
0[oO] @octal \# / { ifExtension magicHashEnabled } { tok_primint positive 2 3 octal } 0[oO] @numspc @octal \# / { ifExtension magicHashEnabled } { tok_primint positive 2 3 octal }
0[xX] @hexadecimal \# / { ifExtension magicHashEnabled } { tok_primint positive 2 3 hexadecimal } 0[xX] @numspc @hexadecimal \# / { ifExtension magicHashEnabled } { tok_primint positive 2 3 hexadecimal }
@negative @decimal \# / { ifExtension magicHashEnabled } { tok_primint negative 1 2 decimal } @negative @decimal \# / { ifExtension magicHashEnabled } { tok_primint negative 1 2 decimal }
@negative 0[bB] @binary \# / { ifExtension magicHashEnabled `alexAndPred` @negative 0[bB] @numspc @binary \# / { ifExtension magicHashEnabled `alexAndPred`
ifExtension binaryLiteralsEnabled } { tok_primint negative 3 4 binary } ifExtension binaryLiteralsEnabled } { tok_primint negative 3 4 binary }
@negative 0[oO] @octal \# / { ifExtension magicHashEnabled } { tok_primint negative 3 4 octal } @negative 0[oO] @numspc @octal \# / { ifExtension magicHashEnabled } { tok_primint negative 3 4 octal }
@negative 0[xX] @hexadecimal \# / { ifExtension magicHashEnabled } { tok_primint negative 3 4 hexadecimal } @negative 0[xX] @numspc @hexadecimal \# / { ifExtension magicHashEnabled } { tok_primint negative 3 4 hexadecimal }
@decimal \# \# / { ifExtension magicHashEnabled } { tok_primword 0 2 decimal } @decimal \# \# / { ifExtension magicHashEnabled } { tok_primword 0 2 decimal }
0[bB] @binary \# \# / { ifExtension magicHashEnabled `alexAndPred` 0[bB] @numspc @binary \# \# / { ifExtension magicHashEnabled `alexAndPred`
ifExtension binaryLiteralsEnabled } { tok_primword 2 4 binary } ifExtension binaryLiteralsEnabled } { tok_primword 2 4 binary }
0[oO] @octal \# \# / { ifExtension magicHashEnabled } { tok_primword 2 4 octal } 0[oO] @numspc @octal \# \# / { ifExtension magicHashEnabled } { tok_primword 2 4 octal }
0[xX] @hexadecimal \# \# / { ifExtension magicHashEnabled } { tok_primword 2 4 hexadecimal } 0[xX] @numspc @hexadecimal \# \# / { ifExtension magicHashEnabled } { tok_primword 2 4 hexadecimal }
-- Unboxed floats and doubles (:: Float#, :: Double#) -- Unboxed floats and doubles (:: Float#, :: Double#)
-- prim_{float,double} work with signed literals -- prim_{float,double} work with signed literals
@signed @floating_point \# / { ifExtension magicHashEnabled } { init_strtoken 1 tok_primfloat } @signed @floating_point \# / { ifExtension magicHashEnabled } { tok_frac 1 tok_primfloat }
@signed @floating_point \# \# / { ifExtension magicHashEnabled } { init_strtoken 2 tok_primdouble } @signed @floating_point \# \# / { ifExtension magicHashEnabled } { tok_frac 2 tok_primdouble }
} }
-- Strings and chars are lexed by hand-written code. The reason is -- Strings and chars are lexed by hand-written code. The reason is
...@@ -943,11 +955,6 @@ strtoken :: (String -> Token) -> Action ...@@ -943,11 +955,6 @@ strtoken :: (String -> Token) -> Action
strtoken f span buf len = strtoken f span buf len =
return (L span $! (f $! lexemeToString buf len)) return (L span $! (f $! lexemeToString buf len))
init_strtoken :: Int -> (String -> Token) -> Action
-- like strtoken, but drops the last N character(s)
init_strtoken drop f span buf len =
return (L span $! (f $! lexemeToString buf (len-drop)))
begin :: Int -> Action begin :: Int -> Action
begin code _span _str _len = do pushLexState code; lexToken begin code _span _str _len = do pushLexState code; lexToken
...@@ -1277,8 +1284,12 @@ tok_integral :: (SourceText -> Integer -> Token) ...@@ -1277,8 +1284,12 @@ tok_integral :: (SourceText -> Integer -> Token)
-> Int -> Int -> Int -> Int
-> (Integer, (Char -> Int)) -> (Integer, (Char -> Int))
-> Action -> Action
tok_integral itint transint transbuf translen (radix,char_to_int) span buf len tok_integral itint transint transbuf translen (radix,char_to_int) span buf len = do
= return $ L span $ itint (SourceText $ lexemeToString buf len) numericUnderscores <- extension numericUnderscoresEnabled -- #14473
let src = lexemeToString buf len
if (not numericUnderscores) && ('_' `elem` src)
then failMsgP "Use NumericUnderscores to allow underscores in integer literals"
else return $ L span $ itint (SourceText src)
$! transint $ parseUnsignedInteger $! transint $ parseUnsignedInteger
(offsetBytes transbuf buf) (subtract translen len) radix char_to_int (offsetBytes transbuf buf) (subtract translen len) radix char_to_int
...@@ -1310,6 +1321,14 @@ octal = (8,octDecDigit) ...@@ -1310,6 +1321,14 @@ octal = (8,octDecDigit)
hexadecimal = (16,hexDigit) hexadecimal = (16,hexDigit)
-- readRational can understand negative rationals, exponents, everything. -- readRational can understand negative rationals, exponents, everything.
tok_frac :: Int -> (String -> Token) -> Action
tok_frac drop f span buf len = do
numericUnderscores <- extension numericUnderscoresEnabled -- #14473
let src = lexemeToString buf (len-drop)
if (not numericUnderscores) && ('_' `elem` src)
then failMsgP "Use NumericUnderscores to allow underscores in floating literals"
else return (L span $! (f $! src))
tok_float, tok_primfloat, tok_primdouble :: String -> Token tok_float, tok_primfloat, tok_primdouble :: String -> Token
tok_float str = ITrational $! readFractionalLit str tok_float str = ITrational $! readFractionalLit str
tok_hex_float str = ITrational $! readHexFractionalLit str tok_hex_float str = ITrational $! readHexFractionalLit str
...@@ -2221,6 +2240,7 @@ data ExtBits ...@@ -2221,6 +2240,7 @@ data ExtBits
| HexFloatLiteralsBit | HexFloatLiteralsBit
| TypeApplicationsBit | TypeApplicationsBit
| StaticPointersBit | StaticPointersBit
| NumericUnderscoresBit
deriving Enum deriving Enum
...@@ -2289,6 +2309,8 @@ typeApplicationEnabled :: ExtsBitmap -> Bool ...@@ -2289,6 +2309,8 @@ typeApplicationEnabled :: ExtsBitmap -> Bool
typeApplicationEnabled = xtest TypeApplicationsBit typeApplicationEnabled = xtest TypeApplicationsBit
staticPointersEnabled :: ExtsBitmap -> Bool staticPointersEnabled :: ExtsBitmap -> Bool
staticPointersEnabled = xtest StaticPointersBit staticPointersEnabled = xtest StaticPointersBit
numericUnderscoresEnabled :: ExtsBitmap -> Bool
numericUnderscoresEnabled = xtest NumericUnderscoresBit
-- PState for parsing options pragmas -- PState for parsing options pragmas
-- --
...@@ -2344,6 +2366,7 @@ mkParserFlags flags = ...@@ -2344,6 +2366,7 @@ mkParserFlags flags =
.|. PatternSynonymsBit `setBitIf` xopt LangExt.PatternSynonyms flags .|. PatternSynonymsBit `setBitIf` xopt LangExt.PatternSynonyms flags
.|. TypeApplicationsBit `setBitIf` xopt LangExt.TypeApplications flags .|. TypeApplicationsBit `setBitIf` xopt LangExt.TypeApplications flags
.|. StaticPointersBit `setBitIf` xopt LangExt.StaticPointers flags .|. StaticPointersBit `setBitIf` xopt LangExt.StaticPointers flags
.|. NumericUnderscoresBit `setBitIf` xopt LangExt.NumericUnderscores flags
setBitIf :: ExtBits -> Bool -> ExtsBitmap setBitIf :: ExtBits -> Bool -> ExtsBitmap
b `setBitIf` cond | cond = xbit b b `setBitIf` cond | cond = xbit b
......
...@@ -323,5 +323,6 @@ parseUnsignedInteger (StringBuffer buf _ cur) len radix char_to_int ...@@ -323,5 +323,6 @@ parseUnsignedInteger (StringBuffer buf _ cur) len radix char_to_int
= inlinePerformIO $ withForeignPtr buf $ \ptr -> return $! let = inlinePerformIO $ withForeignPtr buf $ \ptr -> return $! let
go i x | i == len = x go i x | i == len = x
| otherwise = case fst (utf8DecodeChar (ptr `plusPtr` (cur + i))) of | otherwise = case fst (utf8DecodeChar (ptr `plusPtr` (cur + i))) of
'_' -> go (i + 1) x -- skip "_" (#14473)
char -> go (i + 1) (x * radix + toInteger (char_to_int char)) char -> go (i + 1) (x * radix + toInteger (char_to_int char))
in go 0 0 in go 0 0
...@@ -1142,12 +1142,18 @@ readRational__ r = do ...@@ -1142,12 +1142,18 @@ readRational__ r = do
lexDecDigits = nonnull isDigit lexDecDigits = nonnull isDigit
lexDotDigits ('.':s) = return (span isDigit s) lexDotDigits ('.':s) = return (span' isDigit s)
lexDotDigits s = return ("",s) lexDotDigits s = return ("",s)
nonnull p s = do (cs@(_:_),t) <- return (span p s) nonnull p s = do (cs@(_:_),t) <- return (span' p s)
return (cs,t) return (cs,t)
span' _ xs@[] = (xs, xs)
span' p xs@(x:xs')
| x == '_' = span' p xs' -- skip "_" (#14473)
| p x = let (ys,zs) = span' p xs' in (x:ys,zs)
| otherwise = ([],xs)
readRational :: String -> Rational -- NB: *does* handle a leading "-" readRational :: String -> Rational -- NB: *does* handle a leading "-"
readRational top_s readRational top_s
= case top_s of = case top_s of
...@@ -1176,12 +1182,12 @@ readHexRational str = ...@@ -1176,12 +1182,12 @@ readHexRational str =
readHexRational__ :: String -> Maybe Rational readHexRational__ :: String -> Maybe Rational
readHexRational__ ('0' : x : rest) readHexRational__ ('0' : x : rest)
| x == 'X' || x == 'x' = | x == 'X' || x == 'x' =
do let (front,rest2) = span isHexDigit rest do let (front,rest2) = span' isHexDigit rest
guard (not (null front)) guard (not (null front))
let frontNum = steps 16 0 front let frontNum = steps 16 0 front
case rest2 of case rest2 of
'.' : rest3 -> '.' : rest3 ->
do let (back,rest4) = span isHexDigit rest3 do let (back,rest4) = span' isHexDigit rest3
guard (not (null back)) guard (not (null back))
let backNum = steps 16 frontNum back let backNum = steps 16 frontNum back
exp1 = -4 * length back exp1 = -4 * length back
...@@ -1201,13 +1207,18 @@ readHexRational__ ('0' : x : rest) ...@@ -1201,13 +1207,18 @@ readHexRational__ ('0' : x : rest)
mk :: Integer -> Int -> Rational mk :: Integer -> Int -> Rational
mk n e = fromInteger n * 2^^e mk n e = fromInteger n * 2^^e
dec cs = case span isDigit cs of dec cs = case span' isDigit cs of
(ds,"") | not (null ds) -> Just (steps 10 0 ds) (ds,"") | not (null ds) -> Just (steps 10 0 ds)
_ -> Nothing _ -> Nothing
steps base n ds = foldl' (step base) n ds steps base n ds = foldl' (step base) n ds
step base n d = base * n + fromIntegral (digitToInt d) step base n d = base * n + fromIntegral (digitToInt d)
span' _ xs@[] = (xs, xs)
span' p xs@(x:xs')
| x == '_' = span' p xs' -- skip "_" (#14473)
| p x = let (ys,zs) = span' p xs' in (x:ys,zs)
| otherwise = ([],xs)
readHexRational__ _ = Nothing readHexRational__ _ = Nothing
......
...@@ -553,6 +553,93 @@ by one bit left (negative) or right (positive). Here are some examples: ...@@ -553,6 +553,93 @@ by one bit left (negative) or right (positive). Here are some examples:
.. _numeric-underscores:
Numeric underscores
-------------------
.. ghc-flag:: -XNumericUnderscores
:shortdesc: Enable support for :ref:`numeric underscores <numeric-underscores>`.
:type: dynamic
:reverse: -XNoNumericUnderscores
:category:
:since: 8.6.1
Allow the use of underscores in numeric literals.
GHC allows for numeric literals to be given in decimal, octal, hexadecimal,
binary, or float notation.
The language extension :ghc-flag:`-XNumericUnderscores` adds support for expressing
underscores in numeric literals.
For instance, the numeric literal ``1_000_000`` will be parsed into
``1000000`` when :ghc-flag:`-XNumericUnderscores` is enabled.
That is, underscores in numeric literals are ignored when
:ghc-flag:`-XNumericUnderscores` is enabled.
See also :ghc-ticket:`14473`.
For example: ::
-- decimal
million = 1_000_000
billion = 1_000_000_000
lightspeed = 299_792_458
version = 8_04_1
date = 2017_12_31
-- hexadecimal
red_mask = 0xff_00_00
size1G = 0x3fff_ffff
-- binary
bit8th = 0b01_0000_0000
packbits = 0b1_11_01_0000_0_111
bigbits = 0b1100_1011__1110_1111__0101_0011
-- float
pi = 3.141_592_653_589_793
faraday = 96_485.332_89
avogadro = 6.022_140_857e+23
-- function
isUnderMillion = (< 1_000_000)
clip64M x
| x > 0x3ff_ffff = 0x3ff_ffff
| otherwise = x
test8bit x = (0b01_0000_0000 .&. x) /= 0
About validity: ::
x0 = 1_000_000 -- valid
x1 = 1__000000 -- valid
x2 = 1000000_ -- invalid
x3 = _1000000 -- invalid
e0 = 0.0001 -- valid
e1 = 0.000_1 -- valid
e2 = 0_.0001 -- invalid
e3 = _0.0001 -- invalid
e4 = 0._0001 -- invalid
e5 = 0.0001_ -- invalid
f0 = 1e+23 -- valid
f1 = 1_e+23 -- valid
f2 = 1__e+23 -- valid
f3 = 1e_+23 -- invalid
g0 = 1e+23 -- valid
g1 = 1e+_23 -- invalid
g2 = 1e+23_ -- invalid
h0 = 0xffff -- valid
h1 = 0xff_ff -- valid
h2 = 0x_ffff -- valid
h3 = 0x__ffff -- valid
h4 = _0xffff -- invalid
.. _pattern-guards: .. _pattern-guards:
Pattern guards Pattern guards
......
...@@ -133,4 +133,5 @@ data Extension ...@@ -133,4 +133,5 @@ data Extension
| StrictData | StrictData
| MonadFailDesugaring | MonadFailDesugaring
| EmptyDataDeriving | EmptyDataDeriving
| NumericUnderscores
deriving (Eq, Enum, Show, Generic) deriving (Eq, Enum, Show, Generic)
...@@ -39,7 +39,8 @@ expectedGhcOnlyExtensions :: [String] ...@@ -39,7 +39,8 @@ expectedGhcOnlyExtensions :: [String]
expectedGhcOnlyExtensions = ["RelaxedLayout", expectedGhcOnlyExtensions = ["RelaxedLayout",
"AlternativeLayoutRule", "AlternativeLayoutRule",
"AlternativeLayoutRuleTransitional", "AlternativeLayoutRuleTransitional",
"EmptyDataDeriving"] "EmptyDataDeriving",
"NumericUnderscores"]
expectedCabalOnlyExtensions :: [String] expectedCabalOnlyExtensions :: [String]
expectedCabalOnlyExtensions = ["Generics", expectedCabalOnlyExtensions = ["Generics",
......
{-# LANGUAGE NoNumericUnderscores #-}
-- Test for NumericUnderscores extension.
-- See Trac #14473
-- This is a testcase for integer literal
-- in NO NumericUnderscores extension.
module NoNumericUnderscores0 where
f :: Int -> ()
f 1_000 = ()
f _ = ()
NoNumericUnderscores0.hs:11:3: error:
Use NumericUnderscores to allow underscores in integer literals
{-# LANGUAGE NoNumericUnderscores #-}
-- Test for NumericUnderscores extension.
-- See Trac #14473
-- This is a testcase for floating literal
-- in NO NumericUnderscores extension.
module NoNumericUnderscores1 where
f :: Float -> ()
f 1_000.0_1 = ()
f _ = ()
NoNumericUnderscores1.hs:11:3: error:
Use NumericUnderscores to allow underscores in floating literals
{-# LANGUAGE NumericUnderscores #-}
-- Test for NumericUnderscores extension.
-- See Trac #14473
-- This is a testcase for invalid case of NumericUnderscores.
main :: IO ()
main = do
print [
-- integer
1000000_,
_1000000
]
NumericUnderscoresFail0.hs:9:5: error:
NumericUnderscoresFail0.hs:11:13: error:
NumericUnderscoresFail0.hs:11:20: error:
NumericUnderscoresFail0.hs:12:13: error:
{-# LANGUAGE NumericUnderscores #-}
-- Test for NumericUnderscores extension.
-- See Trac #14473
-- This is a testcase for invalid case of NumericUnderscores.
main :: IO ()
main = do
print [
-- float
0_.0001,
_0.0001,
0.0001_,
0._0001,
-- float with exponent
1e_+23,
1e+23_,
1e+_23
]
NumericUnderscoresFail1.hs:11:14: error:
NumericUnderscoresFail1.hs:13:19: error:
NumericUnderscoresFail1.hs:14:15: error:
NumericUnderscoresFail1.hs:17:14: error: Variable not in scope: e_
NumericUnderscoresFail1.hs:18:18: error:
NumericUnderscoresFail1.hs:19:14: error: Variable not in scope: e
NumericUnderscoresFail1.hs:19:16: error:
...@@ -103,3 +103,10 @@ test('T8501b', normal, compile_fail, ['']) ...@@ -103,3 +103,10 @@ test('T8501b', normal, compile_fail, [''])
test('T8501c', normal, compile_fail, ['']) test('T8501c', normal, compile_fail, [''])
test('T12610', normal, compile_fail, ['']) test('T12610', normal, compile_fail, [''])
test('T14588', normal, compile_fail, ['']) test('T14588', normal, compile_fail, [''])
test('NoNumericUnderscores0', normal, compile_fail, [''])
test('NoNumericUnderscores1', normal, compile_fail, [''])
test('NumericUnderscoresFail0',
grep_errmsg(r'^NumericUnderscoresFail0.hs:'), compile_fail, [''])
test('NumericUnderscoresFail1',
grep_errmsg(r'^NumericUnderscoresFail1.hs:'), compile_fail, [''])
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE BinaryLiterals #-}
{-# LANGUAGE HexFloatLiterals #-}
{-# LANGUAGE NegativeLiterals #-}
-- Test for NumericUnderscores extension.
-- See Trac #14473
-- This is a testcase for boxed literals.
main :: IO ()
main = do
-- Each case corresponds to the definition of Lexer.x
--
-- Normal integral literals
-- decimal
print [ 1_000_000 == 1000000,
1__0 == 10,
299_792_458 == 299792458,
8_04_1 == 8041,
2017_12_31 == 20171231
]
-- binary
print [ 0b01_0000_0000 == 0b0100000000,
0b1_11_01_0000_0_111 == 0b1110100000111,
0b1100_1011__1110_1111__0101_0011 ==
0b110010111110111101010011
]
-- octal
print [ 0o1_000_000 == 0o1000000,
0O1__0 == 0O10
]
-- hexadecimal
print [ 0x1_000_000 == 0x1000000,
0x1__0 == 0x10,
0xff_00_00 == 0xff0000,
0X3fff_ffff == 0x3fffffff
]
-- negative decimal
print [ -1_0 == -10
]
-- negative binary
print [ -0b1_0 == -0b10
]