Browse Source

Slices and slice expressions

gingerBill 9 years ago
parent
commit
9ba2a6d02c
8 changed files with 327 additions and 48 deletions
  1. 1 0
      build.bat
  2. 6 0
      src/checker/checker.cpp
  3. 217 32
      src/checker/expression.cpp
  4. 9 3
      src/checker/statements.cpp
  5. 27 6
      src/checker/type.cpp
  6. 55 4
      src/parser.cpp
  7. 11 2
      src/test.odin
  8. 1 1
      src/tokenizer.cpp

+ 1 - 0
build.bat

@@ -24,6 +24,7 @@ set compiler_warnings= ^
 	-wd4505 -wd4512 -wd4550 ^
 
 set compiler_includes= ^
+
 	rem -I"C:\Program Files\LLVM\include"
 
 set libs= kernel32.lib user32.lib gdi32.lib opengl32.lib ^

+ 6 - 0
src/checker/checker.cpp

@@ -71,6 +71,9 @@ enum BuiltinProcedureId {
 	BuiltinProcedure_offset_of,
 	BuiltinProcedure_offset_of_val,
 	BuiltinProcedure_static_assert,
+	BuiltinProcedure_len,
+	BuiltinProcedure_cap,
+	BuiltinProcedure_copy,
 	BuiltinProcedure_print,
 	BuiltinProcedure_println,
 
@@ -111,6 +114,9 @@ gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = {
 	{STR_LIT("offset_of"),        2, false, Expression_Expression},
 	{STR_LIT("offset_of_val"),    1, false, Expression_Expression},
 	{STR_LIT("static_assert"),    1, false, Expression_Statement},
+	{STR_LIT("len"),              1, false, Expression_Expression},
+	{STR_LIT("cap"),              1, false, Expression_Expression},
+	{STR_LIT("copy"),             2, false, Expression_Expression},
 	{STR_LIT("print"),            1, true,  Expression_Statement},
 	{STR_LIT("println"),          1, true,  Expression_Statement},
 };

+ 217 - 32
src/checker/expression.cpp

@@ -252,8 +252,9 @@ Type *check_type_expression_extra(Checker *c, AstNode *expression, Type *named_t
 			set_base_type(named_type, t);
 			return t;
 		} else {
-			print_checker_error(c, ast_node_token(expression), "Empty array size");
-			return NULL;
+			Type *t = make_type_slice(check_type(c, expression->array_type.element));
+			set_base_type(named_type, t);
+			return t;
 		}
 		break;
 
@@ -335,12 +336,17 @@ Type *check_type(Checker *c, AstNode *expression, Type *named_type) {
 	case AstNode_ParenExpression:
 		return check_type(c, expression->paren_expression.expression, named_type);
 
-	case AstNode_ArrayType:
-		type = make_type_array(check_type(c, expression->array_type.element),
-		                       check_array_count(c, expression->array_type.count));
-		set_base_type(named_type, type);
+	case AstNode_ArrayType: {
+		if (expression->array_type.count != NULL) {
+			type = make_type_array(check_type(c, expression->array_type.element),
+			                       check_array_count(c, expression->array_type.count));
+			set_base_type(named_type, type);
+		} else {
+			type = make_type_slice(check_type(c, expression->array_type.element));
+			set_base_type(named_type, type);
+		}
 		goto end;
-		break;
+	} break;
 
 	case AstNode_StructType: {
 		type = make_type_structure();
@@ -860,35 +866,43 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type) {
 	operand->type = target_type;
 }
 
-b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, b32 bound_checks) {
+b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *value) {
 	Operand operand = {Addressing_Invalid};
 	check_expression(c, &operand, index_value);
-	if (operand.mode == Addressing_Invalid)
+	if (operand.mode == Addressing_Invalid) {
+		if (value) *value = 0;
 		return false;
+	}
 
 	convert_to_typed(c, &operand, &basic_types[Basic_int]);
-	if (operand.mode == Addressing_Invalid)
+	if (operand.mode == Addressing_Invalid) {
+		if (value) *value = 0;
 		return false;
+	}
 
 	if (!is_type_integer(operand.type)) {
 		gbString expr_str = expression_to_string(operand.expression);
 		print_checker_error(c, ast_node_token(operand.expression),
 		                    "Index `%s` must be an integer", expr_str);
 		gb_string_free(expr_str);
+		if (value) *value = 0;
 		return false;
 	}
 
 	if (operand.mode == Addressing_Constant) {
-		if (bound_checks && max_count > 0) { // NOTE(bill): Do array bound checking
+		if (max_count >= 0) { // NOTE(bill): Do array bound checking
 			i64 i = value_to_integer(operand.value).value_integer;
 			if (i < 0) {
 				gbString expr_str = expression_to_string(operand.expression);
 				print_checker_error(c, ast_node_token(operand.expression),
 				                    "Index `%s` cannot be a negative value", expr_str);
 				gb_string_free(expr_str);
+				if (value) *value = 0;
 				return false;
 			}
 
+			if (value) *value = i;
+
 			if (i >= max_count) {
 				gbString expr_str = expression_to_string(operand.expression);
 				print_checker_error(c, ast_node_token(operand.expression),
@@ -896,10 +910,13 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, b32 bound
 				gb_string_free(expr_str);
 				return false;
 			}
+
+			return true;
 		}
 	}
 
 	// NOTE(bill): It's alright :D
+	if (value) *value = -1;
 	return true;
 }
 
@@ -920,10 +937,6 @@ Entity *lookup_field(Type *type, AstNode *field_node, isize *index = NULL) {
 				return f;
 			}
 		}
-	} else {
-		// TODO(bill): Array.count
-		// TODO(bill): Array.elements
-		// TODO(bill): Or should these be functions?
 	}
 
 	return NULL;
@@ -960,7 +973,6 @@ void check_selector(Checker *c, Operand *operand, AstNode *node) {
 
 }
 
-
 b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) {
 	GB_ASSERT(call->kind == AstNode_CallExpression);
 	auto *ce = &call->call_expression;
@@ -972,10 +984,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		if (ce->arg_list_count > bp->arg_count && !bp->variadic)
 			err = "Too many";
 		if (err) {
-			gbString call_str = expression_to_string(call);
-			defer (gb_string_free(call_str));
-			print_checker_error(c, ce->close, "`%s` arguments for `%s`, expected %td, got %td",
-			                    err, call_str, bp->arg_count, ce->arg_list_count);
+			print_checker_error(c, ce->close, "`%s` arguments for `%.*s`, expected %td, got %td",
+			                    err, LIT(call->call_expression.proc->identifier.token.string),
+			                    bp->arg_count, ce->arg_list_count);
 			return false;
 		}
 	}
