Skip to content

Commit 20b1491

Browse files
committed
implement while for nullables and error unions
See #357
1 parent 698829b commit 20b1491

File tree

6 files changed

+330
-84
lines changed

6 files changed

+330
-84
lines changed

src/ast_render.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -725,12 +725,23 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
725725
const char *inline_str = node->data.while_expr.is_inline ? "inline " : "";
726726
fprintf(ar->f, "%swhile (", inline_str);
727727
render_node_grouped(ar, node->data.while_expr.condition);
728+
fprintf(ar->f, ") ");
729+
if (node->data.while_expr.var_symbol) {
730+
fprintf(ar->f, "|%s| ", buf_ptr(node->data.while_expr.var_symbol));
731+
}
728732
if (node->data.while_expr.continue_expr) {
729-
fprintf(ar->f, "; ");
733+
fprintf(ar->f, ": (");
730734
render_node_grouped(ar, node->data.while_expr.continue_expr);
735+
fprintf(ar->f, ") ");
731736
}
732-
fprintf(ar->f, ") ");
733737
render_node_grouped(ar, node->data.while_expr.body);
738+
if (node->data.while_expr.else_node) {
739+
fprintf(ar->f, " else ");
740+
if (node->data.while_expr.err_symbol) {
741+
fprintf(ar->f, "|%s| ", buf_ptr(node->data.while_expr.err_symbol));
742+
}
743+
render_node_grouped(ar, node->data.while_expr.else_node);
744+
}
734745
break;
735746
}
736747
case NodeTypeThisLiteral:

src/ir.cpp

Lines changed: 175 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4658,51 +4658,197 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
46584658
assert(node->type == NodeTypeWhileExpr);
46594659

46604660
AstNode *continue_expr_node = node->data.while_expr.continue_expr;
4661+
AstNode *else_node = node->data.while_expr.else_node;
46614662

46624663
IrBasicBlock *cond_block = ir_build_basic_block(irb, scope, "WhileCond");
46634664
IrBasicBlock *body_block = ir_build_basic_block(irb, scope, "WhileBody");
46644665
IrBasicBlock *continue_block = continue_expr_node ?
46654666
ir_build_basic_block(irb, scope, "WhileContinue") : cond_block;
46664667
IrBasicBlock *end_block = ir_build_basic_block(irb, scope, "WhileEnd");
4668+
IrBasicBlock *else_block = else_node ?
4669+
ir_build_basic_block(irb, scope, "WhileElse") : end_block;
46674670

46684671
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node,
46694672
ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline);
46704673
ir_build_br(irb, scope, node, cond_block, is_comptime);
46714674

