diff --git a/cmd/wcc/lex.c b/cmd/wcc/lex.c index 440e9df3..886906bf 100644 --- a/cmd/wcc/lex.c +++ b/cmd/wcc/lex.c @@ -181,6 +181,9 @@ static u64 parseint(const char *s, u64 n, int base, int *ok) { u64 v = 0; + u64 b = (u64)base; + u64 cutoff = (u64)~0ULL / b; + u64 cutlim = (u64)~0ULL % b; int got = 0; for (u64 i = 0; i < n; i++) { int c = (unsigned char)s[i]; @@ -192,9 +195,11 @@ parseint(const char *s, u64 n, int base, int *ok) else if (c >= 'A' && c <= 'F') d = c - 'A' + 10; else { *ok = 0; return 0; } if (d >= base) { *ok = 0; return 0; } - /* overflow? cheap check */ - if (v > (u64)~0ULL / (u64)base) { *ok = 0; return 0; } - v = v * (u64)base + (u64)d; + if (v > cutoff || (v == cutoff && (u64)d > cutlim)) { + *ok = 0; + return 0; + } + v = v * b + (u64)d; got = 1; } *ok = got; @@ -369,6 +374,7 @@ lexnum(Lex *l, Pos start) /* A typed suffix must be glued (no whitespace) to the digits. */ if (isidstart(lpeek(l, 0))) { u64 sb = l->pos; + i32 sc = l->col; while (isidcont(lpeek(l, 0))) lget(l); u64 sl = l->pos - sb; const char *names[] = { @@ -387,9 +393,8 @@ lexnum(Lex *l, Pos start) if (match) { t.tsuffix = astrndup(l->a, l->src + sb, sl); } else { - /* not a known suffix — rewind so the run becomes a - * separate token. */ l->pos = sb; + l->col = sc; } } return t; diff --git a/lib/ww/syntax/lex.ww b/lib/ww/syntax/lex.ww index fa215d57..d9400657 100644 --- a/lib/ww/syntax/lex.ww +++ b/lib/ww/syntax/lex.ww @@ -269,6 +269,9 @@ fn skipws(l: *lex) bool = { fn parseint(p: *u8, n: u64, base: i32, ok: *bool) u64 = { let v: u64 = 0u64; + let b: u64 = base: u64; + let cutoff: u64 = ~0u64 / b; + let cutlim: u64 = ~0u64 % b; let got: bool = false; let i: u64 = 0u64; for (i < n) { @@ -294,8 +297,11 @@ fn parseint(p: *u8, n: u64, base: i32, ok: *bool) u64 = { }; if (d < 0) { *ok = false; return 0u64; }; if (d >= base) { *ok = false; return 0u64; }; - if (v > ~0u64 / (base: u64)) { *ok = false; return 0u64; }; - v = v * (base: u64) + (d: u64); + if (v > cutoff || (v == cutoff && (d: u64) > cutlim)) { + *ok = false; + return 0u64; + }; + v = v * b + (d: u64); got = true; i += 1u64; }; @@ -551,6 +557,7 @@ fn lexnum(l: *lex, start: *pos, out: *tok) void = { if (pc >= 0) { if (isidstart(pc: rune)) { let sb: u64 = l.lpos; + let sc: i32 = l.col; for (true) { let cc: i32 = lpeek(l, 0u64); if (cc < 0) { break; }; @@ -591,6 +598,7 @@ fn lexnum(l: *lex, start: *pos, out: *tok) void = { out.tsuffix = strings.dup(view); } else { l.lpos = sb; + l.col = sc; }; }; }; diff --git a/lib/ww/syntax/tok_test.ww b/lib/ww/syntax/tok_test.ww index 69d00e66..442bdc98 100644 --- a/lib/ww/syntax/tok_test.ww +++ b/lib/ww/syntax/tok_test.ww @@ -309,3 +309,32 @@ fn checkfloat(src: str, want: u64) void = { checkfloat("1.0e308", 0x7FE1CCF385EBC8A0u64); checkfloat("2.225073858507202e-308", 0x0010000000000001u64); }; + +@test fn intfinalband() void = { + let src: str = "18446744073709551616"; + let l: lex; + lexinit(&l, "t", src.ptr, src.len: u64); + let t: tok; + lexnext(&l, &t); + assert(!(t.kind != tkind.TK_ERR)); + assert(!(l.errs != 1)); +}; + +@test fn suffixrewindcols() void = { + let src: str = "1foo bar"; + let l: lex; + lexinit(&l, "t", src.ptr, src.len: u64); + let t: tok; + lexnext(&l, &t); + assert(!(t.kind != tkind.TK_INT)); + assert(!(t.col != 1)); + assert(!(t.uval != 1u64)); + lexnext(&l, &t); + assert(!(t.kind != tkind.TK_IDENT)); + assert(!(t.col != 2)); + assert(!(t.text != "foo")); + lexnext(&l, &t); + assert(!(t.kind != tkind.TK_IDENT)); + assert(!(t.col != 6)); + assert(!(t.text != "bar")); +}; diff --git a/test/wcc/100_lex.c b/test/wcc/100_lex.c index 38d13dd7..e549d666 100644 --- a/test/wcc/100_lex.c +++ b/test/wcc/100_lex.c @@ -80,6 +80,7 @@ static const struct row rows[] = { { "0xDE_AD_BE_EF", "INT(3735928559)" }, { "0b1010", "INT(10)" }, { "0o777", "INT(511)" }, + { "18446744073709551616", "ERR(18446744073709551616)" }, { "3.14", "FLOAT(3.14)" }, { "1.5e3", "FLOAT(1500)" }, @@ -149,6 +150,28 @@ runerr(const char *src) return ok; } +static int +runrewind(void) +{ + const char *src = "1foo bar"; + Arena *a = newarena(); + Lex l; + lexinit(&l, a, "", src, strlen(src)); + Tok n = lexnext(&l); + Tok f = lexnext(&l); + Tok b = lexnext(&l); + int ok = n.kind == TK_INT && n.pos.col == 1 && n.v.uval == 1 + && f.kind == TK_IDENT && f.pos.col == 2 + && strcmp(f.text, "foo") == 0 + && b.kind == TK_IDENT && b.pos.col == 6 + && strcmp(b.text, "bar") == 0; + if (!ok) + fprintf(stderr, "suffix rewind columns: want 1,2,6 got %d,%d,%d\n", + n.pos.col, f.pos.col, b.pos.col); + freearena(a); + return ok; +} + static const char *const errrows[] = { "'\\uZ'", /* unexpected rune scanning for escape */ "\"\\u00g0\"", /* non-hex digit inside a string escape */ @@ -169,6 +192,8 @@ main(void) fail++; } } + if (!runrewind()) + fail++; for (size_t i = 0; i < sizeof errrows / sizeof errrows[0]; i++) { if (!runerr(errrows[i])) { fprintf(stderr, "errrow %zu failed\n", i); diff --git a/test/wcc/data/r989_intoverflow_dec_overflow/case.ww b/test/wcc/data/r989_intoverflow_dec_overflow/case.ww index 3ce1e089..b61f43d5 100644 --- a/test/wcc/data/r989_intoverflow_dec_overflow/case.ww +++ b/test/wcc/data/r989_intoverflow_dec_overflow/case.ww @@ -1,7 +1,7 @@ //ww:error "bad integer literal" -// 989_intoverflow_reject dec_overflow: a 21-digit decimal exceeds u64. +// Exact final-digit band: MAX/10 with 6 greater than MAX%10. package main; export fn main() i32 = { - let x: u64 = 99999999999999999999u64; + let x: u64 = 18446744073709551616u64; return x: i32; };