add switch statement support to parser

This commit is contained in:
Andrew Kelley 2016-01-19 20:29:09 -07:00
parent 17e574fec6
commit c17309dbc5
7 changed files with 204 additions and 6 deletions

View File

@ -94,7 +94,7 @@ BlockExpression : IfExpression | Block | WhileExpression | ForExpression | Switc
SwitchExpression : "switch" "(" Expression ")" "{" many(SwitchProng) "}"
SwitchProng : (list(SwitchItem, ",") | "else") option("(" "Symbol" ")") "=>" Expression ","
SwitchProng : (list(SwitchItem, ",") | "else") option("," "(" "Symbol" ")") "=>" Expression ","
SwitchItem : Expression | (Expression "..." Expression)

View File

@ -139,6 +139,9 @@ enum NodeType {
NodeTypeIfVarExpr,
NodeTypeWhileExpr,
NodeTypeForExpr,
NodeTypeSwitchExpr,
NodeTypeSwitchProng,
NodeTypeSwitchRange,
NodeTypeLabel,
NodeTypeGoto,
NodeTypeBreak,
@ -411,6 +414,25 @@ struct AstNodeForExpr {
VariableTableEntry *index_var;
};
struct AstNodeSwitchExpr {
AstNode *expr;
ZigList<AstNode *> prongs;
// populated by semantic analyzer
Expr resolved_expr;
};
struct AstNodeSwitchProng {
ZigList<AstNode *> items;
AstNode *var_symbol;
AstNode *expr;
};
struct AstNodeSwitchRange {
AstNode *start;
AstNode *end;
};
struct AstNodeLabel {
Buf name;
@ -623,6 +645,9 @@ struct AstNode {
AstNodeIfVarExpr if_var_expr;
AstNodeWhileExpr while_expr;
AstNodeForExpr for_expr;
AstNodeSwitchExpr switch_expr;
AstNodeSwitchProng switch_prong;
AstNodeSwitchRange switch_range;
AstNodeLabel label;
AstNodeGoto goto_expr;
AstNodeAsmExpr asm_expr;

View File

@ -30,6 +30,8 @@ static AstNode *first_executing_node(AstNode *node) {
return first_executing_node(node->data.slice_expr.array_ref_expr);
case NodeTypeFieldAccessExpr:
return first_executing_node(node->data.field_access_expr.struct_expr);
case NodeTypeSwitchRange:
return first_executing_node(node->data.switch_range.start);
case NodeTypeRoot:
case NodeTypeRootExportDecl:
case NodeTypeFnProto:
@ -61,6 +63,8 @@ static AstNode *first_executing_node(AstNode *node) {
case NodeTypeStructValueField:
case NodeTypeWhileExpr:
case NodeTypeForExpr:
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
case NodeTypeContainerInitExpr:
case NodeTypeArrayType:
return node;
@ -943,6 +947,9 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeForExpr:
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeLabel:
case NodeTypeGoto:
case NodeTypeBreak:
@ -3007,6 +3014,12 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
zig_unreachable();
}
static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
zig_panic("TODO analyze_switch_expr");
}
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
@ -3184,6 +3197,11 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
case NodeTypeArrayType:
return_type = analyze_array_type(g, import, context, expected_type, node);
break;
case NodeTypeSwitchExpr:
return_type = analyze_switch_expr(g, import, context, expected_type, node);
break;
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeDirective:
case NodeTypeFnDecl:
case NodeTypeFnProto:
@ -3338,6 +3356,9 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeForExpr:
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeLabel:
case NodeTypeGoto:
case NodeTypeBreak:
@ -3472,6 +3493,24 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode
}
collect_expr_decl_deps(g, import, node->data.array_type.child_type, decl_node);
break;
case NodeTypeSwitchExpr:
collect_expr_decl_deps(g, import, node->data.switch_expr.expr, decl_node);
for (int i = 0; i < node->data.switch_expr.prongs.length; i += 1) {
AstNode *prong = node->data.switch_expr.prongs.at(i);
collect_expr_decl_deps(g, import, prong, decl_node);
}
break;
case NodeTypeSwitchProng:
for (int i = 0; i < node->data.switch_prong.items.length; i += 1) {
AstNode *child = node->data.switch_prong.items.at(i);
collect_expr_decl_deps(g, import, child, decl_node);
}
collect_expr_decl_deps(g, import, node->data.switch_prong.expr, decl_node);
break;
case NodeTypeSwitchRange:
collect_expr_decl_deps(g, import, node->data.switch_range.start, decl_node);
collect_expr_decl_deps(g, import, node->data.switch_range.end, decl_node);
break;
case NodeTypeVariableDeclaration:
case NodeTypeFnProto:
case NodeTypeExternBlock:
@ -3661,6 +3700,9 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeForExpr:
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeLabel:
case NodeTypeGoto:
case NodeTypeBreak:
@ -3869,6 +3911,10 @@ Expr *get_resolved_expr(AstNode *node) {
return &node->data.label.resolved_expr;
case NodeTypeArrayType:
return &node->data.array_type.resolved_expr;
case NodeTypeSwitchExpr:
return &node->data.switch_expr.resolved_expr;
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeRoot:
case NodeTypeRootExportDecl:
case NodeTypeFnProto:
@ -3902,6 +3948,9 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) {
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeForExpr:
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeAsmExpr:
case NodeTypeContainerInitExpr:
case NodeTypeRoot:
@ -3953,6 +4002,9 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeForExpr:
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeAsmExpr:
case NodeTypeContainerInitExpr:
case NodeTypeRoot:

View File

@ -1965,6 +1965,12 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {
return fn_entry->fn_value;
}
static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeSwitchExpr);
zig_panic("TODO gen_switch_expr");
}
static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
switch (node->type) {
case NodeTypeBinOpExpr:
@ -2040,6 +2046,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
}
case NodeTypeContainerInitExpr:
return gen_container_init_expr(g, node);
case NodeTypeSwitchExpr:
return gen_switch_expr(g, node);
case NodeTypeRoot:
case NodeTypeRootExportDecl:
case NodeTypeFnProto:
@ -2053,6 +2061,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
case NodeTypeStructField:
case NodeTypeStructValueField:
case NodeTypeArrayType:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
zig_unreachable();
}
zig_unreachable();

