浏览代码

Interval expressions in `range`

Ginger Bill 8 年之前
父节点
当前提交
a86896e4d3
共有 6 个文件被更改,包括 316 次插入119 次删除
  1. 10 6
      code/demo.odin
  2. 5 0
      src/checker/expr.c
  3. 103 29
      src/checker/stmt.c
  4. 35 17
      src/parser.c
  5. 117 60
      src/ssa.c
  6. 46 7
      src/tokenizer.c

+ 10 - 6
code/demo.odin

@@ -18,12 +18,16 @@ Thing :: enum f64 {
 }
 
 main :: proc() {
-	msg := "Hello";
-	range index, value : msg {
-		fmt.println(index, value);
-	}
+	msg := "Hellope";
 	list := []int{1, 4, 7, 3, 7, 2, 1};
-	range index, value : list {
-		fmt.println(index, value);
+
+	range value : msg {
+		fmt.println(value);
+	}
+	range value : list {
+		fmt.println(value);
+	}
+	range x : 0 ..< 5 {
+		fmt.println(x);
 	}
 }

+ 5 - 0
src/checker/expr.c

@@ -3790,6 +3790,11 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 		goto error;
 	case_end;
 
+	case_ast_node(i, IntervalExpr, node);
+		error_node(node, "Invalid use of an interval expression");
+		goto error;
+	case_end;
+
 	case_ast_node(i, Ident, node);
 		check_identifier(c, o, node, type_hint);
 	case_end;

+ 103 - 29
src/checker/stmt.c

@@ -594,42 +594,116 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 		u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
 		check_open_scope(c, node);
 
+		Type *val = NULL;
+		Type *idx = NULL;
+		Entity *entities[2] = {0};
+		isize entity_count = 0;
 
-		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;
+		if (rs->expr != NULL && rs->expr->kind == AstNode_IntervalExpr) {
+			ast_node(ie, IntervalExpr, rs->expr);
+			Operand x = {Addressing_Invalid};
+			Operand y = {Addressing_Invalid};
+
+			check_expr(c, &x, ie->left);
+			if (x.mode == Addressing_Invalid) {
+				goto skip_expr;
+			}
+			check_expr(c, &y, ie->right);
+			if (y.mode == Addressing_Invalid) {
+				goto skip_expr;
+			}
+
+			convert_to_typed(c, &x, y.type, 0);
+			if (x.mode == Addressing_Invalid) {
+				goto skip_expr;
+			}
+			convert_to_typed(c, &y, x.type, 0);
+			if (y.mode == Addressing_Invalid) {
+				goto skip_expr;
+			}
+
+			convert_to_typed(c, &x, default_type(y.type), 0);
+			if (x.mode == Addressing_Invalid) {
+				goto skip_expr;
+			}
+			convert_to_typed(c, &y, default_type(x.type), 0);
+			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);
 				}
-				break;
-			case Type_Array:
-				key = t_int;
-				val = t->Array.elem;
-				break;
-			case Type_Slice:
-				key = t_int;
-				val = t->Array.elem;
-				break;
+				goto skip_expr;
 			}
-		}
 
