mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-26 15:34:59 -07:00
Make the parser support as many identifiers on the LHS in for in loops to improve error messages
This commit is contained in:
+18
-9
@@ -1666,7 +1666,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
|
||||
Ast *expr = unparen_expr(rs->expr);
|
||||
|
||||
|
||||
isize max_val_count = 2;
|
||||
if (is_ast_range(expr)) {
|
||||
ast_node(ie, BinaryExpr, expr);
|
||||
Operand x = {};
|
||||
@@ -1739,9 +1739,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
break;
|
||||
|
||||
case Type_Tuple:
|
||||
if (false) {
|
||||
check_not_tuple(ctx, &operand);
|
||||
} else {
|
||||
{
|
||||
isize count = t->Tuple.variables.count;
|
||||
if (count < 1 || count > 3) {
|
||||
check_not_tuple(ctx, &operand);
|
||||
@@ -1759,14 +1757,14 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
if (count > 1) val0 = t->Tuple.variables[0]->type;
|
||||
if (count > 2) val1 = t->Tuple.variables[1]->type;
|
||||
|
||||
if (rs->val1 != nullptr && count < 3) {
|
||||
if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) {
|
||||
gbString s = type_to_string(t);
|
||||
error(operand.expr, "Expected a 3-value tuple on the rhs, got (%s)", s);
|
||||
gb_string_free(s);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rs->val0 != nullptr && count < 2) {
|
||||
if (rs->vals.count > 0 && rs->vals[0] != nullptr && count < 2) {
|
||||
gbString s = type_to_string(t);
|
||||
error(operand.expr, "Expected at least a 2-values tuple on the rhs, got (%s)", s);
|
||||
gb_string_free(s);
|
||||
@@ -1776,6 +1774,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
}
|
||||
break;
|
||||
|
||||
case Type_Struct:
|
||||
if (t->Struct.soa_kind != StructSoa_None) {
|
||||
error(operand.expr, "#soa structures do not yet support for in loop iteration");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1787,9 +1790,9 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
|
||||
error(operand.expr, "Cannot iterate over '%s' of type '%s'", s, t);
|
||||
|
||||
if (rs->val0 != nullptr && rs->val1 == nullptr) {
|
||||
if (rs->vals.count == 1) {
|
||||
if (is_type_map(operand.type) || is_type_bit_set(operand.type)) {
|
||||
gbString v = expr_to_string(rs->val0);
|
||||
gbString v = expr_to_string(rs->vals[0]);
|
||||
defer (gb_string_free(v));
|
||||
error_line("\tSuggestion: place parentheses around the expression\n");
|
||||
error_line("\t for (%s in %s) {\n", v, s);
|
||||
@@ -1800,8 +1803,14 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
|
||||
skip_expr_range_stmt:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird.
|
||||
|
||||
Ast * lhs[2] = {rs->val0, rs->val1};
|
||||
if (rs->vals.count > max_val_count) {
|
||||
error(rs->vals[max_val_count], "Expected a maximum of %td identifier%s, got %td", max_val_count, max_val_count == 1 ? "" : "s", rs->vals.count);
|
||||
}
|
||||
|
||||
Ast * lhs[2] = {};
|
||||
Type *rhs[2] = {val0, val1};
|
||||
if (rs->vals.count > 1) { lhs[1] = rs->vals[1]; }
|
||||
if (rs->vals.count > 0) { lhs[0] = rs->vals[0]; }
|
||||
|
||||
for (isize i = 0; i < 2; i++) {
|
||||
if (lhs[i] == nullptr) {
|
||||
|
||||
+10
-10
@@ -10735,18 +10735,18 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
|
||||
|
||||
Type *val0_type = nullptr;
|
||||
Type *val1_type = nullptr;
|
||||
if (rs->val0 != nullptr && !is_blank_ident(rs->val0)) {
|
||||
val0_type = type_of_expr(rs->val0);
|
||||
if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) {
|
||||
val0_type = type_of_expr(rs->vals[0]);
|
||||
}
|
||||
if (rs->val1 != nullptr && !is_blank_ident(rs->val1)) {
|
||||
val1_type = type_of_expr(rs->val1);
|
||||
if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) {
|
||||
val1_type = type_of_expr(rs->vals[1]);
|
||||
}
|
||||
|
||||
if (val0_type != nullptr) {
|
||||
ir_add_local_for_identifier(proc, rs->val0, true);
|
||||
ir_add_local_for_identifier(proc, rs->vals[0], true);
|
||||
}
|
||||
if (val1_type != nullptr) {
|
||||
ir_add_local_for_identifier(proc, rs->val1, true);
|
||||
ir_add_local_for_identifier(proc, rs->vals[1], true);
|
||||
}
|
||||
|
||||
irValue *val = nullptr;
|
||||
@@ -10851,11 +10851,11 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
|
||||
|
||||
|
||||
if (is_map) {
|
||||
if (val0_type) ir_store_range_stmt_val(proc, rs->val0, key);
|
||||
if (val1_type) ir_store_range_stmt_val(proc, rs->val1, val);
|
||||
if (val0_type) ir_store_range_stmt_val(proc, rs->vals[0], key);
|
||||
if (val1_type) ir_store_range_stmt_val(proc, rs->vals[1], val);
|
||||
} else {
|
||||
if (val0_type) ir_store_range_stmt_val(proc, rs->val0, val);
|
||||
if (val1_type) ir_store_range_stmt_val(proc, rs->val1, key);
|
||||
if (val0_type) ir_store_range_stmt_val(proc, rs->vals[0], val);
|
||||
if (val1_type) ir_store_range_stmt_val(proc, rs->vals[1], key);
|
||||
}
|
||||
|
||||
ir_push_target_list(proc, rs->label, done, loop, nullptr);
|
||||
|
||||
+10
-10
@@ -3889,19 +3889,19 @@ void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
|
||||
|
||||
Type *val0_type = nullptr;
|
||||
Type *val1_type = nullptr;
|
||||
if (rs->val0 != nullptr && !is_blank_ident(rs->val0)) {
|
||||
val0_type = type_of_expr(rs->val0);
|
||||
if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) {
|
||||
val0_type = type_of_expr(rs->vals[0]);
|
||||
}
|
||||
if (rs->val1 != nullptr && !is_blank_ident(rs->val1)) {
|
||||
val1_type = type_of_expr(rs->val1);
|
||||
if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) {
|
||||
val1_type = type_of_expr(rs->vals[1]);
|
||||
}
|
||||
|
||||
if (val0_type != nullptr) {
|
||||
Entity *e = entity_of_node(rs->val0);
|
||||
Entity *e = entity_of_node(rs->vals[0]);
|
||||
lb_add_local(p, e->type, e, true);
|
||||
}
|
||||
if (val1_type != nullptr) {
|
||||
Entity *e = entity_of_node(rs->val1);
|
||||
Entity *e = entity_of_node(rs->vals[1]);
|
||||
lb_add_local(p, e->type, e, true);
|
||||
}
|
||||
|
||||
@@ -4002,11 +4002,11 @@ void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
|
||||
|
||||
|
||||
if (is_map) {
|
||||
if (val0_type) lb_store_range_stmt_val(p, rs->val0, key);
|
||||
if (val1_type) lb_store_range_stmt_val(p, rs->val1, val);
|
||||
if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], key);
|
||||
if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], val);
|
||||
} else {
|
||||
if (val0_type) lb_store_range_stmt_val(p, rs->val0, val);
|
||||
if (val1_type) lb_store_range_stmt_val(p, rs->val1, key);
|
||||
if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], val);
|
||||
if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], key);
|
||||
}
|
||||
|
||||
lb_push_target_list(p, rs->label, done, loop, nullptr);
|
||||
|
||||
+6
-22
@@ -315,8 +315,7 @@ Ast *clone_ast(Ast *node) {
|
||||
break;
|
||||
case Ast_RangeStmt:
|
||||
n->RangeStmt.label = clone_ast(n->RangeStmt.label);
|
||||
n->RangeStmt.val0 = clone_ast(n->RangeStmt.val0);
|
||||
n->RangeStmt.val1 = clone_ast(n->RangeStmt.val1);
|
||||
n->RangeStmt.vals = clone_ast_array(n->RangeStmt.vals);
|
||||
n->RangeStmt.expr = clone_ast(n->RangeStmt.expr);
|
||||
n->RangeStmt.body = clone_ast(n->RangeStmt.body);
|
||||
break;
|
||||
@@ -842,11 +841,10 @@ Ast *ast_for_stmt(AstFile *f, Token token, Ast *init, Ast *cond, Ast *post, Ast
|
||||
return result;
|
||||
}
|
||||
|
||||
Ast *ast_range_stmt(AstFile *f, Token token, Ast *val0, Ast *val1, Token in_token, Ast *expr, Ast *body) {
|
||||
Ast *ast_range_stmt(AstFile *f, Token token, Slice<Ast *> vals, Token in_token, Ast *expr, Ast *body) {
|
||||
Ast *result = alloc_ast_node(f, Ast_RangeStmt);
|
||||
result->RangeStmt.token = token;
|
||||
result->RangeStmt.val0 = val0;
|
||||
result->RangeStmt.val1 = val1;
|
||||
result->RangeStmt.vals = vals;
|
||||
result->RangeStmt.in_token = in_token;
|
||||
result->RangeStmt.expr = expr;
|
||||
result->RangeStmt.body = body;
|
||||
@@ -3914,7 +3912,7 @@ Ast *parse_for_stmt(AstFile *f) {
|
||||
} else {
|
||||
body = parse_block_stmt(f, false);
|
||||
}
|
||||
return ast_range_stmt(f, token, nullptr, nullptr, in_token, rhs, body);
|
||||
return ast_range_stmt(f, token, {}, in_token, rhs, body);
|
||||
}
|
||||
|
||||
if (f->curr_token.kind != Token_Semicolon) {
|
||||
@@ -3954,26 +3952,12 @@ Ast *parse_for_stmt(AstFile *f) {
|
||||
if (is_range) {
|
||||
GB_ASSERT(cond->kind == Ast_AssignStmt);
|
||||
Token in_token = cond->AssignStmt.op;
|
||||
Ast *value = nullptr;
|
||||
Ast *index = nullptr;
|
||||
switch (cond->AssignStmt.lhs.count) {
|
||||
case 1:
|
||||
value = cond->AssignStmt.lhs[0];
|
||||
break;
|
||||
case 2:
|
||||
value = cond->AssignStmt.lhs[0];
|
||||
index = cond->AssignStmt.lhs[1];
|
||||
break;
|
||||
default:
|
||||
syntax_error(cond, "Expected either 1 or 2 identifiers");
|
||||
return ast_bad_stmt(f, token, f->curr_token);
|
||||
}
|
||||
|
||||
Slice<Ast *> vals = cond->AssignStmt.lhs;
|
||||
Ast *rhs = nullptr;
|
||||
if (cond->AssignStmt.rhs.count > 0) {
|
||||
rhs = cond->AssignStmt.rhs[0];
|
||||
}
|
||||
return ast_range_stmt(f, token, value, index, in_token, rhs, body);
|
||||
return ast_range_stmt(f, token, vals, in_token, rhs, body);
|
||||
}
|
||||
|
||||
cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression"));
|
||||
|
||||
+1
-2
@@ -407,8 +407,7 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
|
||||
AST_KIND(RangeStmt, "range statement", struct { \
|
||||
Token token; \
|
||||
Ast *label; \
|
||||
Ast *val0; \
|
||||
Ast *val1; \
|
||||
Slice<Ast *> vals; \
|
||||
Token in_token; \
|
||||
Ast *expr; \
|
||||
Ast *body; \
|
||||
|
||||
Reference in New Issue
Block a user