Explorar el Código

`range` statement

Ginger Bill hace 8 años
padre
commit
a3883a178c
Se han modificado 9 ficheros con 462 adiciones y 75 borrados
  1. 8 1
      code/demo.odin
  2. 4 0
      core/_preload.odin
  3. 2 1
      src/checker/decl.c
  4. 2 2
      src/checker/entity.c
  5. 10 4
      src/checker/expr.c
  6. 99 0
      src/checker/stmt.c
  7. 127 62
      src/parser.c
  8. 210 1
      src/ssa.c
  9. 0 4
      src/tokenizer.c

+ 8 - 1
code/demo.odin

@@ -18,5 +18,12 @@ Thing :: enum f64 {
 }
 
 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);
+	}
 }

+ 4 - 0
core/_preload.odin

@@ -3,6 +3,7 @@
 #import "os.odin";
 #import "fmt.odin";
 #import "mem.odin";
+#import "utf8.odin";
 
 // IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a
 // #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();
 }
 
+__string_decode_rune :: proc(s: string) -> (rune, int) #inline {
+	return utf8.decode_rune(s);
+}
 

+ 2 - 1
src/checker/decl.c

@@ -161,8 +161,9 @@ void check_var_decl_node(Checker *c, AstNodeValueDecl *vd) {
 		}
 		e->flags |= EntityFlag_Visited;
 
-		if (e->type == NULL)
+		if (e->type == NULL) {
 			e->type = init_type;
+		}
 	}
 
 	check_arity_match(c, vd);

+ 2 - 2
src/checker/entity.c

@@ -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("_");
-	return make_entity_variable(a, file_scope, token, NULL);
+	return make_entity_variable(a, scope, token, NULL);
 }
 

+ 10 - 4
src/checker/expr.c

@@ -3809,20 +3809,26 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 	case_end;
 
 	case_ast_node(pl, ProcLit, node);
-		Type *proc_type = check_type(c, pl->type);
-		if (proc_type == NULL) {
+		Type *type = check_type(c, pl->type);
+		if (type == NULL || !is_type_proc(type)) {
 			gbString str = expr_to_string(node);
 			error_node(node, "Invalid procedure literal `%s`", str);
 			gb_string_free(str);
 			check_close_scope(c);
 			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_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);
 
 		o->mode = Addressing_Value;
-		o->type = proc_type;
+		o->type = type;
 	case_end;
 
 	case_ast_node(ge, GiveExpr, node);

+ 99 - 0
src/checker/stmt.c

@@ -133,6 +133,12 @@ bool check_is_terminating(AstNode *node) {
 		}
 	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);
 		bool has_default = false;
 		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);
 	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);
 		Operand x = {0};
 

+ 127 - 62
src/parser.c

@@ -204,6 +204,13 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
 		AstNode *post; \
 		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 { \
 		Token token;        \
 		AstNodeArray list;  \
@@ -847,7 +854,15 @@ AstNode *make_for_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, As
 	result->ForStmt.body  = body;
 	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 *result = make_node(f, AstNode_MatchStmt);
@@ -1195,6 +1210,7 @@ void fix_advance_to_next_stmt(AstFile *f) {
 		case Token_when:
 		case Token_return:
 		case Token_for:
+		case Token_range:
 		case Token_match:
 		case Token_defer:
 		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");
 			}
 			AstNode *curr_proc = f->curr_proc;
+			AstNode *body = NULL;
 			f->curr_proc = type;
-			AstNode *body = parse_body(f);
+			body = parse_body(f);
 			f->curr_proc = curr_proc;
 
 			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);
 }
 
-AstNodeArray parse_identfier_list(AstFile *f) {
+AstNodeArray parse_identifier_list(AstFile *f) {
 	AstNodeArray list = make_ast_node_array(f);
 
 	do {
@@ -2110,8 +2127,63 @@ AstNode *parse_type(AstFile *f) {
 	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) {
-	Token start_token = f->curr_token;
 	AstNodeArray lhs = parse_lhs_expr_list(f);
 	Token token = f->curr_token;
 	switch (token.kind) {
@@ -2143,60 +2215,8 @@ AstNode *parse_simple_stmt(AstFile *f) {
 		return make_assign_stmt(f, token, lhs, rhs);
 	} break;
 
-	case Token_Colon: {
-		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);
-	} break;
+	case Token_Colon:
+		return parse_value_decl(f, lhs);
 	}
 
 	if (lhs.count > 1) {
@@ -2265,7 +2285,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using,
 			is_using = true;
 		}
 
-		AstNodeArray names = parse_identfier_list(f);
+		AstNodeArray names = parse_identifier_list(f);
 		if (names.count == 0) {
 			syntax_error(f->curr_token, "Empty parameter declaration");
 			break;
@@ -2523,9 +2543,14 @@ void parse_proc_signature(AstFile *f,
 AstNode *parse_body(AstFile *f) {
 	AstNodeArray stmts = {0};
 	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);
 	stmts = parse_stmt_list(f);
 	close = expect_token(f, Token_CloseBrace);
+	f->expr_level = prev_expr_level;
 
 	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) {
 			cond = parse_simple_stmt(f);
 			if (is_ast_node_complex_stmt(cond)) {
-				syntax_error(f->curr_token,
-				             "You are not allowed that type of statement in a for statement, it is too complex!");
+				syntax_error(f->curr_token, "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);
 }
 
+
+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) {
 	Token token = f->curr_token;
 	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_when:   return parse_when_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_defer:  return parse_defer_stmt(f);
 	case Token_asm:    return parse_asm_stmt(f);
@@ -2944,7 +3008,8 @@ AstNode *parse_stmt(AstFile *f) {
 		return s;
 
 	case Token_using: {
-		next_token(f);
+		// TODO(bill): Make using statements better
+		Token token = expect_token(f, Token_using);
 		AstNode *node = parse_stmt(f);
 		bool valid = false;
 

+ 210 - 1
src/ssa.c

@@ -1807,7 +1807,7 @@ ssaValue *ssa_string_elem(ssaProcedure *proc, ssaValue *string) {
 }
 ssaValue *ssa_string_len(ssaProcedure *proc, ssaValue *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);
 }
 
@@ -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) {
 	switch (node->kind) {
 	case_ast_node(bs, EmptyStmt, node);
@@ -4312,7 +4435,93 @@ void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
 
 
 		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_ast_node(ms, MatchStmt, node);

+ 0 - 4
src/tokenizer.c

@@ -84,10 +84,6 @@ TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
 TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_type,           "type"), \
 	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_match,          "match"), \
 	TOKEN_KIND(Token_break,          "break"), \