authorgravatar for 57862114+momumi@users.noreply.github.commomumi <57862114+momumi@users.noreply.github.com> 2020-03-15 11:37:36+10:00
committergravatar for 57862114+momumi@users.noreply.github.commomumi <57862114+momumi@users.noreply.github.com> 2020-03-15 12:38:35+10:00
log7aac21c6f59b70deea6ced617f7b6a550e92bab4
treedf02c3553e9c5d66a858c73eaed7b25d723676df
parenteb4d313dbc406b37f6bfdd98988c88c3b8ed542e

allow `_` separators in number literals (stage 1)

* Underscores `_` may be placed between two digits in a int/float literal * Consecutive underscores are not allowed * Fixed parsing bug in exponents of hexadecimal float literals. Exponents should always be base 10, but hex characters would be parsed inside the exponent and everything after them would be ignored. eg: `0x1.0p1ab1` would be parsed as `0x1.0p1`.

6 files changed, 297 insertions(+), 92 deletions(-)

doc/langref.html.in+11
......@@ -885,6 +885,12 @@ const hex_int = 0xff;
885885const another_hex_int = 0xFF;
886886const octal_int = 0o755;
887887const binary_int = 0b11110000;
888
889// underscores may be placed between two digits as a visual separator
890const one_billion = 1_000_000_000;
891const binary_mask = 0b1_1111_1111;
892const permissions = 0o7_5_5;
893const big_address = 0xFF80_0000_0000_0000;
888894 {#code_end#}
889895 {#header_close#}
890896 {#header_open|Runtime Integer Values#}
......@@ -947,6 +953,11 @@ const yet_another = 123.0e+77;
947953const hex_floating_point = 0x103.70p-5;
948954const another_hex_float = 0x103.70;
949955const yet_another_hex_float = 0x103.70P-5;
956
957// underscores may be placed between two digits as a visual separator
958const lightspeed = 299_792_458.000_000;
959const nanosecond = 0.000_000_001;
960const more_hex = 0x1234_5678.9ABC_CDEFp-10;
950961 {#code_end#}
951962 <p>
952963 There is no syntax for NaN, infinity, or negative infinity. For these special values,
lib/std/special/compiler_rt/floatundisf.zig+19-19
......@@ -69,23 +69,23 @@ test "floatundisf" {
6969 test__floatundisf(0, 0.0);
7070 test__floatundisf(1, 1.0);
7171 test__floatundisf(2, 2.0);
72 test__floatundisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62F);
73 test__floatundisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62F);
74 test__floatundisf(0x8000008000000000, 0x1p+63F);
75 test__floatundisf(0x8000010000000000, 0x1.000002p+63F);
76 test__floatundisf(0x8000000000000000, 0x1p+63F);
77 test__floatundisf(0x8000000000000001, 0x1p+63F);
78 test__floatundisf(0xFFFFFFFFFFFFFFFE, 0x1p+64F);
79 test__floatundisf(0xFFFFFFFFFFFFFFFF, 0x1p+64F);
80 test__floatundisf(0x0007FB72E8000000, 0x1.FEDCBAp+50F);
81 test__floatundisf(0x0007FB72EA000000, 0x1.FEDCBAp+50F);
82 test__floatundisf(0x0007FB72EB000000, 0x1.FEDCBAp+50F);
83 test__floatundisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50F);
84 test__floatundisf(0x0007FB72EC000000, 0x1.FEDCBCp+50F);
85 test__floatundisf(0x0007FB72E8000001, 0x1.FEDCBAp+50F);
86 test__floatundisf(0x0007FB72E6000000, 0x1.FEDCBAp+50F);
87 test__floatundisf(0x0007FB72E7000000, 0x1.FEDCBAp+50F);
88 test__floatundisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50F);
89 test__floatundisf(0x0007FB72E4000001, 0x1.FEDCBAp+50F);
90 test__floatundisf(0x0007FB72E4000000, 0x1.FEDCB8p+50F);
72 test__floatundisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62);
73 test__floatundisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62);
74 test__floatundisf(0x8000008000000000, 0x1p+63);
75 test__floatundisf(0x8000010000000000, 0x1.000002p+63);
76 test__floatundisf(0x8000000000000000, 0x1p+63);
77 test__floatundisf(0x8000000000000001, 0x1p+63);
78 test__floatundisf(0xFFFFFFFFFFFFFFFE, 0x1p+64);
79 test__floatundisf(0xFFFFFFFFFFFFFFFF, 0x1p+64);
80 test__floatundisf(0x0007FB72E8000000, 0x1.FEDCBAp+50);
81 test__floatundisf(0x0007FB72EA000000, 0x1.FEDCBAp+50);
82 test__floatundisf(0x0007FB72EB000000, 0x1.FEDCBAp+50);
83 test__floatundisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50);
84 test__floatundisf(0x0007FB72EC000000, 0x1.FEDCBCp+50);
85 test__floatundisf(0x0007FB72E8000001, 0x1.FEDCBAp+50);
86 test__floatundisf(0x0007FB72E6000000, 0x1.FEDCBAp+50);
87 test__floatundisf(0x0007FB72E7000000, 0x1.FEDCBAp+50);
88 test__floatundisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50);
89 test__floatundisf(0x0007FB72E4000001, 0x1.FEDCBAp+50);
90 test__floatundisf(0x0007FB72E4000000, 0x1.FEDCB8p+50);
9191}
src/parse_f128.c+62-17
......@@ -165,22 +165,36 @@ static long long scanexp(struct MuslFILE *f, int pok)
165165 int x;
166166 long long y;
167167 int neg = 0;
168
168
169169 c = shgetc(f);
170170 if (c=='+' || c=='-') {
171171 neg = (c=='-');
172172 c = shgetc(f);
173173 if (c-'0'>=10U && pok) shunget(f);
174174 }
175 if (c-'0'>=10U) {
175 if (c-'0'>=10U && c!='_') {
176176 shunget(f);
177177 return LLONG_MIN;
178178 }
179 for (x=0; c-'0'<10U && x<INT_MAX/10; c = shgetc(f))
180 x = 10*x + c-'0';
181 for (y=x; c-'0'<10U && y<LLONG_MAX/100; c = shgetc(f))
182 y = 10*y + c-'0';
183 for (; c-'0'<10U; c = shgetc(f));
179 for (x=0; ; c = shgetc(f)) {
180 if (c=='_') {
181 continue;
182 } else if (c-'0'<10U && x<INT_MAX/10) {
183 x = 10*x + c-'0';
184 } else {
185 break;
186 }
187 }
188 for (y=x; ; c = shgetc(f)) {
189 if (c=='_') {
190 continue;
191 } else if (c-'0'<10U && y<LLONG_MAX/100) {
192 y = 10*y + c-'0';
193 } else {
194 break;
195 }
196 }
197 for (; c-'0'<10U || c=='_'; c = shgetc(f));
184198 shunget(f);
185199 return neg ? -y : y;
186200}
......@@ -450,16 +464,36 @@ static float128_t decfloat(struct MuslFILE *f, int c, int bits, int emin, int si
450464 j=0;
451465 k=0;
452466
453 /* Don't let leading zeros consume buffer space */
454 for (; c=='0'; c = shgetc(f)) gotdig=1;
467 /* Don't let leading zeros/underscores consume buffer space */
468 for (; ; c = shgetc(f)) {
469 if (c=='_') {
470 continue;
471 } else if (c=='0') {
472 gotdig=1;
473 } else {
474 break;
475 }
476 }
477
455478 if (c=='.') {
456479 gotrad = 1;
457 for (c = shgetc(f); c=='0'; c = shgetc(f)) gotdig=1, lrp--;
480 for (c = shgetc(f); ; c = shgetc(f)) {
481 if (c == '_') {
482 continue;
483 } else if (c=='0') {
484 gotdig=1;
485 lrp--;
486 } else {
487 break;
488 }
489 }
458490 }
459491
460492 x[0] = 0;
461 for (; c-'0'<10U || c=='.'; c = shgetc(f)) {
462 if (c == '.') {
493 for (; c-'0'<10U || c=='.' || c=='_'; c = shgetc(f)) {
494 if (c == '_') {
495 continue;
496 } else if (c == '.') {
463497 if (gotrad) break;
464498 gotrad = 1;
465499 lrp = dc;
......@@ -773,18 +807,29 @@ static float128_t hexfloat(struct MuslFILE *f, int bits, int emin, int sign, int
773807
774808 c = shgetc(f);
775809
776 /* Skip leading zeros */
777 for (; c=='0'; c = shgetc(f)) gotdig = 1;
810 /* Skip leading zeros/underscores */
811 for (; c=='0' || c=='_'; c = shgetc(f)) gotdig = 1;
778812
779813 if (c=='.') {
780814 gotrad = 1;
781815 c = shgetc(f);
782816 /* Count zeros after the radix point before significand */
783 for (rp=0; c=='0'; c = shgetc(f), rp--) gotdig = 1;
817 for (rp=0; ; c = shgetc(f)) {
818 if (c == '_') {
819 continue;
820 } else if (c == '0') {
821 gotdig = 1;
822 rp--;
823 } else {
824 break;
825 }
826 }
784827 }
785828
786 for (; c-'0'<10U || (c|32)-'a'<6U || c=='.'; c = shgetc(f)) {
787 if (c=='.') {
829 for (; c-'0'<10U || (c|32)-'a'<6U || c=='.' || c=='_'; c = shgetc(f)) {
830 if (c=='_') {
831 continue;
832 } else if (c=='.') {
788833 if (gotrad) break;
789834 rp = dc;
790835 gotrad = 1;
src/tokenizer.cpp+81-56
......@@ -177,10 +177,13 @@ enum TokenizeState {
177177 TokenizeStateSymbol,
178178 TokenizeStateZero, // "0", which might lead to "0x"
179179 TokenizeStateNumber, // "123", "0x123"
180 TokenizeStateNumberNoUnderscore, // "12_", "0x12_" next char must be digit
180181 TokenizeStateNumberDot,
181182 TokenizeStateFloatFraction, // "123.456", "0x123.456"
183 TokenizeStateFloatFractionNoUnderscore, // "123.45_", "0x123.45_"
182184 TokenizeStateFloatExponentUnsigned, // "123.456e", "123e", "0x123p"
183 TokenizeStateFloatExponentNumber, // "123.456e-", "123.456e5", "123.456e5e-5"
185 TokenizeStateFloatExponentNumber, // "123.456e7", "123.456e+7", "123.456e-7"
186 TokenizeStateFloatExponentNumberNoUnderscore, // "123.456e7_", "123.456e+7_", "123.456e-7_"
184187 TokenizeStateString,
185188 TokenizeStateStringEscape,
186189 TokenizeStateStringEscapeUnicodeStart,
......@@ -233,14 +236,10 @@ struct Tokenize {
233236 Token *cur_tok;
234237 Tokenization *out;
235238 uint32_t radix;
236 int32_t exp_add_amt;
237 bool is_exp_negative;
239 bool is_trailing_underscore;
238240 size_t char_code_index;
239241 bool unicode;
240242 uint32_t char_code;
241 int exponent_in_bin_or_dec;
242 BigInt specified_exponent;
243 BigInt significand;
244243 size_t remaining_code_units;
245244};
246245
......@@ -426,20 +425,16 @@ void tokenize(Buf *buf, Tokenization *out) {
426425 case '0':
427426 t.state = TokenizeStateZero;
428427 begin_token(&t, TokenIdIntLiteral);
428 t.is_trailing_underscore = false;
429429 t.radix = 10;
430 t.exp_add_amt = 1;
431 t.exponent_in_bin_or_dec = 0;
432430 bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, 0);
433 bigint_init_unsigned(&t.specified_exponent, 0);
434431 break;
435432 case DIGIT_NON_ZERO:
436433 t.state = TokenizeStateNumber;
437434 begin_token(&t, TokenIdIntLiteral);
435 t.is_trailing_underscore = false;
438436 t.radix = 10;
439 t.exp_add_amt = 1;
440 t.exponent_in_bin_or_dec = 0;
441437 bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, get_digit_value(c));
442 bigint_init_unsigned(&t.specified_exponent, 0);
443438 break;
444439 case '"':
445440 begin_token(&t, TokenIdStringLiteral);
......@@ -1189,17 +1184,15 @@ void tokenize(Buf *buf, Tokenization *out) {
11891184 switch (c) {
11901185 case 'b':
11911186 t.radix = 2;
1192 t.state = TokenizeStateNumber;
1187 t.state = TokenizeStateNumberNoUnderscore;
11931188 break;
11941189 case 'o':
11951190 t.radix = 8;
1196 t.exp_add_amt = 3;
1197 t.state = TokenizeStateNumber;
1191 t.state = TokenizeStateNumberNoUnderscore;
11981192 break;
11991193 case 'x':
12001194 t.radix = 16;
1201 t.exp_add_amt = 4;
1202 t.state = TokenizeStateNumber;
1195 t.state = TokenizeStateNumberNoUnderscore;
12031196 break;
12041197 default:
12051198 // reinterpret as normal number
......@@ -1208,9 +1201,27 @@ void tokenize(Buf *buf, Tokenization *out) {
12081201 continue;
12091202 }
12101203 break;
1204 case TokenizeStateNumberNoUnderscore:
1205 if (c == '_') {
1206 invalid_char_error(&t, c);
1207 break;
1208 } else if (get_digit_value(c) < t.radix) {
1209 t.is_trailing_underscore = false;
1210 t.state = TokenizeStateNumber;
1211 }
1212 // fall through
12111213 case TokenizeStateNumber:
12121214 {
1215 if (c == '_') {
1216 t.is_trailing_underscore = true;
1217 t.state = TokenizeStateNumberNoUnderscore;
1218 break;
1219 }
12131220 if (c == '.') {
1221 if (t.is_trailing_underscore) {
1222 invalid_char_error(&t, c);
1223 break;
1224 }
12141225 if (t.radix != 16 && t.radix != 10) {
12151226 invalid_char_error(&t, c);
12161227 }
......@@ -1222,13 +1233,18 @@ void tokenize(Buf *buf, Tokenization *out) {
12221233 invalid_char_error(&t, c);
12231234 }
12241235 t.state = TokenizeStateFloatExponentUnsigned;
1236 t.radix = 10; // exponent is always base 10
12251237 assert(t.cur_tok->id == TokenIdIntLiteral);
1226 bigint_init_bigint(&t.significand, &t.cur_tok->data.int_lit.bigint);
12271238 set_token_id(&t, t.cur_tok, TokenIdFloatLiteral);
12281239 break;
12291240 }
12301241 uint32_t digit_value = get_digit_value(c);
12311242 if (digit_value >= t.radix) {
1243 if (t.is_trailing_underscore) {
1244 invalid_char_error(&t, c);
1245 break;
1246 }
1247
12321248 if (is_symbol_char(c)) {
12331249 invalid_char_error(&t, c);
12341250 }
......@@ -1259,20 +1275,37 @@ void tokenize(Buf *buf, Tokenization *out) {
12591275 continue;
12601276 }
12611277 t.pos -= 1;
1262 t.state = TokenizeStateFloatFraction;
1278 t.state = TokenizeStateFloatFractionNoUnderscore;
12631279 assert(t.cur_tok->id == TokenIdIntLiteral);
1264 bigint_init_bigint(&t.significand, &t.cur_tok->data.int_lit.bigint);
12651280 set_token_id(&t, t.cur_tok, TokenIdFloatLiteral);
12661281 continue;
12671282 }
1283 case TokenizeStateFloatFractionNoUnderscore:
1284 if (c == '_') {
1285 invalid_char_error(&t, c);
1286 } else if (get_digit_value(c) < t.radix) {
1287 t.is_trailing_underscore = false;
1288 t.state = TokenizeStateFloatFraction;
1289 }
1290 // fall through
12681291 case TokenizeStateFloatFraction:
12691292 {
1293 if (c == '_') {
1294 t.is_trailing_underscore = true;
1295 t.state = TokenizeStateFloatFractionNoUnderscore;
1296 break;
1297 }
12701298 if (is_exponent_signifier(c, t.radix)) {
12711299 t.state = TokenizeStateFloatExponentUnsigned;
1300 t.radix = 10; // exponent is always base 10
12721301 break;
12731302 }
12741303 uint32_t digit_value = get_digit_value(c);
12751304 if (digit_value >= t.radix) {
1305 if (t.is_trailing_underscore) {
1306 invalid_char_error(&t, c);
1307 break;
1308 }
12761309 if (is_symbol_char(c)) {
12771310 invalid_char_error(&t, c);
12781311 }
......@@ -1282,46 +1315,47 @@ void tokenize(Buf *buf, Tokenization *out) {
12821315 t.state = TokenizeStateStart;
12831316 continue;
12841317 }
1285 t.exponent_in_bin_or_dec -= t.exp_add_amt;
1286 if (t.radix == 10) {
1287 // For now we use strtod to parse decimal floats, so we just have to get to the
1288 // end of the token.
1289 break;
1290 }
1291 BigInt digit_value_bi;
1292 bigint_init_unsigned(&digit_value_bi, digit_value);
12931318
1294 BigInt radix_bi;
1295 bigint_init_unsigned(&radix_bi, t.radix);
1296
1297 BigInt multiplied;
1298 bigint_mul(&multiplied, &t.significand, &radix_bi);
1299
1300 bigint_add(&t.significand, &multiplied, &digit_value_bi);
1301 break;
1319 // we use parse_f128 to generate the float literal, so just
1320 // need to get to the end of the token
13021321 }
1322 break;
13031323 case TokenizeStateFloatExponentUnsigned:
13041324 switch (c) {
13051325 case '+':
1306 t.is_exp_negative = false;
1307 t.state = TokenizeStateFloatExponentNumber;
1326 t.state = TokenizeStateFloatExponentNumberNoUnderscore;
13081327 break;
13091328 case '-':
1310 t.is_exp_negative = true;
1311 t.state = TokenizeStateFloatExponentNumber;
1329 t.state = TokenizeStateFloatExponentNumberNoUnderscore;
13121330 break;
13131331 default:
13141332 // reinterpret as normal exponent number
13151333 t.pos -= 1;
1316 t.is_exp_negative = false;
1317 t.state = TokenizeStateFloatExponentNumber;
1334 t.state = TokenizeStateFloatExponentNumberNoUnderscore;
13181335 continue;
13191336 }
13201337 break;
1338 case TokenizeStateFloatExponentNumberNoUnderscore:
1339 if (c == '_') {
1340 invalid_char_error(&t, c);
1341 } else if (get_digit_value(c) < t.radix) {
1342 t.is_trailing_underscore = false;
1343 t.state = TokenizeStateFloatExponentNumber;
1344 }
1345 // fall through
13211346 case TokenizeStateFloatExponentNumber:
13221347 {
1348 if (c == '_') {
1349 t.is_trailing_underscore = true;
1350 t.state = TokenizeStateFloatExponentNumberNoUnderscore;
1351 break;
1352 }
13231353 uint32_t digit_value = get_digit_value(c);
13241354 if (digit_value >= t.radix) {
1355 if (t.is_trailing_underscore) {
1356 invalid_char_error(&t, c);
1357 break;
1358 }
13251359 if (is_symbol_char(c)) {
13261360 invalid_char_error(&t, c);
13271361 }
......@@ -1331,21 +1365,9 @@ void tokenize(Buf *buf, Tokenization *out) {
13311365 t.state = TokenizeStateStart;
13321366 continue;
13331367 }
1334 if (t.radix == 10) {
1335 // For now we use strtod to parse decimal floats, so we just have to get to the
1336 // end of the token.
1337 break;
1338 }
1339 BigInt digit_value_bi;
1340 bigint_init_unsigned(&digit_value_bi, digit_value);
1341
1342 BigInt radix_bi;
1343 bigint_init_unsigned(&radix_bi, 10);
1344
1345 BigInt multiplied;
1346 bigint_mul(&multiplied, &t.specified_exponent, &radix_bi);
13471368
1348 bigint_add(&t.specified_exponent, &multiplied, &digit_value_bi);
1369 // we use parse_f128 to generate the float literal, so just
1370 // need to get to the end of the token
13491371 }
13501372 break;
13511373 case TokenizeStateSawDash:
......@@ -1399,6 +1421,9 @@ void tokenize(Buf *buf, Tokenization *out) {
13991421 case TokenizeStateStart:
14001422 case TokenizeStateError:
14011423 break;
1424 case TokenizeStateNumberNoUnderscore:
1425 case TokenizeStateFloatFractionNoUnderscore:
1426 case TokenizeStateFloatExponentNumberNoUnderscore:
14021427 case TokenizeStateNumberDot:
14031428 tokenize_error(&t, "unterminated number literal");
14041429 break;
test/compile_errors.zig+96
......@@ -389,6 +389,102 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
389389 "tmp.zig:5:29: error: invalid token: '.'",
390390 });
391391
392 cases.add("invalid underscore placement in float literal - 1",
393 \\fn main() void {
394 \\ var bad: f128 = 0._0;
395 \\})
396 , &[_][]const u8{
397 "tmp.zig:2:23: error: invalid character: '_'",
398 });
399
400 cases.add("invalid underscore placement in float literal - 2",
401 \\fn main() void {
402 \\ var bad: f128 = 0_.0;
403 \\})
404 , &[_][]const u8{
405 "tmp.zig:2:23: error: invalid character: '.'",
406 });
407
408 cases.add("invalid underscore placement in float literal - 3",
409 \\fn main() void {
410 \\ var bad: f128 = 0.0_;
411 \\})
412 , &[_][]const u8{
413 "tmp.zig:2:25: error: invalid character: ';'",
414 });
415
416 cases.add("invalid underscore placement in float literal - 4",
417 \\fn main() void {
418 \\ var bad: f128 = 1.0e_1;
419 \\})
420 , &[_][]const u8{
421 "tmp.zig:2:25: error: invalid character: '_'",
422 });
423
424 cases.add("invalid underscore placement in float literal - 5",
425 \\fn main() void {
426 \\ var bad: f128 = 1.0e+_1;
427 \\})
428 , &[_][]const u8{
429 "tmp.zig:2:26: error: invalid character: '_'",
430 });
431
432 cases.add("invalid underscore placement in float literal - 6",
433 \\fn main() void {
434 \\ var bad: f128 = 1.0e-_1;
435 \\})
436 , &[_][]const u8{
437 "tmp.zig:2:26: error: invalid character: '_'",
438 });
439
440 cases.add("invalid underscore placement in float literal - 7",
441 \\fn main() void {
442 \\ var bad: f128 = 1.0e-1_;
443 \\})
444 , &[_][]const u8{
445 "tmp.zig:2:28: error: invalid character: ';'",
446 });
447
448 cases.add("invalid underscore placement in float literal - 9",
449 \\fn main() void {
450 \\ var bad: f128 = 1__0.0e-1;
451 \\})
452 , &[_][]const u8{
453 "tmp.zig:2:23: error: invalid character: '_'",
454 });
455
456 cases.add("invalid underscore placement in float literal - 10",
457 \\fn main() void {
458 \\ var bad: f128 = 1.0__0e-1;
459 \\})
460 , &[_][]const u8{
461 "tmp.zig:2:25: error: invalid character: '_'",
462 });
463
464 cases.add("invalid underscore placement in float literal - 11",
465 \\fn main() void {
466 \\ var bad: f128 = 1.0e-1__0;
467 \\})
468 , &[_][]const u8{
469 "tmp.zig:2:28: error: invalid character: '_'",
470 });
471
472 cases.add("invalid underscore placement in float literal - 12",
473 \\fn main() void {
474 \\ var bad: f128 = 0_x0.0;
475 \\})
476 , &[_][]const u8{
477 "tmp.zig:2:23: error: invalid character: 'x'",
478 });
479
480 cases.add("invalid underscore placement in float literal - 13",
481 \\fn main() void {
482 \\ var bad: f128 = 0x_0.0;
483 \\})
484 , &[_][]const u8{
485 "tmp.zig:2:23: error: invalid character: '_'",
486 });
487
392488 cases.add("var args without c calling conv",
393489 \\fn foo(args: ...) void {}
394490 \\comptime {
test/stage1/behavior/math.zig+28
......@@ -411,6 +411,34 @@ test "quad hex float literal parsing accurate" {
411411 comptime S.doTheTest();
412412}
413413
414test "underscore separator parsing" {
415 expect(0_0_0_0 == 0);
416 expect(1_234_567 == 1234567);
417 expect(001_234_567 == 1234567);
418 expect(0_0_1_2_3_4_5_6_7 == 1234567);
419
420 expect(0b0_0_0_0 == 0);
421 expect(0b1010_1010 == 0b10101010);
422 expect(0b0000_1010_1010 == 0b10101010);
423 expect(0b1_0_1_0_1_0_1_0 == 0b10101010);
424
425 expect(0o0_0_0_0 == 0);
426 expect(0o1010_1010 == 0o10101010);
427 expect(0o0000_1010_1010 == 0o10101010);
428 expect(0o1_0_1_0_1_0_1_0 == 0o10101010);
429
430 expect(0x0_0_0_0 == 0);
431 expect(0x1010_1010 == 0x10101010);
432 expect(0x0000_1010_1010 == 0x10101010);
433 expect(0x1_0_1_0_1_0_1_0 == 0x10101010);
434
435 expect(123_456.789_000e1_0 == 123456.789000e10);
436 expect(0_1_2_3_4_5_6.7_8_9_0_0_0e0_0_1_0 == 123456.789000e10);
437
438 expect(0x1234_5678.9ABC_DEF0p-1_0 == 0x12345678.9ABCDEF0p-10);
439 expect(0x1_2_3_4_5_6_7_8.9_A_B_C_D_E_F_0p-0_0_0_1_0 == 0x12345678.9ABCDEF0p-10);
440}
441
414442test "hex float literal within range" {
415443 const a = 0x1.0p16383;
416444 const b = 0x0.1p16387;