@@ -1044,7 +1055,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 				print_checker_error(c, ast_node_token(ce->arg_list), "Expected a structure type for `offset_of`");
 				return false;
 			}
-			if (field_arg->kind != AstNode_Identifier) {
+			if (field_arg == NULL ||
+			    field_arg->kind != AstNode_Identifier) {
 				print_checker_error(c, ast_node_token(field_arg), "Expected an identifier for field argument");
 				return false;
 			}
@@ -1055,7 +1067,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		if (entity == NULL) {
 			gbString type_str = type_to_string(type);
 			print_checker_error(c, ast_node_token(ce->arg_list),
-			                    "`%s` has no field named `%s`", type_str, LIT(field_arg->identifier.token.string));
+			                    "`%s` has no field named `%.*s`", type_str, LIT(field_arg->identifier.token.string));
 			return false;
 		}
 
@@ -1089,7 +1101,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		if (entity == NULL) {
 			gbString type_str = type_to_string(type);
 			print_checker_error(c, ast_node_token(arg),
-			                    "`%s` has no field named `%s`", type_str, LIT(s->selector->identifier.token.string));
+			                    "`%s` has no field named `%.*s`", type_str, LIT(s->selector->identifier.token.string));
 			return false;
 		}
 
@@ -1099,6 +1111,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	} break;
 
 	case BuiltinProcedure_static_assert:
+		// static_assert :: proc(cond: bool)
+
 		if (operand->mode != Addressing_Constant ||
 		    !is_type_boolean(operand->type)) {
 			gbString str = expression_to_string(ce->arg_list);
@@ -1116,6 +1130,92 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		}
 		break;
 
