| ... | @@ -222,9 +222,11 @@ pub const Tokenizer = struct { | ... | @@ -222,9 +222,11 @@ pub const Tokenizer = struct { |
| 222 | }, | 222 | }, |
| 223 | }; | 223 | }; |
| 224 | } else { | 224 | } else { |
| | 225 | // Skip the UTF-8 BOM if present |
| | 226 | const src_start = if (mem.startsWith(u8, buffer, "\xEF\xBB\xBF")) 3 else usize(0); |
| 225 | return Tokenizer{ | 227 | return Tokenizer{ |
| 226 | .buffer = buffer, | 228 | .buffer = buffer, |
| 227 | .index = 0, | 229 | .index = src_start, |
| 228 | .pending_invalid_token = null, | 230 | .pending_invalid_token = null, |
| 229 | }; | 231 | }; |
| 230 | } | 232 | } |
| ... | @@ -1455,6 +1457,13 @@ test "tokenizer - line comment followed by identifier" { | ... | @@ -1455,6 +1457,13 @@ test "tokenizer - line comment followed by identifier" { |
| 1455 | }); | 1457 | }); |
| 1456 | } | 1458 | } |
| 1457 | | 1459 | |
| | 1460 | test "tokenizer - UTF-8 BOM is recognized and skipped" { |
| | 1461 | testTokenize("\xEF\xBB\xBFa;\n", [_]Token.Id{ |
| | 1462 | Token.Id.Identifier, |
| | 1463 | Token.Id.Semicolon, |
| | 1464 | }); |
| | 1465 | } |
| | 1466 | |
| 1458 | fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { | 1467 | fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { |
| 1459 | var tokenizer = Tokenizer.init(source); | 1468 | var tokenizer = Tokenizer.init(source); |
| 1460 | for (expected_tokens) |expected_token_id| { | 1469 | for (expected_tokens) |expected_token_id| { |