4672-
if (continue_expr_node) {
4673-
ir_set_cursor_at_end(irb, continue_block);
4674-
IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, scope);
4675-
if (expr_result == irb->codegen->invalid_instruction)
4676-
return expr_result;
4677-
if (!instr_is_unreachable(expr_result))
4678-
ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime));
4679-
}
4675+
Buf *var_symbol = node->data.while_expr.var_symbol;
4676+
Buf *err_symbol = node->data.while_expr.err_symbol;
4677+
if (err_symbol != nullptr) {
4678+
ir_set_cursor_at_end(irb, cond_block);
4679+
4680+
Scope *payload_scope;
4681+
AstNode *symbol_node = node; // TODO make more accurate
4682+
VariableTableEntry *payload_var;
4683+
if (var_symbol) {
4684+
// TODO make it an error to write to payload variable
4685+
payload_var = ir_create_var(irb, symbol_node, scope, var_symbol,
4686+
true, false, false, is_comptime);
4687+
payload_scope = payload_var->child_scope;
4688+
} else {
4689+
payload_scope = scope;
4690+
}
4691+
IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LVAL_PTR);
4692+
if (err_val_ptr == irb->codegen->invalid_instruction)
4693+
return err_val_ptr;
4694+
IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr);
4695+
IrInstruction *is_err = ir_build_test_err(irb, scope, node->data.while_expr.condition, err_val);
4696+
if (!instr_is_unreachable(is_err)) {
4697+
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err,
4698+
else_block, body_block, is_comptime));
4699+
}
4700+
4701+
ir_set_cursor_at_end(irb, body_block);
4702+
if (var_symbol) {
4703+
IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node,
4704+
err_val_ptr, false);
4705+
IrInstruction *var_value = node->data.while_expr.var_is_ptr ?
4706+
var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value);
4707+
ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, var_value);
4708+
}
4709+
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
4710+
loop_stack_item->break_block = end_block;
4711+
loop_stack_item->continue_block = continue_block;
4712+
loop_stack_item->is_comptime = is_comptime;
4713+
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, payload_scope);
4714+
if (body_result == irb->codegen->invalid_instruction)
4715+
return body_result;
4716+
irb->loop_stack.pop();
4717+
4718+
if (!instr_is_unreachable(body_result))
4719+
ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime));
4720+
4721+
if (continue_expr_node) {
4722+
ir_set_cursor_at_end(irb, continue_block);
4723+
IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope);
4724+
if (expr_result == irb->codegen->invalid_instruction)
4725+
return expr_result;
4726+
if (!instr_is_unreachable(expr_result))
4727+
ir_mark_gen(ir_build_br(irb, payload_scope, node, cond_block, is_comptime));
4728+
}
4729+
4730+
if (else_node) {
4731+
ir_set_cursor_at_end(irb, else_block);
46804732

4681-
ir_set_cursor_at_end(irb, cond_block);
4682-
IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope);
4683-
if (cond_val == irb->codegen->invalid_instruction)
4684-
return cond_val;
4685-
if (!instr_is_unreachable(cond_val)) {
4686-
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val,
4687-
body_block, end_block, is_comptime));
4688-
}
4733+
// TODO make it an error to write to error variable
4734+
AstNode *err_symbol_node = else_node; // TODO make more accurate
4735+
VariableTableEntry *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol,
4736+
true, false, false, is_comptime);
4737+
Scope *err_scope = err_var->child_scope;
4738+
IrInstruction *err_var_value = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr);
4739+
ir_build_var_decl(irb, err_scope, symbol_node, err_var, nullptr, err_var_value);
4740+
4741+
IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope);
4742+
if (else_result == irb->codegen->invalid_instruction)
4743+
return else_result;
4744+
if (!instr_is_unreachable(else_result))
4745+
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
4746+
}
4747+
4748+
ir_set_cursor_at_end(irb, end_block);
4749+
return ir_build_const_void(irb, scope, node);
4750+
} else if (var_symbol != nullptr) {
4751+
ir_set_cursor_at_end(irb, cond_block);
4752+
// TODO make it an error to write to payload variable
4753+
AstNode *symbol_node = node; // TODO make more accurate
4754+
VariableTableEntry *payload_var = ir_create_var(irb, symbol_node, scope, var_symbol,
4755+
true, false, false, is_comptime);
4756+
Scope *child_scope = payload_var->child_scope;
4757+
IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LVAL_PTR);
4758+
if (maybe_val_ptr == irb->codegen->invalid_instruction)
4759+
return maybe_val_ptr;
4760+
IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr);
4761+
IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val);
4762+
if (!instr_is_unreachable(is_non_null)) {
4763+
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null,
4764+
body_block, else_block, is_comptime));
4765+
}
4766+
4767+
ir_set_cursor_at_end(irb, body_block);
4768+
IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false);
4769+
IrInstruction *var_value = node->data.while_expr.var_is_ptr ?
4770+
var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value);
4771+
ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, var_value);
4772+
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
4773+
loop_stack_item->break_block = end_block;
4774+
loop_stack_item->continue_block = continue_block;
4775+
loop_stack_item->is_comptime = is_comptime;
4776+
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, child_scope);
4777+
if (body_result == irb->codegen->invalid_instruction)
4778+
return body_result;
4779+
irb->loop_stack.pop();
4780+
4781+
if (!instr_is_unreachable(body_result))
4782+
ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
4783+
4784+
if (continue_expr_node) {
4785+
ir_set_cursor_at_end(irb, continue_block);
4786+
IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, child_scope);
4787+
if (expr_result == irb->codegen->invalid_instruction)
4788+
return expr_result;
4789+
if (!instr_is_unreachable(expr_result))
4790+
ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime));
4791+
}
4792+
4793+
if (else_node) {
4794+
ir_set_cursor_at_end(irb, else_block);
46894795

4690-
ir_set_cursor_at_end(irb, body_block);
4796+
IrInstruction *else_result = ir_gen_node(irb, else_node, scope);
4797+
if (else_result == irb->codegen->invalid_instruction)
4798+
return else_result;
4799+
if (!instr_is_unreachable(else_result))
4800+
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
4801+
}
46914802

