std.zig.parser now parses labeled blocks.

* There is also some code for switch range parsing
This commit is contained in:
Jimmi Holst Christensen 2018-04-06 15:37:49 +02:00
parent f667744d44
commit 820de1716b
2 changed files with 215 additions and 34 deletions

View File

@ -20,6 +20,8 @@ pub const Node = struct {
FnProto,
ParamDecl,
Block,
Switch,
SwitchCase,
InfixOp,
PrefixOp,
SuffixOp,
@ -55,6 +57,8 @@ pub const Node = struct {
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).iterate(index),
Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).iterate(index),
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).iterate(index),
@ -91,6 +95,8 @@ pub const Node = struct {
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),
Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).firstToken(),
Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).firstToken(),
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).firstToken(),
@ -127,6 +133,8 @@ pub const Node = struct {
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).lastToken(),
Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).lastToken(),
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).lastToken(),
@ -506,9 +514,10 @@ pub const NodeParamDecl = struct {
pub const NodeBlock = struct {
base: Node,
begin_token: Token,
end_token: Token,
label: ?Token,
lbrace: Token,
statements: ArrayList(&Node),
rbrace: Token,
pub fn iterate(self: &NodeBlock, index: usize) ?&Node {
var i = index;
@ -520,11 +529,80 @@ pub const NodeBlock = struct {
}
pub fn firstToken(self: &NodeBlock) Token {
return self.begin_token;
if (self.label) |label| {
return label;
}
return self.lbrace;
}
pub fn lastToken(self: &NodeBlock) Token {
return self.end_token;
return self.rbrace;
}
};
pub const NodeSwitch = struct {
base: Node,
switch_token: Token,
expr: &Node,
cases: ArrayList(&NodeSwitchCase),
rbrace: Token,
pub fn iterate(self: &NodeSwitch, index: usize) ?&Node {
var i = index;
if (i < 1) return self.expr;
i -= 1;
if (i < self.cases.len) return self.cases.at(i);
i -= self.cases.len;
return null;
}
pub fn firstToken(self: &NodeSwitch) Token {
return self.switch_token;
}
pub fn lastToken(self: &NodeSwitch) Token {
return self.rbrace;
}
};
pub const NodeSwitchCase = struct {
base: Node,
items: ArrayList(&Node),
capture: ?Capture,
expr: &Node,
const Capture = struct {
symbol: &NodeIdentifier,
is_ptr: bool,
};
pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node {
var i = index;
if (i < self.items.len) return self.items.at(i);
i -= self.items.len;
if (self.capture) |capture| {
if (i < 1) return &capture.base;
i -= 1;
}
if (i < 1) return self.expr;
i -= 1;
return null;
}
pub fn firstToken(self: &NodeSwitchCase) Token {
return self.items.at(0).firstToken();
}
pub fn lastToken(self: &NodeSwitchCase) Token {
return self.expr.lastToken();
}
};
@ -575,6 +653,7 @@ pub const NodeInfixOp = struct {
Mult,
MultWrap,
Period,
Range,
Sub,
SubWrap,
UnwrapMaybe,
@ -625,6 +704,7 @@ pub const NodeInfixOp = struct {
InfixOp.Mult,
InfixOp.MultWrap,
InfixOp.Period,
InfixOp.Range,
InfixOp.Sub,
InfixOp.SubWrap,
InfixOp.UnwrapMaybe => {},

View File

@ -146,6 +146,8 @@ pub const Parser = struct {
Required,
Expression: DestPtr,
RangeExpressionBegin: DestPtr,
RangeExpressionEnd: DestPtr,
AssignmentExpressionBegin: DestPtr,
AssignmentExpressionEnd: DestPtr,
UnwrapExpressionBegin: DestPtr,
@ -256,7 +258,7 @@ pub const Parser = struct {
}
const name = try self.createStringLiteral(arena, name_token);
const block = try self.createBlock(arena, token);
const block = try self.createBlock(arena, (?Token)(null), token);
const test_decl = try self.createAttachTestDecl(arena, &root_node.decls, token, &name.base, block);
stack.append(State { .Block = block }) catch unreachable;
continue;
@ -643,6 +645,27 @@ pub const Parser = struct {
}
},
State.RangeExpressionBegin => |dest_ptr| {
stack.append(State { .RangeExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .Expression = dest_ptr });
continue;
},
State.RangeExpressionEnd => |dest_ptr| {
const token = self.getNextToken();
if (token.id == Token.Id.Ellipsis3) {
const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.Range);
node.lhs = dest_ptr.get();
dest_ptr.store(&node.base);
stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
continue;
} else {
self.putBackToken(token);
continue;
}
},
State.AssignmentExpressionBegin => |dest_ptr| {
stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .UnwrapExpressionBegin = dest_ptr });
@ -1205,10 +1228,6 @@ pub const Parser = struct {
try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
continue;
},
Token.Id.Identifier => {
dest_ptr.store(&(try self.createIdentifier(arena, token)).base);
continue;
},
Token.Id.Builtin => {
const node = try arena.create(ast.NodeBuiltinCall);
*node = ast.NodeBuiltinCall {
@ -1348,8 +1367,32 @@ pub const Parser = struct {
},
}) catch unreachable;
},
Token.Id.Identifier => {
const next = self.getNextToken();
if (next.id != Token.Id.Colon) {
self.putBackToken(next);
dest_ptr.store(&(try self.createIdentifier(arena, token)).base);
continue;
}
const block = try self.createBlock(arena, (?Token)(token), Token(undefined));
dest_ptr.store(&block.base);
stack.append(State { .Block = block }) catch unreachable;
try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.LBrace,
.ptr = &block.lbrace,
}
});
continue;
},
Token.Id.LBrace => {
@panic("TODO: Block expr");
const block = try self.createBlock(arena, (?Token)(null), token);
dest_ptr.store(&block.base);
stack.append(State { .Block = block }) catch unreachable;
continue;
},
Token.Id.Keyword_fn => {
@panic("TODO: fn proto");
@ -1618,7 +1661,7 @@ pub const Parser = struct {
const token = self.getNextToken();
switch(token.id) {
Token.Id.LBrace => {
const block = try self.createBlock(arena, token);
const block = try self.createBlock(arena, (?Token)(null), token);
fn_proto.body_node = &block.base;
stack.append(State { .Block = block }) catch unreachable;
continue;
@ -1635,7 +1678,7 @@ pub const Parser = struct {
const token = self.getNextToken();
switch (token.id) {
Token.Id.RBrace => {
block.end_token = token;
block.rbrace = token;
continue;
},
else => {
@ -1648,38 +1691,64 @@ pub const Parser = struct {
},
State.Statement => |block| {
{
// Look for comptime var, comptime const
const comptime_token = self.getNextToken();
if (comptime_token.id == Token.Id.Keyword_comptime) {
const next = self.getNextToken();
switch (next.id) {
Token.Id.Keyword_comptime => {
const mut_token = self.getNextToken();
if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
// TODO shouldn't need these casts
const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
mut_token, (?Token)(comptime_token), (?Token)(null), null);
mut_token, (?Token)(next), (?Token)(null), null);
stack.append(State { .VarDecl = var_decl }) catch unreachable;
continue;
} else {
self.putBackToken(mut_token);
@panic("TODO: comptime block");
}
self.putBackToken(mut_token);
}
self.putBackToken(comptime_token);
}
{
// Look for const, var
const mut_token = self.getNextToken();
if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
// TODO shouldn't need these casts
},
Token.Id.Keyword_var, Token.Id.Keyword_const => {
const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
mut_token, (?Token)(null), (?Token)(null), null);
next, (?Token)(null), (?Token)(null), null);
stack.append(State { .VarDecl = var_decl }) catch unreachable;
continue;
},
Token.Id.Identifier => {
const maybe_colon = self.getNextToken();
if (maybe_colon.id != Token.Id.Colon) {
self.putBackToken(maybe_colon);
self.putBackToken(next);
stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
try stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } });
continue;
}
const inner_block = try self.createBlock(arena, (?Token)(next), Token(undefined));
try block.statements.append(&inner_block.base);
stack.append(State { .Block = inner_block }) catch unreachable;
try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.LBrace,
.ptr = &inner_block.lbrace,
}
});
continue;
},
Token.Id.LBrace => {
const inner_block = try self.createBlock(arena, (?Token)(null), next);
try block.statements.append(&inner_block.base);
stack.append(State { .Block = inner_block }) catch unreachable;
continue;
},
else => {
self.putBackToken(next);
stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
try stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } });
continue;
}
self.putBackToken(mut_token);
}
stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
try stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } });
continue;
},
}
}
@ -1905,14 +1974,15 @@ pub const Parser = struct {
return node;
}
fn createBlock(self: &Parser, arena: &mem.Allocator, begin_token: &const Token) !&ast.NodeBlock {
fn createBlock(self: &Parser, arena: &mem.Allocator, label: &const ?Token, lbrace: &const Token) !&ast.NodeBlock {
const node = try arena.create(ast.NodeBlock);
*node = ast.NodeBlock {
.base = self.initNode(ast.Node.Id.Block),
.begin_token = *begin_token,
.end_token = undefined,
.label = *label,
.lbrace = *lbrace,
.statements = ArrayList(&ast.Node).init(arena),
.rbrace = undefined,
};
return node;
}
@ -2340,6 +2410,10 @@ pub const Parser = struct {
},
ast.Node.Id.Block => {
const block = @fieldParentPtr(ast.NodeBlock, "base", base);
if (block.label) |label| {
try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
}
if (block.statements.len == 0) {
try stream.write("{}");
} else {
@ -2747,6 +2821,8 @@ pub const Parser = struct {
},
ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"),
ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
ast.Node.Id.Switch => @panic("TODO switch"),
ast.Node.Id.SwitchCase => @panic("TODO switch case"),
ast.Node.Id.StructField,
ast.Node.Id.UnionTag,
@ -2791,6 +2867,9 @@ pub const Parser = struct {
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
try stack.append(RenderState { .VarDecl = var_decl});
},
ast.Node.Id.Block => {
try stack.append(RenderState { .Expression = base});
},
else => {
try stack.append(RenderState { .Text = ";"});
try stack.append(RenderState { .Expression = base});
@ -3323,6 +3402,28 @@ test "zig fmt: catch" {
);
}
test "zig fmt: blocks" {
try testCanonical(
\\test "blocks" {
\\ {
\\ const a = 0;
\\ const b = 0;
\\ }
\\
\\ blk: {
\\ const a = 0;
\\ const b = 0;
\\ }
\\
\\ const r = blk: {
\\ const a = 0;
\\ const b = 0;
\\ };
\\}
\\
);
}
test "zig fmt: switch" {
try testCanonical(
\\test "switch" {