+	case BuiltinProcedure_len:
+	case BuiltinProcedure_cap: {
+		Type *t = get_base_type(operand->type);
+
+		AddressingMode mode = Addressing_Invalid;
+		Value value = {};
+
+		switch (t->kind) {
+		case Type_Basic:
+			if (id == BuiltinProcedure_len) {
+				if (is_type_string(t)) {
+					if (operand->mode == Addressing_Constant) {
+						mode = Addressing_Constant;
+						value = make_value_integer(operand->value.value_string.len);
+					} else {
+						mode = Addressing_Value;
+					}
+				}
+			}
+			break;
+
+		case Type_Array:
+			mode = Addressing_Constant;
+			value = make_value_integer(t->array.count);
+			break;
+
+		case Type_Slice:
+			mode = Addressing_Value;
+			break;
+		}
+
+		if (mode == Addressing_Invalid) {
+			gbString str = expression_to_string(operand->expression);
+			print_checker_error(c, ast_node_token(operand->expression),
+			                    "Invalid expression `%s` for `%.*s`",
+			                    str, LIT(bp->name));
+			gb_string_free(str);
+			return false;
+		}
+
+		operand->mode = mode;
+		operand->type = &basic_types[Basic_int];
+		operand->value = value;
+
+	} break;
+
+	case BuiltinProcedure_copy: {
+		// copy :: proc(x, y: []Type) -> int
+		Type *dest_type = NULL, *src_type = NULL;
+		Type *d = get_base_type(operand->type);
+		if (d->kind == Type_Slice)
+			dest_type = d->slice.element;
+
+		Operand op = {};
+		check_expression(c, &op, ce->arg_list->next);
+		if (op.mode == Addressing_Invalid)
+			return false;
+		Type *s = get_base_type(op.type);
+		if (s->kind == Type_Slice)
+			src_type = s->slice.element;
+
+		if (dest_type == NULL || src_type == NULL) {
+			print_checker_error(c, ast_node_token(call), "`copy` only expects slices as arguments");
+			return false;
+		}
+
+		if (!are_types_identical(dest_type, src_type)) {
+			gbString d_arg = expression_to_string(ce->arg_list);
+			gbString s_arg = expression_to_string(ce->arg_list->next);
+			gbString d_str = type_to_string(dest_type);
+			gbString s_str = type_to_string(src_type);
+			defer (gb_string_free(d_arg));
+			defer (gb_string_free(s_arg));
+			defer (gb_string_free(d_str));
+			defer (gb_string_free(s_str));
+			print_checker_error(c, ast_node_token(call),
+			                    "Arguments to `copy`, %s, %s, have different element types: %s vs %s",
+			                    d_arg, s_arg, d_str, s_str);
+			return false;
+		}
+
+		operand->type = &basic_types[Basic_int]; // Returns number of elements copied
+		operand->mode = Addressing_Value;
+	} break;
+
+
 	case BuiltinProcedure_print:
 	case BuiltinProcedure_println: {
 		for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) {
@@ -1170,7 +1270,6 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
 					operand->mode = Addressing_Value;
 					check_not_tuple(c, operand);
 					check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"));
-
 				}
 
 				if (i < tuple->variable_count && param_index == param_count) {
@@ -1179,7 +1278,7 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
 				}
 			}
 
-			if (param_index < param_count)
+			if (param_index >= param_count)
 				break;
 		}
 
@@ -1392,8 +1491,7 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
 			goto error;
 
 		b32 valid = false;
-		b32 bound_checks = false;
-		i64 max_count = 0;
+		i64 max_count = -1;
 		Type *t = get_base_type(operand->type);
 		switch (t->kind) {
 		case Type_Basic:
@@ -1401,7 +1499,6 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
 				valid = true;
 				if (operand->mode == Addressing_Constant) {
 					max_count = operand->value.value_string.len;
-					bound_checks = true;
 				}
 				operand->mode = Addressing_Value;
 				operand->type = &basic_types[Basic_u8];
@@ -1411,16 +1508,19 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
 		case Type_Array:
 			valid = true;
 			max_count = t->array.count;
-			bound_checks = max_count > 0;
 			if (operand->mode != Addressing_Variable)
 				operand->mode = Addressing_Value;
 			operand->type = t->array.element;
 			break;
 
+		case Type_Slice:
+			valid = true;
+			operand->type = t->slice.element;
+			operand->mode = Addressing_Variable;
+			break;
+
 		case Type_Pointer:
 			valid = true;
-			bound_checks = false;
-			max_count = 0;
 			operand->mode = Addressing_Variable;
 			operand->type = get_base_type(t->pointer.element);
 			break;
@@ -1442,7 +1542,92 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
 			goto error;
 		}
 