4692-
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
4693-
loop_stack_item->break_block = end_block;
4694-
loop_stack_item->continue_block = continue_block;
4695-
loop_stack_item->is_comptime = is_comptime;
4696-
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, scope);
4697-
if (body_result == irb->codegen->invalid_instruction)
4698-
return body_result;
4699-
irb->loop_stack.pop();
4803+
ir_set_cursor_at_end(irb, end_block);
4804+
return ir_build_const_void(irb, scope, node);
4805+
} else {
4806+
if (continue_expr_node) {
4807+
ir_set_cursor_at_end(irb, continue_block);
4808+
IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, scope);
4809+
if (expr_result == irb->codegen->invalid_instruction)
4810+
return expr_result;
4811+
if (!instr_is_unreachable(expr_result))
4812+
ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime));
4813+
}
4814+
4815+
ir_set_cursor_at_end(irb, cond_block);
4816+
IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope);
4817+
if (cond_val == irb->codegen->invalid_instruction)
4818+
return cond_val;
4819+
if (!instr_is_unreachable(cond_val)) {
4820+
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val,
4821+
body_block, else_block, is_comptime));
4822+
}
4823+
4824+
ir_set_cursor_at_end(irb, body_block);
4825+
4826+
LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
4827+
loop_stack_item->break_block = end_block;
4828+
loop_stack_item->continue_block = continue_block;
4829+
loop_stack_item->is_comptime = is_comptime;
4830+
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, scope);
4831+
if (body_result == irb->codegen->invalid_instruction)
4832+
return body_result;
4833+
irb->loop_stack.pop();
4834+
4835+
if (!instr_is_unreachable(body_result))
4836+
ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
4837+
4838+
if (else_node) {
4839+
ir_set_cursor_at_end(irb, else_block);
47004840

4701-
if (!instr_is_unreachable(body_result))
4702-
ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
4703-
ir_set_cursor_at_end(irb, end_block);
4841+
IrInstruction *else_result = ir_gen_node(irb, else_node, scope);
4842+
if (else_result == irb->codegen->invalid_instruction)
4843+
return else_result;
4844+
if (!instr_is_unreachable(else_result))
4845+
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
4846+
}
47044847

4705-
return ir_build_const_void(irb, scope, node);
4848+
ir_set_cursor_at_end(irb, end_block);
4849+
4850+
return ir_build_const_void(irb, scope, node);
4851+
}
47064852
}
47074853

47084854
static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) {

src/parser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1654,7 +1654,7 @@ static AstNode *ast_parse_while_expr(ParseContext *pc, size_t *token_index, bool
16541654
ast_eat_token(pc, token_index, TokenIdBinOr);
16551655
}
16561656

1657-
node->data.while_expr.body = ast_parse_block_or_expression(pc, token_index, true);
1657+
node->data.while_expr.else_node = ast_parse_block_or_expression(pc, token_index, true);
16581658
}
16591659

16601660
return node;

std/linked_list.zig

Lines changed: 18 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -187,43 +187,6 @@ pub fn LinkedList(comptime T: type) -> type {
187187
};
188188
return node;
189189
}
190-
191-
/// Iterate through the elements of the list.
192-
///
193-
/// Returns:
194-
/// A list iterator with a next() method.
195-
pub fn iterate(list: &List) -> List.Iterator(false) {
196-
List.Iterator(false) {
197-
.node = list.first,
198-
}
199-
}
200-
201-
/// Iterate through the elements of the list backwards.
202-
///
203-
/// Returns:
204-
/// A list iterator with a next() method.
205-
pub fn iterateBackwards(list: &List) -> List.Iterator(true) {
206-
List.Iterator(true) {
207-
.node = list.last,
208-
}
209-
}
210-
211-
/// Abstract iteration over a linked list.
212-
pub fn Iterator(comptime backwards: bool) -> type {
213-
struct {
214-
const It = this;
215-
216-
node: ?&Node,
217-
218-
/// Return the next element of the list, until the end.
219-
/// When no more elements are available, return null.
220-
pub fn next(it: &It) -> ?&Node {
221-
const current = it.node ?? return null;
222-
it.node = if (backwards) current.prev else current.next;
223-
return current;
224-
}
225-
}
226-
}
227190
}
228191
}
229192

@@ -249,16 +212,24 @@ test "basic linked list test" {
249212
list.insertBefore(five, four); // {1, 2, 4, 5}
250213
list.insertAfter(two, three); // {1, 2, 3, 4, 5}
251214

252-
// Traverse the list forwards and backwards.
253-
var it = list.iterate();
254-
var it_reverse = list.iterateBackwards();
255-
var index: u32 = 1;
256-
while (true) {
257-
const node = it.next() ?? break;
258-
const node_reverse = it_reverse.next() ?? break;
259-
assert (node.data == index);
260-
assert (node_reverse.data == (6 - index));
261-
index += 1;
215+
// traverse forwards
216+
{
217+
var it = list.first;
218+
var index: u32 = 1;
219+
while (it) |node| : (it = node.next) {
220+
assert(node.data == index);
221+
index += 1;
222+
}
223+
}
224+
225+
// traverse backwards
226+
{
227+
var it = list.last;
228+
var index: u32 = 1;
229+
while (it) |node| : (it = node.prev) {
230+
assert(node.data == (6 - index));
231+
index += 1;
232+
}
262233
}
263234

264235
var first = list.popFirst(); // {2, 3, 4, 5}

0 commit comments

Comments
 (0)