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, };