aboutsummaryrefslogtreecommitdiff
path: root/src/link/tapi/parse.zig
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2023-04-01 14:22:29 +0200
committerGitHub <noreply@github.com>2023-04-01 14:22:29 +0200
commit381dc2d9509ffeaf60a1775ee0983c7dd1b9e346 (patch)
tree34dc901cf638a113cd7bf84d90125b9c64685b3d /src/link/tapi/parse.zig
parent575a98a0813181c268e86d590b6267583736bafd (diff)
parenta0a854b1fc5e64a07b2026ac0668f8d417ab6bf1 (diff)
downloadzig-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.zig701
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");
}