-		if (key == NULL) {
-			gbString s = expr_to_string(operand.expr);
-			error_node(operand.expr, "Cannot iterate over %s", s);
-			gb_string_free(s);
+			if (!is_type_integer(x.type) && !is_type_float(x.type)) {
+				error(ie->op, "Only numerical 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));
+
+				bool ok = compare_exact_values(Token_Lt, a, b);
+				if (!ok) {
+					// TODO(bill): Better error message
+					error(ie->op, "Invalid interval expression");
+					goto skip_expr;
+				}
+			}
+
+			add_type_and_value(&c->info, ie->left,  x.mode, x.type, x.value);
+			add_type_and_value(&c->info, ie->right, y.mode, y.type, y.value);
+			val = x.type;
+			idx = t_int;
+		} else {
+			Operand operand = {Addressing_Invalid};
+			check_expr(c, &operand, rs->expr);
+
+			if (operand.mode != Addressing_Invalid) {
+				Type *t = base_type(type_deref(operand.type));
+				switch (t->kind) {
+				case Type_Basic:
+					if (is_type_string(t)) {
+						val = t_rune;
+						idx = t_int;
+					}
+					break;
+				case Type_Array:
+					val = t->Array.elem;
+					idx = t_int;
+					break;
+				case Type_Slice:
+					val = t->Array.elem;
+					idx = t_int;
+					break;
+				}
+			}
+
+			if (val == NULL) {
+				gbString s = expr_to_string(operand.expr);
+				error_node(node, "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};
+	skip_expr:
+		AstNode *lhs[2] = {rs->value, rs->index};
+		Type *   rhs[2] = {val, idx};
 
 		for (isize i = 0; i < 2; i++) {
 			if (lhs[i] == NULL) {

+ 35 - 17
src/parser.c

@@ -160,6 +160,7 @@ AST_NODE_KIND(_ExprBegin,  "",  i32) \
 		AstNode *body; \
 		AstNode *else_expr; \
 	}) \
+	AST_NODE_KIND(IntervalExpr, "interval expression", struct { Token op; AstNode *left, *right; }) \
 AST_NODE_KIND(_ExprEnd,       "", i32) \
 AST_NODE_KIND(_StmtBegin,     "", i32) \
 	AST_NODE_KIND(BadStmt,    "bad statement",                 struct { Token begin, end; }) \
@@ -205,11 +206,11 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
 		AstNode *body; \
 	}) \
 	AST_NODE_KIND(RangeStmt, "range statement", struct { \
-		Token        token; \
-		AstNode *    key; \
-		AstNode *    value; \
-		AstNode *    expr; \
-		AstNode *    body; \
+		Token    token; \
+		AstNode *value; \
+		AstNode *index; \
+		AstNode *expr; \
+		AstNode *body; \
 	}) \
 	AST_NODE_KIND(CaseClause, "case clause", struct { \
 		Token token;        \
@@ -451,6 +452,8 @@ Token ast_node_token(AstNode *node) {
 		return node->GiveExpr.token;
 	case AstNode_IfExpr:
 		return node->IfExpr.token;
+	case AstNode_IntervalExpr:
+		return ast_node_token(node->IntervalExpr.left);
 
 	case AstNode_BadStmt:
 		return node->BadStmt.begin;
@@ -700,6 +703,18 @@ AstNode *make_demaybe_expr(AstFile *f, AstNode *expr, Token op) {
 	return result;
 }
 
+AstNode *make_interval_expr(AstFile *f, Token op, AstNode *left, AstNode *right) {
+	AstNode *result = make_node(f, AstNode_IntervalExpr);
+
+	result->IntervalExpr.op = op;
+	result->IntervalExpr.left = left;
+	result->IntervalExpr.right = right;
+
+	return result;
+}
+
+
+
 
 AstNode *make_basic_lit(AstFile *f, Token basic_lit) {
 	AstNode *result = make_node(f, AstNode_BasicLit);
@@ -854,12 +869,12 @@ 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 *make_range_stmt(AstFile *f, Token token, AstNode *value, AstNode *index, 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.index = index;
+	result->RangeStmt.expr  = expr;
 	result->RangeStmt.body  = body;
 	return result;
 }
@@ -2791,28 +2806,31 @@ AstNode *parse_range_stmt(AstFile *f) {
 	isize prev_level = f->expr_level;
 	f->expr_level = -1;
 	AstNode *expr = parse_expr(f, false);
+	if (f->curr_token.kind == Token_Interval) {
+		Token op = expect_token(f, Token_Interval);
+		AstNode *right = parse_expr(f, false);
+		expr = make_interval_expr(f, op, expr, right);
+	}
 	f->expr_level = prev_level;
 
-	AstNode *key   = NULL;
 	AstNode *value = NULL;
-	AstNode *body = parse_block_stmt(f, false);
+	AstNode *index = NULL;
+	AstNode *body  = parse_block_stmt(f, false);
 
 	switch (names.count) {
-	case 0:
-		break;
 	case 1:
-		key = names.e[0];
+		value = names.e[0];
 		break;
 	case 2:
-		key = names.e[0];
-		value = names.e[1];
+		value = names.e[0];
+		index = names.e[1];
 		break;
 	default:
-		error_node(names.e[names.count-1], "Expected at most 2 expressions");
+		error(token, "Expected at 1 or 2 identifiers");
 		return make_bad_stmt(f, token, f->curr_token);
 	}
 
-	return make_range_stmt(f, token, key, value, expr, body);
+	return make_range_stmt(f, token, value, index, expr, body);
 }
 
 AstNode *parse_case_clause(AstFile *f) {

+ 117 - 60
src/ssa.c

@@ -3908,8 +3908,15 @@ void ssa_build_when_stmt(ssaProcedure *proc, AstNodeWhenStmt *ws) {
 	}
 }
 
+void ssa_emit_increment(ssaProcedure *proc, ssaValue *addr) {
+	GB_ASSERT(is_type_pointer(ssa_type(addr)));
+	Type *type = type_deref(ssa_type(addr));
+	ssa_emit_store(proc, addr, ssa_emit_arith(proc, Token_Add, ssa_emit_load(proc, addr), v_one, type));
+
+}
+
 void ssa_build_range_indexed(ssaProcedure *proc, ssaValue *expr, Type *val_type,
-                             ssaValue **key_, ssaValue **val_, ssaBlock **loop_, ssaBlock **done_) {
+                             ssaValue **val_, ssaValue **idx_, ssaBlock **loop_, ssaBlock **done_) {
 	ssaValue *count = NULL;
 	Type *expr_type = base_type(ssa_type(expr));
 	switch (expr_type->kind) {
@@ -3924,8 +3931,8 @@ void ssa_build_range_indexed(ssaProcedure *proc, ssaValue *expr, Type *val_type,
 		break;
 	}
 
-	ssaValue *idx = NULL;
 	ssaValue *val = NULL;
+	ssaValue *idx = NULL;
 	ssaBlock *loop = NULL;
 	ssaBlock *done = NULL;
 	ssaBlock *body = NULL;
@@ -3933,15 +3940,15 @@ void ssa_build_range_indexed(ssaProcedure *proc, ssaValue *expr, Type *val_type,
 	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");
+	loop = ssa_add_block(proc, NULL, "range.index.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");
+	body = ssa_add_block(proc, NULL, "range.index.body");
+	done = ssa_add_block(proc, NULL, "range.index.done");
 	ssaValue *cond = ssa_emit_comp(proc, Token_Lt, incr, count);
 	ssa_emit_if(proc, cond, body, done);
 	proc->curr_block = body;
@@ -3962,15 +3969,15 @@ void ssa_build_range_indexed(ssaProcedure *proc, ssaValue *expr, Type *val_type,
 		}
 	}
 
-	if (key_)  *key_  = idx;
 	if (val_)  *val_  = val;
+	if (idx_)  *idx_  = idx;
 	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 **val_, ssaValue **idx_, ssaBlock **loop_, ssaBlock **done_) {
 	ssaValue *count = v_zero;
 	Type *expr_type = base_type(ssa_type(expr));
 	switch (expr_type->kind) {
@@ -3982,8 +3989,8 @@ void ssa_build_range_string(ssaProcedure *proc, ssaValue *expr, Type *val_type,
 		break;
 	}
 
-	ssaValue *idx = NULL;
 	ssaValue *val = NULL;
+	ssaValue *idx = NULL;
 	ssaBlock *loop = NULL;
 	ssaBlock *done = NULL;
 	ssaBlock *body = NULL;
@@ -3994,14 +4001,14 @@ void ssa_build_range_string(ssaProcedure *proc, ssaValue *expr, Type *val_type,
 	ssaValue *offset_ = ssa_add_local_generated(proc, t_int);
 	ssa_emit_store(proc, index, v_zero);
 
-	loop = ssa_add_block(proc, NULL, "rangestring.loop");
+	loop = ssa_add_block(proc, NULL, "range.string.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");
+	body = ssa_add_block(proc, NULL, "range.string.body");
+	done = ssa_add_block(proc, NULL, "range.string.done");
 
 	ssaValue *offset = ssa_emit_load(proc, offset_);
 
@@ -4023,14 +4030,61 @@ void ssa_build_range_string(ssaProcedure *proc, ssaValue *expr, Type *val_type,
 	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));
+	ssa_emit_increment(proc, index);
+
+	if (val_)  *val_  = val;
+	if (idx_)  *idx_  = idx;
+	if (loop_) *loop_ = loop;
+	if (done_) *done_ = done;
+}
+
+void ssa_build_range_interval(ssaProcedure *proc, AstNodeIntervalExpr *node, Type *val_type,
+                              ssaValue **val_, ssaValue **idx_, ssaBlock **loop_, ssaBlock **done_) {
+
+	ssaValue *lower = ssa_build_expr(proc, node->left);
+	ssaValue *upper = ssa_build_expr(proc, node->right);
+
+	ssaValue *val = NULL;
+	ssaValue *idx = NULL;
+	ssaBlock *loop = NULL;
+	ssaBlock *done = NULL;
+	ssaBlock *body = NULL;
+
+	if (val_type == NULL) {
+		val_type = ssa_type(lower);
+	}
+	ssaValue *value = ssa_add_local_generated(proc, val_type);
+	ssa_emit_store(proc, value, lower);
+
+	ssaValue *index = ssa_add_local_generated(proc, t_int);
+	ssa_emit_store(proc, index, ssa_make_const_int(proc->module->allocator, 0));
+
+	loop = ssa_add_block(proc, NULL, "range.interval.loop");
+	ssa_emit_jump(proc, loop);
+	proc->curr_block = loop;
+
+	body = ssa_add_block(proc, NULL, "range.interval.body");
+	done = ssa_add_block(proc, NULL, "range.interval.done");
+
+	ssaValue *cond = ssa_emit_comp(proc, Token_Lt, ssa_emit_load(proc, value), upper);
+	ssa_emit_if(proc, cond, body, done);
+	proc->curr_block = body;
+
+	if (value != NULL) {
+		val = ssa_emit_load(proc, value);
+	}
+	idx = ssa_emit_load(proc, index);
+
+	ssa_emit_increment(proc, value);
+	ssa_emit_increment(proc, index);
 
-	if (key_)  *key_  = idx;
 	if (val_)  *val_  = val;
+	if (idx_)  *idx_  = idx;
 	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);
@@ -4441,77 +4495,80 @@ void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
 	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);
-		}
+		Type *idx_type = NULL;
 		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 (rs->index != NULL && !ssa_is_blank_ident(rs->index)) {
+			idx_type = type_of_expr(proc->module->info, rs->index);
 		}
+
 		if (val_type != NULL) {
 			ssa_add_local_for_identifier(proc, rs->value, true);
 		}
+		if (idx_type != NULL) {
+			ssa_add_local_for_identifier(proc, rs->index, true);
+		}
 
-		ssaValue *key = NULL;
 		ssaValue *val = NULL;
+		ssaValue *index = 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);
+		if (rs->expr->kind == AstNode_IntervalExpr) {
+			ssa_build_range_interval(proc, &rs->expr->IntervalExpr, val_type, &val, &index, &loop, &done);
+		} else {
+			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, &val, &index, &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, &val, &index, &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, &val, &index, &loop, &done);
+			} break;
+			default:
+				GB_PANIC("Cannot range over %s", type_to_string(expr_type));
+				break;
 			}
-			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);
-		}
+		ssaAddr idx_addr = {0};
 		if (val_type != NULL) {
 			val_addr = ssa_build_addr(proc, rs->value);
 		}
-		if (key_type != NULL) {
-			ssa_addr_store(proc, key_addr, key);
+		if (idx_type != NULL) {
+			idx_addr = ssa_build_addr(proc, rs->index);
 		}
 		if (val_type != NULL) {
 			ssa_addr_store(proc, val_addr, val);
 		}
+		if (idx_type != NULL) {
+			ssa_addr_store(proc, idx_addr, index);
+		}
 
 		ssa_push_target_list(proc, done, loop, NULL);
 

+ 46 - 7
src/tokenizer.c

@@ -78,7 +78,7 @@ TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \
 	TOKEN_KIND(Token_Period, "."), \
 	TOKEN_KIND(Token_Comma, ","), \
 	TOKEN_KIND(Token_Ellipsis, ".."), \
-	TOKEN_KIND(Token_RangeExclusive, "..<"), \
+	TOKEN_KIND(Token_Interval, "..<"), \
 TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
 \
 TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
@@ -286,6 +286,14 @@ typedef enum TokenizerInitError {
 } TokenizerInitError;
 
 
+typedef struct TokenizerState {
+	Rune  curr_rune;   // current character
+	u8 *  curr;        // character pos
+	u8 *  read_curr;   // pos from start
+	u8 *  line;        // current line pos
+	isize line_count;
+} TokenizerState;
+
 typedef struct Tokenizer {
 	String fullpath;
 	u8 *start;
@@ -302,6 +310,25 @@ typedef struct Tokenizer {
 } Tokenizer;
 
 
+TokenizerState save_tokenizer_state(Tokenizer *t) {
+	TokenizerState state = {0};
+	state.curr_rune  = t->curr_rune;
+	state.curr       = t->curr;
+	state.read_curr  = t->read_curr;
+	state.line       = t->line;
+	state.line_count = t->line_count;
+	return state;
+}
+
+void restore_tokenizer_state(Tokenizer *t, TokenizerState *state) {
+	 t->curr_rune  = state->curr_rune;
+	 t->curr       = state->curr;
+	 t->read_curr  = state->read_curr;
+	 t->line       = state->line;
+	 t->line_count = state->line_count;
+}
+
+
 void tokenizer_err(Tokenizer *t, char *msg, ...) {
 	va_list va;
 	isize column = t->read_curr - t->line+1;
@@ -456,23 +483,27 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
 		if (t->curr_rune == 'b') { // Binary
 			advance_to_next_rune(t);
 			scan_mantissa(t, 2);
-			if (t->curr - prev <= 2)
+			if (t->curr - prev <= 2) {
 				token.kind = Token_Invalid;
+			}
 		} else if (t->curr_rune == 'o') { // Octal
 			advance_to_next_rune(t);
 			scan_mantissa(t, 8);
-			if (t->curr - prev <= 2)
+			if (t->curr - prev <= 2) {
 				token.kind = Token_Invalid;
+			}
 		} else if (t->curr_rune == 'd') { // Decimal
 			advance_to_next_rune(t);
 			scan_mantissa(t, 10);
-			if (t->curr - prev <= 2)
+			if (t->curr - prev <= 2) {
 				token.kind = Token_Invalid;
+			}
 		} else if (t->curr_rune == 'x') { // Hexadecimal
 			advance_to_next_rune(t);
 			scan_mantissa(t, 16);
-			if (t->curr - prev <= 2)
+			if (t->curr - prev <= 2) {
 				token.kind = Token_Invalid;
+			}
 		} else {
 			seen_decimal_point = false;
 			scan_mantissa(t, 10);
@@ -491,8 +522,15 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
 
 fraction:
 	if (t->curr_rune == '.') {
-		token.kind = Token_Float;
+		// HACK(bill): This may be inefficient
+		TokenizerState state = save_tokenizer_state(t);
 		advance_to_next_rune(t);
+		if (t->curr_rune == '.') {
+			// TODO(bill): Clean up this shit
+			restore_tokenizer_state(t, &state);
+			goto end;
+		}
+		token.kind = Token_Float;
 		scan_mantissa(t, 10);
 	}
 
@@ -506,6 +544,7 @@ exponent:
 		scan_mantissa(t, 10);
 	}
 
+end:
 	token.string.len = t->curr - token.string.text;
 	return token;
 }
@@ -801,7 +840,7 @@ Token tokenizer_get_token(Tokenizer *t) {
 				token.kind = Token_Ellipsis;
 				if (t->curr_rune == '<') {
 					advance_to_next_rune(t);
-					token.kind = Token_RangeExclusive;
+					token.kind = Token_Interval;
 				}
 			}
 			break;