harec

[hare] Hare compiler, written in C11 for POSIX OSs
Log | Files | Refs | README | LICENSE

commit c76479f8797dc039116561447c8e16bdc23bc4c5
parent a1021d0577c993b65da4a19438def0cc7368ef64
Author: Bor Grošelj Simić <bgs@turminal.net>
Date:   Fri, 10 Mar 2023 02:01:56 +0100

rewrite lex_literal

The old version grew another boolean variable for the state with every new
feature that was added. The state is now condensed into base information
plus 4 flags that work together nicely. Actual conversion to numbers is
also greatly simplified and the memory issues that the old one had are
avoided.

Signed-off-by: Bor Grošelj Simić <bgs@turminal.net>

Diffstat:
Mrt/cstrings.ha | 2++
Msrc/lex.c | 359++++++++++++++++++++++++++++++++++---------------------------------------------
Mtests/00-constants.ha | 317++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
3 files changed, 437 insertions(+), 241 deletions(-)

diff --git a/rt/cstrings.ha b/rt/cstrings.ha @@ -4,6 +4,8 @@ type string = struct { capacity: size, }; +export fn toutf8(s: str) []u8 = *(&s: *[]u8); + fn constchar(s: str) *const char = { let s = &s: *string; return s.data: *const char; diff --git a/src/lex.c b/src/lex.c @@ -300,7 +300,7 @@ lex_name(struct lexer *lexer, struct token *out) } static uintmax_t -compute_exp(uintmax_t n, int exponent, bool _signed, struct location *loc) +compute_exp(uintmax_t n, int exponent, bool _signed) { if (n == 0) { return 0; @@ -309,246 +309,192 @@ compute_exp(uintmax_t n, int exponent, bool _signed, struct location *loc) uintmax_t old = n; n *= 10; if (n / 10 != old) { - error(loc, "Integer literal overflow"); + errno = ERANGE; + return INT64_MAX; } } if (_signed && n > (uintmax_t)INT64_MIN) { - error(loc, "Integer literal overflow"); + errno = ERANGE; + return INT64_MAX; } return n; } -static uint32_t +static void lex_literal(struct lexer *lexer, struct token *out) { - uint32_t c = next(lexer, &out->loc, true); - assert(c != C_EOF && c <= 0x7F && isdigit(c)); + enum bases { + BIN = 1, OCT, HEX, DEC = 0x07, MASK = DEC + }; + static_assert((BIN | OCT | HEX | DEC) == DEC, "DEC bits must be a superset of all other bases"); + enum flags { + FLT = 3, EXP, SUFF, DIG, + }; + + static const char chrs[][24] = { + [BIN] = "01", + [OCT] = "01234567", + [DEC] = "0123456789", + [HEX] = "0123456789abcdefABCDEF", + }; - bool started = false, leadingzero = false; - int base = 10; - const char *basechrs = "0123456789"; + static const char matching_states[0x80][6] = { + ['.'] = {DEC, /*HEX,*/ 0}, + ['e'] = {DEC, DEC | 1<<FLT, 0}, + ['+'] = {DEC | 1<<EXP | 1<<DIG, DEC | 1<<FLT | 1<<EXP | 1<<DIG, 0}, + ['-'] = {DEC | 1<<EXP | 1<<DIG, DEC | 1<<FLT | 1<<EXP | 1<<DIG, 0}, + ['i'] = {BIN, OCT, HEX, DEC, DEC | 1<<EXP, 0}, + ['u'] = {BIN, OCT, HEX, DEC, DEC | 1<<EXP, 0}, + ['z'] = {BIN, OCT, HEX, DEC, DEC | 1<<EXP, 0}, + ['f'] = {DEC, DEC | 1<<FLT, DEC | 1<<EXP, DEC | 1<<FLT | 1<<EXP, 0}, + }; + int state = DEC, base = 10, oldstate = DEC; + uint32_t c = next(lexer, &out->loc, true), last = 0; + assert(c != C_EOF && c <= 0x7F && isdigit(c)); if (c == '0') { - switch ((c = next(lexer, NULL, true))) { - case 'b': + c = next(lexer, NULL, true); + if (c <= 0x7F && isdigit(c)) { + error(&out->loc, "Leading zero in base 10 literal"); + } else if (c == 'b') { + state = BIN | 1 << DIG; base = 2; - basechrs = "01"; - consume(lexer, 2); - break; - case 'o': + } else if (c == 'o') { + state = OCT | 1 << DIG; base = 8; - basechrs = "01234567"; - consume(lexer, 2); - break; - case 'x': + } else if (c == 'x') { + state = HEX | 1 << DIG; base = 16; - basechrs = "0123456789ABCDEFabcdef"; - consume(lexer, 2); - break; - default: - started = true; - leadingzero = true; - push(lexer, c, true); - break; } - } else { - started = true; } - - char *suff = NULL; - char *exp = NULL; - bool isfloat = false; - while ((c = next(lexer, NULL, true)) != C_EOF) { - if (!strchr(basechrs, c)) { - switch (c) { - case '.': - if (!started) { - push(lexer, c, true); - goto finalize; - } - if (lexer->require_int) { - push(lexer, '.', true); - goto finalize; - } - if (isfloat || suff || exp) { - push(lexer, c, true); - goto finalize; - } - if (!strchr(basechrs, c = next(lexer, NULL, false))) { - push(lexer, c, false); - push(lexer, '.', true); - goto finalize; - } else { - push(lexer, c, false); - } - isfloat = true; - break; - case 'e': - if (!started) { - push(lexer, c, true); - goto finalize; - } - if (exp || suff) { - push(lexer, c, true); - goto finalize; - } - // exponent is always in base 10 - basechrs = "0123456789"; - c = next(lexer, NULL, true); - if (c != '-' && c != '+' && !strchr(basechrs, c)) { - push(lexer, c, true); - push(lexer, 'e', true); - goto finalize; - }; - exp = &lexer->buf[lexer->buflen - 1]; - break; - case 'f': - if (base != 10) { - push(lexer, c, true); - goto finalize; - } - // Fallthrough - case 'i': - case 'u': - case 'z': - if (suff || !started) { - push(lexer, c, true); - goto finalize; - } - suff = &lexer->buf[lexer->buflen - 1]; - basechrs = "0123456789"; - break; - default: - push(lexer, c, true); - goto finalize; + if (state != DEC) { + last = c; + c = next(lexer, NULL, true); + } + size_t exp = 0, suff = 0; + do { + if (strchr(chrs[state & MASK], c)) { + state &= ~(1 << DIG); + last = c; + continue; + } else if (c > 0x7f || !strchr(matching_states[c], state)) { + goto end; + } + oldstate = state; + switch (c) { + case '.': + if (lexer->require_int) { + goto want_int; } + state |= 1 << FLT; + break; + case '-': + state |= 1 << FLT; + /* fallthrough */ + case 'e': + case '+': + state |= 1 << EXP; + exp = lexer->buflen - 1; + break; + case 'f': + state |= 1 << FLT; + /* fallthrough */ + case 'i': + case 'u': + case 'z': + state |= DEC | 1 << SUFF; + suff = lexer->buflen - 1; + break; + default: + goto end; } - started = true; - } - -finalize: - if (!started) { - error(&out->loc, "Invalid literal"); - } - if (leadingzero && lexer->buflen >= 2 && strchr(basechrs, lexer->buf[1])) { - error(&out->loc, "Leading zero in base 10 literal"); + if (state & 1 << FLT && lexer->require_int) { + error(&out->loc, "Expected integer literal"); + } + last = c; + state |= 1 << DIG; + } while ((c = next(lexer, NULL, true)) != C_EOF); + last = 0; +end: + if (last && !strchr("iuz", last) && !strchr(chrs[state & MASK], last)) { + state = oldstate; + push(lexer, c, true); + push(lexer, last, true); + } else if (c != C_EOF) { +want_int: + push(lexer, c, true); } - lexer->require_int = false; out->token = T_LITERAL; - if (isfloat) { - out->storage = STORAGE_FCONST; - } else { - out->storage = STORAGE_ICONST; - } - if (suff) { - const char *suffs[] = { - [STORAGE_U8] = "u8", - [STORAGE_U16] = "u16", - [STORAGE_U32] = "u32", - [STORAGE_U64] = "u64", - [STORAGE_I8] = "i8", - [STORAGE_I16] = "i16", - [STORAGE_I32] = "i32", - [STORAGE_I64] = "i64", + lexer->require_int = false; - [STORAGE_UINT] = "u", - [STORAGE_INT] = "i", - [STORAGE_SIZE] = "z", - [STORAGE_F32] = "f32", - [STORAGE_F64] = "f64", - }; - bool isvalid = false; - for (enum type_storage i = 0; - i < sizeof(suffs) / sizeof(suffs[0]); ++i) { - if (suffs[i] && strcmp(suff, suffs[i]) == 0) { - isvalid = true; - out->storage = i; + enum kind { + UNKNOWN = -1, + ICONST, SIGNED, UNSIGNED, FLOAT + } kind = UNKNOWN; + static const struct { + const char suff[4]; + enum kind kind; + enum type_storage storage; + } storages[] = { + {"f32", FLOAT, STORAGE_F32}, + {"f64", FLOAT, STORAGE_F64}, + {"i", SIGNED, STORAGE_INT}, + {"i16", SIGNED, STORAGE_I16}, + {"i32", SIGNED, STORAGE_I32}, + {"i64", SIGNED, STORAGE_I64}, + {"i8", SIGNED, STORAGE_I8}, + {"u", UNSIGNED, STORAGE_UINT}, + {"u16", UNSIGNED, STORAGE_U16}, + {"u32", UNSIGNED, STORAGE_U32}, + {"u64", UNSIGNED, STORAGE_U64}, + {"u8", UNSIGNED, STORAGE_U8}, + {"z", UNSIGNED, STORAGE_SIZE}, + }; + if (suff) { + for (size_t i = 0; i < sizeof storages / sizeof storages[0]; i++) { + if (!strcmp(storages[i].suff, lexer->buf + suff)) { + out->storage = storages[i].storage; + kind = storages[i].kind; break; } } - if (!isvalid) { - error(&out->loc, "Invalid numeric suffix"); + if (kind == UNKNOWN) { + error(&out->loc, "Invalid suffix '%s'", lexer->buf + suff); } } - - intmax_t exponent = 0; - if (exp) { - char *endptr = NULL; - errno = 0; - exponent = strtoimax(exp, &endptr, 10); - if (errno == ERANGE) { - error(&out->loc, "Numerical exponent overflow"); - } - // integers can't have negative exponents - if (exponent < 0 && !suff) { + if (state & 1 << FLT) { + if (kind == UNKNOWN) { out->storage = STORAGE_FCONST; - } - enum type_storage s = out->storage; - bool valid = exponent >= 0 - || s == STORAGE_F32 - || s == STORAGE_F64 - || s == STORAGE_FCONST; - if (endptr == exp || !valid) { - error(&out->loc, "Integers cannot have negative exponents"); - } - } - - if (isfloat) { - switch (out->storage) { - case STORAGE_F32: - case STORAGE_F64: - case STORAGE_FCONST: - break; - default: + } else if (kind != FLOAT) { error(&out->loc, "Unexpected decimal point in integer literal"); } + out->fval = strtod(lexer->buf, NULL); + consume(lexer, -1); + return; } + if (kind == UNKNOWN) { + kind = ICONST; + out->storage = STORAGE_ICONST; + } + uintmax_t exponent = 0; errno = 0; - switch (out->storage) { - case STORAGE_U8: - case STORAGE_U16: - case STORAGE_U32: - case STORAGE_UINT: - case STORAGE_U64: - case STORAGE_SIZE: - out->uval = compute_exp(strtoumax(lexer->buf, NULL, base), - exponent, false, &out->loc); - break; - case STORAGE_ICONST: - out->uval = compute_exp(strtoumax(lexer->buf, NULL, base), - exponent, false, &out->loc); - if (out->uval > (uintmax_t)INT64_MAX) { - out->storage = STORAGE_U64; - break; - } - // Fallthrough - case STORAGE_I8: - case STORAGE_I16: - case STORAGE_I32: - case STORAGE_INT: - case STORAGE_I64: - out->uval = compute_exp(strtoumax(lexer->buf, NULL, base), - exponent, true, &out->loc); - if (out->uval == (uintmax_t)INT64_MIN) { - // XXX: Hack - out->ival = INT64_MIN; - } else { - out->ival = (intmax_t)out->uval; - } - break; - case STORAGE_F32: - case STORAGE_F64: - case STORAGE_FCONST: - out->fval = strtod(lexer->buf, NULL); - break; - default: - assert(0); + if (exp != 0) { + exponent = strtoumax(lexer->buf + exp + 1, NULL, 10); } - if (errno == ERANGE && !isfloat) { + out->uval = strtoumax(lexer->buf + (base == 10 ? 0 : 2), NULL, base); + out->uval = compute_exp(out->uval, exponent, kind == SIGNED); + if (errno == ERANGE) { error(&out->loc, "Integer literal overflow"); } + if (kind == ICONST && out->uval > (uintmax_t)INT64_MAX) { + out->storage = STORAGE_U64; + } else if (kind == SIGNED && out->uval == (uintmax_t)INT64_MIN) { + // XXX: Hack + out->ival = INT64_MIN; + } else if (kind != UNSIGNED) { + out->ival = (intmax_t)out->uval; + } consume(lexer, -1); - return out->token; } static uint32_t @@ -953,7 +899,8 @@ _lex(struct lexer *lexer, struct token *out) if (c <= 0x7F && isdigit(c)) { push(lexer, c, false); - return lex_literal(lexer, out); + lex_literal(lexer, out); + return T_LITERAL; } lexer->require_int = false; diff --git a/tests/00-constants.ha b/tests/00-constants.ha @@ -1,4 +1,4 @@ -use rt::{compile, exited, EXIT_SUCCESS}; +use rt::{compile, exited, EXIT_SUCCESS, toutf8}; type my_enum = enum u8 { FOO, @@ -137,51 +137,298 @@ fn aggregates() void = { u2arr as [3]u8; }; -fn basics() void = { - let i1 = 13, i2 = 13i, i3 = 13i8, i4 = 13i16, i5 = 13i32, i6 = 13i64; - let u1 = 13u, u2 = 13z, u3 = 13u8, u4 = 13u16, u5 = 13u32, u6 = 13u64; - let n1 = -13, n2 = -13u; - let b1 = true, b2 = false; - let p1: nullable *int = null; - let r1 = 'x', r2 = '\x0A', r3 = '\u1234', r4 = '\0', r5 = '\a', - r6 = '\b', r7 = '\f', r8 = '\n', r9 = '\r', r10 = '\t', - r11 = '\v', r12 = '\\', r13 = '\'', r14 = '\"', - r15 = '\U12345678'; - let f1 = 1.0, f2 = 1f32, f3 = 1.0e2, f4 = 1.0f64; - let f5 = 1.23e+45, f6 = 9.87e-65, f7 = 1e-7, f8 = 5.0e-324; - let ie1 = 1e5i; - - let failures: [_]str = [ - // exponent overflow - "let x: u64 = 1e100;", +fn numeric() void = { + let want: [_]i64 = [ + 42, 42, 42, 42, 42, 42, 42, 42, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, + 100, 100, 100, 100 + ]; + let i = [ + // basics + (42, 42i, 42i8, 42i16, 42i32, 42i64), // decimal + (42e0, 42e0i, 42e0i8, 42e0i16, 42e0i32, 42e0i64), // with exp + (42e00, 42e00i, 42e00i8, 42e00i16, 42e00i32, 42e00i64), // with leading zeros in exp + (42e+0, 42e+0i, 42e+0i8, 42e+0i16, 42e+0i32, 42e+0i64), // with + in exp + (42e+00, 42e+00i, 42e+00i8, 42e+00i16, 42e+00i32, 42e+00i64), // with + and leading zeros in exp + (0b101010, 0b101010i, 0b101010i8, 0b101010i16, 0b101010i32, 0b101010i64), // binary + (0o52, 0o52i, 0o52i8, 0o52i16, 0o52i32, 0o52i64), // octal + (0x2a, 0x2ai, 0x2ai8, 0x2ai16, 0x2ai32, 0x2ai64), // hex + + // single digit + (0, 0i, 0i8, 0i16, 0i32, 0i64), // zero + (0b0, 0b0i, 0b0i8, 0b0i16, 0b0i32, 0b0i64), // binary + (0o0, 0o0i, 0o0i8, 0o0i16, 0o0i32, 0o0i64), // octal + (0x0, 0x0i, 0x0i8, 0x0i16, 0x0i32, 0x0i64), // hex + + (1, 1i, 1i8, 1i16, 1i32, 1i64), // nonzero + (0b1, 0b1i, 0b1i8, 0b1i16, 0b1i32, 0b1i64), // binary + (0o1, 0o1i, 0o1i8, 0o1i16, 0o1i32, 0o1i64), // octal + (0x1, 0x1i, 0x1i8, 0x1i16, 0x1i32, 0x1i64), // hex + + // with leading zero + (0b00, 0b00i, 0b00i8, 0b00i16, 0b00i32, 0b00i64), // binary + (0o00, 0o00i, 0o00i8, 0o00i16, 0o00i32, 0o00i64), // octal + (0x00, 0x00i, 0x00i8, 0x00i16, 0x00i32, 0x00i64), // hex + + (0b01, 0b01i, 0b01i8, 0b01i16, 0b01i32, 0b01i64), // binary with leading zero + (0o01, 0o01i, 0o01i8, 0o01i16, 0o01i32, 0o01i64), // octal + (0x01, 0x01i, 0x01i8, 0x01i16, 0x01i32, 0x01i64), // hex + + // exponents + (1e2, 1e2i, 1e2i8, 1e2i16, 1e2i32, 1e2i64), + (1e02, 1e02i, 1e02i8, 1e02i16, 1e02i32, 1e02i64), // with leading zeros in exp + (1e+2, 1e+2i, 1e+2i8, 1e+2i16, 1e+2i32, 1e+2i64), // with + in exp + (1e+02, 1e+02i, 1e+02i8, 1e+02i16, 1e+02i32, 1e+02i64), // with + and leading zeros in exp + ]; + for (let j = 0z; j < len(i); j += 1) { + let t = &i[j]; + assert(want[j] == t.0 && t.0 == t.1 && t.1 == t.2 && t.2 == t.3 + && t.3 == t.4 && t.4 == t.5); + }; + + let u = [ + // basics + (42z, 42u, 42u8, 42u16, 42u32, 42u64), // decimal + (42e0z, 42e0u, 42e0u8, 42e0u16, 42e0u32, 42e0u64), // with exp + (42e00z, 42e00u, 42e00u8, 42e00u16, 42e00u32, 42e00u64), // with leading zeros in exp + (42e+0z, 42e+0u, 42e+0u8, 42e+0u16, 42e+0u32, 42e+0u64), // with + in exp + (42e+00z, 42e+00u, 42e+00u8, 42e+00u16, 42e+00u32, 42e+00u64), // with + and leading zeros in exp + (0b101010z, 0b101010u, 0b101010u8, 0b101010u16, 0b101010u32, 0b101010u64), // binary + (0o52z, 0o52u, 0o52u8, 0o52u16, 0o52u32, 0o52u64), // octal + (0x2az, 0x2au, 0x2au8, 0x2au16, 0x2au32, 0x2au64), // hex + + // single digit + (0z, 0u, 0u8, 0u16, 0u32, 0u64), // zero + (0b0z, 0b0u, 0b0u8, 0b0u16, 0b0u32, 0b0u64), // binary + (0o0z, 0o0u, 0o0u8, 0o0u16, 0o0u32, 0o0u64), // octal + (0x0z, 0x0u, 0x0u8, 0x0u16, 0x0u32, 0x0u64), // hex + + (1z, 1u, 1u8, 1u16, 1u32, 1u64), // nonzero + (0b1z, 0b1u, 0b1u8, 0b1u16, 0b1u32, 0b1u64), // binary + (0o1z, 0o1u, 0o1u8, 0o1u16, 0o1u32, 0o1u64), // octal + (0x1z, 0x1u, 0x1u8, 0x1u16, 0x1u32, 0x1u64), // hex + + // with leading zero + (0b00z, 0b00u, 0b00u8, 0b00u16, 0b00u32, 0b00u64), // binary + (0o00z, 0o00u, 0o00u8, 0o00u16, 0o00u32, 0o00u64), // octal + (0x00z, 0x00u, 0x00u8, 0x00u16, 0x00u32, 0x00u64), // hex + + (0b01z, 0b01u, 0b01u8, 0b01u16, 0b01u32, 0b01u64), // binary with leading zero + (0o01z, 0o01u, 0o01u8, 0o01u16, 0o01u32, 0o01u64), // octal + (0x01z, 0x01u, 0x01u8, 0x01u16, 0x01u32, 0x01u64), // hex + + // exponents + (1e2z, 1e2u, 1e2u8, 1e2u16, 1e2u32, 1e2u64), + (1e02z, 1e02u, 1e02u8, 1e02u16, 1e02u32, 1e02u64), // with leading zeros in exp + (1e+2z, 1e+2u, 1e+2u8, 1e+2u16, 1e+2u32, 1e+2u64), // with + in exp + (1e+02z, 1e+02u, 1e+02u8, 1e+02u16, 1e+02u32, 1e+02u64), // with + and leading zeros in exp + ]; + for (let j = 0z; j < len(u); j += 1) { + let t = &u[j]; + assert(want[j]: u64 == t.0: u64 && t.0: u64 == t.1 + && t.1 == t.2 && t.2 == t.3 && t.3 == t.4 && t.4 == t.5); + }; + + let f = [0.0, 0.00, 0.0e0, 0.00e0, 0.0e1, 0.00e1, 0.0e+0, 0.0e+1, 0.0e-0, 0.0e00, + 0.0e01, 0.0e+01, 0.0e+00, 0.0e-00, 0e-0, 0e-00, 0e-1, 0e-01]; + for (let j = 0z; j < len(f); j+= 1) { + assert(f[j] == 0.0); + }; + + let _f32 = [0.0f32, 0.00f32, 0.0e0f32, 0.00e0f32, 0.0e1f32, 0.00e1f32, 0.0e+0f32, + 0.0e+1f32, 0.0e-0f32, 0.0e00f32, 0.0e01f32, 0.0e+01f32, 0.0e+00f32, 0.0e-00, + 0f32, 0e0f32, 0e1f32, 0e00f32, 0e01f32, 0e+0f32, 0e+00f32, 0e+1f32, + 0e+01f32, 0e-0f32, 0e-00f32, 0e-1f32, 0e-01f32]; + for (let j = 0z; j < len(_f32); j+= 1) { + assert(_f32[j] == 0f32); + }; + + let _f64 = [0.0f64, 0.00f64, 0.0e0f64, 0.00e0f64, 0.0e1f64, 0.00e1f64, 0.0e+0f64, + 0.0e+1f64, 0.0e-0f64, 0.0e00f64, 0.0e01f64, 0.0e+01f64, 0.0e+00f64, 0.0e-00, + 0f64, 0e0f64, 0e1f64, 0e00f64, 0e01f64, 0e+0f64, 0e+00f64, 0e+1f64, + 0e+01f64, 0e-0f64, 0e-00f64, 0e-1f64, 0e-01f64]; + for (let j = 0z; j < len(_f64); j+= 1) { + assert(_f64[j] == 0f64); + }; + + // double tuple subscript special case + let tup = (('a', 'b'), 'c'); + assert(tup.0.0 == 'a'); + // exponents + assert(tup.0e0.0 == 'a'); + assert(tup.0.0e0 == 'a'); + assert(tup.0e0.0e0 == 'a'); + assert(tup.0e+0.0 == 'a'); + assert(tup.0.0e+0 == 'a'); + assert(tup.0e+0.0e+0 == 'a'); + // signed + assert(tup.0i.0 == 'a'); + assert(tup.0.0i == 'a'); + assert(tup.0i.0i == 'a'); + assert(tup.0i32.0 == 'a'); + assert(tup.0.0i32 == 'a'); + assert(tup.0i32.0i32 == 'a'); + // unsigned + assert(tup.0u.0 == 'a'); + assert(tup.0.0u == 'a'); + assert(tup.0u.0u == 'a'); + assert(tup.0u32.0 == 'a'); + assert(tup.0.0u32 == 'a'); + assert(tup.0u32.0u32 == 'a'); + // bases + assert(tup.0b0.0 == 'a'); + assert(tup.0.0b0 == 'a'); + assert(tup.0b0.0b0 == 'a'); + assert(tup.0o0.0 == 'a'); + assert(tup.0.0o0 == 'a'); + assert(tup.0o0.0o0 == 'a'); + assert(tup.0x0.0 == 'a'); + assert(tup.0.0x0 == 'a'); + assert(tup.0x0.0x0 == 'a'); + + // zero with large exponent + assert(0e10000000 == 0); + assert(0e010000000 == 0); + assert(0e+10000000 == 0); + assert(0e+010000000 == 0); - // various invalid literals - "let x = 0x;", - "let x = 0xz;", - "let x = 0xu;", - "let x = 0xu64;", - "let x = 0be+0;", - "let x = 0bf64;", + // f32 and f64 are valid hex literals + assert(0xf32 == 3890); + assert(0xf64 == 3940); + assert(0x1f32 == 7986); + assert(0x1f64 == 8036); + assert(0xf321 == 62241); + assert(0xf641 == 63041); + + + // e is a valid hex digit + assert(0xe == 14); + assert(0xe+1 == 15); + assert(0xe-1 == 13); + assert(0x1e == 30); + assert(0x1e+1 == 31); + assert(0x1e-1 == 29); + assert(0x1e1 == 481); + assert(0x1e1f32 == 1974066); + + let v = if (true) 5else 10; + assert(v == 5); + + let invalid: [_]str = [ + + // invalid base + "0b", "0o", // 0x tested separately + "00b", "00o", "00x", + "01b", "01o", "01x", + "1b", "1o", "1x", + "11b", "11o", "11x", + + // base with exponent + "0b1e1", + "0o1e1", + // with +/- + "0b1e+1", + "0o1e+1", // invalid digits in smaller bases - "let x = 0b41;", - "let x = 0b14;", - "let x = 0o82;", - "let x = 0o28;", - - // leading zeros - "let x = 05;" - "let x = 0000000010;" + "0b41", "0b14", + "0o82", "0o28", + + // leading zeroes + "05", "00000010", "00.0", "01.0", + "05e3", "00000010e3", "00.0e3", "01.0e3", + "05e+3", "00000010e+3", "00.0e+3", "01.0e+3", + "05e-3", "00000010e-3", "00.0e-3", "01.0e-3", + + // invalid sequences of special characters + "1.", + "1..", + "1..1", + "1.1.", + "1.1.1", + + "1e", + "1e+", + "1e-", + + "1e1+", + "1e1-", + + "1ee", + "1e+e", "1ee+", "1e+e+", + "1e-e", "1ee-", "1e-e-", + "1e+e-", "1e-e+", + + "1ee1", + "1e+e1", "1ee+1", "1e+e+1", + "1e-e1", "1ee-1", "1e-e-1", + "1e+e-1", "1e-e+1", + + "1e1e", + "1e+1e", "1e1e+", "1e+1e+", + "1e-1e", "1e1e-", "1e-1e-", + "1e+1e-", "1e-1e+", + + "1e1e1", + "1e+1e1", "1e1e+1", "1e+1e+1", + "1e-1e1", "1e1e-1", "1e-1e-1", + "1e+1e-1", "1e-1e+1", + + "1.e", "1e.", + "1.e1", "1e.1", + "1.1e", "1e1.", + "1e1.1", + + "1.e+", "1e+.", + "1.e+1", "1e+.1", + "1.1e+", "1e+1.", + "1e+1.1", + + "1.e-", "1e-.", + "1.e-1", "1e-.1", + "1.1e-", "1e-1.", + "1e-1.1", + ]; + let extra: [_]str = [ + "4e-0i;", "4e-1i;", + "4e-0i8;", "4e-1i8;", + + "0b1e-1f32;", + "0o1e-1f32;", + "0x1e+1f32;", + "0x1e-1f32;", + + // exponent overflow + "let t: u64 = 1e1000;", ]; - for (let i = 0z; i < len(failures); i += 1) { - assert(compile(failures[i]) as exited != EXIT_SUCCESS); + let suffix = [";", "i;", "i8;", "f32;"]; + let buf: [256]u8 = [0...]; + for (let i = 0z; i < len(invalid); i += 1) { + for (let j = 0z; j < len(suffix); j += 1) { + let buf = buf[..0]; + append(buf, toutf8("let t = ")...); + append(buf, toutf8(invalid[i])...); + append(buf, toutf8(suffix[j])...); + assert(compile(*(&buf: *str)) as exited != EXIT_SUCCESS); + }; }; + for (let i = 0z; i < len(extra); i += 1) { + assert(compile(extra[i]) as exited != EXIT_SUCCESS); + }; +}; + +fn basics() void = { + let b1 = true, b2 = false; + let p1: nullable *int = null; + let r1 = ['x', '\x0A', '\u1234', '\0', '\a', '\b', '\f', '\n', '\r', '\t', + '\v', '\\', '\'', '\"', '\U12345678']; }; export fn main() void = { // The interaction between constants and result type reduction is tested // in 30-reduction.c basics(); + numeric(); assignment(); aggregates(); };