mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 07:38:48 +00:00
range statement
This commit is contained in:
+8
-1
@@ -18,5 +18,12 @@ Thing :: enum f64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
fmt.println(Thing.A, Thing.B, Thing.C, Thing.D);
|
msg := "Hello";
|
||||||
|
range index, value : msg {
|
||||||
|
fmt.println(index, value);
|
||||||
|
}
|
||||||
|
list := []int{1, 4, 7, 3, 7, 2, 1};
|
||||||
|
range index, value : list {
|
||||||
|
fmt.println(index, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#import "os.odin";
|
#import "os.odin";
|
||||||
#import "fmt.odin";
|
#import "fmt.odin";
|
||||||
#import "mem.odin";
|
#import "mem.odin";
|
||||||
|
#import "utf8.odin";
|
||||||
|
|
||||||
// IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a
|
// IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a
|
||||||
// #shared_global_scope due to the internals of the compiler.
|
// #shared_global_scope due to the internals of the compiler.
|
||||||
@@ -329,4 +330,7 @@ __substring_expr_error :: proc(file: string, line, column: int, low, high: int)
|
|||||||
__debug_trap();
|
__debug_trap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__string_decode_rune :: proc(s: string) -> (rune, int) #inline {
|
||||||
|
return utf8.decode_rune(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -161,8 +161,9 @@ void check_var_decl_node(Checker *c, AstNodeValueDecl *vd) {
|
|||||||
}
|
}
|
||||||
e->flags |= EntityFlag_Visited;
|
e->flags |= EntityFlag_Visited;
|
||||||
|
|
||||||
if (e->type == NULL)
|
if (e->type == NULL) {
|
||||||
e->type = init_type;
|
e->type = init_type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
check_arity_match(c, vd);
|
check_arity_match(c, vd);
|
||||||
|
|||||||
@@ -182,8 +182,8 @@ Entity *make_entity_implicit_value(gbAllocator a, String name, Type *type, Impli
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Entity *make_entity_dummy_variable(gbAllocator a, Scope *file_scope, Token token) {
|
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
|
||||||
token.string = str_lit("_");
|
token.string = str_lit("_");
|
||||||
return make_entity_variable(a, file_scope, token, NULL);
|
return make_entity_variable(a, scope, token, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-4
@@ -3809,20 +3809,26 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
|
|||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(pl, ProcLit, node);
|
case_ast_node(pl, ProcLit, node);
|
||||||
Type *proc_type = check_type(c, pl->type);
|
Type *type = check_type(c, pl->type);
|
||||||
if (proc_type == NULL) {
|
if (type == NULL || !is_type_proc(type)) {
|
||||||
gbString str = expr_to_string(node);
|
gbString str = expr_to_string(node);
|
||||||
error_node(node, "Invalid procedure literal `%s`", str);
|
error_node(node, "Invalid procedure literal `%s`", str);
|
||||||
gb_string_free(str);
|
gb_string_free(str);
|
||||||
check_close_scope(c);
|
check_close_scope(c);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
if (pl->tags != 0) {
|
||||||
|
error_node(node, "A procedure literal cannot have tags");
|
||||||
|
pl->tags = 0; // TODO(bill): Should I zero this?!
|
||||||
|
}
|
||||||
|
|
||||||
check_open_scope(c, pl->type);
|
check_open_scope(c, pl->type);
|
||||||
check_proc_body(c, empty_token, c->context.decl, proc_type, pl->body);
|
check_procedure_later(c, c->curr_ast_file, empty_token, c->context.decl, type, pl->body, pl->tags);
|
||||||
|
// check_proc_body(c, empty_token, c->context.decl, type, pl->body);
|
||||||
check_close_scope(c);
|
check_close_scope(c);
|
||||||
|
|
||||||
o->mode = Addressing_Value;
|
o->mode = Addressing_Value;
|
||||||
o->type = proc_type;
|
o->type = type;
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ge, GiveExpr, node);
|
case_ast_node(ge, GiveExpr, node);
|
||||||
|
|||||||
@@ -133,6 +133,12 @@ bool check_is_terminating(AstNode *node) {
|
|||||||
}
|
}
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
|
case_ast_node(rs, RangeStmt, node);
|
||||||
|
if (!check_has_break(rs->body, true)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ms, MatchStmt, node);
|
case_ast_node(ms, MatchStmt, node);
|
||||||
bool has_default = false;
|
bool has_default = false;
|
||||||
for_array(i, ms->body->BlockStmt.stmts) {
|
for_array(i, ms->body->BlockStmt.stmts) {
|
||||||
@@ -584,6 +590,99 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
|||||||
check_close_scope(c);
|
check_close_scope(c);
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
|
case_ast_node(rs, RangeStmt, node);
|
||||||
|
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
|
||||||
|
check_open_scope(c, node);
|
||||||
|
|
||||||
|
|
||||||
|
Operand operand = {Addressing_Invalid};
|
||||||
|
check_expr(c, &operand, rs->expr);
|
||||||
|
|
||||||
|
Type *key = NULL;
|
||||||
|
Type *val = NULL;
|
||||||
|
if (operand.mode != Addressing_Invalid) {
|
||||||
|
Type *t = base_type(type_deref(operand.type));
|
||||||
|
switch (t->kind) {
|
||||||
|
case Type_Basic:
|
||||||
|
if (is_type_string(t)) {
|
||||||
|
key = t_int;
|
||||||
|
val = t_rune;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Type_Array:
|
||||||
|
key = t_int;
|
||||||
|
val = t->Array.elem;
|
||||||
|
break;
|
||||||
|
case Type_Slice:
|
||||||
|
key = t_int;
|
||||||
|
val = t->Array.elem;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == NULL) {
|
||||||
|
gbString s = expr_to_string(operand.expr);
|
||||||
|
error_node(operand.expr, "Cannot iterate over %s", s);
|
||||||
|
gb_string_free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *entities[2] = {0};
|
||||||
|
isize entity_count = 0;
|
||||||
|
AstNode *lhs[2] = {rs->key, rs->value};
|
||||||
|
Type * rhs[2] = {key, val};
|
||||||
|
|
||||||
|
for (isize i = 0; i < 2; i++) {
|
||||||
|
if (lhs[i] == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
AstNode *name = lhs[i];
|
||||||
|
Type * type = rhs[i];
|
||||||
|
|
||||||
|
Entity *entity = NULL;
|
||||||
|
if (name->kind == AstNode_Ident) {
|
||||||
|
Token token = name->Ident;
|
||||||
|
String str = token.string;
|
||||||
|
Entity *found = NULL;
|
||||||
|
|
||||||
|
if (str_ne(str, str_lit("_"))) {
|
||||||
|
found = current_scope_lookup_entity(c->context.scope, str);
|
||||||
|
}
|
||||||
|
if (found == NULL) {
|
||||||
|
entity = make_entity_variable(c->allocator, c->context.scope, token, type);
|
||||||
|
add_entity_definition(&c->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_node(name, "A variable declaration must be an identifier");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity == NULL) {
|
||||||
|
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
entities[entity_count++] = entity;
|
||||||
|
|
||||||
|
if (type == NULL) {
|
||||||
|
entity->type = t_invalid;
|
||||||
|
entity->flags |= EntityFlag_Used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (isize i = 0; i < entity_count; i++) {
|
||||||
|
add_entity(c, c->context.scope, entities[i]->identifier, entities[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
check_stmt(c, rs->body, new_flags);
|
||||||
|
|
||||||
|
check_close_scope(c);
|
||||||
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ms, MatchStmt, node);
|
case_ast_node(ms, MatchStmt, node);
|
||||||
Operand x = {0};
|
Operand x = {0};
|
||||||
|
|
||||||
|
|||||||
+127
-62
@@ -204,6 +204,13 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
|
|||||||
AstNode *post; \
|
AstNode *post; \
|
||||||
AstNode *body; \
|
AstNode *body; \
|
||||||
}) \
|
}) \
|
||||||
|
AST_NODE_KIND(RangeStmt, "range statement", struct { \
|
||||||
|
Token token; \
|
||||||
|
AstNode * key; \
|
||||||
|
AstNode * value; \
|
||||||
|
AstNode * expr; \
|
||||||
|
AstNode * body; \
|
||||||
|
}) \
|
||||||
AST_NODE_KIND(CaseClause, "case clause", struct { \
|
AST_NODE_KIND(CaseClause, "case clause", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
AstNodeArray list; \
|
AstNodeArray list; \
|
||||||
@@ -847,7 +854,15 @@ AstNode *make_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, As
|
|||||||
result->ForStmt.body = body;
|
result->ForStmt.body = body;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
AstNode *make_range_stmt(AstFile *f, Token token, AstNode *key, AstNode *value, AstNode *expr, AstNode *body) {
|
||||||
|
AstNode *result = make_node(f, AstNode_RangeStmt);
|
||||||
|
result->RangeStmt.token = token;
|
||||||
|
result->RangeStmt.key = key;
|
||||||
|
result->RangeStmt.value = value;
|
||||||
|
result->RangeStmt.expr = expr;
|
||||||
|
result->RangeStmt.body = body;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
AstNode *make_match_stmt(AstFile *f, Token token, AstNode *init, AstNode *tag, AstNode *body) {
|
AstNode *make_match_stmt(AstFile *f, Token token, AstNode *init, AstNode *tag, AstNode *body) {
|
||||||
AstNode *result = make_node(f, AstNode_MatchStmt);
|
AstNode *result = make_node(f, AstNode_MatchStmt);
|
||||||
@@ -1195,6 +1210,7 @@ void fix_advance_to_next_stmt(AstFile *f) {
|
|||||||
case Token_when:
|
case Token_when:
|
||||||
case Token_return:
|
case Token_return:
|
||||||
case Token_for:
|
case Token_for:
|
||||||
|
case Token_range:
|
||||||
case Token_match:
|
case Token_match:
|
||||||
case Token_defer:
|
case Token_defer:
|
||||||
case Token_asm:
|
case Token_asm:
|
||||||
@@ -1749,8 +1765,9 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
|
|||||||
syntax_error(token, "A procedure tagged as `#foreign` cannot have a body");
|
syntax_error(token, "A procedure tagged as `#foreign` cannot have a body");
|
||||||
}
|
}
|
||||||
AstNode *curr_proc = f->curr_proc;
|
AstNode *curr_proc = f->curr_proc;
|
||||||
|
AstNode *body = NULL;
|
||||||
f->curr_proc = type;
|
f->curr_proc = type;
|
||||||
AstNode *body = parse_body(f);
|
body = parse_body(f);
|
||||||
f->curr_proc = curr_proc;
|
f->curr_proc = curr_proc;
|
||||||
|
|
||||||
return make_proc_lit(f, type, body, tags, foreign_name, link_name);
|
return make_proc_lit(f, type, body, tags, foreign_name, link_name);
|
||||||
@@ -2062,7 +2079,7 @@ AstNodeArray parse_rhs_expr_list(AstFile *f) {
|
|||||||
return parse_expr_list(f, false);
|
return parse_expr_list(f, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNodeArray parse_identfier_list(AstFile *f) {
|
AstNodeArray parse_identifier_list(AstFile *f) {
|
||||||
AstNodeArray list = make_ast_node_array(f);
|
AstNodeArray list = make_ast_node_array(f);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
@@ -2110,8 +2127,63 @@ AstNode *parse_type(AstFile *f) {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AstNode *parse_value_decl(AstFile *f, AstNodeArray lhs) {
|
||||||
|
parse_check_name_list_for_reserves(f, lhs);
|
||||||
|
|
||||||
|
AstNode *type = NULL;
|
||||||
|
AstNodeArray values = {0};
|
||||||
|
bool is_mutable = true;
|
||||||
|
|
||||||
|
if (allow_token(f, Token_Colon)) {
|
||||||
|
if (!allow_token(f, Token_type)) {
|
||||||
|
type = parse_type_attempt(f);
|
||||||
|
}
|
||||||
|
} else if (f->curr_token.kind != Token_Eq &&
|
||||||
|
f->curr_token.kind != Token_Semicolon) {
|
||||||
|
syntax_error(f->curr_token, "Expected a type separator `:` or `=`");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
switch (f->curr_token.kind) {
|
||||||
|
case Token_Colon:
|
||||||
|
is_mutable = false;
|
||||||
|
/*fallthrough*/
|
||||||
|
case Token_Eq:
|
||||||
|
next_token(f);
|
||||||
|
values = parse_rhs_expr_list(f);
|
||||||
|
if (values.count > lhs.count) {
|
||||||
|
syntax_error(f->curr_token, "Too many values on the right hand side of the declaration");
|
||||||
|
} else if (values.count < lhs.count && !is_mutable) {
|
||||||
|
syntax_error(f->curr_token, "All constant declarations must be defined");
|
||||||
|
} else if (values.count == 0) {
|
||||||
|
syntax_error(f->curr_token, "Expected an expression for this declaration");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_mutable) {
|
||||||
|
if (type == NULL && values.count == 0) {
|
||||||
|
syntax_error(f->curr_token, "Missing variable type or initialization");
|
||||||
|
return make_bad_decl(f, f->curr_token, f->curr_token);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (type == NULL && values.count == 0 && lhs.count > 0) {
|
||||||
|
syntax_error(f->curr_token, "Missing constant value");
|
||||||
|
return make_bad_decl(f, f->curr_token, f->curr_token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.e == NULL) {
|
||||||
|
values = make_ast_node_array(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNodeArray specs = {0};
|
||||||
|
array_init_reserve(&specs, heap_allocator(), 1);
|
||||||
|
return make_value_decl(f, is_mutable, lhs, type, values);
|
||||||
|
}
|
||||||
|
|
||||||
AstNode *parse_simple_stmt(AstFile *f) {
|
AstNode *parse_simple_stmt(AstFile *f) {
|
||||||
Token start_token = f->curr_token;
|
|
||||||
AstNodeArray lhs = parse_lhs_expr_list(f);
|
AstNodeArray lhs = parse_lhs_expr_list(f);
|
||||||
Token token = f->curr_token;
|
Token token = f->curr_token;
|
||||||
switch (token.kind) {
|
switch (token.kind) {
|
||||||
@@ -2143,60 +2215,8 @@ AstNode *parse_simple_stmt(AstFile *f) {
|
|||||||
return make_assign_stmt(f, token, lhs, rhs);
|
return make_assign_stmt(f, token, lhs, rhs);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Token_Colon: {
|
case Token_Colon:
|
||||||
parse_check_name_list_for_reserves(f, lhs);
|
return parse_value_decl(f, lhs);
|
||||||
|
|
||||||
AstNode *type = NULL;
|
|
||||||
AstNodeArray values = {0};
|
|
||||||
bool is_mutable = true;
|
|
||||||
|
|
||||||
if (allow_token(f, Token_Colon)) {
|
|
||||||
if (!allow_token(f, Token_type)) {
|
|
||||||
type = parse_type_attempt(f);
|
|
||||||
}
|
|
||||||
} else if (f->curr_token.kind != Token_Eq &&
|
|
||||||
f->curr_token.kind != Token_Semicolon) {
|
|
||||||
syntax_error(f->curr_token, "Expected a type separator `:` or `=`");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
switch (f->curr_token.kind) {
|
|
||||||
case Token_Colon:
|
|
||||||
is_mutable = false;
|
|
||||||
/*fallthrough*/
|
|
||||||
case Token_Eq:
|
|
||||||
next_token(f);
|
|
||||||
values = parse_rhs_expr_list(f);
|
|
||||||
if (values.count > lhs.count) {
|
|
||||||
syntax_error(f->curr_token, "Too many values on the right hand side of the declaration");
|
|
||||||
} else if (values.count < lhs.count && !is_mutable) {
|
|
||||||
syntax_error(f->curr_token, "All constant declarations must be defined");
|
|
||||||
} else if (values.count == 0) {
|
|
||||||
syntax_error(f->curr_token, "Expected an expression for this declaration");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_mutable) {
|
|
||||||
if (type == NULL && values.count == 0) {
|
|
||||||
syntax_error(f->curr_token, "Missing variable type or initialization");
|
|
||||||
return make_bad_decl(f, f->curr_token, f->curr_token);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (type == NULL && values.count == 0 && lhs.count > 0) {
|
|
||||||
syntax_error(f->curr_token, "Missing constant value");
|
|
||||||
return make_bad_decl(f, f->curr_token, f->curr_token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values.e == NULL) {
|
|
||||||
values = make_ast_node_array(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
AstNodeArray specs = {0};
|
|
||||||
array_init_reserve(&specs, heap_allocator(), 1);
|
|
||||||
return make_value_decl(f, is_mutable, lhs, type, values);
|
|
||||||
} break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lhs.count > 1) {
|
if (lhs.count > 1) {
|
||||||
@@ -2265,7 +2285,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using,
|
|||||||
is_using = true;
|
is_using = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNodeArray names = parse_identfier_list(f);
|
AstNodeArray names = parse_identifier_list(f);
|
||||||
if (names.count == 0) {
|
if (names.count == 0) {
|
||||||
syntax_error(f->curr_token, "Empty parameter declaration");
|
syntax_error(f->curr_token, "Empty parameter declaration");
|
||||||
break;
|
break;
|
||||||
@@ -2523,9 +2543,14 @@ void parse_proc_signature(AstFile *f,
|
|||||||
AstNode *parse_body(AstFile *f) {
|
AstNode *parse_body(AstFile *f) {
|
||||||
AstNodeArray stmts = {0};
|
AstNodeArray stmts = {0};
|
||||||
Token open, close;
|
Token open, close;
|
||||||
|
isize prev_expr_level = f->expr_level;
|
||||||
|
|
||||||
|
// NOTE(bill): The body may be within an expression so reset to zero
|
||||||
|
f->expr_level = 0;
|
||||||
open = expect_token(f, Token_OpenBrace);
|
open = expect_token(f, Token_OpenBrace);
|
||||||
stmts = parse_stmt_list(f);
|
stmts = parse_stmt_list(f);
|
||||||
close = expect_token(f, Token_CloseBrace);
|
close = expect_token(f, Token_CloseBrace);
|
||||||
|
f->expr_level = prev_expr_level;
|
||||||
|
|
||||||
return make_block_stmt(f, stmts, open, close);
|
return make_block_stmt(f, stmts, open, close);
|
||||||
}
|
}
|
||||||
@@ -2727,8 +2752,7 @@ AstNode *parse_for_stmt(AstFile *f) {
|
|||||||
if (f->curr_token.kind != Token_Semicolon) {
|
if (f->curr_token.kind != Token_Semicolon) {
|
||||||
cond = parse_simple_stmt(f);
|
cond = parse_simple_stmt(f);
|
||||||
if (is_ast_node_complex_stmt(cond)) {
|
if (is_ast_node_complex_stmt(cond)) {
|
||||||
syntax_error(f->curr_token,
|
syntax_error(f->curr_token, "You are not allowed that type of statement in a for statement, it is too complex!");
|
||||||
"You are not allowed that type of statement in a for statement, it is too complex!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2752,6 +2776,45 @@ AstNode *parse_for_stmt(AstFile *f) {
|
|||||||
return make_for_stmt(f, token, init, cond, end, body);
|
return make_for_stmt(f, token, init, cond, end, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AstNode *parse_range_stmt(AstFile *f) {
|
||||||
|
if (f->curr_proc == NULL) {
|
||||||
|
syntax_error(f->curr_token, "You cannot use a range statement in the file scope");
|
||||||
|
return make_bad_stmt(f, f->curr_token, f->curr_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
Token token = expect_token(f, Token_range);
|
||||||
|
AstNodeArray names = parse_identifier_list(f);
|
||||||
|
parse_check_name_list_for_reserves(f, names);
|
||||||
|
Token colon = expect_token_after(f, Token_Colon, "range name list");
|
||||||
|
|
||||||
|
isize prev_level = f->expr_level;
|
||||||
|
f->expr_level = -1;
|
||||||
|
AstNode *expr = parse_expr(f, false);
|
||||||
|
f->expr_level = prev_level;
|
||||||
|
|
||||||
|
AstNode *key = NULL;
|
||||||
|
AstNode *value = NULL;
|
||||||
|
AstNode *body = parse_block_stmt(f, false);
|
||||||
|
|
||||||
|
switch (names.count) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
key = names.e[0];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
key = names.e[0];
|
||||||
|
value = names.e[1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error_node(names.e[names.count-1], "Expected at most 2 expressions");
|
||||||
|
return make_bad_stmt(f, token, f->curr_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_range_stmt(f, token, key, value, expr, body);
|
||||||
|
}
|
||||||
|
|
||||||
AstNode *parse_case_clause(AstFile *f) {
|
AstNode *parse_case_clause(AstFile *f) {
|
||||||
Token token = f->curr_token;
|
Token token = f->curr_token;
|
||||||
AstNodeArray list = make_ast_node_array(f);
|
AstNodeArray list = make_ast_node_array(f);
|
||||||
@@ -2929,6 +2992,7 @@ AstNode *parse_stmt(AstFile *f) {
|
|||||||
case Token_if: return parse_if_stmt(f);
|
case Token_if: return parse_if_stmt(f);
|
||||||
case Token_when: return parse_when_stmt(f);
|
case Token_when: return parse_when_stmt(f);
|
||||||
case Token_for: return parse_for_stmt(f);
|
case Token_for: return parse_for_stmt(f);
|
||||||
|
case Token_range: return parse_range_stmt(f);
|
||||||
case Token_match: return parse_match_stmt(f);
|
case Token_match: return parse_match_stmt(f);
|
||||||
case Token_defer: return parse_defer_stmt(f);
|
case Token_defer: return parse_defer_stmt(f);
|
||||||
case Token_asm: return parse_asm_stmt(f);
|
case Token_asm: return parse_asm_stmt(f);
|
||||||
@@ -2944,7 +3008,8 @@ AstNode *parse_stmt(AstFile *f) {
|
|||||||
return s;
|
return s;
|
||||||
|
|
||||||
case Token_using: {
|
case Token_using: {
|
||||||
next_token(f);
|
// TODO(bill): Make using statements better
|
||||||
|
Token token = expect_token(f, Token_using);
|
||||||
AstNode *node = parse_stmt(f);
|
AstNode *node = parse_stmt(f);
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
|
|
||||||
|
|||||||
@@ -1807,7 +1807,7 @@ ssaValue *ssa_string_elem(ssaProcedure *proc, ssaValue *string) {
|
|||||||
}
|
}
|
||||||
ssaValue *ssa_string_len(ssaProcedure *proc, ssaValue *string) {
|
ssaValue *ssa_string_len(ssaProcedure *proc, ssaValue *string) {
|
||||||
Type *t = ssa_type(string);
|
Type *t = ssa_type(string);
|
||||||
GB_ASSERT(t->kind == Type_Basic && t->Basic.kind == Basic_string);
|
GB_ASSERT_MSG(t->kind == Type_Basic && t->Basic.kind == Basic_string, "%s", type_to_string(t));
|
||||||
return ssa_emit_struct_ev(proc, string, 1);
|
return ssa_emit_struct_ev(proc, string, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3908,6 +3908,129 @@ void ssa_build_when_stmt(ssaProcedure *proc, AstNodeWhenStmt *ws) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ssa_build_range_indexed(ssaProcedure *proc, ssaValue *expr, Type *val_type,
|
||||||
|
ssaValue **key_, ssaValue **val_, ssaBlock **loop_, ssaBlock **done_) {
|
||||||
|
ssaValue *count = NULL;
|
||||||
|
Type *expr_type = base_type(ssa_type(expr));
|
||||||
|
switch (expr_type->kind) {
|
||||||
|
case Type_Array:
|
||||||
|
count = ssa_make_const_int(proc->module->allocator, expr_type->Array.count);
|
||||||
|
break;
|
||||||
|
case Type_Slice:
|
||||||
|
count = ssa_slice_len(proc, expr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GB_PANIC("Cannot do range_indexed of %s", type_to_string(expr_type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssaValue *idx = NULL;
|
||||||
|
ssaValue *val = NULL;
|
||||||
|
ssaBlock *loop = NULL;
|
||||||
|
ssaBlock *done = NULL;
|
||||||
|
ssaBlock *body = NULL;
|
||||||
|
|
||||||
|
ssaValue *index = ssa_add_local_generated(proc, t_int);
|
||||||
|
ssa_emit_store(proc, index, ssa_make_const_int(proc->module->allocator, -1));
|
||||||
|
|
||||||
|
loop = ssa_add_block(proc, NULL, "rangeindex.loop");
|
||||||
|
ssa_emit_jump(proc, loop);
|
||||||
|
proc->curr_block = loop;
|
||||||
|
|
||||||
|
ssaValue *incr = ssa_emit_arith(proc, Token_Add, ssa_emit_load(proc, index), v_one, t_int);
|
||||||
|
ssa_emit_store(proc, index, incr);
|
||||||
|
|
||||||
|
body = ssa_add_block(proc, NULL, "rangeindex.body");
|
||||||
|
done = ssa_add_block(proc, NULL, "rangeindex.done");
|
||||||
|
ssaValue *cond = ssa_emit_comp(proc, Token_Lt, incr, count);
|
||||||
|
ssa_emit_if(proc, cond, body, done);
|
||||||
|
proc->curr_block = body;
|
||||||
|
|
||||||
|
idx = ssa_emit_load(proc, index);
|
||||||
|
if (val_type != NULL) {
|
||||||
|
switch (expr_type->kind) {
|
||||||
|
case Type_Array: {
|
||||||
|
val = ssa_emit_load(proc, ssa_emit_array_ep(proc, expr, idx));
|
||||||
|
} break;
|
||||||
|
case Type_Slice: {
|
||||||
|
ssaValue *elem = ssa_slice_elem(proc, expr);
|
||||||
|
val = ssa_emit_load(proc, ssa_emit_ptr_offset(proc, elem, idx));
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
GB_PANIC("Cannot do range_indexed of %s", type_to_string(expr_type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_) *key_ = idx;
|
||||||
|
if (val_) *val_ = val;
|
||||||
|
if (loop_) *loop_ = loop;
|
||||||
|
if (done_) *done_ = done;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ssa_build_range_string(ssaProcedure *proc, ssaValue *expr, Type *val_type,
|
||||||
|
ssaValue **key_, ssaValue **val_, ssaBlock **loop_, ssaBlock **done_) {
|
||||||
|
ssaValue *count = v_zero;
|
||||||
|
Type *expr_type = base_type(ssa_type(expr));
|
||||||
|
switch (expr_type->kind) {
|
||||||
|
case Type_Basic:
|
||||||
|
count = ssa_string_len(proc, expr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GB_PANIC("Cannot do range_string of %s", type_to_string(expr_type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssaValue *idx = NULL;
|
||||||
|
ssaValue *val = NULL;
|
||||||
|
ssaBlock *loop = NULL;
|
||||||
|
ssaBlock *done = NULL;
|
||||||
|
ssaBlock *body = NULL;
|
||||||
|
|
||||||
|
ssaValue *index = ssa_add_local_generated(proc, t_int);
|
||||||
|
ssa_emit_store(proc, index, v_zero);
|
||||||
|
|
||||||
|
ssaValue *offset_ = ssa_add_local_generated(proc, t_int);
|
||||||
|
ssa_emit_store(proc, index, v_zero);
|
||||||
|
|
||||||
|
loop = ssa_add_block(proc, NULL, "rangestring.loop");
|
||||||
|
ssa_emit_jump(proc, loop);
|
||||||
|
proc->curr_block = loop;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
body = ssa_add_block(proc, NULL, "rangestring.body");
|
||||||
|
done = ssa_add_block(proc, NULL, "rangestring.done");
|
||||||
|
|
||||||
|
ssaValue *offset = ssa_emit_load(proc, offset_);
|
||||||
|
|
||||||
|
ssaValue *cond = ssa_emit_comp(proc, Token_Lt, offset, count);
|
||||||
|
ssa_emit_if(proc, cond, body, done);
|
||||||
|
proc->curr_block = body;
|
||||||
|
|
||||||
|
|
||||||
|
ssaValue *str_elem = ssa_emit_ptr_offset(proc, ssa_string_elem(proc, expr), offset);
|
||||||
|
ssaValue *str_len = ssa_emit_arith(proc, Token_Sub, count, offset, t_int);
|
||||||
|
ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 1);
|
||||||
|
args[0] = ssa_emit_string(proc, str_elem, str_len);
|
||||||
|
ssaValue *rune_and_len = ssa_emit_global_call(proc, "__string_decode_rune", args, 1);
|
||||||
|
ssaValue *len = ssa_emit_struct_ev(proc, rune_and_len, 1);
|
||||||
|
ssa_emit_store(proc, offset_, ssa_emit_arith(proc, Token_Add, offset, len, t_int));
|
||||||
|
|
||||||
|
|
||||||
|
idx = ssa_emit_load(proc, index);
|
||||||
|
if (val_type != NULL) {
|
||||||
|
val = ssa_emit_struct_ev(proc, rune_and_len, 0);
|
||||||
|
}
|
||||||
|
ssa_emit_store(proc, index, ssa_emit_arith(proc, Token_Add, ssa_emit_load(proc, index), v_one, t_int));
|
||||||
|
|
||||||
|
if (key_) *key_ = idx;
|
||||||
|
if (val_) *val_ = val;
|
||||||
|
if (loop_) *loop_ = loop;
|
||||||
|
if (done_) *done_ = done;
|
||||||
|
}
|
||||||
|
|
||||||
void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
|
void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
|
||||||
switch (node->kind) {
|
switch (node->kind) {
|
||||||
case_ast_node(bs, EmptyStmt, node);
|
case_ast_node(bs, EmptyStmt, node);
|
||||||
@@ -4312,7 +4435,93 @@ void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
|
|||||||
|
|
||||||
|
|
||||||
proc->curr_block = done;
|
proc->curr_block = done;
|
||||||
|
case_end;
|
||||||
|
|
||||||
|
|
||||||
|
case_ast_node(rs, RangeStmt, node);
|
||||||
|
ssa_emit_comment(proc, str_lit("RangeStmt"));
|
||||||
|
|
||||||
|
Type *key_type = NULL;
|
||||||
|
Type *val_type = NULL;
|
||||||
|
if (rs->key != NULL && !ssa_is_blank_ident(rs->key)) {
|
||||||
|
key_type = type_of_expr(proc->module->info, rs->key);
|
||||||
|
}
|
||||||
|
if (rs->value != NULL && !ssa_is_blank_ident(rs->value)) {
|
||||||
|
val_type = type_of_expr(proc->module->info, rs->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_type != NULL) {
|
||||||
|
ssa_add_local_for_identifier(proc, rs->key, true);
|
||||||
|
}
|
||||||
|
if (val_type != NULL) {
|
||||||
|
ssa_add_local_for_identifier(proc, rs->value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssaValue *key = NULL;
|
||||||
|
ssaValue *val = NULL;
|
||||||
|
ssaBlock *loop = NULL;
|
||||||
|
ssaBlock *done = NULL;
|
||||||
|
|
||||||
|
Type *expr_type = type_of_expr(proc->module->info, rs->expr);
|
||||||
|
Type *et = base_type(type_deref(expr_type));
|
||||||
|
bool deref = is_type_pointer(expr_type);
|
||||||
|
switch (et->kind) {
|
||||||
|
case Type_Array: {
|
||||||
|
ssaValue *array = ssa_build_addr(proc, rs->expr).addr;
|
||||||
|
if (deref) {
|
||||||
|
array = ssa_emit_load(proc, array);
|
||||||
|
}
|
||||||
|
ssa_build_range_indexed(proc, array, val_type, &key, &val, &loop, &done);
|
||||||
|
} break;
|
||||||
|
case Type_Slice: {
|
||||||
|
ssaValue *slice = ssa_build_expr(proc, rs->expr);
|
||||||
|
if (deref) {
|
||||||
|
slice = ssa_emit_load(proc, slice);
|
||||||
|
}
|
||||||
|
ssa_build_range_indexed(proc, slice, val_type, &key, &val, &loop, &done);
|
||||||
|
} break;
|
||||||
|
case Type_Basic: {
|
||||||
|
ssaValue *string = ssa_build_expr(proc, rs->expr);
|
||||||
|
if (deref) {
|
||||||
|
string = ssa_emit_load(proc, string);
|
||||||
|
}
|
||||||
|
if (is_type_untyped(expr_type)) {
|
||||||
|
ssaValue *s = ssa_add_local_generated(proc, t_string);
|
||||||
|
ssa_emit_store(proc, s, string);
|
||||||
|
string = ssa_emit_load(proc, s);
|
||||||
|
}
|
||||||
|
ssa_build_range_string(proc, string, val_type, &key, &val, &loop, &done);
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
GB_PANIC("Cannot range over %s", type_to_string(expr_type));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssaAddr key_addr = {0};
|
||||||
|
ssaAddr val_addr = {0};
|
||||||
|
if (key_type != NULL) {
|
||||||
|
key_addr = ssa_build_addr(proc, rs->key);
|
||||||
|
}
|
||||||
|
if (val_type != NULL) {
|
||||||
|
val_addr = ssa_build_addr(proc, rs->value);
|
||||||
|
}
|
||||||
|
if (key_type != NULL) {
|
||||||
|
ssa_addr_store(proc, key_addr, key);
|
||||||
|
}
|
||||||
|
if (val_type != NULL) {
|
||||||
|
ssa_addr_store(proc, val_addr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssa_push_target_list(proc, done, loop, NULL);
|
||||||
|
|
||||||
|
ssa_open_scope(proc);
|
||||||
|
ssa_build_stmt(proc, rs->body);
|
||||||
|
ssa_close_scope(proc, ssaDeferExit_Default, NULL);
|
||||||
|
|
||||||
|
ssa_pop_target_list(proc);
|
||||||
|
ssa_emit_jump(proc, loop);
|
||||||
|
proc->curr_block = done;
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ms, MatchStmt, node);
|
case_ast_node(ms, MatchStmt, node);
|
||||||
|
|||||||
@@ -84,10 +84,6 @@ TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
|
|||||||
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
||||||
TOKEN_KIND(Token_type, "type"), \
|
TOKEN_KIND(Token_type, "type"), \
|
||||||
TOKEN_KIND(Token_proc, "proc"), \
|
TOKEN_KIND(Token_proc, "proc"), \
|
||||||
/* TOKEN_KIND(Token_var, "var"), */\
|
|
||||||
/* TOKEN_KIND(Token_const, "const"), */\
|
|
||||||
/* TOKEN_KIND(Token_import, "import"), */\
|
|
||||||
/* TOKEN_KIND(Token_include, "include"), */\
|
|
||||||
TOKEN_KIND(Token_macro, "macro"), \
|
TOKEN_KIND(Token_macro, "macro"), \
|
||||||
TOKEN_KIND(Token_match, "match"), \
|
TOKEN_KIND(Token_match, "match"), \
|
||||||
TOKEN_KIND(Token_break, "break"), \
|
TOKEN_KIND(Token_break, "break"), \
|
||||||
|
|||||||
Reference in New Issue
Block a user