diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2023-04-01 14:22:29 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-01 14:22:29 +0200 |
| commit | 381dc2d9509ffeaf60a1775ee0983c7dd1b9e346 (patch) | |
| tree | 34dc901cf638a113cd7bf84d90125b9c64685b3d /src/link/tapi/parse.zig | |
| parent | 575a98a0813181c268e86d590b6267583736bafd (diff) | |
| parent | a0a854b1fc5e64a07b2026ac0668f8d417ab6bf1 (diff) | |
| download | zig-381dc2d9509ffeaf60a1775ee0983c7dd1b9e346.tar.gz zig-381dc2d9509ffeaf60a1775ee0983c7dd1b9e346.zip | |
Merge pull request #15139 from ziglang/macos-13-fixes
Fixes for latest macOS 13.3 SDK release
Diffstat (limited to 'src/link/tapi/parse.zig')
| -rw-r--r-- | src/link/tapi/parse.zig | 701 |
1 files changed, 378 insertions, 323 deletions
diff --git a/src/link/tapi/parse.zig b/src/link/tapi/parse.zig index eb7bb2a0cf..09774cf00a 100644 --- a/src/link/tapi/parse.zig +++ b/src/link/tapi/parse.zig @@ -1,8 +1,7 @@ const std = @import("std"); const assert = std.debug.assert; -const log = std.log.scoped(.tapi); +const log = std.log.scoped(.yaml); const mem = std.mem; -const testing = std.testing; const Allocator = mem.Allocator; const Tokenizer = @import("Tokenizer.zig"); @@ -11,9 +10,9 @@ const TokenIndex = Tokenizer.TokenIndex; const TokenIterator = Tokenizer.TokenIterator; pub const ParseError = error{ + InvalidEscapeSequence, MalformedYaml, NestedDocuments, - UnexpectedTag, UnexpectedEof, UnexpectedToken, Unhandled, @@ -22,6 +21,8 @@ pub const ParseError = error{ pub const Node = struct { tag: Tag, tree: *const Tree, + start: TokenIndex, + end: TokenIndex, pub const Tag = enum { doc, @@ -61,9 +62,12 @@ pub const Node = struct { } pub const Doc = struct { - base: Node = Node{ .tag = Tag.doc, .tree = undefined }, - start: ?TokenIndex = null, - end: ?TokenIndex = null, + base: Node = Node{ + .tag = Tag.doc, + .tree = undefined, + .start = undefined, + .end = undefined, + }, directive: ?TokenIndex = null, value: ?*Node = null, @@ -86,10 +90,8 @@ pub const Node = struct { _ = fmt; if (self.directive) |id| { try std.fmt.format(writer, "{{ ", .{}); - const directive = self.base.tree.tokens[id]; - try std.fmt.format(writer, ".directive = {s}, ", .{ - self.base.tree.source[directive.start..directive.end], - }); + const directive = self.base.tree.getRaw(id, id); + try std.fmt.format(writer, ".directive = {s}, ", .{directive}); } if (self.value) |node| { try std.fmt.format(writer, "{}", .{node}); @@ -101,22 +103,27 @@ pub const Node = struct { }; pub const Map = struct { - base: Node = Node{ .tag = Tag.map, .tree = undefined }, - start: ?TokenIndex = null, - end: ?TokenIndex = null, + base: Node = Node{ + .tag = Tag.map, + .tree = undefined, + .start = undefined, + .end = undefined, + }, values: std.ArrayListUnmanaged(Entry) = .{}, pub const base_tag: Node.Tag = .map; pub const Entry = struct { key: TokenIndex, - value: *Node, + value: ?*Node, }; pub fn deinit(self: *Map, allocator: Allocator) void { for (self.values.items) |entry| { - entry.value.deinit(allocator); - allocator.destroy(entry.value); + if (entry.value) |value| { + value.deinit(allocator); + allocator.destroy(value); + } } self.values.deinit(allocator); } @@ -131,20 +138,24 @@ pub const Node = struct { _ = fmt; try std.fmt.format(writer, "{{ ", .{}); for (self.values.items) |entry| { - const key = self.base.tree.tokens[entry.key]; - try std.fmt.format(writer, "{s} => {}, ", .{ - self.base.tree.source[key.start..key.end], - entry.value, - }); + const key = self.base.tree.getRaw(entry.key, entry.key); + if (entry.value) |value| { + try std.fmt.format(writer, "{s} => {}, ", .{ key, value }); + } else { + try std.fmt.format(writer, "{s} => null, ", .{key}); + } } return std.fmt.format(writer, " }}", .{}); } }; pub const List = struct { - base: Node = Node{ .tag = Tag.list, .tree = undefined }, - start: ?TokenIndex = null, - end: ?TokenIndex = null, + base: Node = Node{ + .tag = Tag.list, + .tree = undefined, + .start = undefined, + .end = undefined, + }, values: std.ArrayListUnmanaged(*Node) = .{}, pub const base_tag: Node.Tag = .list; @@ -174,15 +185,18 @@ pub const Node = struct { }; pub const Value = struct { - base: Node = Node{ .tag = Tag.value, .tree = undefined }, - start: ?TokenIndex = null, - end: ?TokenIndex = null, + base: Node = Node{ + .tag = Tag.value, + .tree = undefined, + .start = undefined, + .end = undefined, + }, + string_value: std.ArrayListUnmanaged(u8) = .{}, pub const base_tag: Node.Tag = .value; pub fn deinit(self: *Value, allocator: Allocator) void { - _ = self; - _ = allocator; + self.string_value.deinit(allocator); } pub fn format( @@ -193,11 +207,8 @@ pub const Node = struct { ) !void { _ = options; _ = fmt; - const start = self.base.tree.tokens[self.start.?]; - const end = self.base.tree.tokens[self.end.?]; - return std.fmt.format(writer, "{s}", .{ - self.base.tree.source[start.start..end.end], - }); + const raw = self.base.tree.getRaw(self.base.start, self.base.end); + return std.fmt.format(writer, "{s}", .{raw}); } }; }; @@ -233,6 +244,21 @@ pub const Tree = struct { self.docs.deinit(self.allocator); } + pub fn getDirective(self: Tree, doc_index: usize) ?[]const u8 { + assert(doc_index < self.docs.items.len); + const doc = self.docs.items[doc_index].cast(Node.Doc) orelse return null; + const id = doc.directive orelse return null; + return self.getRaw(id, id); + } + + pub fn getRaw(self: Tree, start: TokenIndex, end: TokenIndex) []const u8 { + assert(start <= end); + assert(start < self.tokens.len and end < self.tokens.len); + const start_token = self.tokens[start]; + const end_token = self.tokens[end]; + return self.source[start_token.start..end_token.end]; + } + pub fn parse(self: *Tree, source: []const u8) !void { var tokenizer = Tokenizer{ .buffer = source }; var tokens = std.ArrayList(Token).init(self.allocator); @@ -252,8 +278,8 @@ pub const Tree = struct { }); switch (token.id) { - .Eof => break, - .NewLine => { + .eof => break, + .new_line => { line += 1; prev_line_last_col = token.end; }, @@ -272,20 +298,20 @@ pub const Tree = struct { .line_cols = &self.line_cols, }; - while (true) { - if (parser.token_it.peek() == null) return; + parser.eatCommentsAndSpace(&.{}); - const pos = parser.token_it.pos; - const token = parser.token_it.next(); + while (true) { + parser.eatCommentsAndSpace(&.{}); + const token = parser.token_it.next() orelse break; - log.debug("Next token: {}, {}", .{ pos, token }); + log.debug("(main) next {s}@{d}", .{ @tagName(token.id), parser.token_it.pos - 1 }); switch (token.id) { - .Space, .Comment, .NewLine => {}, - .Eof => break, + .eof => break, else => { - const doc = try parser.doc(pos); - try self.docs.append(self.allocator, &doc.base); + parser.token_it.seekBy(-1); + const doc = try parser.doc(); + try self.docs.append(self.allocator, doc); }, } } @@ -298,355 +324,308 @@ const Parser = struct { token_it: *TokenIterator, line_cols: *const std.AutoHashMap(TokenIndex, LineCol), - fn doc(self: *Parser, start: TokenIndex) ParseError!*Node.Doc { + fn value(self: *Parser) ParseError!?*Node { + self.eatCommentsAndSpace(&.{}); + + const pos = self.token_it.pos; + const token = self.token_it.next() orelse return error.UnexpectedEof; + + log.debug(" next {s}@{d}", .{ @tagName(token.id), pos }); + + switch (token.id) { + .literal => if (self.eatToken(.map_value_ind, &.{ .new_line, .comment })) |_| { + // map + self.token_it.seekTo(pos); + return self.map(); + } else { + // leaf value + self.token_it.seekTo(pos); + return self.leaf_value(); + }, + .single_quoted, .double_quoted => { + // leaf value + self.token_it.seekBy(-1); + return self.leaf_value(); + }, + .seq_item_ind => { + // list + self.token_it.seekBy(-1); + return self.list(); + }, + .flow_seq_start => { + // list + self.token_it.seekBy(-1); + return self.list_bracketed(); + }, + else => return null, + } + } + + fn doc(self: *Parser) ParseError!*Node { const node = try self.allocator.create(Node.Doc); errdefer self.allocator.destroy(node); - node.* = .{ .start = start }; + node.* = .{}; node.base.tree = self.tree; + node.base.start = self.token_it.pos; - self.token_it.seekTo(start); - - log.debug("Doc start: {}, {}", .{ start, self.tree.tokens[start] }); + log.debug("(doc) begin {s}@{d}", .{ @tagName(self.tree.tokens[node.base.start].id), node.base.start }); - const explicit_doc: bool = if (self.eatToken(.DocStart)) |_| explicit_doc: { - if (self.eatToken(.Tag)) |_| { - node.directive = try self.expectToken(.Literal); + // Parse header + const explicit_doc: bool = if (self.eatToken(.doc_start, &.{})) |doc_pos| explicit_doc: { + if (self.getCol(doc_pos) > 0) return error.MalformedYaml; + if (self.eatToken(.tag, &.{ .new_line, .comment })) |_| { + node.directive = try self.expectToken(.literal, &.{ .new_line, .comment }); } - _ = try self.expectToken(.NewLine); break :explicit_doc true; } else false; - while (true) { - const pos = self.token_it.pos; - const token = self.token_it.next(); - - log.debug("Next token: {}, {}", .{ pos, token }); + // Parse value + node.value = try self.value(); + if (node.value == null) { + self.token_it.seekBy(-1); + } + errdefer if (node.value) |val| { + val.deinit(self.allocator); + self.allocator.destroy(val); + }; - switch (token.id) { - .Tag => { - return error.UnexpectedTag; - }, - .Literal, .SingleQuote, .DoubleQuote => { - _ = try self.expectToken(.MapValueInd); - const map_node = try self.map(pos); - node.value = &map_node.base; - }, - .SeqItemInd => { - const list_node = try self.list(pos); - node.value = &list_node.base; - }, - .FlowSeqStart => { - const list_node = try self.list_bracketed(pos); - node.value = &list_node.base; - }, - .DocEnd => { - if (explicit_doc) break; - return error.UnexpectedToken; - }, - .DocStart, .Eof => { - self.token_it.seekBy(-1); - break; - }, - else => { - return error.UnexpectedToken; - }, + // Parse footer + footer: { + if (self.eatToken(.doc_end, &.{})) |pos| { + if (!explicit_doc) return error.UnexpectedToken; + if (self.getCol(pos) > 0) return error.MalformedYaml; + node.base.end = pos; + break :footer; + } + if (self.eatToken(.doc_start, &.{})) |pos| { + if (!explicit_doc) return error.UnexpectedToken; + if (self.getCol(pos) > 0) return error.MalformedYaml; + self.token_it.seekBy(-1); + node.base.end = pos - 1; + break :footer; + } + if (self.eatToken(.eof, &.{})) |pos| { + node.base.end = pos - 1; + break :footer; } + return error.UnexpectedToken; } - node.end = self.token_it.pos - 1; + log.debug("(doc) end {s}@{d}", .{ @tagName(self.tree.tokens[node.base.end].id), node.base.end }); - log.debug("Doc end: {}, {}", .{ node.end.?, self.tree.tokens[node.end.?] }); - - return node; + return &node.base; } - fn map(self: *Parser, start: TokenIndex) ParseError!*Node.Map { + fn map(self: *Parser) ParseError!*Node { const node = try self.allocator.create(Node.Map); errdefer self.allocator.destroy(node); - node.* = .{ .start = start }; + node.* = .{}; node.base.tree = self.tree; + node.base.start = self.token_it.pos; + errdefer { + for (node.values.items) |entry| { + if (entry.value) |val| { + val.deinit(self.allocator); + self.allocator.destroy(val); + } + } + node.values.deinit(self.allocator); + } - self.token_it.seekTo(start); - - log.debug("Map start: {}, {}", .{ start, self.tree.tokens[start] }); + log.debug("(map) begin {s}@{d}", .{ @tagName(self.tree.tokens[node.base.start].id), node.base.start }); - const col = self.getCol(start); + const col = self.getCol(node.base.start); while (true) { - self.eatCommentsAndSpace(); + self.eatCommentsAndSpace(&.{}); - // Parse key. + // Parse key const key_pos = self.token_it.pos; - if (self.getCol(key_pos) != col) { + if (self.getCol(key_pos) < col) { break; } - const key = self.token_it.next(); + const key = self.token_it.next() orelse return error.UnexpectedEof; switch (key.id) { - .Literal => {}, - else => { + .literal => {}, + .doc_start, .doc_end, .eof => { self.token_it.seekBy(-1); break; }, + else => { + // TODO key not being a literal + return error.Unhandled; + }, } - log.debug("Map key: {}, '{s}'", .{ key, self.tree.source[key.start..key.end] }); + log.debug("(map) key {s}@{d}", .{ self.tree.getRaw(key_pos, key_pos), key_pos }); // Separator - _ = try self.expectToken(.MapValueInd); - - // Parse value. - const value: *Node = value: { - if (self.eatToken(.NewLine)) |_| { - self.eatCommentsAndSpace(); - - // Explicit, complex value such as list or map. - const value_pos = self.token_it.pos; - const value = self.token_it.next(); - switch (value.id) { - .Literal, .SingleQuote, .DoubleQuote => { - // Assume nested map. - const map_node = try self.map(value_pos); - break :value &map_node.base; - }, - .SeqItemInd => { - // Assume list of values. - const list_node = try self.list(value_pos); - break :value &list_node.base; - }, - else => { - log.err("{}", .{key}); - return error.Unhandled; - }, - } - } else { - self.eatCommentsAndSpace(); - - const value_pos = self.token_it.pos; - const value = self.token_it.next(); - switch (value.id) { - .Literal, .SingleQuote, .DoubleQuote => { - // Assume leaf value. - const leaf_node = try self.leaf_value(value_pos); - break :value &leaf_node.base; - }, - .FlowSeqStart => { - const list_node = try self.list_bracketed(value_pos); - break :value &list_node.base; - }, - else => { - log.err("{}", .{key}); - return error.Unhandled; - }, + _ = try self.expectToken(.map_value_ind, &.{ .new_line, .comment }); + + // Parse value + const val = try self.value(); + errdefer if (val) |v| { + v.deinit(self.allocator); + self.allocator.destroy(v); + }; + + if (val) |v| { + if (self.getCol(v.start) < self.getCol(key_pos)) { + return error.MalformedYaml; + } + if (v.cast(Node.Value)) |_| { + if (self.getCol(v.start) == self.getCol(key_pos)) { + return error.MalformedYaml; } } - }; - log.debug("Map value: {}", .{value}); + } try node.values.append(self.allocator, .{ .key = key_pos, - .value = value, + .value = val, }); - - _ = self.eatToken(.NewLine); } - node.end = self.token_it.pos - 1; + node.base.end = self.token_it.pos - 1; - log.debug("Map end: {}, {}", .{ node.end.?, self.tree.tokens[node.end.?] }); + log.debug("(map) end {s}@{d}", .{ @tagName(self.tree.tokens[node.base.end].id), node.base.end }); - return node; + return &node.base; } - fn list(self: *Parser, start: TokenIndex) ParseError!*Node.List { + fn list(self: *Parser) ParseError!*Node { const node = try self.allocator.create(Node.List); errdefer self.allocator.destroy(node); - node.* = .{ - .start = start, - }; + node.* = .{}; node.base.tree = self.tree; + node.base.start = self.token_it.pos; + errdefer { + for (node.values.items) |val| { + val.deinit(self.allocator); + self.allocator.destroy(val); + } + node.values.deinit(self.allocator); + } - self.token_it.seekTo(start); - - log.debug("List start: {}, {}", .{ start, self.tree.tokens[start] }); - - const col = self.getCol(start); + log.debug("(list) begin {s}@{d}", .{ @tagName(self.tree.tokens[node.base.start].id), node.base.start }); while (true) { - self.eatCommentsAndSpace(); + self.eatCommentsAndSpace(&.{}); - if (self.getCol(self.token_it.pos) != col) { - break; - } - _ = self.eatToken(.SeqItemInd) orelse { - break; - }; + _ = self.eatToken(.seq_item_ind, &.{}) orelse break; - const pos = self.token_it.pos; - const token = self.token_it.next(); - const value: *Node = value: { - switch (token.id) { - .Literal, .SingleQuote, .DoubleQuote => { - if (self.eatToken(.MapValueInd)) |_| { - // nested map - const map_node = try self.map(pos); - break :value &map_node.base; - } else { - // standalone (leaf) value - const leaf_node = try self.leaf_value(pos); - break :value &leaf_node.base; - } - }, - .FlowSeqStart => { - const list_node = try self.list_bracketed(pos); - break :value &list_node.base; - }, - else => { - log.err("{}", .{token}); - return error.Unhandled; - }, - } - }; - try node.values.append(self.allocator, value); - - _ = self.eatToken(.NewLine); + const val = (try self.value()) orelse return error.MalformedYaml; + try node.values.append(self.allocator, val); } - node.end = self.token_it.pos - 1; + node.base.end = self.token_it.pos - 1; - log.debug("List end: {}, {}", .{ node.end.?, self.tree.tokens[node.end.?] }); + log.debug("(list) end {s}@{d}", .{ @tagName(self.tree.tokens[node.base.end].id), node.base.end }); - return node; + return &node.base; } - fn list_bracketed(self: *Parser, start: TokenIndex) ParseError!*Node.List { + fn list_bracketed(self: *Parser) ParseError!*Node { const node = try self.allocator.create(Node.List); errdefer self.allocator.destroy(node); - node.* = .{ .start = start }; + node.* = .{}; node.base.tree = self.tree; + node.base.start = self.token_it.pos; + errdefer { + for (node.values.items) |val| { + val.deinit(self.allocator); + self.allocator.destroy(val); + } + node.values.deinit(self.allocator); + } - self.token_it.seekTo(start); - - log.debug("List start: {}, {}", .{ start, self.tree.tokens[start] }); + log.debug("(list) begin {s}@{d}", .{ @tagName(self.tree.tokens[node.base.start].id), node.base.start }); - _ = try self.expectToken(.FlowSeqStart); + _ = try self.expectToken(.flow_seq_start, &.{}); while (true) { - _ = self.eatToken(.NewLine); - self.eatCommentsAndSpace(); + self.eatCommentsAndSpace(&.{.comment}); - const pos = self.token_it.pos; - const token = self.token_it.next(); - - log.debug("Next token: {}, {}", .{ pos, token }); + if (self.eatToken(.flow_seq_end, &.{.comment})) |pos| { + node.base.end = pos; + break; + } + _ = self.eatToken(.comma, &.{.comment}); - const value: *Node = value: { - switch (token.id) { - .FlowSeqStart => { - const list_node = try self.list_bracketed(pos); - break :value &list_node.base; - }, - .FlowSeqEnd => { - break; - }, - .Literal, .SingleQuote, .DoubleQuote => { - const leaf_node = try self.leaf_value(pos); - _ = self.eatToken(.Comma); - // TODO newline - break :value &leaf_node.base; - }, - else => { - log.err("{}", .{token}); - return error.Unhandled; - }, - } - }; - try node.values.append(self.allocator, value); + const val = (try self.value()) orelse return error.MalformedYaml; + try node.values.append(self.allocator, val); } - node.end = self.token_it.pos - 1; - - log.debug("List end: {}, {}", .{ node.end.?, self.tree.tokens[node.end.?] }); + log.debug("(list) end {s}@{d}", .{ @tagName(self.tree.tokens[node.base.end].id), node.base.end }); - return node; + return &node.base; } - fn leaf_value(self: *Parser, start: TokenIndex) ParseError!*Node.Value { + fn leaf_value(self: *Parser) ParseError!*Node { const node = try self.allocator.create(Node.Value); errdefer self.allocator.destroy(node); - node.* = .{ .start = start }; + node.* = .{ .string_value = .{} }; node.base.tree = self.tree; - - self.token_it.seekTo(start); - - log.debug("Leaf start: {}, {}", .{ node.start.?, self.tree.tokens[node.start.?] }); - - parse: { - if (self.eatToken(.SingleQuote)) |_| { - node.start = node.start.? + 1; - while (true) { - const tok = self.token_it.next(); - switch (tok.id) { - .SingleQuote => { - node.end = self.token_it.pos - 2; - break :parse; - }, - .NewLine => return error.UnexpectedToken, - else => {}, - } - } - } - - if (self.eatToken(.DoubleQuote)) |_| { - node.start = node.start.? + 1; - while (true) { - const tok = self.token_it.next(); - switch (tok.id) { - .DoubleQuote => { - node.end = self.token_it.pos - 2; - break :parse; - }, - .NewLine => return error.UnexpectedToken, - else => {}, - } - } - } - - // TODO handle multiline strings in new block scope - while (true) { - const tok = self.token_it.next(); - switch (tok.id) { - .Literal => {}, - .Space => { - const trailing = self.token_it.pos - 2; - self.eatCommentsAndSpace(); - if (self.token_it.peek()) |peek| { - if (peek.id != .Literal) { - node.end = trailing; - break; - } + node.base.start = self.token_it.pos; + errdefer node.string_value.deinit(self.allocator); + + // TODO handle multiline strings in new block scope + while (self.token_it.next()) |tok| { + switch (tok.id) { + .single_quoted => { + node.base.end = self.token_it.pos - 1; + const raw = self.tree.getRaw(node.base.start, node.base.end); + try self.parseSingleQuoted(node, raw); + break; + }, + .double_quoted => { + node.base.end = self.token_it.pos - 1; + const raw = self.tree.getRaw(node.base.start, node.base.end); + try self.parseDoubleQuoted(node, raw); + break; + }, + .literal => {}, + .space => { + const trailing = self.token_it.pos - 2; + self.eatCommentsAndSpace(&.{}); + if (self.token_it.peek()) |peek| { + if (peek.id != .literal) { + node.base.end = trailing; + const raw = self.tree.getRaw(node.base.start, node.base.end); + try node.string_value.appendSlice(self.allocator, raw); + break; } - }, - else => { - self.token_it.seekBy(-1); - node.end = self.token_it.pos - 1; - break; - }, - } + } + }, + else => { + self.token_it.seekBy(-1); + node.base.end = self.token_it.pos - 1; + const raw = self.tree.getRaw(node.base.start, node.base.end); + try node.string_value.appendSlice(self.allocator, raw); + break; + }, } } - log.debug("Leaf end: {}, {}", .{ node.end.?, self.tree.tokens[node.end.?] }); + log.debug("(leaf) {s}", .{self.tree.getRaw(node.base.start, node.base.end)}); - return node; + return &node.base; } - fn eatCommentsAndSpace(self: *Parser) void { - while (true) { - _ = self.token_it.peek() orelse return; - const token = self.token_it.next(); + fn eatCommentsAndSpace(self: *Parser, comptime exclusions: []const Token.Id) void { + log.debug("eatCommentsAndSpace", .{}); + outer: while (self.token_it.next()) |token| { + log.debug(" (token '{s}')", .{@tagName(token.id)}); switch (token.id) { - .Comment, .Space => {}, + .comment, .space, .new_line => |space| { + inline for (exclusions) |excl| { + if (excl == space) { + self.token_it.seekBy(-1); + break :outer; + } + } else continue; + }, else => { self.token_it.seekBy(-1); break; @@ -655,25 +634,24 @@ const Parser = struct { } } - fn eatToken(self: *Parser, id: Token.Id) ?TokenIndex { - while (true) { - const pos = self.token_it.pos; - _ = self.token_it.peek() orelse return null; - const token = self.token_it.next(); - switch (token.id) { - .Comment, .Space => continue, - else => |next_id| if (next_id == id) { - return pos; - } else { - self.token_it.seekTo(pos); - return null; - }, - } + fn eatToken(self: *Parser, id: Token.Id, comptime exclusions: []const Token.Id) ?TokenIndex { + log.debug("eatToken('{s}')", .{@tagName(id)}); + self.eatCommentsAndSpace(exclusions); + const pos = self.token_it.pos; + const token = self.token_it.next() orelse return null; + if (token.id == id) { + log.debug(" (found at {d})", .{pos}); + return pos; + } else { + log.debug(" (not found)", .{}); + self.token_it.seekBy(-1); + return null; } } - fn expectToken(self: *Parser, id: Token.Id) ParseError!TokenIndex { - return self.eatToken(id) orelse error.UnexpectedToken; + fn expectToken(self: *Parser, id: Token.Id, comptime exclusions: []const Token.Id) ParseError!TokenIndex { + log.debug("expectToken('{s}')", .{@tagName(id)}); + return self.eatToken(id, exclusions) orelse error.UnexpectedToken; } fn getLine(self: *Parser, index: TokenIndex) usize { @@ -683,8 +661,85 @@ const Parser = struct { fn getCol(self: *Parser, index: TokenIndex) usize { return self.line_cols.get(index).?.col; } + + fn parseSingleQuoted(self: *Parser, node: *Node.Value, raw: []const u8) ParseError!void { + assert(raw[0] == '\'' and raw[raw.len - 1] == '\''); + + const raw_no_quotes = raw[1 .. raw.len - 1]; + try node.string_value.ensureTotalCapacity(self.allocator, raw_no_quotes.len); + + var state: enum { + start, + escape, + } = .start; + var index: usize = 0; + + while (index < raw_no_quotes.len) : (index += 1) { + const c = raw_no_quotes[index]; + switch (state) { + .start => switch (c) { + '\'' => { + state = .escape; + }, + else => { + node.string_value.appendAssumeCapacity(c); + }, + }, + .escape => switch (c) { + '\'' => { + state = .start; + node.string_value.appendAssumeCapacity(c); + }, + else => return error.InvalidEscapeSequence, + }, + } + } + } + + fn parseDoubleQuoted(self: *Parser, node: *Node.Value, raw: []const u8) ParseError!void { + assert(raw[0] == '"' and raw[raw.len - 1] == '"'); + + const raw_no_quotes = raw[1 .. raw.len - 1]; + try node.string_value.ensureTotalCapacity(self.allocator, raw_no_quotes.len); + + var state: enum { + start, + escape, + } = .start; + + var index: usize = 0; + while (index < raw_no_quotes.len) : (index += 1) { + const c = raw_no_quotes[index]; + switch (state) { + .start => switch (c) { + '\\' => { + state = .escape; + }, + else => { + node.string_value.appendAssumeCapacity(c); + }, + }, + .escape => switch (c) { + 'n' => { + state = .start; + node.string_value.appendAssumeCapacity('\n'); + }, + 't' => { + state = .start; + node.string_value.appendAssumeCapacity('\t'); + }, + '"' => { + state = .start; + node.string_value.appendAssumeCapacity('"'); + }, + else => return error.InvalidEscapeSequence, + }, + } + } + } }; test { + std.testing.refAllDecls(@This()); _ = @import("parse/test.zig"); } |
