stage2 parser: token ids in their own array

To prevent cache misses, token ids go in their own array, and the
start/end offsets go in a different one.

perf measurement before:
         2,667,914      cache-misses:u
     2,139,139,935      instructions:u
       894,167,331      cycles:u

perf measurement after:
         1,757,723      cache-misses:u
     2,069,932,298      instructions:u
       858,105,570      cycles:u
master
Andrew Kelley 2020-05-22 12:34:12 -04:00
parent 295bca9b5f
commit 8df0841d6a
6 changed files with 217 additions and 240 deletions

View File

@ -10,7 +10,8 @@ pub const NodeIndex = usize;
pub const Tree = struct {
/// Reference to externally-owned data.
source: []const u8,
tokens: []const Token,
token_ids: []const Token.Id,
token_locs: []const Token.Loc,
errors: []const Error,
/// undefined on parse error (when errors field is not empty)
root_node: *Node.Root,
@ -23,26 +24,27 @@ pub const Tree = struct {
generated: bool = false,
pub fn deinit(self: *Tree) void {
self.gpa.free(self.tokens);
self.gpa.free(self.token_ids);
self.gpa.free(self.token_locs);
self.gpa.free(self.errors);
self.arena.promote(self.gpa).deinit();
}
pub fn renderError(self: *Tree, parse_error: *const Error, stream: var) !void {
return parse_error.render(self.tokens, stream);
return parse_error.render(self.token_ids, stream);
}
pub fn tokenSlice(self: *Tree, token_index: TokenIndex) []const u8 {
return self.tokenSlicePtr(self.tokens[token_index]);
return self.tokenSliceLoc(self.token_locs[token_index]);
}
pub fn tokenSlicePtr(self: *Tree, token: Token) []const u8 {
pub fn tokenSliceLoc(self: *Tree, token: Token.Loc) []const u8 {
return self.source[token.start..token.end];
}
pub fn getNodeSource(self: *const Tree, node: *const Node) []const u8 {
const first_token = self.tokens[node.firstToken()];
const last_token = self.tokens[node.lastToken()];
const first_token = self.token_locs[node.firstToken()];
const last_token = self.token_locs[node.lastToken()];
return self.source[first_token.start..last_token.end];
}
@ -54,7 +56,7 @@ pub const Tree = struct {
};
/// Return the Location of the token relative to the offset specified by `start_index`.
pub fn tokenLocationPtr(self: *Tree, start_index: usize, token: Token) Location {
pub fn tokenLocationLoc(self: *Tree, start_index: usize, token: Token.Loc) Location {
var loc = Location{
.line = 0,
.column = 0,
@ -82,14 +84,14 @@ pub const Tree = struct {
}
pub fn tokenLocation(self: *Tree, start_index: usize, token_index: TokenIndex) Location {
return self.tokenLocationPtr(start_index, self.tokens[token_index]);
return self.tokenLocationLoc(start_index, self.token_locs[token_index]);
}
pub fn tokensOnSameLine(self: *Tree, token1_index: TokenIndex, token2_index: TokenIndex) bool {
return self.tokensOnSameLinePtr(self.tokens[token1_index], self.tokens[token2_index]);
return self.tokensOnSameLineLoc(self.token_locs[token1_index], self.token_locs[token2_index]);
}
pub fn tokensOnSameLinePtr(self: *Tree, token1: Token, token2: Token) bool {
pub fn tokensOnSameLineLoc(self: *Tree, token1: Token.Loc, token2: Token.Loc) bool {
return mem.indexOfScalar(u8, self.source[token1.end..token2.start], '\n') == null;
}
@ -100,7 +102,7 @@ pub const Tree = struct {
/// Skips over comments
pub fn prevToken(self: *Tree, token_index: TokenIndex) TokenIndex {
var index = token_index - 1;
while (self.tokens[index].id == Token.Id.LineComment) {
while (self.token_ids[index] == Token.Id.LineComment) {
index -= 1;
}
return index;
@ -109,7 +111,7 @@ pub const Tree = struct {
/// Skips over comments
pub fn nextToken(self: *Tree, token_index: TokenIndex) TokenIndex {
var index = token_index + 1;
while (self.tokens[index].id == Token.Id.LineComment) {
while (self.token_ids[index] == Token.Id.LineComment) {
index += 1;
}
return index;
@ -166,7 +168,7 @@ pub const Error = union(enum) {
DeclBetweenFields: DeclBetweenFields,
InvalidAnd: InvalidAnd,
pub fn render(self: *const Error, tokens: []const Token, stream: var) !void {
pub fn render(self: *const Error, tokens: []const Token.Id, stream: var) !void {
switch (self.*) {
.InvalidToken => |*x| return x.render(tokens, stream),
.ExpectedContainerMembers => |*x| return x.render(tokens, stream),
@ -321,7 +323,7 @@ pub const Error = union(enum) {
pub const ExpectedCall = struct {
node: *Node,
pub fn render(self: *const ExpectedCall, tokens: []const Token, stream: var) !void {
pub fn render(self: *const ExpectedCall, tokens: []const Token.Id, stream: var) !void {
return stream.print("expected " ++ @tagName(Node.Id.Call) ++ ", found {}", .{
@tagName(self.node.id),
});
@ -331,7 +333,7 @@ pub const Error = union(enum) {
pub const ExpectedCallOrFnProto = struct {
node: *Node,
pub fn render(self: *const ExpectedCallOrFnProto, tokens: []const Token, stream: var) !void {
pub fn render(self: *const ExpectedCallOrFnProto, tokens: []const Token.Id, stream: var) !void {
return stream.print("expected " ++ @tagName(Node.Id.Call) ++ " or " ++
@tagName(Node.Id.FnProto) ++ ", found {}", .{@tagName(self.node.id)});
}
@ -341,14 +343,14 @@ pub const Error = union(enum) {
token: TokenIndex,
expected_id: Token.Id,
pub fn render(self: *const ExpectedToken, tokens: []const Token, stream: var) !void {
pub fn render(self: *const ExpectedToken, tokens: []const Token.Id, stream: var) !void {
const found_token = tokens[self.token];
switch (found_token.id) {
switch (found_token) {
.Invalid => {
return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()});
},
else => {
const token_name = found_token.id.symbol();
const token_name = found_token.symbol();
return stream.print("expected '{}', found '{}'", .{ self.expected_id.symbol(), token_name });
},
}
@ -359,11 +361,11 @@ pub const Error = union(enum) {
token: TokenIndex,
end_id: Token.Id,
pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token, stream: var) !void {
pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Id, stream: var) !void {
const actual_token = tokens[self.token];
return stream.print("expected ',' or '{}', found '{}'", .{
self.end_id.symbol(),
actual_token.id.symbol(),
actual_token.symbol(),
});
}
};
@ -374,9 +376,9 @@ pub const Error = union(enum) {
token: TokenIndex,
pub fn render(self: *const ThisError, tokens: []const Token, stream: var) !void {
pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: var) !void {
const actual_token = tokens[self.token];
return stream.print(msg, .{actual_token.id.symbol()});
return stream.print(msg, .{actual_token.symbol()});
}
};
}
@ -387,7 +389,7 @@ pub const Error = union(enum) {
token: TokenIndex,
pub fn render(self: *const ThisError, tokens: []const Token, stream: var) !void {
pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: var) !void {
return stream.writeAll(msg);
}
};

View File

@ -16,28 +16,32 @@ pub const Error = error{ParseError} || Allocator.Error;
pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!*Tree {
// TODO optimization idea: ensureCapacity on the tokens list and
// then appendAssumeCapacity inside the loop.
var tokens = std.ArrayList(Token).init(gpa);
defer tokens.deinit();
var token_ids = std.ArrayList(Token.Id).init(gpa);
defer token_ids.deinit();
var token_locs = std.ArrayList(Token.Loc).init(gpa);
defer token_locs.deinit();
var tokenizer = std.zig.Tokenizer.init(source);
while (true) {
const tree_token = try tokens.addOne();
tree_token.* = tokenizer.next();
if (tree_token.id == .Eof) break;
const token = tokenizer.next();
try token_ids.append(token.id);
try token_locs.append(token.loc);
if (token.id == .Eof) break;
}
var parser: Parser = .{
.source = source,
.arena = std.heap.ArenaAllocator.init(gpa),
.gpa = gpa,
.tokens = tokens.items,
.token_ids = token_ids.items,
.token_locs = token_locs.items,
.errors = .{},
.tok_i = 0,
};
defer parser.errors.deinit(gpa);
errdefer parser.arena.deinit();
while (tokens.items[parser.tok_i].id == .LineComment) parser.tok_i += 1;
while (token_ids.items[parser.tok_i] == .LineComment) parser.tok_i += 1;
const root_node = try parser.parseRoot();
@ -45,7 +49,8 @@ pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!*Tree {
tree.* = .{
.gpa = gpa,
.source = source,
.tokens = tokens.toOwnedSlice(),
.token_ids = token_ids.toOwnedSlice(),
.token_locs = token_locs.toOwnedSlice(),
.errors = parser.errors.toOwnedSlice(gpa),
.root_node = root_node,
.arena = parser.arena.state,
@ -58,9 +63,8 @@ const Parser = struct {
arena: std.heap.ArenaAllocator,
gpa: *Allocator,
source: []const u8,
/// TODO: Optimization idea: have this be several arrays of the token fields rather
/// than an array of structs.
tokens: []const Token,
token_ids: []const Token.Id,
token_locs: []const Token.Loc,
tok_i: TokenIndex,
errors: std.ArrayListUnmanaged(AstError),
@ -80,19 +84,6 @@ const Parser = struct {
return node;
}
/// Helper function for appending elements to a singly linked list.
fn llpush(
p: *Parser,
comptime T: type,
it: *?*std.SinglyLinkedList(T).Node,
data: T,
) !*?*std.SinglyLinkedList(T).Node {
const llnode = try p.arena.allocator.create(std.SinglyLinkedList(T).Node);
llnode.* = .{ .data = data };
it.* = llnode;
return &llnode.next;
}
/// ContainerMembers
/// <- TestDecl ContainerMembers
/// / TopLevelComptime ContainerMembers
@ -228,7 +219,7 @@ const Parser = struct {
// try to continue parsing
const index = p.tok_i;
p.findNextContainerMember();
const next = p.tokens[p.tok_i].id;
const next = p.token_ids[p.tok_i];
switch (next) {
.Eof => break,
else => {
@ -257,7 +248,7 @@ const Parser = struct {
});
}
const next = p.tokens[p.tok_i].id;
const next = p.token_ids[p.tok_i];
switch (next) {
.Eof => break,
.Keyword_comptime => {
@ -291,7 +282,7 @@ const Parser = struct {
var level: u32 = 0;
while (true) {
const tok = p.nextToken();
switch (tok.ptr.id) {
switch (p.token_ids[tok]) {
// any of these can start a new top level declaration
.Keyword_test,
.Keyword_comptime,
@ -308,7 +299,7 @@ const Parser = struct {
.Identifier,
=> {
if (level == 0) {
p.putBackToken(tok.index);
p.putBackToken(tok);
return;
}
},
@ -325,13 +316,13 @@ const Parser = struct {
.RBrace => {
if (level == 0) {
// end of container, exit
p.putBackToken(tok.index);
p.putBackToken(tok);
return;
}
level -= 1;
},
.Eof => {
p.putBackToken(tok.index);
p.putBackToken(tok);
return;
},
else => {},
@ -344,11 +335,11 @@ const Parser = struct {
var level: u32 = 0;
while (true) {
const tok = p.nextToken();
switch (tok.ptr.id) {
switch (p.token_ids[tok]) {
.LBrace => level += 1,
.RBrace => {
if (level == 0) {
p.putBackToken(tok.index);
p.putBackToken(tok);
return;
}
level -= 1;
@ -359,7 +350,7 @@ const Parser = struct {
}
},
.Eof => {
p.putBackToken(tok.index);
p.putBackToken(tok);
return;
},
else => {},
@ -454,8 +445,8 @@ const Parser = struct {
}
if (extern_export_inline_token) |token| {
if (p.tokens[token].id == .Keyword_inline or
p.tokens[token].id == .Keyword_noinline)
if (p.token_ids[token] == .Keyword_inline or
p.token_ids[token] == .Keyword_noinline)
{
try p.errors.append(p.gpa, .{
.ExpectedFn = .{ .token = p.tok_i },
@ -722,7 +713,7 @@ const Parser = struct {
const defer_token = p.eatToken(.Keyword_defer) orelse p.eatToken(.Keyword_errdefer);
if (defer_token) |token| {
const payload = if (p.tokens[token].id == .Keyword_errdefer)
const payload = if (p.token_ids[token] == .Keyword_errdefer)
try p.parsePayload()
else
null;
@ -2269,7 +2260,7 @@ const Parser = struct {
/// / EQUAL
fn parseAssignOp(p: *Parser) !?*Node {
const token = p.nextToken();
const op: Node.InfixOp.Op = switch (token.ptr.id) {
const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
.AsteriskEqual => .AssignMul,
.SlashEqual => .AssignDiv,
.PercentEqual => .AssignMod,
@ -2285,14 +2276,14 @@ const Parser = struct {
.MinusPercentEqual => .AssignSubWrap,
.Equal => .Assign,
else => {
p.putBackToken(token.index);
p.putBackToken(token);
return null;
},
};
const node = try p.arena.allocator.create(Node.InfixOp);
node.* = .{
.op_token = token.index,
.op_token = token,
.lhs = undefined, // set by caller
.op = op,
.rhs = undefined, // set by caller
@ -2309,7 +2300,7 @@ const Parser = struct {
/// / RARROWEQUAL
fn parseCompareOp(p: *Parser) !?*Node {
const token = p.nextToken();
const op: Node.InfixOp.Op = switch (token.ptr.id) {
const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
.EqualEqual => .EqualEqual,
.BangEqual => .BangEqual,
.AngleBracketLeft => .LessThan,
@ -2317,12 +2308,12 @@ const Parser = struct {
.AngleBracketLeftEqual => .LessOrEqual,
.AngleBracketRightEqual => .GreaterOrEqual,
else => {
p.putBackToken(token.index);
p.putBackToken(token);
return null;
},
};
return p.createInfixOp(token.index, op);
return p.createInfixOp(token, op);
}
/// BitwiseOp
@ -2333,19 +2324,19 @@ const Parser = struct {
/// / KEYWORD_catch Payload?
fn parseBitwiseOp(p: *Parser) !?*Node {
const token = p.nextToken();
const op: Node.InfixOp.Op = switch (token.ptr.id) {
const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
.Ampersand => .BitAnd,
.Caret => .BitXor,
.Pipe => .BitOr,
.Keyword_orelse => .UnwrapOptional,
.Keyword_catch => .{ .Catch = try p.parsePayload() },
else => {
p.putBackToken(token.index);
p.putBackToken(token);
return null;
},
};
return p.createInfixOp(token.index, op);
return p.createInfixOp(token, op);
}
/// BitShiftOp
@ -2353,16 +2344,16 @@ const Parser = struct {
/// / RARROW2
fn parseBitShiftOp(p: *Parser) !?*Node {
const token = p.nextToken();
const op: Node.InfixOp.Op = switch (token.ptr.id) {
const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
.AngleBracketAngleBracketLeft => .BitShiftLeft,
.AngleBracketAngleBracketRight => .BitShiftRight,
else => {
p.putBackToken(token.index);
p.putBackToken(token);
return null;
},
};
return p.createInfixOp(token.index, op);
return p.createInfixOp(token, op);
}
/// AdditionOp
@ -2373,19 +2364,19 @@ const Parser = struct {
/// / MINUSPERCENT
fn parseAdditionOp(p: *Parser) !?*Node {
const token = p.nextToken();
const op: Node.InfixOp.Op = switch (token.ptr.id) {
const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
.Plus => .Add,
.Minus => .Sub,
.PlusPlus => .ArrayCat,
.PlusPercent => .AddWrap,
.MinusPercent => .SubWrap,
else => {
p.putBackToken(token.index);
p.putBackToken(token);
return null;
},
};
return p.createInfixOp(token.index, op);
return p.createInfixOp(token, op);
}
/// MultiplyOp
@ -2397,7 +2388,7 @@ const Parser = struct {
/// / ASTERISKPERCENT
fn parseMultiplyOp(p: *Parser) !?*Node {
const token = p.nextToken();
const op: Node.InfixOp.Op = switch (token.ptr.id) {
const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
.PipePipe => .MergeErrorSets,
.Asterisk => .Mul,
.Slash => .Div,
@ -2405,12 +2396,12 @@ const Parser = struct {
.AsteriskAsterisk => .ArrayMult,
.AsteriskPercent => .MulWrap,
else => {
p.putBackToken(token.index);
p.putBackToken(token);
return null;
},
};
return p.createInfixOp(token.index, op);
return p.createInfixOp(token, op);
}
/// PrefixOp
@ -2423,7 +2414,7 @@ const Parser = struct {
/// / KEYWORD_await
fn parsePrefixOp(p: *Parser) !?*Node {
const token = p.nextToken();
const op: Node.PrefixOp.Op = switch (token.ptr.id) {
const op: Node.PrefixOp.Op = switch (p.token_ids[token]) {
.Bang => .BoolNot,
.Minus => .Negation,
.Tilde => .BitNot,
@ -2432,14 +2423,14 @@ const Parser = struct {
.Keyword_try => .Try,
.Keyword_await => .Await,
else => {
p.putBackToken(token.index);
p.putBackToken(token);
return null;
},
};
const node = try p.arena.allocator.create(Node.PrefixOp);
node.* = .{
.op_token = token.index,
.op_token = token,
.op = op,
.rhs = undefined, // set by caller
};
@ -2493,7 +2484,7 @@ const Parser = struct {
// If the token encountered was **, there will be two nodes instead of one.
// The attributes should be applied to the rightmost operator.
const prefix_op = node.cast(Node.PrefixOp).?;
var ptr_info = if (p.tokens[prefix_op.op_token].id == .AsteriskAsterisk)
var ptr_info = if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk)
&prefix_op.rhs.cast(Node.PrefixOp).?.op.PtrType
else
&prefix_op.op.PtrType;
@ -2812,7 +2803,8 @@ const Parser = struct {
return null;
};
if (p.eatToken(.Identifier)) |ident| {
const token_slice = p.source[p.tokens[ident].start..p.tokens[ident].end];
const token_loc = p.token_locs[ident];
const token_slice = p.source[token_loc.start..token_loc.end];
if (!std.mem.eql(u8, token_slice, "c")) {
p.putBackToken(ident);
} else {
@ -2879,7 +2871,7 @@ const Parser = struct {
fn parseContainerDeclType(p: *Parser) !?ContainerDeclType {
const kind_token = p.nextToken();
const init_arg_expr = switch (kind_token.ptr.id) {
const init_arg_expr = switch (p.token_ids[kind_token]) {
.Keyword_struct => Node.ContainerDecl.InitArg{ .None = {} },
.Keyword_enum => blk: {
if (p.eatToken(.LParen) != null) {
@ -2914,13 +2906,13 @@ const Parser = struct {
break :blk Node.ContainerDecl.InitArg{ .None = {} };
},
else => {
p.putBackToken(kind_token.index);
p.putBackToken(kind_token);
return null;
},
};
return ContainerDeclType{
.kind_token = kind_token.index,
.kind_token = kind_token,
.init_arg_expr = init_arg_expr,
};
}
@ -2973,7 +2965,7 @@ const Parser = struct {
while (try nodeParseFn(p)) |item| {
try list.append(item);
switch (p.tokens[p.tok_i].id) {
switch (p.token_ids[p.tok_i]) {
.Comma => _ = p.nextToken(),
// all possible delimiters
.Colon, .RParen, .RBrace, .RBracket => break,
@ -2994,13 +2986,13 @@ const Parser = struct {
fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.InfixOp.Op) NodeParseFn {
return struct {
pub fn parse(p: *Parser) Error!?*Node {
const op_token = if (token == .Keyword_and) switch (p.tokens[p.tok_i].id) {
.Keyword_and => p.nextToken().index,
const op_token = if (token == .Keyword_and) switch (p.token_ids[p.tok_i]) {
.Keyword_and => p.nextToken(),
.Invalid_ampersands => blk: {
try p.errors.append(p.gpa, .{
.InvalidAnd = .{ .token = p.tok_i },
});
break :blk p.nextToken().index;
break :blk p.nextToken();
},
else => return null,
} else p.eatToken(token) orelse return null;
@ -3104,7 +3096,7 @@ const Parser = struct {
var tok_i = start_tok_i;
var count: usize = 1; // including first_line
while (true) : (tok_i += 1) {
switch (p.tokens[tok_i].id) {
switch (p.token_ids[tok_i]) {
.LineComment => continue,
.MultilineStringLiteralLine => count += 1,
else => break,
@ -3118,7 +3110,7 @@ const Parser = struct {
lines[0] = first_line;
count = 1;
while (true) : (tok_i += 1) {
switch (p.tokens[tok_i].id) {
switch (p.token_ids[tok_i]) {
.LineComment => continue,
.MultilineStringLiteralLine => {
lines[count] = tok_i;
@ -3215,7 +3207,7 @@ const Parser = struct {
}
fn tokensOnSameLine(p: *Parser, token1: TokenIndex, token2: TokenIndex) bool {
return std.mem.indexOfScalar(u8, p.source[p.tokens[token1].end..p.tokens[token2].start], '\n') == null;
return std.mem.indexOfScalar(u8, p.source[p.token_locs[token1].end..p.token_locs[token2].start], '\n') == null;
}
/// Eat a single-line doc comment on the same line as another node
@ -3239,7 +3231,7 @@ const Parser = struct {
.PrefixOp => {
var prefix_op = rightmost_op.cast(Node.PrefixOp).?;
// If the token encountered was **, there will be two nodes
if (p.tokens[prefix_op.op_token].id == .AsteriskAsterisk) {
if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk) {
rightmost_op = prefix_op.rhs;
prefix_op = rightmost_op.cast(Node.PrefixOp).?;
}
@ -3328,11 +3320,7 @@ const Parser = struct {
}
fn eatToken(p: *Parser, id: Token.Id) ?TokenIndex {
return if (p.eatAnnotatedToken(id)) |token| token.index else null;
}
fn eatAnnotatedToken(p: *Parser, id: Token.Id) ?AnnotatedToken {
return if (p.tokens[p.tok_i].id == id) p.nextToken() else null;
return if (p.token_ids[p.tok_i] == id) p.nextToken() else null;
}
fn expectToken(p: *Parser, id: Token.Id) Error!TokenIndex {
@ -3341,29 +3329,25 @@ const Parser = struct {
fn expectTokenRecoverable(p: *Parser, id: Token.Id) !?TokenIndex {
const token = p.nextToken();
if (token.ptr.id != id) {
if (p.token_ids[token] != id) {
try p.errors.append(p.gpa, .{
.ExpectedToken = .{ .token = token.index, .expected_id = id },
.ExpectedToken = .{ .token = token, .expected_id = id },
});
// go back so that we can recover properly
p.putBackToken(token.index);
p.putBackToken(token);
return null;
}
return token.index;
return token;
}
fn nextToken(p: *Parser) AnnotatedToken {
const result = AnnotatedToken{
.index = p.tok_i,
.ptr = &p.tokens[p.tok_i],
};
fn nextToken(p: *Parser) TokenIndex {
const result = p.tok_i;
p.tok_i += 1;
assert(result.ptr.id != .LineComment);
if (p.tok_i >= p.tokens.len) return result;
assert(p.token_ids[result] != .LineComment);
if (p.tok_i >= p.token_ids.len) return result;
while (true) {
const next_tok = p.tokens[p.tok_i];
if (next_tok.id != .LineComment) return result;
if (p.token_ids[p.tok_i] != .LineComment) return result;
p.tok_i += 1;
}
}
@ -3371,18 +3355,12 @@ const Parser = struct {
fn putBackToken(p: *Parser, putting_back: TokenIndex) void {
while (p.tok_i > 0) {
p.tok_i -= 1;
const prev_tok = p.tokens[p.tok_i];
if (prev_tok.id == .LineComment) continue;
if (p.token_ids[p.tok_i] == .LineComment) continue;
assert(putting_back == p.tok_i);
return;
}
}
const AnnotatedToken = struct {
index: TokenIndex,
ptr: *const Token,
};
fn expectNode(
p: *Parser,
parseFn: NodeParseFn,

View File

@ -3181,7 +3181,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
defer tree.deinit();
for (tree.errors) |*parse_error| {
const token = tree.tokens[parse_error.loc()];
const token = tree.token_locs[parse_error.loc()];
const loc = tree.tokenLocation(0, parse_error.loc());
try stderr.print("(memory buffer):{}:{}: error: ", .{ loc.line + 1, loc.column + 1 });
try tree.renderError(parse_error, stderr);

View File

@ -68,11 +68,12 @@ fn renderRoot(
tree: *ast.Tree,
) (@TypeOf(stream).Error || Error)!void {
// render all the line comments at the beginning of the file
for (tree.tokens) |token, i| {
if (token.id != .LineComment) break;
try stream.print("{}\n", .{mem.trimRight(u8, tree.tokenSlicePtr(token), " ")});
const next_token = &tree.tokens[i + 1];
const loc = tree.tokenLocationPtr(token.end, next_token.*);
for (tree.token_ids) |token_id, i| {
if (token_id != .LineComment) break;
const token_loc = tree.token_locs[i];
try stream.print("{}\n", .{mem.trimRight(u8, tree.tokenSliceLoc(token_loc), " ")});
const next_token = tree.token_locs[i + 1];
const loc = tree.tokenLocationLoc(token_loc.end, next_token);
if (loc.line >= 2) {
try stream.writeByte('\n');
}
@ -101,8 +102,8 @@ fn renderRoot(
while (token_index != 0) {
token_index -= 1;
const token = tree.tokens[token_index];
switch (token.id) {
const token_id = tree.token_ids[token_index];
switch (token_id) {
.LineComment => {},
.DocComment => {
copy_start_token_index = token_index;
@ -111,12 +112,13 @@ fn renderRoot(
else => break,
}
if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: off")) {
const token_loc = tree.token_locs[token_index];
if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: off")) {
if (!found_fmt_directive) {
fmt_active = false;
found_fmt_directive = true;
}
} else if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: on")) {
} else if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: on")) {
if (!found_fmt_directive) {
fmt_active = true;
found_fmt_directive = true;
@ -135,7 +137,7 @@ fn renderRoot(
if (decl_i >= root_decls.len) {
// If there's no next reformatted `decl`, just copy the
// remaining input tokens and bail out.
const start = tree.tokens[copy_start_token_index].start;
const start = tree.token_locs[copy_start_token_index].start;
try copyFixingWhitespace(stream, tree.source[start..]);
return;
}
@ -143,15 +145,16 @@ fn renderRoot(
var decl_first_token_index = decl.firstToken();
while (token_index < decl_first_token_index) : (token_index += 1) {
const token = tree.tokens[token_index];
switch (token.id) {
const token_id = tree.token_ids[token_index];
switch (token_id) {
.LineComment => {},
.Eof => unreachable,
else => continue,
}
if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: on")) {
const token_loc = tree.token_locs[token_index];
if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: on")) {
fmt_active = true;
} else if (mem.eql(u8, mem.trim(u8, tree.tokenSlicePtr(token)[2..], " "), "zig fmt: off")) {
} else if (mem.eql(u8, mem.trim(u8, tree.tokenSliceLoc(token_loc)[2..], " "), "zig fmt: off")) {
fmt_active = false;
}
}
@ -163,8 +166,8 @@ fn renderRoot(
token_index = copy_end_token_index;
while (token_index != 0) {
token_index -= 1;
const token = tree.tokens[token_index];
switch (token.id) {
const token_id = tree.token_ids[token_index];
switch (token_id) {
.LineComment => {},
.DocComment => {
copy_end_token_index = token_index;
@ -174,8 +177,8 @@ fn renderRoot(
}
}
const start = tree.tokens[copy_start_token_index].start;
const end = tree.tokens[copy_end_token_index].start;
const start = tree.token_locs[copy_start_token_index].start;
const end = tree.token_locs[copy_end_token_index].start;
try copyFixingWhitespace(stream, tree.source[start..end]);
}
@ -194,13 +197,13 @@ fn renderExtraNewlineToken(tree: *ast.Tree, stream: var, start_col: *usize, firs
var prev_token = first_token;
if (prev_token == 0) return;
var newline_threshold: usize = 2;
while (tree.tokens[prev_token - 1].id == .DocComment) {
if (tree.tokenLocation(tree.tokens[prev_token - 1].end, prev_token).line == 1) {
while (tree.token_ids[prev_token - 1] == .DocComment) {
if (tree.tokenLocation(tree.token_locs[prev_token - 1].end, prev_token).line == 1) {
newline_threshold += 1;
}
prev_token -= 1;
}
const prev_token_end = tree.tokens[prev_token - 1].end;
const prev_token_end = tree.token_locs[prev_token - 1].end;
const loc = tree.tokenLocation(prev_token_end, first_token);
if (loc.line >= newline_threshold) {
try stream.writeByte('\n');
@ -265,7 +268,7 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree,
const src_has_trailing_comma = blk: {
const maybe_comma = tree.nextToken(field.lastToken());
break :blk tree.tokens[maybe_comma].id == .Comma;
break :blk tree.token_ids[maybe_comma] == .Comma;
};
// The trailing comma is emitted at the end, but if it's not present
@ -327,11 +330,11 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree,
.DocComment => {
const comment = @fieldParentPtr(ast.Node.DocComment, "base", decl);
const kind = tree.tokens[comment.first_line].id;
const kind = tree.token_ids[comment.first_line];
try renderToken(tree, stream, comment.first_line, indent, start_col, .Newline);
var tok_i = comment.first_line + 1;
while (true) : (tok_i += 1) {
const tok_id = tree.tokens[tok_i].id;
const tok_id = tree.token_ids[tok_i];
if (tok_id == kind) {
try stream.writeByteNTimes(' ', indent);
try renderToken(tree, stream, tok_i, indent, start_col, .Newline);
@ -436,13 +439,13 @@ fn renderExpression(
try renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.lhs, op_space);
const after_op_space = blk: {
const loc = tree.tokenLocation(tree.tokens[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token));
const loc = tree.tokenLocation(tree.token_locs[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token));
break :blk if (loc.line == 0) op_space else Space.Newline;
};
try renderToken(tree, stream, infix_op_node.op_token, indent, start_col, after_op_space);
if (after_op_space == Space.Newline and
tree.tokens[tree.nextToken(infix_op_node.op_token)].id != .MultilineStringLiteralLine)
tree.token_ids[tree.nextToken(infix_op_node.op_token)] != .MultilineStringLiteralLine)
{
try stream.writeByteNTimes(' ', indent + indent_delta);
start_col.* = indent + indent_delta;
@ -463,10 +466,10 @@ fn renderExpression(
switch (prefix_op_node.op) {
.PtrType => |ptr_info| {
const op_tok_id = tree.tokens[prefix_op_node.op_token].id;
const op_tok_id = tree.token_ids[prefix_op_node.op_token];
switch (op_tok_id) {
.Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
.LBracket => if (tree.tokens[prefix_op_node.op_token + 2].id == .Identifier)
.LBracket => if (tree.token_ids[prefix_op_node.op_token + 2] == .Identifier)
try stream.writeAll("[*c")
else
try stream.writeAll("[*"),
@ -578,8 +581,8 @@ fn renderExpression(
try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
const starts_with_comment = tree.tokens[lbracket + 1].id == .LineComment;
const ends_with_comment = tree.tokens[rbracket - 1].id == .LineComment;
const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
const new_indent = if (ends_with_comment) indent + indent_delta else indent;
const new_space = if (ends_with_comment) Space.Newline else Space.None;
try renderExpression(allocator, stream, tree, new_indent, start_col, array_info.len_expr, new_space);
@ -653,7 +656,7 @@ fn renderExpression(
return renderToken(tree, stream, rtoken, indent, start_col, space);
}
if (exprs.len == 1 and tree.tokens[exprs[0].lastToken() + 1].id == .RBrace) {
if (exprs.len == 1 and tree.token_ids[exprs[0].lastToken() + 1] == .RBrace) {
const expr = exprs[0];
switch (lhs) {
.dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
@ -675,17 +678,17 @@ fn renderExpression(
for (exprs) |expr, i| {
if (i + 1 < exprs.len) {
const expr_last_token = expr.lastToken() + 1;
const loc = tree.tokenLocation(tree.tokens[expr_last_token].end, exprs[i+1].firstToken());
const loc = tree.tokenLocation(tree.token_locs[expr_last_token].end, exprs[i+1].firstToken());
if (loc.line != 0) break :blk count;
count += 1;
} else {
const expr_last_token = expr.lastToken();
const loc = tree.tokenLocation(tree.tokens[expr_last_token].end, rtoken);
const loc = tree.tokenLocation(tree.token_locs[expr_last_token].end, rtoken);
if (loc.line == 0) {
// all on one line
const src_has_trailing_comma = trailblk: {
const maybe_comma = tree.prevToken(rtoken);
break :trailblk tree.tokens[maybe_comma].id == .Comma;
break :trailblk tree.token_ids[maybe_comma] == .Comma;
};
if (src_has_trailing_comma) {
break :blk 1; // force row size 1
@ -723,7 +726,7 @@ fn renderExpression(
var new_indent = indent + indent_delta;
if (tree.tokens[tree.nextToken(lbrace)].id != .MultilineStringLiteralLine) {
if (tree.token_ids[tree.nextToken(lbrace)] != .MultilineStringLiteralLine) {
try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline);
try stream.writeByteNTimes(' ', new_indent);
} else {
@ -750,7 +753,7 @@ fn renderExpression(
}
col = 1;
if (tree.tokens[tree.nextToken(comma)].id != .MultilineStringLiteralLine) {
if (tree.token_ids[tree.nextToken(comma)] != .MultilineStringLiteralLine) {
try renderToken(tree, stream, comma, new_indent, start_col, Space.Newline); // ,
} else {
try renderToken(tree, stream, comma, new_indent, start_col, Space.None); // ,
@ -819,11 +822,11 @@ fn renderExpression(
const src_has_trailing_comma = blk: {
const maybe_comma = tree.prevToken(rtoken);
break :blk tree.tokens[maybe_comma].id == .Comma;
break :blk tree.token_ids[maybe_comma] == .Comma;
};
const src_same_line = blk: {
const loc = tree.tokenLocation(tree.tokens[lbrace].end, rtoken);
const loc = tree.tokenLocation(tree.token_locs[lbrace].end, rtoken);
break :blk loc.line == 0;
};
@ -929,7 +932,7 @@ fn renderExpression(
const src_has_trailing_comma = blk: {
const maybe_comma = tree.prevToken(call.rtoken);
break :blk tree.tokens[maybe_comma].id == .Comma;
break :blk tree.token_ids[maybe_comma] == .Comma;
};
if (src_has_trailing_comma) {
@ -983,8 +986,8 @@ fn renderExpression(
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
const starts_with_comment = tree.tokens[lbracket + 1].id == .LineComment;
const ends_with_comment = tree.tokens[rbracket - 1].id == .LineComment;
const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
const new_indent = if (ends_with_comment) indent + indent_delta else indent;
const new_space = if (ends_with_comment) Space.Newline else Space.None;
try renderExpression(allocator, stream, tree, new_indent, start_col, index_expr, new_space);
@ -1226,9 +1229,9 @@ fn renderExpression(
var maybe_comma = tree.prevToken(container_decl.lastToken());
// Doc comments for a field may also appear after the comma, eg.
// field_name: T, // comment attached to field_name
if (tree.tokens[maybe_comma].id == .DocComment)
if (tree.token_ids[maybe_comma] == .DocComment)
maybe_comma = tree.prevToken(maybe_comma);
break :blk tree.tokens[maybe_comma].id == .Comma;
break :blk tree.token_ids[maybe_comma] == .Comma;
};
const fields_and_decls = container_decl.fieldsAndDecls();
@ -1321,7 +1324,7 @@ fn renderExpression(
const src_has_trailing_comma = blk: {
const maybe_comma = tree.prevToken(err_set_decl.rbrace_token);
break :blk tree.tokens[maybe_comma].id == .Comma;
break :blk tree.token_ids[maybe_comma] == .Comma;
};
if (src_has_trailing_comma) {
@ -1353,7 +1356,7 @@ fn renderExpression(
try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None);
const comma_token = tree.nextToken(node.lastToken());
assert(tree.tokens[comma_token].id == .Comma);
assert(tree.token_ids[comma_token] == .Comma);
try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // ,
try renderExtraNewline(tree, stream, start_col, decls[i + 1]);
} else {
@ -1378,7 +1381,7 @@ fn renderExpression(
const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
var skip_first_indent = true;
if (tree.tokens[multiline_str_literal.firstToken() - 1].id != .LineComment) {
if (tree.token_ids[multiline_str_literal.firstToken() - 1] != .LineComment) {
try stream.print("\n", .{});
skip_first_indent = false;
}
@ -1406,7 +1409,7 @@ fn renderExpression(
if (builtin_call.params_len < 2) break :blk false;
const last_node = builtin_call.params()[builtin_call.params_len - 1];
const maybe_comma = tree.nextToken(last_node.lastToken());
break :blk tree.tokens[maybe_comma].id == .Comma;
break :blk tree.token_ids[maybe_comma] == .Comma;
};
const lparen = tree.nextToken(builtin_call.builtin_token);
@ -1443,8 +1446,8 @@ fn renderExpression(
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);
if (fn_proto.visib_token) |visib_token_index| {
const visib_token = tree.tokens[visib_token_index];
assert(visib_token.id == .Keyword_pub or visib_token.id == .Keyword_export);
const visib_token = tree.token_ids[visib_token_index];
assert(visib_token == .Keyword_pub or visib_token == .Keyword_export);
try renderToken(tree, stream, visib_token_index, indent, start_col, Space.Space); // pub
}
@ -1466,7 +1469,7 @@ fn renderExpression(
try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.Space); // fn
break :blk tree.nextToken(fn_proto.fn_token);
};
assert(tree.tokens[lparen].id == .LParen);
assert(tree.token_ids[lparen] == .LParen);
const rparen = tree.prevToken(
// the first token for the annotation expressions is the left
@ -1482,10 +1485,10 @@ fn renderExpression(
.InferErrorSet => |node| tree.prevToken(node.firstToken()),
.Invalid => unreachable,
});
assert(tree.tokens[rparen].id == .RParen);
assert(tree.token_ids[rparen] == .RParen);
const src_params_trailing_comma = blk: {
const maybe_comma = tree.tokens[rparen - 1].id;
const maybe_comma = tree.token_ids[rparen - 1];
break :blk maybe_comma == .Comma or maybe_comma == .LineComment;
};
@ -1622,7 +1625,7 @@ fn renderExpression(
const src_has_trailing_comma = blk: {
const last_node = switch_case.items()[switch_case.items_len - 1];
const maybe_comma = tree.nextToken(last_node.lastToken());
break :blk tree.tokens[maybe_comma].id == .Comma;
break :blk tree.token_ids[maybe_comma] == .Comma;
};
if (switch_case.items_len == 1 or !src_has_trailing_comma) {
@ -1967,7 +1970,7 @@ fn renderExpression(
try renderAsmOutput(allocator, stream, tree, indent_extra, start_col, asm_output, Space.Newline);
try stream.writeByteNTimes(' ', indent_once);
const comma_or_colon = tree.nextToken(asm_output.lastToken());
break :blk switch (tree.tokens[comma_or_colon].id) {
break :blk switch (tree.token_ids[comma_or_colon]) {
.Comma => tree.nextToken(comma_or_colon),
else => comma_or_colon,
};
@ -2002,7 +2005,7 @@ fn renderExpression(
try renderAsmInput(allocator, stream, tree, indent_extra, start_col, asm_input, Space.Newline);
try stream.writeByteNTimes(' ', indent_once);
const comma_or_colon = tree.nextToken(asm_input.lastToken());
break :blk switch (tree.tokens[comma_or_colon].id) {
break :blk switch (tree.token_ids[comma_or_colon]) {
.Comma => tree.nextToken(comma_or_colon),
else => comma_or_colon,
};
@ -2205,7 +2208,7 @@ fn renderStatement(
try renderExpression(allocator, stream, tree, indent, start_col, base, Space.None);
const semicolon_index = tree.nextToken(base.lastToken());
assert(tree.tokens[semicolon_index].id == .Semicolon);
assert(tree.token_ids[semicolon_index] == .Semicolon);
try renderToken(tree, stream, semicolon_index, indent, start_col, Space.Newline);
} else {
try renderExpression(allocator, stream, tree, indent, start_col, base, Space.Newline);
@ -2243,22 +2246,25 @@ fn renderTokenOffset(
return;
}
var token = tree.tokens[token_index];
try stream.writeAll(mem.trimRight(u8, tree.tokenSlicePtr(token)[token_skip_bytes..], " "));
var token_loc = tree.token_locs[token_index];
try stream.writeAll(mem.trimRight(u8, tree.tokenSliceLoc(token_loc)[token_skip_bytes..], " "));
if (space == Space.NoComment)
return;
var next_token = tree.tokens[token_index + 1];
var next_token_id = tree.token_ids[token_index + 1];
var next_token_loc = tree.token_locs[token_index + 1];
if (space == Space.Comma) switch (next_token.id) {
if (space == Space.Comma) switch (next_token_id) {
.Comma => return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline),
.LineComment => {
try stream.writeAll(", ");
return renderToken(tree, stream, token_index + 1, indent, start_col, Space.Newline);
},
else => {
if (token_index + 2 < tree.tokens.len and tree.tokens[token_index + 2].id == .MultilineStringLiteralLine) {
if (token_index + 2 < tree.token_ids.len and
tree.token_ids[token_index + 2] == .MultilineStringLiteralLine)
{
try stream.writeAll(",");
return;
} else {
@ -2271,19 +2277,20 @@ fn renderTokenOffset(
// Skip over same line doc comments
var offset: usize = 1;
if (next_token.id == .DocComment) {
const loc = tree.tokenLocationPtr(token.end, next_token);
if (next_token_id == .DocComment) {
const loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
if (loc.line == 0) {
offset += 1;
next_token = tree.tokens[token_index + offset];
next_token_id = tree.token_ids[token_index + offset];
next_token_loc = tree.token_locs[token_index + offset];
}
}
if (next_token.id != .LineComment) blk: {
if (next_token_id != .LineComment) blk: {
switch (space) {
Space.None, Space.NoNewline => return,
Space.Newline => {
if (next_token.id == .MultilineStringLiteralLine) {
if (next_token_id == .MultilineStringLiteralLine) {
return;
} else {
try stream.writeAll("\n");
@ -2292,7 +2299,7 @@ fn renderTokenOffset(
}
},
Space.Space, Space.SpaceOrOutdent => {
if (next_token.id == .MultilineStringLiteralLine)
if (next_token_id == .MultilineStringLiteralLine)
return;
try stream.writeByte(' ');
return;
@ -2302,14 +2309,15 @@ fn renderTokenOffset(
}
while (true) {
const comment_is_empty = mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ").len == 2;
const comment_is_empty = mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " ").len == 2;
if (comment_is_empty) {
switch (space) {
Space.Newline => {
offset += 1;
token = next_token;
next_token = tree.tokens[token_index + offset];
if (next_token.id != .LineComment) {
token_loc = next_token_loc;
next_token_id = tree.token_ids[token_index + offset];
next_token_loc = tree.token_locs[token_index + offset];
if (next_token_id != .LineComment) {
try stream.writeByte('\n');
start_col.* = 0;
return;
@ -2322,18 +2330,19 @@ fn renderTokenOffset(
}
}
var loc = tree.tokenLocationPtr(token.end, next_token);
var loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
if (loc.line == 0) {
try stream.print(" {}", .{mem.trimRight(u8, tree.tokenSlicePtr(next_token), " ")});
try stream.print(" {}", .{mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " ")});
offset = 2;
token = next_token;
next_token = tree.tokens[token_index + offset];
if (next_token.id != .LineComment) {
token_loc = next_token_loc;
next_token_loc = tree.token_locs[token_index + offset];
next_token_id = tree.token_ids[token_index + offset];
if (next_token_id != .LineComment) {
switch (space) {
Space.None, Space.Space => {
try stream.writeByte('\n');
const after_comment_token = tree.tokens[token_index + offset];
const next_line_indent = switch (after_comment_token.id) {
const after_comment_token = tree.token_ids[token_index + offset];
const next_line_indent = switch (after_comment_token) {
.RParen, .RBrace, .RBracket => indent,
else => indent + indent_delta,
};
@ -2346,7 +2355,7 @@ fn renderTokenOffset(
start_col.* = indent;
},
Space.Newline => {
if (next_token.id == .MultilineStringLiteralLine) {
if (next_token_id == .MultilineStringLiteralLine) {
return;
} else {
try stream.writeAll("\n");
@ -2359,7 +2368,7 @@ fn renderTokenOffset(
}
return;
}
loc = tree.tokenLocationPtr(token.end, next_token);
loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
}
while (true) {
@ -2369,15 +2378,16 @@ fn renderTokenOffset(
const newline_count = if (loc.line <= 1) @as(u8, 1) else @as(u8, 2);
try stream.writeByteNTimes('\n', newline_count);
try stream.writeByteNTimes(' ', indent);
try stream.writeAll(mem.trimRight(u8, tree.tokenSlicePtr(next_token), " "));
try stream.writeAll(mem.trimRight(u8, tree.tokenSliceLoc(next_token_loc), " "));
offset += 1;
token = next_token;
next_token = tree.tokens[token_index + offset];
if (next_token.id != .LineComment) {
token_loc = next_token_loc;
next_token_loc = tree.token_locs[token_index + offset];
next_token_id = tree.token_ids[token_index + offset];
if (next_token_id != .LineComment) {
switch (space) {
Space.Newline => {
if (next_token.id == .MultilineStringLiteralLine) {
if (next_token_id == .MultilineStringLiteralLine) {
return;
} else {
try stream.writeAll("\n");
@ -2388,8 +2398,8 @@ fn renderTokenOffset(
Space.None, Space.Space => {
try stream.writeByte('\n');
const after_comment_token = tree.tokens[token_index + offset];
const next_line_indent = switch (after_comment_token.id) {
const after_comment_token = tree.token_ids[token_index + offset];
const next_line_indent = switch (after_comment_token) {
.RParen, .RBrace, .RBracket => blk: {
if (indent > indent_delta) {
break :blk indent - indent_delta;
@ -2412,7 +2422,7 @@ fn renderTokenOffset(
}
return;
}
loc = tree.tokenLocationPtr(token.end, next_token);
loc = tree.tokenLocationLoc(token_loc.end, next_token_loc);
}
}
@ -2448,7 +2458,7 @@ fn renderDocCommentsToken(
) (@TypeOf(stream).Error || Error)!void {
var tok_i = comment.first_line;
while (true) : (tok_i += 1) {
switch (tree.tokens[tok_i].id) {
switch (tree.token_ids[tok_i]) {
.DocComment, .ContainerDocComment => {
if (comment.first_line < first_token) {
try renderToken(tree, stream, tok_i, indent, start_col, Space.Newline);

View File

@ -3,8 +3,12 @@ const mem = std.mem;
pub const Token = struct {
id: Id,
start: usize,
end: usize,
loc: Loc,
pub const Loc = struct {
start: usize,
end: usize,
};
pub const Keyword = struct {
bytes: []const u8,
@ -426,8 +430,10 @@ pub const Tokenizer = struct {
var state: State = .start;
var result = Token{
.id = .Eof,
.start = self.index,
.end = undefined,
.loc = .{
.start = self.index,
.end = undefined,
},
};
var seen_escape_digits: usize = undefined;
var remaining_code_units: usize = undefined;
@ -436,7 +442,7 @@ pub const Tokenizer = struct {
switch (state) {
.start => switch (c) {
' ', '\n', '\t', '\r' => {
result.start = self.index + 1;
result.loc.start = self.index + 1;
},
'"' => {
state = .string_literal;
@ -686,7 +692,7 @@ pub const Tokenizer = struct {
.identifier => switch (c) {
'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
else => {
if (Token.getKeyword(self.buffer[result.start..self.index])) |id| {
if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |id| {
result.id = id;
}
break;
@ -1313,7 +1319,7 @@ pub const Tokenizer = struct {
=> {},
.identifier => {
if (Token.getKeyword(self.buffer[result.start..self.index])) |id| {
if (Token.getKeyword(self.buffer[result.loc.start..self.index])) |id| {
result.id = id;
}
},
@ -1420,7 +1426,7 @@ pub const Tokenizer = struct {
}
}
result.end = self.index;
result.loc.end = self.index;
return result;
}
@ -1430,8 +1436,10 @@ pub const Tokenizer = struct {
if (invalid_length == 0) return;
self.pending_invalid_token = .{
.id = .Invalid,
.start = self.index,
.end = self.index + invalid_length,
.loc = .{
.start = self.index,
.end = self.index + invalid_length,
},
};
}

View File

@ -247,27 +247,6 @@ pub const Context = struct {
}
};
/// Helper function to append items to a singly linked list.
fn llpusher(c: *Context, list: *std.SinglyLinkedList(*ast.Node)) LinkedListPusher {
assert(list.first == null);
return .{
.c = c,
.it = &list.first,
};
}
fn llpush(
c: *Context,
comptime T: type,
it: *?*std.SinglyLinkedList(T).Node,
data: T,
) !*?*std.SinglyLinkedList(T).Node {
const llnode = try c.arena.create(std.SinglyLinkedList(T).Node);
llnode.* = .{ .data = data };
it.* = llnode;
return &llnode.next;
}
fn getMangle(c: *Context) u32 {
c.mangle_count += 1;
return c.mangle_count;