From 00920ddd36a8ebb851b52c3c048c0017753176bd Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Thu, 30 Jul 2026 10:24:19 +0200 Subject: complete js parser + minifier --- src/z/parser/js/ast/pat.zig | 190 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 src/z/parser/js/ast/pat.zig (limited to 'src/z/parser/js/ast/pat.zig') diff --git a/src/z/parser/js/ast/pat.zig b/src/z/parser/js/ast/pat.zig new file mode 100644 index 0000000..fdc8f55 --- /dev/null +++ b/src/z/parser/js/ast/pat.zig @@ -0,0 +1,190 @@ +const std = @import("std"); +const Loc = @import("loc.zig").Loc; +const Expr = @import("expr.zig").Expr; +const PropKey = @import("expr.zig").PropKey; +const Parser = @import("../parser.zig").Parser; +const Formatter = @import("../formatter.zig").Formatter; +const ParseError = @import("../parser.zig").ParseError; + +pub const PatType = union(enum) { + ident: []const u8, + object: ObjectPat, + array: ArrayPat, + assign: AssignPat, + rest: RestPat, +}; + +pub const Pat = struct { + loc: Loc, + data: PatType, + + pub fn parse(p: *Parser) ParseError!Pat { + const start = p.tok().loc.start; + + switch (p.peek()) { + .identifier, .await, .yield => { + const t = p.advanceTok(); + const name = p.tokenSlice(t); + return Pat{ .loc = .{ .start = start, .end = t.loc.end }, .data = .{ .ident = name } }; + }, + .lbrace => { + p.advance(); + var props: [128]PatProp = undefined; + var count: usize = 0; + var rest: ?*const Pat = null; + + while (p.peek() != .rbrace and p.peek() != .eof) { + if (p.peek() == .@"...") { + p.advance(); + const pat = try Pat.parse(p); + rest = p.arena.patPtr(pat.data, pat.loc); + break; + } + if (p.peek() == .identifier) { + const name_tok = p.tok(); + const name = p.tokenSlice(name_tok); + p.advance(); + if (p.peek() == .@":") { + p.advance(); + const val_pat = try Pat.parse(p); + props[count] = .{ .key_value = .{ .key = .{ .ident = name }, .value = p.arena.patPtr(val_pat.data, val_pat.loc) } }; + count += 1; + } else { + props[count] = .{ .shorthand = name }; + count += 1; + } + } else if (p.peek() == .lbracket) { + const key = try p.parsePropKey(); + try p.expect(.@":"); + const val_pat = try Pat.parse(p); + props[count] = .{ .key_value = .{ .key = key, .value = p.arena.patPtr(val_pat.data, val_pat.loc) } }; + count += 1; + } else { + break; + } + if (!p.match(.comma)) break; + } + + try p.expect(.rbrace); + return Pat{ .loc = .{ .start = start, .end = p.tok().loc.start }, .data = .{ .object = .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .props = props[0..count], .rest = rest } } }; + }, + .lbracket => { + p.advance(); + var elems: [128]?Pat = undefined; + var count: usize = 0; + var rest: ?*const Pat = null; + + while (p.peek() != .rbracket and p.peek() != .eof) { + if (p.match(.comma)) { + elems[count] = null; + count += 1; + } else if (p.peek() == .@"...") { + p.advance(); + const pat = try Pat.parse(p); + rest = p.arena.patPtr(pat.data, pat.loc); + break; + } else { + const pat = try Pat.parse(p); + elems[count] = pat; + count += 1; + if (!p.match(.comma)) break; + } + } + + try p.expect(.rbracket); + return Pat{ .loc = .{ .start = start, .end = p.tok().loc.start }, .data = .{ .array = .{ .loc = .{ .start = start, .end = p.tok().loc.start }, .elems = elems[0..count], .rest = rest } } }; + }, + else => return ParseError.ExpectedParameterName, + } + } + + pub fn fmt(self: @This(), f: *Formatter) void { + switch (self.data) { + .ident => |name| f.write(name), + .object => |o| o.fmt(f), + .array => |a| a.fmt(f), + .assign => |a| { + a.left.*.fmt(f); + f.write(" = "); + a.right.*.fmt(f); + }, + .rest => |r| { + f.write("..."); + r.arg.*.fmt(f); + }, + } + } +}; + +pub const ObjectPat = struct { + loc: Loc, + props: []const PatProp, + rest: ?*const Pat, + + pub fn fmt(self: @This(), f: *Formatter) void { + f.writeChar('{'); + var first = true; + for (self.props) |prop| { + if (!first) f.write(", "); + first = false; + switch (prop) { + .key_value => |kv| { + kv.key.fmt(f); + f.write(": "); + kv.value.*.fmt(f); + }, + .shorthand => |name| f.write(name), + .rest => |r| { + f.write("..."); + r.*.fmt(f); + }, + } + } + if (self.rest) |rest| { + if (!first) f.write(", "); + f.write("..."); + rest.*.fmt(f); + } + f.writeChar('}'); + } +}; + +pub const PatProp = union(enum) { + key_value: struct { + key: PropKey, + value: *const Pat, + }, + shorthand: []const u8, + rest: *const Pat, +}; + +pub const ArrayPat = struct { + loc: Loc, + elems: []const ?Pat, + rest: ?*const Pat, + + pub fn fmt(self: @This(), f: *Formatter) void { + f.writeChar('['); + for (self.elems, 0..) |elem, i| { + if (i > 0) f.write(", "); + if (elem) |pat| pat.fmt(f); + } + if (self.rest) |rest| { + if (self.elems.len > 0) f.write(", "); + f.write("..."); + rest.*.fmt(f); + } + f.writeChar(']'); + } +}; + +pub const AssignPat = struct { + loc: Loc, + left: *const Pat, + right: *const Expr, +}; + +pub const RestPat = struct { + loc: Loc, + arg: *const Pat, +}; -- cgit v1.2.3-70-g09d2