feature. See also
. The project being documented here (as the example) is the Zig library itself.
Parse.parseContainerMembers
fn parseContainerMembers(p: *Parse) Error!Members
File
Code
fn parseContainerMembers(p: *Parse) Error!Members {
const scratch_top = p.scratch.items.len;
defer p.scratch.shrinkRetainingCapacity(scratch_top);
var field_state: union(enum) {
none,
seen,
end: Node.Index,
err,
} = .none;
var last_field: TokenIndex = undefined;
while (p.eatToken(.container_doc_comment)) |_| {}
var trailing = false;
while (true) {
const doc_comment = try p.eatDocComments();
sw: switch (p.tokenTag(p.tok_i)) {
.keyword_test => {
if (doc_comment) |some| {
try p.warnMsg(.{ .tag = .test_doc_comment, .token = some });
}
const maybe_test_decl_node = try p.expectTestDeclRecoverable();
if (maybe_test_decl_node) |test_decl_node| {
if (field_state == .seen) {
field_state = .{ .end = test_decl_node };
}
try p.scratch.append(p.gpa, test_decl_node);
}
trailing = false;
},
.keyword_comptime => switch (p.tokenTag(p.tok_i + 1)) {
.l_brace => {
if (doc_comment) |some| {
try p.warnMsg(.{ .tag = .comptime_doc_comment, .token = some });
}
const comptime_token = p.nextToken();
const opt_block = p.parseBlock() catch |err| switch (err) {
error.OutOfMemory => |e| return e,
error.ParseError => blk: {
p.findNextContainerMember();
break :blk null;
},
};
if (opt_block) |block| {
const comptime_node = try p.addNode(.{
.tag = .@"comptime",
.main_token = comptime_token,
.data = .{ .node = block },
});
if (field_state == .seen) {
field_state = .{ .end = comptime_node };
}
try p.scratch.append(p.gpa, comptime_node);
}
trailing = false;
},
else => {
const identifier = p.tok_i;
defer last_field = identifier;
switch (field_state) {
.none => field_state = .seen,
.err, .seen => {},
.end => |node| {
try p.warnMsg(.{
.tag = .decl_between_fields,
.token = p.nodeMainToken(node),
});
try p.warnMsg(.{
.tag = .previous_field,
.is_note = true,
.token = last_field,
});
try p.warnMsg(.{
.tag = .next_field,
.is_note = true,
.token = identifier,
});
field_state = .err;
},
}
const container_field = p.expectContainerField() catch |err| switch (err) {
error.OutOfMemory => |e| return e,
error.ParseError => {
p.findNextContainerMember();
continue;
},
};
try p.scratch.append(p.gpa, container_field);
switch (p.tokenTag(p.tok_i)) {
.comma => {
p.tok_i += 1;
trailing = true;
continue;
},
.r_brace, .eof => {
trailing = false;
break;
},
else => {},
}
// Report error but recover parser.
try p.warn(.expected_comma_after_field);
p.findNextContainerMember();
},
},
.keyword_pub,
.keyword_const,
.keyword_var,
.keyword_threadlocal,
.keyword_export,
.keyword_extern,
.keyword_inline,
.keyword_noinline,
.keyword_fn,
=> |t| {
if (t == .keyword_extern) {
switch (p.tokenTag(p.tok_i + 1)) {
.keyword_struct,
.keyword_union,
.keyword_enum,
.keyword_opaque,
=> |ct| continue :sw ct,
else => {},
}
}
if (t == .keyword_inline) {
switch (p.tokenTag(p.tok_i + 1)) {
.keyword_for,
.keyword_while,
=> |ct| continue :sw ct,
else => {},
}
}
p.tok_i += @intFromBool(t == .keyword_pub);
const opt_top_level_decl = try p.expectTopLevelDeclRecoverable();
if (opt_top_level_decl) |top_level_decl| {
if (field_state == .seen) {
field_state = .{ .end = top_level_decl };
}
try p.scratch.append(p.gpa, top_level_decl);
}
trailing = p.tokenTag(p.tok_i - 1) == .semicolon;
},
.eof, .r_brace => {
if (doc_comment) |tok| {
try p.warnMsg(.{
.tag = .unattached_doc_comment,
.token = tok,
});
}
break;
},
else => {
const c_container = p.parseCStyleContainer() catch |err| switch (err) {
error.OutOfMemory => |e| return e,
error.ParseError => false,
};
if (c_container) continue;
const identifier = p.tok_i;
defer last_field = identifier;
switch (field_state) {
.none => field_state = .seen,
.err, .seen => {},
.end => |node| {
try p.warnMsg(.{
.tag = .decl_between_fields,
.token = p.nodeMainToken(node),
});
try p.warnMsg(.{
.tag = .previous_field,
.is_note = true,
.token = last_field,
});
try p.warnMsg(.{
.tag = .next_field,
.is_note = true,
.token = identifier,
});
field_state = .err;
},
}
const container_field = p.expectContainerField() catch |err| switch (err) {
error.OutOfMemory => |e| return e,
error.ParseError => {
p.findNextContainerMember();
continue;
},
};
try p.scratch.append(p.gpa, container_field);
switch (p.tokenTag(p.tok_i)) {
.comma => {
p.tok_i += 1;
trailing = true;
continue;
},
.r_brace, .eof => {
trailing = false;
break;
},
else => {},
}
// Report error but recover parser.
try p.warn(.expected_comma_after_field);
if (p.tokenTag(p.tok_i) == .semicolon and p.tokenTag(identifier) == .identifier) {
try p.warnMsg(.{
.tag = .var_const_decl,
.is_note = true,
.token = identifier,
});
}
p.findNextContainerMember();
continue;
},
}
}
const items = p.scratch.items[scratch_top..];
if (items.len <= 2) {
return Members{
.len = items.len,
.data = .{ .opt_node_and_opt_node = .{
if (items.len >= 1) items[0].toOptional() else .none,
if (items.len >= 2) items[1].toOptional() else .none,
} },
.trailing = trailing,
};
} else {
return Members{
.len = items.len,
.data = .{ .extra_range = try p.listToSpan(items) },
.trailing = trailing,
};
}
}