-		check_index_value(c, expression->index_expression.value, max_count, bound_checks);
+		check_index_value(c, expression->index_expression.value, max_count, NULL);
+	} break;
+
+
+	case AstNode_SliceExpression: {
+		auto *se = &expression->slice_expression;
+		check_expression(c, operand, se->expression);
+		if (operand->mode == Addressing_Invalid)
+			goto error;
+
+		b32 valid = false;
+		i64 max_count = -1;
+		Type *t = get_base_type(operand->type);
+		switch (t->kind) {
+		case Type_Basic:
+			if (is_type_string(t)) {
+				valid = true;
+				if (operand->mode == Addressing_Constant) {
+					max_count = operand->value.value_string.len;
+				}
+				operand->mode = Addressing_Value;
+			}
+			break;
+
+		case Type_Array:
+			valid = true;
+			max_count = t->array.count;
+			if (operand->mode != Addressing_Variable) {
+				gbString str = expression_to_string(expression);
+				print_checker_error(c, ast_node_token(expression), "Cannot slice array `%s`, value is not addressable", str);
+				gb_string_free(str);
+				goto error;
+			}
+			operand->type = make_type_slice(t->array.element);
+			operand->mode = Addressing_Value;
+			break;
+
+		case Type_Slice:
+			valid = true;
+			operand->mode = Addressing_Value;
+			break;
+
+		case Type_Pointer:
+			valid = true;
+			operand->type = make_type_slice(get_base_type(t->pointer.element));
+			operand->mode = Addressing_Value;
+			break;
+		}
+
+		if (!valid) {
+			gbString str = expression_to_string(operand->expression);
+			print_checker_error(c, ast_node_token(operand->expression),
+			                    "Cannot slice `%s`", str);
+			gb_string_free(str);
+			goto error;
+		}
+
+		i64 indices[3] = {};
+		AstNode *nodes[3] = {se->low, se->high, se->max};
+		for (isize i = 0; i < gb_count_of(nodes); i++) {
+			AstNode *node = nodes[i];
+			i64 index = max_count;
+			if (node != NULL) {
+				i64 capacity = -1;
+				if (max_count >= 0)
+					capacity = max_count;
+				i64 j = 0;
+				if (check_index_value(c, node, capacity, &j)) {
+					index = j;
+				}
+			} else if (i == 0) {
+				index = 0;
+			}
+			indices[i] = index;
+		}
+
+		for (isize i = 0; i < gb_count_of(indices); i++) {
+			i64 a = indices[i];
+			for (isize j = i+1; j < gb_count_of(indices); j++) {
+				i64 b = indices[j];
+				if (a > b && b >= 0) {
+					print_checker_error(c, se->close, "Invalid slice indices: [%td > %td]", a, b);
+				}
+			}
+		}
+
 	} break;
 
 	case AstNode_CastExpression: {

+ 9 - 3
src/checker/statements.cpp

@@ -38,8 +38,14 @@ b32 check_assignable_to(Checker *c, Operand *operand, Type *type) {
 
 	if (sb->kind == Type_Array && tb->kind == Type_Array) {
 		if (are_types_identical(sb->array.element, tb->array.element)) {
-			if (tb->array.count == 0) // NOTE(bill): Not static size
-				return true;
+			return sb->array.count == tb->array.count;
+		}
+	}
+
+	if ((sb->kind == Type_Array || sb->kind == Type_Slice) &&
+	    tb->kind == Type_Slice) {
+		if (are_types_identical(sb->array.element, tb->slice.element)) {
+			return true;
 		}
 	}
 
@@ -233,7 +239,7 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNode *in
 	if (i < lhs_count) {
 		if (lhs[i]->type == NULL)
 			print_checker_error(c, lhs[i]->token, "Too few values on the right hand side of the declaration");
-	} else if (rhs != NULL) {	
+	} else if (rhs != NULL) {
 		print_checker_error(c, ast_node_token(rhs), "Too many values on the right hand side of the declaration");
 	}
 }

+ 27 - 6
src/checker/type.cpp

@@ -55,6 +55,7 @@ enum TypeKind {
 
 	Type_Basic,
 	Type_Array,
+	Type_Slice,
 	Type_Structure,
 	Type_Pointer,
 	Type_Named,
@@ -71,6 +72,9 @@ struct Type {
 			Type *element;
 			i64 count;
 		} array;
+		struct {
+			Type *element;
+		} slice;
 		struct {
 			// Theses are arrays
 			Entity **fields; // Entity_Variable
@@ -111,6 +115,7 @@ void set_base_type(Type *t, Type *base) {
 }
 
 
+// TODO(bill): Remove heap allocation
 Type *alloc_type(TypeKind kind) {
 	Type *t = gb_alloc_item(gb_heap_allocator(), Type);
 	t->kind = kind;
@@ -123,6 +128,7 @@ Type *make_type_basic(BasicType basic) {
 	t->basic = basic;
 	return t;
 }
+
 Type *make_type_array(Type *element, i64 count) {
 	Type *t = alloc_type(Type_Array);
 	t->array.element = element;
@@ -130,6 +136,12 @@ Type *make_type_array(Type *element, i64 count) {
 	return t;
 }
 
+Type *make_type_slice(Type *element) {
+	Type *t = alloc_type(Type_Slice);
+	t->array.element = element;
+	return t;
+}
+
 Type *make_type_structure(void) {
 	Type *t = alloc_type(Type_Structure);
 	return t;
@@ -166,7 +178,7 @@ Type *make_type_procedure(Scope *scope, Type *params, isize params_count, Type *
 
 
 
-#define STR_LIT(x) {cast(u8 *)x, gb_size_of(x)-1}
+#define STR_LIT(x) {cast(u8 *)(x), gb_size_of(x)-1}
 gb_global Type basic_types[] = {
 	{Type_Basic, {Basic_Invalid,        0,                                      STR_LIT("invalid type")}},
 	{Type_Basic, {Basic_bool,           BasicFlag_Boolean,                      STR_LIT("bool")}},
@@ -483,10 +495,11 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
 }
 
 i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) {
-	GB_ASSERT(t->kind == Type_Structure);
-	type_set_offsets(s, allocator, t);
-	if (gb_is_between(index, 0, t->structure.field_count-1)) {
-		return t->structure.offsets[index];
+	if (t->kind == Type_Structure) {
+		type_set_offsets(s, allocator, t);
+		if (gb_is_between(index, 0, t->structure.field_count-1)) {
+			return t->structure.offsets[index];
+		}
 	}
 	return 0;
 }
@@ -501,6 +514,7 @@ gbString write_type_to_string(gbString str, Type *type) {
 	case Type_Basic:
 		str = gb_string_append_length(str, type->basic.name.text, type->basic.name.len);
 		break;
+
 	case Type_Array:
 		if (type->array.count >= 0) {
 			str = gb_string_appendc(str, gb_bprintf("[%td]", type->array.count));
@@ -509,12 +523,18 @@ gbString write_type_to_string(gbString str, Type *type) {
 		}
 		str = write_type_to_string(str, type->array.element);
 		break;
+
+	case Type_Slice:
+		str = gb_string_appendc(str, "[]");
+		str = write_type_to_string(str, type->array.element);
+		break;
+
 	case Type_Structure: {
 		str = gb_string_appendc(str, "struct{");
 		for (isize i = 0; i < type->structure.field_count; i++) {
 			Entity *f = type->structure.fields[i];
 			GB_ASSERT(f->kind == Entity_Variable);
-			if (i < type->structure.field_count-1)
+			if (i > 0)
 				str = gb_string_appendc(str, "; ");
 			str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
 			str = gb_string_appendc(str, ": ");
@@ -564,6 +584,7 @@ gbString write_type_to_string(gbString str, Type *type) {
 		}
 		break;
 	}
+
 	return str;
 }
 

+ 55 - 4
src/parser.cpp

@@ -47,6 +47,7 @@ AstNode__ExpressionBegin,
 	AstNode_CallExpression,
 	AstNode_SelectorExpression,
 	AstNode_IndexExpression,
+	AstNode_SliceExpression,
 	AstNode_CastExpression,
 	AstNode_DereferenceExpression,
 AstNode__ExpressionEnd,
@@ -117,7 +118,7 @@ struct AstNode {
 		struct { Token op; AstNode *left, *right; }                 binary_expression;
 		struct { AstNode *expression; Token open, close; }          paren_expression;
 		struct { Token token; AstNode *operand, *selector; }        selector_expression;
-	struct { AstNode *expression, *value; Token open, close; }  index_expression;
+		struct { AstNode *expression, *value; Token open, close; }  index_expression;
 		struct { Token token; AstNode *type_expression, *operand; } cast_expression;
 		struct {
 			AstNode *proc, *arg_list;
@@ -125,6 +126,12 @@ struct AstNode {
 			Token open, close;
 		} call_expression;
 		struct { Token op; AstNode *operand; } dereference_expression;
+		struct {
+			AstNode *expression;
+			Token open, close;
+			AstNode *low, *high, *max;
+			b32 triple_indexed; // [(1st):2nd:3rd]
+		} slice_expression;
 
 		struct { Token begin, end; }              bad_statement;
 		struct { Token token; }                   empty_statement;
@@ -271,6 +278,8 @@ Token ast_node_token(AstNode *node) {
 		return ast_node_token(node->selector_expression.selector);
 	case AstNode_IndexExpression:
 		return node->index_expression.open;
+	case AstNode_SliceExpression:
+		return node->slice_expression.open;
 	case AstNode_CastExpression:
 		return node->cast_expression.token;
 	case AstNode_DereferenceExpression:
@@ -439,6 +448,19 @@ gb_inline AstNode *make_index_expression(Parser *p, AstNode *expression, AstNode
 	return result;
 }
 
+
+gb_inline AstNode *make_slice_expression(Parser *p, AstNode *expression, Token open, Token close, AstNode *low, AstNode *high, AstNode *max, b32 triple_indexed) {
+	AstNode *result = make_node(p, AstNode_SliceExpression);
+	result->slice_expression.expression = expression;
+	result->slice_expression.open = open;
+	result->slice_expression.close = close;
+	result->slice_expression.low = low;
+	result->slice_expression.high = high;
+	result->slice_expression.max = max;
+	result->slice_expression.triple_indexed = triple_indexed;
+	return result;
+}
+
 gb_inline AstNode *make_cast_expression(Parser *p, Token token, AstNode *type_expression, AstNode *operand) {
 	AstNode *result = make_node(p, AstNode_CastExpression);
 	result->cast_expression.token = token;
@@ -865,14 +887,43 @@ AstNode *parse_atom_expression(Parser *p, b32 lhs) {
 			if (lhs) {
 				// TODO(bill): Handle this
 			}
-			AstNode *value;
 			Token open, close;
+			AstNode *indices[3] = {};
 
 			open = expect_token(p, Token_OpenBracket);
-			value = parse_expression(p, false);
+			if (p->cursor[0].kind != Token_Colon)
+				indices[0] = parse_expression(p, false);
+			isize colon_count = 0;
+			Token colons[2] = {};
+
+			while (p->cursor[0].kind == Token_Colon && colon_count < 2) {
+				colons[colon_count++] = p->cursor[0];
+				next_token(p);
+				if (p->cursor[0].kind != Token_Colon &&
+				    p->cursor[0].kind != Token_CloseBracket &&
+				    p->cursor[0].kind != Token_EOF) {
+					indices[colon_count] = parse_expression(p, false);
+				}
+			}
 			close = expect_token(p, Token_CloseBracket);
 
-			operand = make_index_expression(p, operand, value, open, close);
+			if (colon_count == 0) {
+				operand = make_index_expression(p, operand, indices[0], open, close);
+			} else {
+				b32 triple_indexed = false;
+				if (colon_count == 2) {
+					triple_indexed = true;
+					if (indices[1] == NULL) {
+						print_parse_error(p, colons[0], "Second index is required in a triple indexed slice");
+						indices[1] = make_bad_expression(p, colons[0], colons[1]);
+					}
+					if (indices[2] == NULL) {
+						print_parse_error(p, colons[1], "Third index is required in a triple indexed slice");
+						indices[2] = make_bad_expression(p, colons[1], close);
+					}
+				}
+				operand = make_slice_expression(p, operand, open, close, indices[0], indices[1], indices[2], triple_indexed);
+			}
 		} break;
 
 		case Token_Pointer: // Deference

+ 11 - 2
src/test.odin

@@ -1,9 +1,18 @@
-type float: f32;
+type Vec2: struct {
+	x, y: f32;
+}
+
+
+print_string_array :: proc(args: []string) {
+	args[0] = "";
+}
 
 main :: proc() {
 	thing :: proc(n: int) -> int, f32 {
 		return n*n, 13.37;
 	}
 
-	_, _ := 1, 2;
+	thang :: proc(a: int, b: f32, s: string) {
+	}
+
 }

+ 1 - 1
src/tokenizer.cpp

@@ -195,7 +195,7 @@ char const *TOKEN_STRINGS[] = {
 "_KeywordBegin",
 	"type",
 	"proc",
-	"switch",
+	"match",
 	"break",
 	"continue",
 	"fallthrough",