std.zig.parser now parses switch

master
Jimmi Holst Christensen 2018-04-07 01:38:38 +02:00
parent 820de1716b
commit bdff5bfa3e
3 changed files with 251 additions and 36 deletions

View File

@ -22,6 +22,7 @@ pub const Node = struct {
Block,
Switch,
SwitchCase,
SwitchElse,
InfixOp,
PrefixOp,
SuffixOp,
@ -59,6 +60,7 @@ pub const Node = struct {
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.SwitchElse => @fieldParentPtr(NodeSwitchElse, "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),
@ -97,6 +99,7 @@ pub const Node = struct {
Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),
Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).firstToken(),
Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).firstToken(),
Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).firstToken(),
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).firstToken(),
@ -135,6 +138,7 @@ pub const Node = struct {
Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).lastToken(),
Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).lastToken(),
Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).firstToken(),
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).lastToken(),
@ -606,6 +610,23 @@ pub const NodeSwitchCase = struct {
}
};
pub const NodeSwitchElse = struct {
base: Node,
token: Token,
pub fn iterate(self: &NodeSwitchElse, index: usize) ?&Node {
return null;
}
pub fn firstToken(self: &NodeSwitchElse) Token {
return self.token;
}
pub fn lastToken(self: &NodeSwitchElse) Token {
return self.token;
}
};
pub const NodeInfixOp = struct {
base: Node,
op_token: Token,

View File

@ -98,10 +98,15 @@ pub const Parser = struct {
ptr: &?&ast.Node,
};
fn ListState(comptime T: type) type {
const ExprListCtx = struct {
list: &ArrayList(&ast.Node),
end: Token.Id,
ptr: &Token,
};
fn ListSave(comptime T: type) type {
return struct {
list: &ArrayList(T),
end: Token.Id,
ptr: &Token,
};
}
@ -129,11 +134,16 @@ pub const Parser = struct {
FnDef: &ast.NodeFnProto,
Block: &ast.NodeBlock,
Statement: &ast.NodeBlock,
ExprListItemOrEnd: ListState(&ast.Node),
ExprListCommaOrEnd: ListState(&ast.Node),
FieldInitListItemOrEnd: ListState(&ast.NodeFieldInitializer),
FieldInitListCommaOrEnd: ListState(&ast.NodeFieldInitializer),
ExprListItemOrEnd: ExprListCtx,
ExprListCommaOrEnd: ExprListCtx,
FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldListCommaOrEnd: &ast.NodeContainerDecl,
SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase),
SwitchCaseCapture: &?ast.NodeSwitchCase.Capture,
SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase),
SwitchCaseItem: &ArrayList(&ast.Node),
SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
/// A state that can be appended before any other State. If an error occures,
/// the parser will first try looking for the closest optional state. If an
@ -245,17 +255,8 @@ pub const Parser = struct {
Token.Id.Keyword_test => {
stack.append(State.TopLevel) catch unreachable;
const name_token = self.getNextToken();
if (name_token.id != Token.Id.StringLiteral) {
try self.parseError(&stack, token, "expected {}, found {}", @tagName(Token.Id.StringLiteral), @tagName(name_token.id));
continue;
}
const lbrace = self.getNextToken();
if (lbrace.id != Token.Id.LBrace) {
try self.parseError(&stack, token, "expected {}, found {}", @tagName(Token.Id.LBrace), @tagName(name_token.id));
continue;
}
const name_token = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue;
const lbrace = (try self.eatToken(&stack, Token.Id.LBrace)) ?? continue;
const name = try self.createStringLiteral(arena, name_token);
const block = try self.createBlock(arena, (?Token)(null), token);
@ -974,9 +975,8 @@ pub const Parser = struct {
stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
.FieldInitListItemOrEnd = ListState(&ast.NodeFieldInitializer) {
.FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) {
.list = &node.op.StructInitializer,
.end = Token.Id.RBrace,
.ptr = &node.rtoken,
}
});
@ -992,7 +992,7 @@ pub const Parser = struct {
stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
.ExprListItemOrEnd = ListState(&ast.Node) {
.ExprListItemOrEnd = ExprListCtx {
.list = &node.op.ArrayInitializer,
.end = Token.Id.RBrace,
.ptr = &node.rtoken,
@ -1084,7 +1084,7 @@ pub const Parser = struct {
stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
.ExprListItemOrEnd = ListState(&ast.Node) {
.ExprListItemOrEnd = ExprListCtx {
.list = &node.op.Call.params,
.end = Token.Id.RParen,
.ptr = &node.rtoken,
@ -1238,7 +1238,7 @@ pub const Parser = struct {
};
dest_ptr.store(&node.base);
stack.append(State {
.ExprListItemOrEnd = ListState(&ast.Node) {
.ExprListItemOrEnd = ExprListCtx {
.list = &node.params,
.end = Token.Id.RParen,
.ptr = &node.rparen_token,
@ -1400,6 +1400,43 @@ pub const Parser = struct {
Token.Id.Keyword_asm => {
@panic("TODO: inline asm");
},
Token.Id.Keyword_if => {
@panic("TODO: inline if");
},
Token.Id.Keyword_while => {
@panic("TODO: inline while");
},
Token.Id.Keyword_for => {
@panic("TODO: inline for");
},
Token.Id.Keyword_switch => {
const node = try arena.create(ast.NodeSwitch);
*node = ast.NodeSwitch {
.base = self.initNode(ast.Node.Id.Switch),
.switch_token = token,
.expr = undefined,
.cases = ArrayList(&ast.NodeSwitchCase).init(arena),
.rbrace = undefined,
};
dest_ptr.store(&node.base);
stack.append(State {
.SwitchCaseOrEnd = ListSave(&ast.NodeSwitchCase) {
.list = &node.cases,
.ptr = &node.rbrace,
},
}) catch unreachable;
try stack.append(State { .ExpectToken = Token.Id.LBrace });
try stack.append(State { .ExpectToken = Token.Id.RParen });
try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
try stack.append(State { .ExpectToken = Token.Id.LParen });
},
Token.Id.Keyword_comptime => {
@panic("TODO: inline comptime");
},
Token.Id.Keyword_suspend => {
@panic("TODO: inline suspend");
},
else => {
try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id));
continue;
@ -1463,8 +1500,7 @@ pub const Parser = struct {
State.FieldInitListItemOrEnd => |list_state| {
var token = self.getNextToken();
const IdTag = @TagType(Token.Id);
if (IdTag(list_state.end) == token.id){
if (token.id == Token.Id.RBrace){
*list_state.ptr = token;
continue;
}
@ -1497,13 +1533,82 @@ pub const Parser = struct {
});
},
State.SwitchCaseOrEnd => |list_state| {
var token = self.getNextToken();
if (token.id == Token.Id.RBrace){
*list_state.ptr = token;
continue;
}
self.putBackToken(token);
const node = try arena.create(ast.NodeSwitchCase);
*node = ast.NodeSwitchCase {
.base = self.initNode(ast.Node.Id.SwitchCase),
.items = ArrayList(&ast.Node).init(arena),
.capture = null,
.expr = undefined,
};
try list_state.list.append(node);
stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable;
try stack.append(State { .Expression = DestPtr{ .Field = &node.expr } });
try stack.append(State { .SwitchCaseCapture = &node.capture });
const maybe_else = self.getNextToken();
if (maybe_else.id == Token.Id.Keyword_else) {
const else_node = try arena.create(ast.NodeSwitchElse);
*else_node = ast.NodeSwitchElse {
.base = self.initNode(ast.Node.Id.SwitchElse),
.token = maybe_else,
};
try node.items.append(&else_node.base);
try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
continue;
} else {
self.putBackToken(maybe_else);
try stack.append(State { .SwitchCaseItem = &node.items });
continue;
}
},
State.SwitchCaseCapture => |capture| {
const token = self.getNextToken();
if (token.id != Token.Id.Pipe) {
self.putBackToken(token);
continue;
}
const is_ptr = blk: {
const asterik = self.getNextToken();
if (asterik.id == Token.Id.Asterisk) {
break :blk true;
} else {
self.putBackToken(asterik);
break :blk false;
}
};
const ident = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
_ = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
*capture = ast.NodeSwitchCase.Capture {
.symbol = try self.createIdentifier(arena, ident),
.is_ptr = is_ptr
};
},
State.SwitchCaseItem => |case_items| {
stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;
try stack.append(State { .RangeExpressionBegin = DestPtr{ .Field = try case_items.addOne() } });
},
State.ExprListCommaOrEnd => |list_state| {
try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .ExprListItemOrEnd = list_state });
continue;
},
State.FieldInitListCommaOrEnd => |list_state| {
try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .FieldInitListItemOrEnd = list_state });
try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .FieldInitListItemOrEnd = list_state });
continue;
},
@ -1513,6 +1618,16 @@ pub const Parser = struct {
continue;
},
State.SwitchCaseCommaOrEnd => |list_state| {
try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .SwitchCaseOrEnd = list_state });
continue;
},
State.SwitchCaseItemCommaOrEnd => |case_items| {
try self.commaOrEnd(&stack, Token.Id.EqualAngleBracketRight, null, State { .SwitchCaseItem = case_items });
continue;
},
State.AddrOfModifiers => |addr_of_info| {
var token = self.getNextToken();
switch (token.id) {
@ -1741,6 +1856,11 @@ pub const Parser = struct {
stack.append(State { .Block = inner_block }) catch unreachable;
continue;
},
Token.Id.Keyword_switch => {
self.putBackToken(next);
stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } }) catch unreachable;
continue;
},
else => {
self.putBackToken(next);
stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
@ -1754,7 +1874,7 @@ pub const Parser = struct {
}
}
fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, ptr: &Token, state_after_comma: &const State) !void {
fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, maybe_ptr: ?&Token, state_after_comma: &const State) !void {
var token = self.getNextToken();
switch (token.id) {
Token.Id.Comma => {
@ -1763,7 +1883,9 @@ pub const Parser = struct {
else => {
const IdTag = @TagType(Token.Id);
if (IdTag(*end) == token.id) {
*ptr = token;
if (maybe_ptr) |ptr| {
*ptr = token;
}
return;
}
@ -2498,7 +2620,8 @@ pub const Parser = struct {
ast.NodeInfixOp.InfixOp.Sub => " - ",
ast.NodeInfixOp.InfixOp.SubWrap => " -% ",
ast.NodeInfixOp.InfixOp.UnwrapMaybe => " ?? ",
else => unreachable,
ast.NodeInfixOp.InfixOp.Range => " ... ",
ast.NodeInfixOp.InfixOp.Catch => unreachable,
};
try stack.append(RenderState { .Text = text });
@ -2821,8 +2944,73 @@ 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.Switch => {
const switch_node = @fieldParentPtr(ast.NodeSwitch, "base", base);
try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token));
try stack.append(RenderState { .Text = "}"});
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState { .Indent = indent });
try stack.append(RenderState { .Text = "\n"});
const cases = switch_node.cases.toSliceConst();
var i = cases.len;
while (i != 0) {
i -= 1;
const node = cases[i];
try stack.append(RenderState { .Expression = &node.base});
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState {
.Text = blk: {
if (i != 0) {
const prev_node = cases[i - 1];
const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
if (loc.line >= 2) {
break :blk "\n\n";
}
}
break :blk "\n";
},
});
if (i != 0) {
try stack.append(RenderState { .Text = "," });
}
}
try stack.append(RenderState { .Indent = indent + indent_delta});
try stack.append(RenderState { .Text = ") {"});
try stack.append(RenderState { .Expression = switch_node.expr });
},
ast.Node.Id.SwitchCase => {
const switch_case = @fieldParentPtr(ast.NodeSwitchCase, "base", base);
try stack.append(RenderState { .Expression = switch_case.expr });
if (switch_case.capture) |capture| {
try stack.append(RenderState { .Text = "| "});
try stack.append(RenderState { .Expression = &capture.symbol.base });
if (capture.is_ptr) {
try stack.append(RenderState { .Text = "*"});
}
try stack.append(RenderState { .Text = "|"});
}
try stack.append(RenderState { .Text = " => "});
const items = switch_case.items.toSliceConst();
var i = items.len;
while (i != 0) {
i -= 1;
try stack.append(RenderState { .Expression = items[i] });
if (i != 0) {
try stack.append(RenderState { .Text = ", " });
}
}
},
ast.Node.Id.SwitchElse => {
const switch_else = @fieldParentPtr(ast.NodeSwitchElse, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token));
},
ast.Node.Id.StructField,
ast.Node.Id.UnionTag,
@ -2867,7 +3055,7 @@ pub const Parser = struct {
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
try stack.append(RenderState { .VarDecl = var_decl});
},
ast.Node.Id.Block => {
ast.Node.Id.Block, ast.Node.Id.Switch => {
try stack.append(RenderState { .Expression = base});
},
else => {
@ -3436,24 +3624,24 @@ test "zig fmt: switch" {
\\ else => {
\\ const a = 1;
\\ const b = a;
\\ },
\\ }
\\ }
\\
\\ const res = switch (0) {
\\ 0 => 0,
\\ 1 => 2,
\\ else => 4,
\\ else => 4
\\ };
\\
\\ const Union = union(enum) {
\\ Int: i64,
\\ Float: f64,
\\ Float: f64
\\ };
\\
\\ const u = Union { .Int = 0 };
\\ const u = Union{ .Int = 0 };
\\ switch (u) {
\\ Union.Int => |int| {},
\\ Union.Float => |*float| unreachable,
\\ Union.Float => |*float| unreachable
\\ }
\\}
\\

View File

@ -86,6 +86,7 @@ pub const Token = struct {
PipeEqual,
Equal,
EqualEqual,
EqualAngleBracketRight,
BangEqual,
LParen,
RParen,
@ -688,6 +689,11 @@ pub const Tokenizer = struct {
self.index += 1;
break;
},
'>' => {
result.id = Token.Id.EqualAngleBracketRight;
self.index += 1;
break;
},
else => {
result.id = Token.Id.Equal;
break;