mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-19 21:10:30 +00:00
inline for loops (only for 'in' based for loops)
This commit is contained in:
@@ -1018,6 +1018,44 @@ quaternions :: proc() {
|
||||
fmt.println(c);
|
||||
fmt.println(q);
|
||||
}
|
||||
{ // Memory layout of Quaternions
|
||||
q := 1 + 2i + 3j + 4k;
|
||||
a := transmute([4]f64)q;
|
||||
fmt.println("Quaternion memory layout: xyzw/(ijkr)");
|
||||
fmt.println(q); // 1.000+2.000i+3.000j+4.000k
|
||||
fmt.println(a); // [2.000, 3.000, 4.000, 1.000]
|
||||
}
|
||||
}
|
||||
|
||||
inline_for_statement :: proc() {
|
||||
fmt.println("\n#inline for statements");
|
||||
|
||||
fmt.println("Ranges");
|
||||
inline for x, i in 1..<4 {
|
||||
fmt.println(x, i);
|
||||
}
|
||||
|
||||
fmt.println("Strings");
|
||||
inline for r, i in "Hello, 世界" {
|
||||
fmt.println(r, i);
|
||||
}
|
||||
|
||||
fmt.println("Arrays");
|
||||
inline for elem, idx in ([4]int{1, 4, 9, 16}) {
|
||||
fmt.println(elem, idx);
|
||||
}
|
||||
|
||||
|
||||
Foo_Enum :: enum {
|
||||
A = 1,
|
||||
B,
|
||||
C = 6,
|
||||
D,
|
||||
}
|
||||
fmt.println("Enum types");
|
||||
inline for elem, idx in Foo_Enum {
|
||||
fmt.println(elem, idx);
|
||||
}
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
@@ -1040,5 +1078,6 @@ main :: proc() {
|
||||
deferred_procedure_associations();
|
||||
reflection();
|
||||
quaternions();
|
||||
inline_for_statement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +132,10 @@ bool check_is_terminating(Ast *node) {
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(rs, InlineRangeStmt, node);
|
||||
return false;
|
||||
case_end;
|
||||
|
||||
case_ast_node(rs, RangeStmt, node);
|
||||
return false;
|
||||
case_end;
|
||||
@@ -587,6 +591,236 @@ void add_constant_switch_case(CheckerContext *ctx, Map<TypeAndToken> *seen, Oper
|
||||
multi_map_insert(seen, key, tap);
|
||||
}
|
||||
|
||||
void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
|
||||
ast_node(irs, InlineRangeStmt, node);
|
||||
check_open_scope(ctx, node);
|
||||
|
||||
Type *val0 = nullptr;
|
||||
Type *val1 = nullptr;
|
||||
Entity *entities[2] = {};
|
||||
isize entity_count = 0;
|
||||
|
||||
Ast *expr = unparen_expr(irs->expr);
|
||||
|
||||
ExactValue inline_for_depth = exact_value_i64(0);
|
||||
|
||||
if (is_ast_range(expr)) {
|
||||
ast_node(ie, BinaryExpr, expr);
|
||||
Operand x = {Addressing_Invalid};
|
||||
Operand y = {Addressing_Invalid};
|
||||
|
||||
check_expr(ctx, &x, ie->left);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
}
|
||||
check_expr(ctx, &y, ie->right);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
}
|
||||
|
||||
convert_to_typed(ctx, &x, y.type);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
}
|
||||
convert_to_typed(ctx, &y, x.type);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
}
|
||||
|
||||
convert_to_typed(ctx, &x, default_type(y.type));
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
}
|
||||
convert_to_typed(ctx, &y, default_type(x.type));
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
}
|
||||
|
||||
if (!are_types_identical(x.type, y.type)) {
|
||||
if (x.type != t_invalid &&
|
||||
y.type != t_invalid) {
|
||||
gbString xt = type_to_string(x.type);
|
||||
gbString yt = type_to_string(y.type);
|
||||
gbString expr_str = expr_to_string(x.expr);
|
||||
error(ie->op, "Mismatched types in interval expression '%s' : '%s' vs '%s'", expr_str, xt, yt);
|
||||
gb_string_free(expr_str);
|
||||
gb_string_free(yt);
|
||||
gb_string_free(xt);
|
||||
}
|
||||
goto skip_expr;
|
||||
}
|
||||
|
||||
Type *type = x.type;
|
||||
if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) {
|
||||
error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
|
||||
goto skip_expr;
|
||||
}
|
||||
|
||||
if (x.mode == Addressing_Constant &&
|
||||
y.mode == Addressing_Constant) {
|
||||
ExactValue a = x.value;
|
||||
ExactValue b = y.value;
|
||||
|
||||
GB_ASSERT(are_types_identical(x.type, y.type));
|
||||
|
||||
TokenKind op = Token_Lt;
|
||||
switch (ie->op.kind) {
|
||||
case Token_Ellipsis: op = Token_LtEq; break;
|
||||
case Token_RangeHalf: op = Token_Lt; break;
|
||||
default: error(ie->op, "Invalid range operator"); break;
|
||||
}
|
||||
bool ok = compare_exact_values(op, a, b);
|
||||
if (!ok) {
|
||||
// TODO(bill): Better error message
|
||||
error(ie->op, "Invalid interval range");
|
||||
goto skip_expr;
|
||||
}
|
||||
|
||||
inline_for_depth = exact_value_sub(b, a);
|
||||
if (ie->op.kind == Token_Ellipsis) {
|
||||
inline_for_depth = exact_value_increment_one(inline_for_depth);
|
||||
}
|
||||
|
||||
} else {
|
||||
error(ie->op, "Interval expressions must be constant");
|
||||
goto skip_expr;
|
||||
}
|
||||
|
||||
add_type_and_value(&ctx->checker->info, ie->left, x.mode, x.type, x.value);
|
||||
add_type_and_value(&ctx->checker->info, ie->right, y.mode, y.type, y.value);
|
||||
val0 = type;
|
||||
val1 = t_int;
|
||||
} else {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr_or_type(ctx, &operand, irs->expr);
|
||||
|
||||
if (operand.mode == Addressing_Type) {
|
||||
if (!is_type_enum(operand.type)) {
|
||||
gbString t = type_to_string(operand.type);
|
||||
error(operand.expr, "Cannot iterate over the type '%s'", t);
|
||||
gb_string_free(t);
|
||||
goto skip_expr;
|
||||
} else {
|
||||
val0 = operand.type;
|
||||
val1 = t_int;
|
||||
add_type_info_type(ctx, operand.type);
|
||||
|
||||
Type *bt = base_type(operand.type);
|
||||
inline_for_depth = exact_value_i64(bt->Enum.fields.count);
|
||||
goto skip_expr;
|
||||
}
|
||||
} else if (operand.mode != Addressing_Invalid) {
|
||||
Type *t = base_type(operand.type);
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
if (is_type_string(t) && t->Basic.kind != Basic_cstring) {
|
||||
val0 = t_rune;
|
||||
val1 = t_int;
|
||||
inline_for_depth = exact_value_i64(operand.value.value_string.len);
|
||||
}
|
||||
break;
|
||||
case Type_Array:
|
||||
val0 = t->Array.elem;
|
||||
val1 = t_int;
|
||||
inline_for_depth = exact_value_i64(t->Array.count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (val0 == nullptr) {
|
||||
gbString s = expr_to_string(operand.expr);
|
||||
gbString t = type_to_string(operand.type);
|
||||
error(operand.expr, "Cannot iterate over '%s' of type '%s' in an 'inline for' statement", s, t);
|
||||
gb_string_free(t);
|
||||
gb_string_free(s);
|
||||
} else if (operand.mode != Addressing_Constant) {
|
||||
error(operand.expr, "An 'inline for' expression must be known at compile time");
|
||||
}
|
||||
}
|
||||
|
||||
skip_expr:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird.
|
||||
|
||||
Ast * lhs[2] = {irs->val0, irs->val1};
|
||||
Type *rhs[2] = {val0, val1};
|
||||
|
||||
for (isize i = 0; i < 2; i++) {
|
||||
if (lhs[i] == nullptr) {
|
||||
continue;
|
||||
}
|
||||
Ast * name = lhs[i];
|
||||
Type *type = rhs[i];
|
||||
|
||||
Entity *entity = nullptr;
|
||||
if (name->kind == Ast_Ident) {
|
||||
Token token = name->Ident.token;
|
||||
String str = token.string;
|
||||
Entity *found = nullptr;
|
||||
|
||||
if (!is_blank_ident(str)) {
|
||||
found = scope_lookup_current(ctx->scope, str);
|
||||
}
|
||||
if (found == nullptr) {
|
||||
bool is_immutable = true;
|
||||
entity = alloc_entity_variable(ctx->scope, token, type, is_immutable, EntityState_Resolved);
|
||||
entity->flags |= EntityFlag_Value;
|
||||
add_entity_definition(&ctx->checker->info, name, entity);
|
||||
} else {
|
||||
TokenPos pos = found->token.pos;
|
||||
error(token,
|
||||
"Redeclaration of '%.*s' in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(str), LIT(pos.file), pos.line, pos.column);
|
||||
entity = found;
|
||||
}
|
||||
} else {
|
||||
error(name, "A variable declaration must be an identifier");
|
||||
}
|
||||
|
||||
if (entity == nullptr) {
|
||||
entity = alloc_entity_dummy_variable(builtin_pkg->scope, ast_token(name));
|
||||
}
|
||||
|
||||
entities[entity_count++] = entity;
|
||||
|
||||
if (type == nullptr) {
|
||||
entity->type = t_invalid;
|
||||
entity->flags |= EntityFlag_Used;
|
||||
}
|
||||
}
|
||||
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
add_entity(ctx->checker, ctx->scope, entities[i]->identifier, entities[i]);
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Minimize the amount of nesting of an 'inline for'
|
||||
i64 prev_inline_for_depth = ctx->inline_for_depth;
|
||||
defer (ctx->inline_for_depth = prev_inline_for_depth);
|
||||
{
|
||||
i64 v = exact_value_to_i64(inline_for_depth);
|
||||
if (v <= 0) {
|
||||
// Do nothing
|
||||
} else {
|
||||
ctx->inline_for_depth = gb_max(ctx->inline_for_depth, 1) * v;
|
||||
}
|
||||
|
||||
if (ctx->inline_for_depth >= MAX_INLINE_FOR_DEPTH && prev_inline_for_depth < MAX_INLINE_FOR_DEPTH) {
|
||||
if (prev_inline_for_depth > 0) {
|
||||
error(node, "Nested 'inline for' loop cannot be inlined as it exceeds the maximum inline for depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH);
|
||||
} else {
|
||||
error(node, "'inline for' loop cannot be inlined as it exceeds the maximum inline for depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH);
|
||||
}
|
||||
error_line("\tUse a normal 'for' loop instead by removing the 'inline' prefix\n");
|
||||
ctx->inline_for_depth = MAX_INLINE_FOR_DEPTH;
|
||||
}
|
||||
}
|
||||
|
||||
check_stmt(ctx, irs->body, mod_flags);
|
||||
|
||||
|
||||
check_close_scope(ctx);
|
||||
}
|
||||
|
||||
void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
|
||||
ast_node(ss, SwitchStmt, node);
|
||||
|
||||
@@ -1298,6 +1532,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
check_close_scope(ctx);
|
||||
case_end;
|
||||
|
||||
|
||||
case_ast_node(rs, RangeStmt, node);
|
||||
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
|
||||
|
||||
@@ -1320,29 +1555,29 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
|
||||
check_expr(ctx, &x, ie->left);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
goto skip_expr_range_stmt;
|
||||
}
|
||||
check_expr(ctx, &y, ie->right);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
goto skip_expr_range_stmt;
|
||||
}
|
||||
|
||||
convert_to_typed(ctx, &x, y.type);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
goto skip_expr_range_stmt;
|
||||
}
|
||||
convert_to_typed(ctx, &y, x.type);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
goto skip_expr_range_stmt;
|
||||
}
|
||||
|
||||
convert_to_typed(ctx, &x, default_type(y.type));
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
goto skip_expr_range_stmt;
|
||||
}
|
||||
convert_to_typed(ctx, &y, default_type(x.type));
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
goto skip_expr_range_stmt;
|
||||
}
|
||||
|
||||
if (!are_types_identical(x.type, y.type)) {
|
||||
@@ -1356,13 +1591,13 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
gb_string_free(yt);
|
||||
gb_string_free(xt);
|
||||
}
|
||||
goto skip_expr;
|
||||
goto skip_expr_range_stmt;
|
||||
}
|
||||
|
||||
Type *type = x.type;
|
||||
if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) {
|
||||
error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
|
||||
goto skip_expr;
|
||||
goto skip_expr_range_stmt;
|
||||
}
|
||||
|
||||
if (x.mode == Addressing_Constant &&
|
||||
@@ -1382,18 +1617,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
if (!ok) {
|
||||
// TODO(bill): Better error message
|
||||
error(ie->op, "Invalid interval range");
|
||||
goto skip_expr;
|
||||
goto skip_expr_range_stmt;
|
||||
}
|
||||
}
|
||||
|
||||
if (x.mode != Addressing_Constant) {
|
||||
x.value = empty_exact_value;
|
||||
}
|
||||
if (y.mode != Addressing_Constant) {
|
||||
y.value = empty_exact_value;
|
||||
}
|
||||
|
||||
|
||||
add_type_and_value(&ctx->checker->info, ie->left, x.mode, x.type, x.value);
|
||||
add_type_and_value(&ctx->checker->info, ie->right, y.mode, y.type, y.value);
|
||||
val0 = type;
|
||||
@@ -1407,12 +1634,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
gbString t = type_to_string(operand.type);
|
||||
error(operand.expr, "Cannot iterate over the type '%s'", t);
|
||||
gb_string_free(t);
|
||||
goto skip_expr;
|
||||
goto skip_expr_range_stmt;
|
||||
} else {
|
||||
val0 = operand.type;
|
||||
val1 = t_int;
|
||||
add_type_info_type(ctx, operand.type);
|
||||
goto skip_expr;
|
||||
goto skip_expr_range_stmt;
|
||||
}
|
||||
} else if (operand.mode != Addressing_Invalid) {
|
||||
bool is_ptr = is_type_pointer(operand.type);
|
||||
@@ -1457,7 +1684,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
}
|
||||
}
|
||||
|
||||
skip_expr:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird.
|
||||
skip_expr_range_stmt:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird.
|
||||
|
||||
Ast * lhs[2] = {rs->val0, rs->val1};
|
||||
Type *rhs[2] = {val0, val1};
|
||||
|
||||
@@ -1515,6 +1743,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
||||
check_close_scope(ctx);
|
||||
case_end;
|
||||
|
||||
case_ast_node(irs, InlineRangeStmt, node);
|
||||
check_inline_range_stmt(ctx, node, mod_flags);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ss, SwitchStmt, node);
|
||||
check_switch_stmt(ctx, node, mod_flags);
|
||||
case_end;
|
||||
|
||||
@@ -276,6 +276,9 @@ struct CheckerContext {
|
||||
CheckerPolyPath *poly_path;
|
||||
isize poly_level; // TODO(bill): Actually handle correctly
|
||||
|
||||
#define MAX_INLINE_FOR_DEPTH 1024ll
|
||||
i64 inline_for_depth;
|
||||
|
||||
bool in_enum_type;
|
||||
bool collect_delayed_decls;
|
||||
bool allow_polymorphic_types;
|
||||
|
||||
@@ -813,6 +813,10 @@ gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue const &x, ExactV
|
||||
return exact_binary_operator_value(op, x, y);
|
||||
}
|
||||
|
||||
gb_inline ExactValue exact_value_increment_one(ExactValue const &x) {
|
||||
return exact_binary_operator_value(Token_Add, x, exact_value_i64(1));
|
||||
}
|
||||
|
||||
|
||||
i32 cmp_f64(f64 a, f64 b) {
|
||||
return (a > b) - (a < b);
|
||||
|
||||
144
src/ir.cpp
144
src/ir.cpp
@@ -9061,6 +9061,150 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
|
||||
ir_start_block(proc, done);
|
||||
case_end;
|
||||
|
||||
case_ast_node(rs, InlineRangeStmt, node);
|
||||
ir_emit_comment(proc, str_lit("InlineRangeStmt"));
|
||||
ir_open_scope(proc); // Open scope here
|
||||
|
||||
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->val1 != nullptr && !is_blank_ident(rs->val1)) {
|
||||
val1_type = type_of_expr(rs->val1);
|
||||
}
|
||||
|
||||
if (val0_type != nullptr) {
|
||||
ir_add_local_for_identifier(proc, rs->val0, true);
|
||||
}
|
||||
if (val1_type != nullptr) {
|
||||
ir_add_local_for_identifier(proc, rs->val1, true);
|
||||
}
|
||||
|
||||
irValue *val = nullptr;
|
||||
irValue *key = nullptr;
|
||||
irBlock *loop = nullptr;
|
||||
irBlock *done = nullptr;
|
||||
Ast *expr = unparen_expr(rs->expr);
|
||||
|
||||
TypeAndValue tav = type_and_value_of_expr(expr);
|
||||
|
||||
if (is_ast_range(expr)) {
|
||||
|
||||
irAddr val0_addr = {};
|
||||
irAddr val1_addr = {};
|
||||
if (val0_type) val0_addr = ir_build_addr(proc, rs->val0);
|
||||
if (val1_type) val1_addr = ir_build_addr(proc, rs->val1);
|
||||
|
||||
TokenKind op = expr->BinaryExpr.op.kind;
|
||||
Ast *start_expr = expr->BinaryExpr.left;
|
||||
Ast *end_expr = expr->BinaryExpr.right;
|
||||
GB_ASSERT(start_expr->tav.mode == Addressing_Constant);
|
||||
GB_ASSERT(end_expr->tav.mode == Addressing_Constant);
|
||||
|
||||
ExactValue start = start_expr->tav.value;
|
||||
ExactValue end = end_expr->tav.value;
|
||||
if (op == Token_Ellipsis) { // .. [start, end]
|
||||
ExactValue index = exact_value_i64(0);
|
||||
for (ExactValue val = start;
|
||||
compare_exact_values(Token_LtEq, val, end);
|
||||
val = exact_value_increment_one(val), index = exact_value_increment_one(index)) {
|
||||
|
||||
if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, val));
|
||||
if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, index));
|
||||
|
||||
ir_build_stmt(proc, rs->body);
|
||||
}
|
||||
} else if (op == Token_RangeHalf) { // ..< [start, end)
|
||||
ExactValue index = exact_value_i64(0);
|
||||
for (ExactValue val = start;
|
||||
compare_exact_values(Token_Lt, val, end);
|
||||
val = exact_value_increment_one(val), index = exact_value_increment_one(index)) {
|
||||
|
||||
if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, val));
|
||||
if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, index));
|
||||
|
||||
ir_build_stmt(proc, rs->body);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else if (tav.mode == Addressing_Type) {
|
||||
GB_ASSERT(is_type_enum(type_deref(tav.type)));
|
||||
Type *et = type_deref(tav.type);
|
||||
Type *bet = base_type(et);
|
||||
|
||||
irAddr val0_addr = {};
|
||||
irAddr val1_addr = {};
|
||||
if (val0_type) val0_addr = ir_build_addr(proc, rs->val0);
|
||||
if (val1_type) val1_addr = ir_build_addr(proc, rs->val1);
|
||||
|
||||
for_array(i, bet->Enum.fields) {
|
||||
Entity *field = bet->Enum.fields[i];
|
||||
GB_ASSERT(field->kind == Entity_Constant);
|
||||
if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, field->Constant.value));
|
||||
if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, exact_value_i64(i)));
|
||||
|
||||
ir_build_stmt(proc, rs->body);
|
||||
}
|
||||
} else {
|
||||
irAddr val0_addr = {};
|
||||
irAddr val1_addr = {};
|
||||
if (val0_type) val0_addr = ir_build_addr(proc, rs->val0);
|
||||
if (val1_type) val1_addr = ir_build_addr(proc, rs->val1);
|
||||
|
||||
GB_ASSERT(expr->tav.mode == Addressing_Constant);
|
||||
|
||||
Type *t = base_type(expr->tav.type);
|
||||
|
||||
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
GB_ASSERT(is_type_string(t));
|
||||
{
|
||||
ExactValue value = expr->tav.value;
|
||||
GB_ASSERT(value.kind == ExactValue_String);
|
||||
String str = value.value_string;
|
||||
Rune codepoint = 0;
|
||||
isize offset = 0;
|
||||
do {
|
||||
isize width = gb_utf8_decode(str.text+offset, str.len-offset, &codepoint);
|
||||
if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, exact_value_i64(codepoint)));
|
||||
if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, exact_value_i64(offset)));
|
||||
ir_build_stmt(proc, rs->body);
|
||||
|
||||
offset += width;
|
||||
} while (offset < str.len);
|
||||
}
|
||||
break;
|
||||
case Type_Array:
|
||||
if (t->Array.count > 0) {
|
||||
irValue *val = ir_build_expr(proc, expr);
|
||||
irValue *val_addr = ir_address_from_load_or_generate_local(proc, val);
|
||||
|
||||
for (i64 i = 0; i < t->Array.count; i++) {
|
||||
if (val0_type) {
|
||||
// NOTE(bill): Due to weird legacy issues in LLVM, this needs to be an i32
|
||||
irValue *elem = ir_emit_array_epi(proc, val_addr, cast(i32)i);
|
||||
ir_addr_store(proc, val0_addr, ir_emit_load(proc, elem));
|
||||
}
|
||||
if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, exact_value_i64(i)));
|
||||
|
||||
ir_build_stmt(proc, rs->body);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("Invalid inline for type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ir_close_scope(proc, irDeferExit_Default, nullptr);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ss, SwitchStmt, node);
|
||||
ir_emit_comment(proc, str_lit("SwitchStmt"));
|
||||
if (ss->init != nullptr) {
|
||||
|
||||
@@ -51,6 +51,7 @@ Token ast_token(Ast *node) {
|
||||
case Ast_ReturnStmt: return node->ReturnStmt.token;
|
||||
case Ast_ForStmt: return node->ForStmt.token;
|
||||
case Ast_RangeStmt: return node->RangeStmt.token;
|
||||
case Ast_InlineRangeStmt: return node->InlineRangeStmt.inline_token;
|
||||
case Ast_CaseClause: return node->CaseClause.token;
|
||||
case Ast_SwitchStmt: return node->SwitchStmt.token;
|
||||
case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token;
|
||||
@@ -257,6 +258,12 @@ Ast *clone_ast(Ast *node) {
|
||||
n->RangeStmt.expr = clone_ast(n->RangeStmt.expr);
|
||||
n->RangeStmt.body = clone_ast(n->RangeStmt.body);
|
||||
break;
|
||||
case Ast_InlineRangeStmt:
|
||||
n->InlineRangeStmt.val0 = clone_ast(n->InlineRangeStmt.val0);
|
||||
n->InlineRangeStmt.val1 = clone_ast(n->InlineRangeStmt.val1);
|
||||
n->InlineRangeStmt.expr = clone_ast(n->InlineRangeStmt.expr);
|
||||
n->InlineRangeStmt.body = clone_ast(n->InlineRangeStmt.body);
|
||||
break;
|
||||
case Ast_CaseClause:
|
||||
n->CaseClause.list = clone_ast_array(n->CaseClause.list);
|
||||
n->CaseClause.stmts = clone_ast_array(n->CaseClause.stmts);
|
||||
@@ -748,6 +755,18 @@ Ast *ast_range_stmt(AstFile *f, Token token, Ast *val0, Ast *val1, Token in_toke
|
||||
return result;
|
||||
}
|
||||
|
||||
Ast *ast_inline_range_stmt(AstFile *f, Token inline_token, Token for_token, Ast *val0, Ast *val1, Token in_token, Ast *expr, Ast *body) {
|
||||
Ast *result = alloc_ast_node(f, Ast_InlineRangeStmt);
|
||||
result->InlineRangeStmt.inline_token = inline_token;
|
||||
result->InlineRangeStmt.for_token = for_token;
|
||||
result->InlineRangeStmt.val0 = val0;
|
||||
result->InlineRangeStmt.val1 = val1;
|
||||
result->InlineRangeStmt.in_token = in_token;
|
||||
result->InlineRangeStmt.expr = expr;
|
||||
result->InlineRangeStmt.body = body;
|
||||
return result;
|
||||
}
|
||||
|
||||
Ast *ast_switch_stmt(AstFile *f, Token token, Ast *init, Ast *tag, Ast *body) {
|
||||
Ast *result = alloc_ast_node(f, Ast_SwitchStmt);
|
||||
result->SwitchStmt.token = token;
|
||||
@@ -1119,6 +1138,17 @@ Token advance_token(AstFile *f) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
bool peek_token_kind(AstFile *f, TokenKind kind) {
|
||||
for (isize i = f->curr_token_index+1; i < f->tokens.count; i++) {
|
||||
Token tok = f->tokens[i];
|
||||
if (kind != Token_Comment && tok.kind == Token_Comment) {
|
||||
continue;
|
||||
}
|
||||
return tok.kind == kind;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Token expect_token(AstFile *f, TokenKind kind) {
|
||||
Token prev = f->curr_token;
|
||||
if (prev.kind != kind) {
|
||||
@@ -2092,7 +2122,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
|
||||
bool prev_allow_range = f->allow_range;
|
||||
f->allow_range = true;
|
||||
elem = parse_expr(f, false);
|
||||
elem = parse_expr(f, true);
|
||||
f->allow_range = prev_allow_range;
|
||||
if (allow_token(f, Token_Semicolon)) {
|
||||
underlying = parse_type(f);
|
||||
@@ -2650,7 +2680,7 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) {
|
||||
allow_token(f, Token_in);
|
||||
bool prev_allow_range = f->allow_range;
|
||||
f->allow_range = true;
|
||||
Ast *expr = parse_expr(f, false);
|
||||
Ast *expr = parse_expr(f, true);
|
||||
f->allow_range = prev_allow_range;
|
||||
|
||||
auto rhs = array_make<Ast *>(heap_allocator(), 0, 1);
|
||||
@@ -3779,10 +3809,55 @@ Ast *parse_stmt(AstFile *f) {
|
||||
Token token = f->curr_token;
|
||||
switch (token.kind) {
|
||||
// Operands
|
||||
case Token_inline:
|
||||
if (peek_token_kind(f, Token_for)) {
|
||||
Token inline_token = expect_token(f, Token_inline);
|
||||
Token for_token = expect_token(f, Token_for);
|
||||
Ast *val0 = nullptr;
|
||||
Ast *val1 = nullptr;
|
||||
Token in_token = {};
|
||||
Ast *expr = nullptr;
|
||||
Ast *body = nullptr;
|
||||
|
||||
bool bad_stmt = false;
|
||||
|
||||
if (f->curr_token.kind != Token_in) {
|
||||
Array<Ast *> idents = parse_ident_list(f, false);
|
||||
switch (idents.count) {
|
||||
case 1:
|
||||
val0 = idents[0];
|
||||
break;
|
||||
case 2:
|
||||
val0 = idents[0];
|
||||
val1 = idents[1];
|
||||
break;
|
||||
default:
|
||||
syntax_error(for_token, "Expected either 1 or 2 identifiers");
|
||||
bad_stmt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
in_token = expect_token(f, Token_in);
|
||||
|
||||
bool prev_allow_range = f->allow_range;
|
||||
f->allow_range = true;
|
||||
expr = parse_expr(f, true);
|
||||
f->allow_range = prev_allow_range;
|
||||
|
||||
if (allow_token(f, Token_do)) {
|
||||
body = convert_stmt_to_body(f, parse_stmt(f));
|
||||
} else {
|
||||
body = parse_block_stmt(f, false);
|
||||
}
|
||||
if (bad_stmt) {
|
||||
return ast_bad_stmt(f, inline_token, f->curr_token);
|
||||
}
|
||||
return ast_inline_range_stmt(f, inline_token, for_token, val0, val1, in_token, expr, body);
|
||||
}
|
||||
/* fallthrough */
|
||||
case Token_no_inline:
|
||||
case Token_context: // Also allows for `context =`
|
||||
case Token_proc:
|
||||
case Token_inline:
|
||||
case Token_no_inline:
|
||||
case Token_Ident:
|
||||
case Token_Integer:
|
||||
case Token_Float:
|
||||
|
||||
@@ -326,6 +326,15 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
|
||||
Ast *expr; \
|
||||
Ast *body; \
|
||||
}) \
|
||||
AST_KIND(InlineRangeStmt, "inline range statement", struct { \
|
||||
Token inline_token; \
|
||||
Token for_token; \
|
||||
Ast *val0; \
|
||||
Ast *val1; \
|
||||
Token in_token; \
|
||||
Ast *expr; \
|
||||
Ast *body; \
|
||||
}) \
|
||||
AST_KIND(CaseClause, "case clause", struct { \
|
||||
Token token; \
|
||||
Array<Ast *> list; \
|
||||
|
||||
Reference in New Issue
Block a user