diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2026-08-04 22:28:25 +0200 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2026-08-04 22:28:25 +0200 |
| commit | 356c2bc0ccb1794b5ec219079d8c5b85ddcc4c87 (patch) | |
| tree | defa9a0e3c46af6e59e3be3d3ac1482c8fad24c8 /src/z/parser/js | |
| parent | 06d1607aabad0a967c0500efe305de3fa732f8d3 (diff) | |
add templates to tokenizer
Diffstat (limited to 'src/z/parser/js')
| -rw-r--r-- | src/z/parser/js/grammar/literal.zig | 63 | ||||
| -rw-r--r-- | src/z/parser/js/grammar/punctuator.zig | 5 | ||||
| -rw-r--r-- | src/z/parser/js/grammar/root.zig | 13 |
3 files changed, 74 insertions, 7 deletions
diff --git a/src/z/parser/js/grammar/literal.zig b/src/z/parser/js/grammar/literal.zig index 6a91291..039ebf4 100644 --- a/src/z/parser/js/grammar/literal.zig +++ b/src/z/parser/js/grammar/literal.zig @@ -4,11 +4,17 @@ const root = @import("root.zig"); const Lexer = root.Lexer; const Token = root.Token; -pub const Literal = enum { +pub const Literal = union(enum) { // NOTE: currently only decimal literals are tokenized - numeric, - bigint, - string, + numeric: void, + bigint: void, + string: void, + template: enum { + whole, + start, + middle, + end, + }, const digits = "_0123456789abcdef"; @@ -60,9 +66,52 @@ pub const Literal = enum { }, '\'', '"' => |quote| { token.kind.literal = .string; - while (try lexer.consume() != quote) { - if (quote == '\\') { - try lexer.consume(); + while (true) { + switch (try lexer.consume()) { + quote => break, + '\\' => try lexer.consume(), + else => {}, + } + } + }, + '`' => { + token.kind.literal = .{ .template = undefined }; + + while (true) { + switch (try lexer.consume()) { + '`' => { + token.kind.literal.template = .whole; + break; + }, + '\\' => try lexer.consume(), + '$' => if (lexer.peekChar() == '{') { + lexer.skip() catch unreachable; + lexer.context.template_nesting += 1; + token.kind.literal.template = .start; + break; + }, + else => {}, + } + } + }, + '}' => if (lexer.context.template_nesting > 0) { + lexer.context.template_nesting -= 1; + token.kind.literal = .{ .template = undefined }; + + while (true) { + switch (try lexer.consume()) { + '`' => { + token.kind.literal.template = .end; + break; + }, + '\\' => try lexer.consume(), + '$' => if (lexer.peekChar() == '{') { + lexer.skip() catch unreachable; + lexer.context.template_nesting += 1; + token.kind.literal.template = .middle; + break; + }, + else => {}, } } }, diff --git a/src/z/parser/js/grammar/punctuator.zig b/src/z/parser/js/grammar/punctuator.zig index a72ab8a..4fd5d16 100644 --- a/src/z/parser/js/grammar/punctuator.zig +++ b/src/z/parser/js/grammar/punctuator.zig @@ -86,6 +86,11 @@ pub const Punctuator = enum { defer lexer.revert(); if (punctuatormap.getLongestPrefix(lexer.buffer)) |pair| { + + if (std.mem.eql(u8, pair.key, "}") and lexer.context.template_nesting > 0) { + return Lexer.Error.UnexpectedToken; + } + const token = lexer.start(.{ .punctuator = pair.value }); try lexer.skipTo(pair.key.len); lexer.commit(token); diff --git a/src/z/parser/js/grammar/root.zig b/src/z/parser/js/grammar/root.zig index 03b386e..56f3990 100644 --- a/src/z/parser/js/grammar/root.zig +++ b/src/z/parser/js/grammar/root.zig @@ -11,6 +11,7 @@ const std = @import("std"); const parser = @import("../../root.zig"); + pub const Lexer = parser.Lexer(Grammar); pub const Token = parser.Token(Grammar); @@ -28,7 +29,12 @@ pub const Grammar = union(enum) { identifier: Identifier, end_of_file: void, + pub const Context = struct { + template_nesting: usize = 0, + }; + pub inline fn tokenize(comptime lexer: *Lexer) Lexer.Error!void { + @setEvalBranchQuota(lexer.buffer.len * 1000); comptime { while (lexer.peekChar()) |_| { Whitespace.tokenize(lexer) catch { @@ -50,3 +56,10 @@ pub const Grammar = union(enum) { test { _ = std.testing.refAllDecls(@This()); } + +test "z.js" { + comptime var lexer: Lexer = .init(@embedFile("../../../z.js")); + Grammar.tokenize(&lexer) catch |err| { + std.debug.print("error: {} at {s}", .{ err, lexer.buffer }); + }; +} |