parent
0454e610bf
commit
b6a679c0ed
|
@ -45,7 +45,7 @@ TypeExpr = PrefixOpExpression | "var"
|
|||
|
||||
BlockOrExpression = Block | Expression
|
||||
|
||||
Expression = ReturnExpression | AssignmentExpression
|
||||
Expression = ReturnExpression | BreakExpression | AssignmentExpression
|
||||
|
||||
AsmExpression = "asm" option("volatile") "(" String option(AsmOutput) ")"
|
||||
|
||||
|
@ -79,12 +79,14 @@ SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbo
|
|||
|
||||
SwitchItem = Expression | (Expression "..." Expression)
|
||||
|
||||
ForExpression(body) = "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body
|
||||
ForExpression(body) = "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
|
||||
|
||||
BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression
|
||||
|
||||
ReturnExpression = option("%") "return" option(Expression)
|
||||
|
||||
BreakExpression = "break" option(Expression)
|
||||
|
||||
Defer(body) = option("%") "defer" body
|
||||
|
||||
IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
|
||||
|
@ -151,7 +153,7 @@ GotoExpression = "goto" Symbol
|
|||
|
||||
GroupedExpression = "(" Expression ")"
|
||||
|
||||
KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "this" | "unreachable"
|
||||
KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
|
||||
|
||||
ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" many(ContainerMember) "}"
|
||||
```
|
||||
|
|
|
@ -623,6 +623,7 @@ struct AstNodeForExpr {
|
|||
AstNode *elem_node; // always a symbol
|
||||
AstNode *index_node; // always a symbol, might be null
|
||||
AstNode *body;
|
||||
AstNode *else_node; // can be null
|
||||
bool elem_is_ptr;
|
||||
bool is_inline;
|
||||
};
|
||||
|
@ -774,6 +775,7 @@ struct AstNodeBoolLiteral {
|
|||
};
|
||||
|
||||
struct AstNodeBreakExpr {
|
||||
AstNode *expr; // may be null
|
||||
};
|
||||
|
||||
struct AstNodeContinueExpr {
|
||||
|
|
|
@ -485,6 +485,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case NodeTypeBreak:
|
||||
{
|
||||
fprintf(ar->f, "break");
|
||||
if (node->data.break_expr.expr) {
|
||||
fprintf(ar->f, " ");
|
||||
render_node_grouped(ar, node->data.break_expr.expr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeTypeDefer:
|
||||
{
|
||||
const char *defer_str = defer_string(node->data.defer.kind);
|
||||
|
@ -880,11 +889,10 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
|||
fprintf(ar->f, "| ");
|
||||
}
|
||||
render_node_grouped(ar, node->data.for_expr.body);
|
||||
break;
|
||||
}
|
||||
case NodeTypeBreak:
|
||||
{
|
||||
fprintf(ar->f, "break");
|
||||
if (node->data.for_expr.else_node) {
|
||||
fprintf(ar->f, " else");
|
||||
render_node_grouped(ar, node->data.for_expr.else_node);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeTypeContinue:
|
||||
|
|
133
src/ir.cpp
133
src/ir.cpp
|
@ -22,6 +22,8 @@ struct LoopStackItem {
|
|||
IrBasicBlock *break_block;
|
||||
IrBasicBlock *continue_block;
|
||||
IrInstruction *is_comptime;
|
||||
ZigList<IrInstruction *> *incoming_values;
|
||||
ZigList<IrBasicBlock *> *incoming_blocks;
|
||||
};
|
||||
|
||||
struct IrBuilder {
|
||||
|
@ -56,6 +58,18 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *sc
|
|||
static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction);
|
||||
static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type);
|
||||
|
||||
static LoopStackItem *add_loop_stack_item(IrBuilder *irb, IrBasicBlock *break_block, IrBasicBlock *continue_block,
|
||||
IrInstruction *is_comptime, ZigList<IrBasicBlock *> *incoming_blocks, ZigList<IrInstruction *> *incoming_values)
|
||||
{
|
||||
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
|
||||
loop_stack_item->break_block = break_block;
|
||||
loop_stack_item->continue_block = continue_block;
|
||||
loop_stack_item->is_comptime = is_comptime;
|
||||
loop_stack_item->incoming_blocks = incoming_blocks;
|
||||
loop_stack_item->incoming_values = incoming_values;
|
||||
return loop_stack_item;
|
||||
}
|
||||
|
||||
ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
|
||||
assert(const_val->type->id == TypeTableEntryIdPointer);
|
||||
assert(const_val->special == ConstValSpecialStatic);
|
||||
|
@ -4693,6 +4707,8 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
|
|||
return err_val_ptr;
|
||||
IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr);
|
||||
IrInstruction *is_err = ir_build_test_err(irb, scope, node->data.while_expr.condition, err_val);
|
||||
IrBasicBlock *after_cond_block = irb->current_basic_block;
|
||||
IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
|
||||
if (!instr_is_unreachable(is_err)) {
|
||||
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err,
|
||||
else_block, body_block, is_comptime));
|
||||
|
@ -4706,10 +4722,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
|
|||
var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value);
|
||||
ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, var_value);
|
||||
}
|
||||
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
|
||||
loop_stack_item->break_block = end_block;
|
||||
loop_stack_item->continue_block = continue_block;
|
||||
loop_stack_item->is_comptime = is_comptime;
|
||||
ZigList<IrInstruction *> incoming_values = {0};
|
||||
ZigList<IrBasicBlock *> incoming_blocks = {0};
|
||||
add_loop_stack_item(irb, end_block, continue_block, is_comptime, &incoming_blocks, &incoming_values);
|
||||
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, payload_scope);
|
||||
if (body_result == irb->codegen->invalid_instruction)
|
||||
return body_result;
|
||||
|
@ -4727,6 +4742,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
|
|||
ir_mark_gen(ir_build_br(irb, payload_scope, node, cond_block, is_comptime));
|
||||
}
|
||||
|
||||
IrInstruction *else_result = nullptr;
|
||||
if (else_node) {
|
||||
ir_set_cursor_at_end(irb, else_block);
|
||||
|
||||
|
@ -4738,15 +4754,23 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
|
|||
IrInstruction *err_var_value = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr);
|
||||
ir_build_var_decl(irb, err_scope, symbol_node, err_var, nullptr, err_var_value);
|
||||
|
||||
IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope);
|
||||
else_result = ir_gen_node(irb, else_node, err_scope);
|
||||
if (else_result == irb->codegen->invalid_instruction)
|
||||
return else_result;
|
||||
if (!instr_is_unreachable(else_result))
|
||||
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
|
||||
}
|
||||
|
||||
IrBasicBlock *after_else_block = irb->current_basic_block;
|
||||
ir_set_cursor_at_end(irb, end_block);
|
||||
return ir_build_const_void(irb, scope, node);
|
||||
if (else_result) {
|
||||
incoming_blocks.append(after_else_block);
|
||||
incoming_values.append(else_result);
|
||||
} else {
|
||||
incoming_blocks.append(after_cond_block);
|
||||
incoming_values.append(void_else_result);
|
||||
}
|
||||
|
||||
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
|
||||
} else if (var_symbol != nullptr) {
|
||||
ir_set_cursor_at_end(irb, cond_block);
|
||||
// TODO make it an error to write to payload variable
|
||||
|
@ -4759,6 +4783,8 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
|
|||
return maybe_val_ptr;
|
||||
IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr);
|
||||
IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val);
|
||||
IrBasicBlock *after_cond_block = irb->current_basic_block;
|
||||
IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
|
||||
if (!instr_is_unreachable(is_non_null)) {
|
||||
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null,
|
||||
body_block, else_block, is_comptime));
|
||||
|
@ -4769,10 +4795,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
|
|||
IrInstruction *var_value = node->data.while_expr.var_is_ptr ?
|
||||
var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value);
|
||||
ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, var_value);
|
||||
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
|
||||
loop_stack_item->break_block = end_block;
|
||||
loop_stack_item->continue_block = continue_block;
|
||||
loop_stack_item->is_comptime = is_comptime;
|
||||
ZigList<IrInstruction *> incoming_values = {0};
|
||||
ZigList<IrBasicBlock *> incoming_blocks = {0};
|
||||
add_loop_stack_item(irb, end_block, continue_block, is_comptime, &incoming_blocks, &incoming_values);
|
||||
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, child_scope);
|
||||
if (body_result == irb->codegen->invalid_instruction)
|
||||
return body_result;
|
||||
|
@ -4790,18 +4815,27 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
|
|||
ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime));
|
||||
}
|
||||
|
||||
IrInstruction *else_result = nullptr;
|
||||
if (else_node) {
|
||||
ir_set_cursor_at_end(irb, else_block);
|
||||
|
||||
IrInstruction *else_result = ir_gen_node(irb, else_node, scope);
|
||||
else_result = ir_gen_node(irb, else_node, scope);
|
||||
if (else_result == irb->codegen->invalid_instruction)
|
||||
return else_result;
|
||||
if (!instr_is_unreachable(else_result))
|
||||
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
|
||||
}
|
||||
|
||||
IrBasicBlock *after_else_block = irb->current_basic_block;
|
||||
ir_set_cursor_at_end(irb, end_block);
|
||||
return ir_build_const_void(irb, scope, node);
|
||||
if (else_result) {
|
||||
incoming_blocks.append(after_else_block);
|
||||
incoming_values.append(else_result);
|
||||
} else {
|
||||
incoming_blocks.append(after_cond_block);
|
||||
incoming_values.append(void_else_result);
|
||||
}
|
||||
|
||||
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
|
||||
} else {
|
||||
if (continue_expr_node) {
|
||||
ir_set_cursor_at_end(irb, continue_block);
|
||||
|
@ -4816,6 +4850,8 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
|
|||
IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope);
|
||||
if (cond_val == irb->codegen->invalid_instruction)
|
||||
return cond_val;
|
||||
IrBasicBlock *after_cond_block = irb->current_basic_block;
|
||||
IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
|
||||
if (!instr_is_unreachable(cond_val)) {
|
||||
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val,
|
||||
body_block, else_block, is_comptime));
|
||||
|
@ -4823,10 +4859,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
|
|||
|
||||
ir_set_cursor_at_end(irb, body_block);
|
||||
|
||||
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
|
||||
loop_stack_item->break_block = end_block;
|
||||
loop_stack_item->continue_block = continue_block;
|
||||
loop_stack_item->is_comptime = is_comptime;
|
||||
ZigList<IrInstruction *> incoming_values = {0};
|
||||
ZigList<IrBasicBlock *> incoming_blocks = {0};
|
||||
add_loop_stack_item(irb, end_block, continue_block, is_comptime, &incoming_blocks, &incoming_values);
|
||||
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, scope);
|
||||
if (body_result == irb->codegen->invalid_instruction)
|
||||
return body_result;
|
||||
|
@ -4835,19 +4870,27 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
|
|||
if (!instr_is_unreachable(body_result))
|
||||
ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
|
||||
|
||||
IrInstruction *else_result = nullptr;
|
||||
if (else_node) {
|
||||
ir_set_cursor_at_end(irb, else_block);
|
||||
|
||||
IrInstruction *else_result = ir_gen_node(irb, else_node, scope);
|
||||
else_result = ir_gen_node(irb, else_node, scope);
|
||||
if (else_result == irb->codegen->invalid_instruction)
|
||||
return else_result;
|
||||
if (!instr_is_unreachable(else_result))
|
||||
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
|
||||
}
|
||||
|
||||
IrBasicBlock *after_else_block = irb->current_basic_block;
|
||||
ir_set_cursor_at_end(irb, end_block);
|
||||
if (else_result) {
|
||||
incoming_blocks.append(after_else_block);
|
||||
incoming_values.append(else_result);
|
||||
} else {
|
||||
incoming_blocks.append(after_cond_block);
|
||||
incoming_values.append(void_else_result);
|
||||
}
|
||||
|
||||
return ir_build_const_void(irb, scope, node);
|
||||
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4858,6 +4901,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
|
|||
AstNode *elem_node = node->data.for_expr.elem_node;
|
||||
AstNode *index_node = node->data.for_expr.index_node;
|
||||
AstNode *body_node = node->data.for_expr.body;
|
||||
AstNode *else_node = node->data.for_expr.else_node;
|
||||
|
||||
if (!elem_node) {
|
||||
add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter"));
|
||||
|
@ -4915,6 +4959,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
|
|||
IrBasicBlock *cond_block = ir_build_basic_block(irb, child_scope, "ForCond");
|
||||
IrBasicBlock *body_block = ir_build_basic_block(irb, child_scope, "ForBody");
|
||||
IrBasicBlock *end_block = ir_build_basic_block(irb, child_scope, "ForEnd");
|
||||
IrBasicBlock *else_block = else_node ? ir_build_basic_block(irb, child_scope, "ForElse") : end_block;
|
||||
IrBasicBlock *continue_block = ir_build_basic_block(irb, child_scope, "ForContinue");
|
||||
|
||||
IrInstruction *len_val = ir_build_array_len(irb, child_scope, node, array_val);
|
||||
|
@ -4923,7 +4968,9 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
|
|||
ir_set_cursor_at_end(irb, cond_block);
|
||||
IrInstruction *index_val = ir_build_load_ptr(irb, child_scope, node, index_ptr);
|
||||
IrInstruction *cond = ir_build_bin_op(irb, child_scope, node, IrBinOpCmpLessThan, index_val, len_val, false);
|
||||
ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, end_block, is_comptime));
|
||||
IrBasicBlock *after_cond_block = irb->current_basic_block;
|
||||
IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
|
||||
ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime));
|
||||
|
||||
ir_set_cursor_at_end(irb, body_block);
|
||||
IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false);
|
||||
|
@ -4935,10 +4982,9 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
|
|||
}
|
||||
ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_var_ptr, elem_val));
|
||||
|
||||
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
|
||||
loop_stack_item->break_block = end_block;
|
||||
loop_stack_item->continue_block = continue_block;
|
||||
loop_stack_item->is_comptime = is_comptime;
|
||||
ZigList<IrInstruction *> incoming_values = {0};
|
||||
ZigList<IrBasicBlock *> incoming_blocks = {0};
|
||||
add_loop_stack_item(irb, end_block, continue_block, is_comptime, &incoming_blocks, &incoming_values);
|
||||
IrInstruction *body_result = ir_gen_node(irb, body_node, child_scope);
|
||||
irb->loop_stack.pop();
|
||||
|
||||
|
@ -4950,9 +4996,28 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
|
|||
ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val));
|
||||
ir_build_br(irb, child_scope, node, cond_block, is_comptime);
|
||||
|
||||
ir_set_cursor_at_end(irb, end_block);
|
||||
return ir_build_const_void(irb, child_scope, node);
|
||||
IrInstruction *else_result = nullptr;
|
||||
if (else_node) {
|
||||
ir_set_cursor_at_end(irb, else_block);
|
||||
|
||||
else_result = ir_gen_node(irb, else_node, parent_scope);
|
||||
if (else_result == irb->codegen->invalid_instruction)
|
||||
return else_result;
|
||||
if (!instr_is_unreachable(else_result))
|
||||
ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
|
||||
}
|
||||
IrBasicBlock *after_else_block = irb->current_basic_block;
|
||||
ir_set_cursor_at_end(irb, end_block);
|
||||
|
||||
if (else_result) {
|
||||
incoming_blocks.append(after_else_block);
|
||||
incoming_values.append(else_result);
|
||||
} else {
|
||||
incoming_blocks.append(after_cond_block);
|
||||
incoming_values.append(void_else_value);
|
||||
}
|
||||
|
||||
return ir_build_phi(irb, parent_scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_this_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
|
||||
|
@ -5512,8 +5577,20 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *scope, AstNode *node)
|
|||
is_comptime = loop_stack_item->is_comptime;
|
||||
}
|
||||
|
||||
IrInstruction *result_value;
|
||||
if (node->data.break_expr.expr) {
|
||||
result_value = ir_gen_node(irb, node->data.break_expr.expr, scope);
|
||||
if (result_value == irb->codegen->invalid_instruction)
|
||||
return irb->codegen->invalid_instruction;
|
||||
} else {
|
||||
result_value = ir_build_const_void(irb, scope, node);
|
||||
}
|
||||
|
||||
IrBasicBlock *dest_block = loop_stack_item->break_block;
|
||||
ir_gen_defers_for_block(irb, scope, dest_block->scope, false);
|
||||
|
||||
loop_stack_item->incoming_blocks->append(irb->current_basic_block);
|
||||
loop_stack_item->incoming_values->append(result_value);
|
||||
return ir_build_br(irb, scope, node, dest_block, is_comptime);
|
||||
}
|
||||
|
||||
|
|
|
@ -641,7 +641,7 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
|
|||
|
||||
/*
|
||||
PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl
|
||||
KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "this" | "unreachable"
|
||||
KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
|
||||
*/
|
||||
static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
|
@ -677,10 +677,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
|
|||
AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token);
|
||||
*token_index += 1;
|
||||
return node;
|
||||
} else if (token->id == TokenIdKeywordBreak) {
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBreak, token);
|
||||
*token_index += 1;
|
||||
return node;
|
||||
} else if (token->id == TokenIdKeywordContinue) {
|
||||
AstNode *node = ast_create_node(pc, NodeTypeContinue, token);
|
||||
*token_index += 1;
|
||||
|
@ -1447,6 +1443,24 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index) {
|
|||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
BreakExpression : "break" option(Expression)
|
||||
*/
|
||||
static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
|
||||
if (token->id == TokenIdKeywordBreak) {
|
||||
*token_index += 1;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeBreak, token);
|
||||
node->data.break_expr.expr = ast_parse_expression(pc, token_index, false);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
Defer(body) = option("%") "defer" body
|
||||
*/
|
||||
|
@ -1668,7 +1682,7 @@ static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index) {
|
|||
}
|
||||
|
||||
/*
|
||||
ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body
|
||||
ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
|
||||
*/
|
||||
static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
||||
Token *first_token = &pc->tokens->at(*token_index);
|
||||
|
@ -1728,6 +1742,13 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool m
|
|||
|
||||
node->data.for_expr.body = ast_parse_block_or_expression(pc, token_index, true);
|
||||
|
||||
Token *else_tok = &pc->tokens->at(*token_index);
|
||||
if (else_tok->id == TokenIdKeywordElse) {
|
||||
*token_index += 1;
|
||||
|
||||
node->data.for_expr.else_node = ast_parse_block_or_expression(pc, token_index, true);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@ -1981,7 +2002,7 @@ static AstNode *ast_parse_block_or_expression(ParseContext *pc, size_t *token_in
|
|||
}
|
||||
|
||||
/*
|
||||
Expression = ReturnExpression | AssignmentExpression
|
||||
Expression = ReturnExpression | BreakExpression | AssignmentExpression
|
||||
*/
|
||||
static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
|
@ -1990,6 +2011,10 @@ static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool
|
|||
if (return_expr)
|
||||
return return_expr;
|
||||
|
||||
AstNode *break_expr = ast_parse_break_expr(pc, token_index);
|
||||
if (break_expr)
|
||||
return break_expr;
|
||||
|
||||
AstNode *ass_expr = ast_parse_ass_expr(pc, token_index, false);
|
||||
if (ass_expr)
|
||||
return ass_expr;
|
||||
|
|
|
@ -134,3 +134,64 @@ fn getNumberOrNull() -> ?i32 {
|
|||
};
|
||||
}
|
||||
|
||||
test "while on nullable with else result follow else prong" {
|
||||
const result = while (returnNull()) |value| {
|
||||
break value;
|
||||
} else {
|
||||
i32(2)
|
||||
};
|
||||
assert(result == 2);
|
||||
}
|
||||
|
||||
test "while on nullable with else result follow break prong" {
|
||||
const result = while (returnMaybe(10)) |value| {
|
||||
break value;
|
||||
} else {
|
||||
i32(2)
|
||||
};
|
||||
assert(result == 10);
|
||||
}
|
||||
|
||||
test "while on error union with else result follow else prong" {
|
||||
const result = while (returnError()) |value| {
|
||||
break value;
|
||||
} else |err| {
|
||||
i32(2)
|
||||
};
|
||||
assert(result == 2);
|
||||
}
|
||||
|
||||
test "while on error union with else result follow break prong" {
|
||||
const result = while (returnSuccess(10)) |value| {
|
||||
break value;
|
||||
} else |err| {
|
||||
i32(2)
|
||||
};
|
||||
assert(result == 10);
|
||||
}
|
||||
|
||||
test "while on bool with else result follow else prong" {
|
||||
const result = while (returnFalse()) {
|
||||
break i32(10);
|
||||
} else {
|
||||
i32(2)
|
||||
};
|
||||
assert(result == 2);
|
||||
}
|
||||
|
||||
test "while on bool with else result follow break prong" {
|
||||
const result = while (returnTrue()) {
|
||||
break i32(10);
|
||||
} else {
|
||||
i32(2)
|
||||
};
|
||||
assert(result == 10);
|
||||
}
|
||||
|
||||
fn returnNull() -> ?i32 { null }
|
||||
fn returnMaybe(x: i32) -> ?i32 { x }
|
||||
error YouWantedAnError;
|
||||
fn returnError() -> %i32 { error.YouWantedAnError }
|
||||
fn returnSuccess(x: i32) -> %i32 { x }
|
||||
fn returnFalse() -> bool { false }
|
||||
fn returnTrue() -> bool { true }
|
||||
|
|
Loading…
Reference in New Issue