allow commas in numbers

This commit is contained in:
Emil Lerch 2026-07-18 08:05:36 -07:00
parent 58a94dd163
commit f73834b333
Signed by: lobo
GPG key ID: A7B62D657EF764F8
2 changed files with 43 additions and 1 deletions

View file

@ -486,6 +486,16 @@ test "eval number with underscores" {
try testing.expectEqual(@as(f64, 1_000_001.0), result);
}
test "eval number with commas" {
const result = try testEval("1,000 * 2.3");
try testing.expectApproxEqAbs(@as(f64, 2300.0), result, 1e-10);
}
test "eval commas not confused with function args" {
const result = try testEval("max(1,000, 500)");
try testing.expectEqual(@as(f64, 1000.0), result);
}
test "eval bitwise not in standard mode" {
const result = try testEval("~0");
// ~0 as i64 = -1

View file

@ -93,7 +93,7 @@ pub fn parseNumber(token_text: []const u8) !NumberValue {
var buf: [128]u8 = undefined;
var buf_len: usize = 0;
for (token_text) |c| {
if (c != '_') {
if (c != '_' and c != ',') {
if (buf_len >= buf.len) return error.InvalidNumber;
buf[buf_len] = c;
buf_len += 1;
@ -367,6 +367,14 @@ pub const Tokenizer = struct {
} else if (ch == '_') {
// Digit separator
self.pos += 1;
} else if (ch == ',') {
// Comma as digit separator, but only if followed by a digit
// (to avoid eating commas in function args like max(1, 2))
if (self.pos + 1 < self.source.len and predicate(self.source[self.pos + 1])) {
self.pos += 1;
} else {
break;
}
} else {
break;
}
@ -474,6 +482,24 @@ test "tokenize hex with underscores" {
try testing.expectEqualStrings("0xFF_FF", t.text("0xFF_FF"));
}
test "tokenize number with commas" {
var tok = Tokenizer.init("1,000,000", .standard);
const t = tok.next();
try testing.expectEqual(TokenKind.number, t.kind);
try testing.expectEqualStrings("1,000,000", t.text("1,000,000"));
try testing.expectEqual(TokenKind.eof, tok.next().kind);
}
test "tokenize comma not eaten in function args" {
var tok = Tokenizer.init("max(1, 2)", .standard);
try testing.expectEqual(TokenKind.identifier, tok.next().kind); // max
try testing.expectEqual(TokenKind.left_paren, tok.next().kind); // (
try testing.expectEqual(TokenKind.number, tok.next().kind); // 1
try testing.expectEqual(TokenKind.comma, tok.next().kind); // ,
try testing.expectEqual(TokenKind.number, tok.next().kind); // 2
try testing.expectEqual(TokenKind.right_paren, tok.next().kind); // )
}
test "tokenize function call" {
var tok = Tokenizer.init("sin(3.14)", .standard);
try testing.expectEqual(TokenKind.identifier, tok.next().kind);
@ -584,6 +610,12 @@ test "parseNumber hex with underscores" {
try testing.expectEqual(Base.hex, result.base);
}
test "parseNumber with commas" {
const result = try parseNumber("1,000,000");
try testing.expectEqual(@as(?u64, 1_000_000), result.int_value);
try testing.expectEqual(Base.decimal, result.base);
}
// -- ImplicitMulStream tests --
test "implicit mul: number followed by identifier (2pi)" {