View File

@ -123,6 +123,12 @@ const char *node_type_str(NodeType node_type) {
return "WhileExpr";
case NodeTypeForExpr:
return "ForExpr";
case NodeTypeSwitchExpr:
return "SwitchExpr";
case NodeTypeSwitchProng:
return "SwitchProng";
case NodeTypeSwitchRange:
return "SwitchRange";
case NodeTypeLabel:
return "Label";
case NodeTypeGoto:
@ -342,6 +348,30 @@ void ast_print(AstNode *node, int indent) {
}
ast_print(node->data.for_expr.body, indent + 2);
break;
case NodeTypeSwitchExpr:
fprintf(stderr, "%s\n", node_type_str(node->type));
ast_print(node->data.switch_expr.expr, indent + 2);
for (int i = 0; i < node->data.switch_expr.prongs.length; i += 1) {
AstNode *child_node = node->data.switch_expr.prongs.at(i);
ast_print(child_node, indent + 2);
}
break;
case NodeTypeSwitchProng:
fprintf(stderr, "%s\n", node_type_str(node->type));
for (int i = 0; i < node->data.switch_prong.items.length; i += 1) {
AstNode *child_node = node->data.switch_prong.items.at(i);
ast_print(child_node, indent + 2);
}
if (node->data.switch_prong.var_symbol) {
ast_print(node->data.switch_prong.var_symbol, indent + 2);
}
ast_print(node->data.switch_prong.expr, indent + 2);
break;
case NodeTypeSwitchRange:
fprintf(stderr, "%s\n", node_type_str(node->type));
ast_print(node->data.switch_range.start, indent + 2);
ast_print(node->data.switch_range.end, indent + 2);
break;
case NodeTypeLabel:
fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.label.name));
break;
@ -2167,7 +2197,80 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, int *token_index, bool mand
}
/*
BlockExpression : IfExpression | Block | WhileExpression | ForExpression
SwitchExpression : "switch" "(" Expression ")" "{" many(SwitchProng) "}"
SwitchProng : (list(SwitchItem, ",") | "else") option("," "(" "Symbol" ")") "=>" Expression ","
SwitchItem : Expression | (Expression "..." Expression)
*/
static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index);
if (token->id != TokenIdKeywordSwitch) {
if (mandatory) {
ast_invalid_token_error(pc, token);
} else {
return nullptr;
}
}
*token_index += 1;
AstNode *node = ast_create_node(pc, NodeTypeSwitchExpr, token);
ast_eat_token(pc, token_index, TokenIdLParen);
node->data.switch_expr.expr = ast_parse_expression(pc, token_index, true);
ast_eat_token(pc, token_index, TokenIdRParen);
ast_eat_token(pc, token_index, TokenIdLBrace);
for (;;) {
Token *token = &pc->tokens->at(*token_index);
if (token->id == TokenIdRBrace) {
*token_index += 1;
return node;
}
AstNode *prong_node = ast_create_node(pc, NodeTypeSwitchProng, token);
node->data.switch_expr.prongs.append(prong_node);
if (token->id == TokenIdKeywordElse) {
*token_index += 1;
} else for (;;) {
AstNode *expr1 = ast_parse_expression(pc, token_index, true);
Token *ellipsis_tok = &pc->tokens->at(*token_index);
if (ellipsis_tok->id == TokenIdEllipsis) {
*token_index += 1;
AstNode *range_node = ast_create_node(pc, NodeTypeSwitchRange, ellipsis_tok);
prong_node->data.switch_prong.items.append(range_node);
range_node->data.switch_range.start = expr1;
range_node->data.switch_range.end = ast_parse_expression(pc, token_index, true);
} else {
prong_node->data.switch_prong.items.append(expr1);
}
Token *comma_tok = &pc->tokens->at(*token_index);
if (comma_tok->id == TokenIdComma) {
*token_index += 1;
continue;
}
break;
}
Token *arrow_or_comma = &pc->tokens->at(*token_index);
if (arrow_or_comma->id == TokenIdComma) {
*token_index += 1;
ast_eat_token(pc, token_index, TokenIdLParen);
prong_node->data.switch_prong.var_symbol = ast_parse_symbol(pc, token_index);
ast_eat_token(pc, token_index, TokenIdRParen);
}
ast_eat_token(pc, token_index, TokenIdFatArrow);
prong_node->data.switch_prong.expr = ast_parse_expression(pc, token_index, true);
ast_eat_token(pc, token_index, TokenIdComma);
}
}
/*
BlockExpression : IfExpression | Block | WhileExpression | ForExpression | SwitchExpression
*/
static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index);
@ -2176,10 +2279,6 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool ma
if (if_expr)
return if_expr;
AstNode *block = ast_parse_block(pc, token_index, false);
if (block)
return block;
AstNode *while_expr = ast_parse_while_expr(pc, token_index, false);
if (while_expr)
return while_expr;
@ -2188,6 +2287,14 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool ma
if (for_expr)
return for_expr;
AstNode *switch_expr = ast_parse_switch_expr(pc, token_index, false);
if (switch_expr)
return switch_expr;
AstNode *block = ast_parse_block(pc, token_index, false);
if (block)
return block;
if (mandatory)
ast_invalid_token_error(pc, token);

View File

@ -243,6 +243,8 @@ static void end_token(Tokenize *t) {
t->cur_tok->id = TokenIdKeywordNull;
} else if (mem_eql_str(token_mem, token_len, "noalias")) {
t->cur_tok->id = TokenIdKeywordNoAlias;
} else if (mem_eql_str(token_mem, token_len, "switch")) {
t->cur_tok->id = TokenIdKeywordSwitch;
}
t->cur_tok = nullptr;
@ -1035,6 +1037,7 @@ const char * token_name(TokenId id) {
case TokenIdKeywordBreak: return "break";
case TokenIdKeywordNull: return "null";
case TokenIdKeywordNoAlias: return "noalias";
case TokenIdKeywordSwitch: return "switch";
case TokenIdLParen: return "(";
case TokenIdRParen: return ")";
case TokenIdComma: return ",";

View File

@ -36,6 +36,7 @@ enum TokenId {
TokenIdKeywordBreak,
TokenIdKeywordNull,
TokenIdKeywordNoAlias,
TokenIdKeywordSwitch,
TokenIdLParen,
TokenIdRParen,
TokenIdComma,