Browse Source

min, max, abs

Ginger Bill 9 years ago
parent
commit
25e9b9bc87
11 changed files with 335 additions and 55 deletions
  1. 1 1
      examples/basic.odin
  2. 8 15
      examples/demo.odin
  3. 5 5
      examples/file.odin
  4. 3 10
      examples/math.odin
  5. 29 16
      examples/runtime.odin
  6. 8 0
      src/checker/checker.cpp
  7. 160 7
      src/checker/expr.cpp
  8. 31 1
      src/codegen/ssa.cpp
  9. 4 0
      src/gb/gb.h
  10. 78 0
      src/parser.cpp
  11. 8 0
      src/tokenizer.cpp

+ 1 - 1
examples/basic.odin

@@ -3,7 +3,7 @@
 #load "file.odin"
 #load "file.odin"
 
 
 print_string :: proc(s: string) {
 print_string :: proc(s: string) {
-	file_write(file_get_standard(FileStandard.OUTPUT), s as []byte)
+	file_write(file_get_standard(File_Standard.OUTPUT), s as []byte)
 }
 }
 
 
 byte_reverse :: proc(b: []byte) {
 byte_reverse :: proc(b: []byte) {

+ 8 - 15
examples/demo.odin

@@ -4,24 +4,17 @@
 
 
 main :: proc() {
 main :: proc() {
 
 
+	print_int(min(1, 2)); nl()
+	print_int(max(1, 2)); nl()
+	print_int(abs(-1337)); nl()
 
 
-	match x := "1"; x {
-	case "1":
-		print_string("1!\n")
-	case "2":
-		print_string("2!\n")
-		if true {
-			break
-		}
-	case "3", "4":
-		print_string("3 or 4!\n")
-		fallthrough
-	default:
-		print_string("default!\n")
-	}
+	a, b, c := 1, 2, -1337
+	print_int(min(a, b)); nl()
+	print_int(max(a, b)); nl()
+	print_int(abs(c) as int); nl()
 
 
-	nl()
 
 
+	nl()
 /*
 /*
 	Vec3   :: type struct { x, y, z: f32 }
 	Vec3   :: type struct { x, y, z: f32 }
 	Entity :: type struct {
 	Entity :: type struct {

+ 5 - 5
examples/file.odin

@@ -34,7 +34,7 @@ file_write :: proc(f: ^File, buf: []byte) -> bool {
 	return WriteFile(f.handle, ^buf[0], len(buf) as i32, ^bytes_written, null) != 0
 	return WriteFile(f.handle, ^buf[0], len(buf) as i32, ^bytes_written, null) != 0
 }
 }
 
 
-FileStandard :: type enum {
+File_Standard :: type enum {
 	INPUT,
 	INPUT,
 	OUTPUT,
 	OUTPUT,
 	ERROR,
 	ERROR,
@@ -42,12 +42,12 @@ FileStandard :: type enum {
 }
 }
 
 
 __std_file_set := false;
 __std_file_set := false;
-__std_files: [FileStandard.COUNT as int]File;
+__std_files: [File_Standard.COUNT as int]File;
 
 
-file_get_standard :: proc(std: FileStandard) -> ^File {
-	// using FileStandard;
+file_get_standard :: proc(std: File_Standard) -> ^File {
+	// using File_Standard;
 	if (!__std_file_set) {
 	if (!__std_file_set) {
-		using FileStandard
+		using File_Standard
 		__std_files[INPUT] .handle = GetStdHandle(STD_INPUT_HANDLE)
 		__std_files[INPUT] .handle = GetStdHandle(STD_INPUT_HANDLE)
 		__std_files[OUTPUT].handle = GetStdHandle(STD_OUTPUT_HANDLE)
 		__std_files[OUTPUT].handle = GetStdHandle(STD_OUTPUT_HANDLE)
 		__std_files[ERROR] .handle = GetStdHandle(STD_ERROR_HANDLE)
 		__std_files[ERROR] .handle = GetStdHandle(STD_ERROR_HANDLE)

+ 3 - 10
examples/math.odin

@@ -29,17 +29,10 @@ fsqrt    :: proc(x: f32) -> f32 #foreign "llvm.sqrt.f32"
 fsin     :: proc(x: f32) -> f32 #foreign "llvm.sin.f32"
 fsin     :: proc(x: f32) -> f32 #foreign "llvm.sin.f32"
 fcos     :: proc(x: f32) -> f32 #foreign "llvm.cos.f32"
 fcos     :: proc(x: f32) -> f32 #foreign "llvm.cos.f32"
 flerp    :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t }
 flerp    :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t }
-fclamp   :: proc(x, lower, upper: f32) -> f32 { return fmin(fmax(x, lower), upper) }
+fclamp   :: proc(x, lower, upper: f32) -> f32 { return min(max(x, lower), upper) }
 fclamp01 :: proc(x: f32) -> f32 { return fclamp(x, 0, 1) }
 fclamp01 :: proc(x: f32) -> f32 { return fclamp(x, 0, 1) }
-fabs     :: proc(x: f32) -> f32 { if x < 0 { x = -x } return x }
 fsign    :: proc(x: f32) -> f32 { if x >= 0 { return +1 } return -1 }
 fsign    :: proc(x: f32) -> f32 { if x >= 0 { return +1 } return -1 }
 
 
-fmin     :: proc(a, b: f32) -> f32 { if a < b { return a } return b }
-fmax     :: proc(a, b: f32) -> f32 { if a > b { return a } return b }
-fmin3    :: proc(a, b, c: f32) -> f32 { return fmin(fmin(a, b), c) }
-fmax3    :: proc(a, b, c: f32) -> f32 { return fmax(fmax(a, b), c) }
-
-
 copy_sign :: proc(x, y: f32) -> f32 {
 copy_sign :: proc(x, y: f32) -> f32 {
 	ix := x transmute u32
 	ix := x transmute u32
 	iy := y transmute u32
 	iy := y transmute u32
@@ -76,8 +69,8 @@ remainder :: proc(x, y: f32) -> f32 {
 }
 }
 
 
 fmod :: proc(x, y: f32) -> f32 {
 fmod :: proc(x, y: f32) -> f32 {
-	y = fabs(y)
-	result := remainder(fabs(x), y)
+	y = abs(y)
+	result := remainder(abs(x), y)
 	if fsign(result) < 0 {
 	if fsign(result) < 0 {
 		result += y
 		result += y
 	}
 	}

+ 29 - 16
examples/runtime.odin

@@ -11,8 +11,15 @@ heap_free :: proc(ptr: rawptr) {
 }
 }
 
 
 
 
+memory_zero :: proc(data: rawptr, len: int) {
+	d := slice_ptr(data as ^byte, len)
+	for i := 0; i < len; i++ {
+		d[i] = 0
+	}
+}
+
 memory_compare :: proc(dst, src: rawptr, len: int) -> int {
 memory_compare :: proc(dst, src: rawptr, len: int) -> int {
-	s1, s2: ^u8 = dst, src
+	s1, s2: ^byte = dst, src
 	for i := 0; i < len; i++ {
 	for i := 0; i < len; i++ {
 		a := ptr_offset(s1, i)^
 		a := ptr_offset(s1, i)^
 		b := ptr_offset(s2, i)^
 		b := ptr_offset(s2, i)^
@@ -31,7 +38,7 @@ memory_copy :: proc(dst, src: rawptr, n: int) #inline {
 	v128b :: type {4}u32
 	v128b :: type {4}u32
 	static_assert(align_of(v128b) == 16)
 	static_assert(align_of(v128b) == 16)
 
 
-	d, s: ^u8 = dst, src
+	d, s: ^byte = dst, src
 
 
 	for ; s as uint % 16 != 0 && n != 0; n-- {
 	for ; s as uint % 16 != 0 && n != 0; n-- {
 		d^ = s^
 		d^ = s^
@@ -158,7 +165,7 @@ memory_copy :: proc(dst, src: rawptr, n: int) #inline {
 }
 }
 
 
 memory_move :: proc(dst, src: rawptr, n: int) #inline {
 memory_move :: proc(dst, src: rawptr, n: int) #inline {
-	d, s: ^u8 = dst, src
+	d, s: ^byte = dst, src
 	if d == s {
 	if d == s {
 		return
 		return
 	}
 	}
@@ -264,7 +271,7 @@ __string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >
 
 
 
 
 
 
-AllocationMode :: type enum {
+Allocation_Mode :: type enum {
 	ALLOC,
 	ALLOC,
 	DEALLOC,
 	DEALLOC,
 	DEALLOC_ALL,
 	DEALLOC_ALL,
@@ -273,12 +280,12 @@ AllocationMode :: type enum {
 
 
 
 
 
 
-AllocatorProc :: type proc(allocator_data: rawptr, mode: AllocationMode,
-                           size, alignment: int,
-                           old_memory: rawptr, old_size: int, flags: u64) -> rawptr
+Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode,
+                            size, alignment: int,
+                            old_memory: rawptr, old_size: int, flags: u64) -> rawptr
 
 
 Allocator :: type struct {
 Allocator :: type struct {
-	procedure: AllocatorProc;
+	procedure: Allocator_Proc;
 	data:      rawptr
 	data:      rawptr
 }
 }
 
 
@@ -309,18 +316,18 @@ alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_AL
 alloc_align :: proc(size, alignment: int) -> rawptr #inline {
 alloc_align :: proc(size, alignment: int) -> rawptr #inline {
 	__check_context()
 	__check_context()
 	a := context.allocator
 	a := context.allocator
-	return a.procedure(a.data, AllocationMode.ALLOC, size, alignment, null, 0, 0)
+	return a.procedure(a.data, Allocation_Mode.ALLOC, size, alignment, null, 0, 0)
 }
 }
 
 
 dealloc :: proc(ptr: rawptr) #inline {
 dealloc :: proc(ptr: rawptr) #inline {
 	__check_context()
 	__check_context()
 	a := context.allocator
 	a := context.allocator
-	_ = a.procedure(a.data, AllocationMode.DEALLOC, 0, 0, ptr, 0, 0)
+	_ = a.procedure(a.data, Allocation_Mode.DEALLOC, 0, 0, ptr, 0, 0)
 }
 }
 dealloc_all :: proc(ptr: rawptr) #inline {
 dealloc_all :: proc(ptr: rawptr) #inline {
 	__check_context()
 	__check_context()
 	a := context.allocator
 	a := context.allocator
-	_ = a.procedure(a.data, AllocationMode.DEALLOC_ALL, 0, 0, ptr, 0, 0)
+	_ = a.procedure(a.data, Allocation_Mode.DEALLOC_ALL, 0, 0, ptr, 0, 0)
 }
 }
 
 
 
 
@@ -328,7 +335,7 @@ resize       :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { r
 resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline {
 resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline {
 	__check_context()
 	__check_context()
 	a := context.allocator
 	a := context.allocator
-	return a.procedure(a.data, AllocationMode.RESIZE, new_size, alignment, ptr, old_size, 0)
+	return a.procedure(a.data, Allocation_Mode.RESIZE, new_size, alignment, ptr, old_size, 0)
 }
 }
 
 
 
 
@@ -355,19 +362,25 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
 	if new_memory == null {
 	if new_memory == null {
 		return null
 		return null
 	}
 	}
-	_ = copy(slice_ptr(new_memory as ^u8, new_size), slice_ptr(old_memory as ^u8, old_size))
+	min_size := old_size;
+	if min_size > new_size {
+		min_size = new_size;
+	}
+	memory_copy(new_memory, old_memory, min_size);
 	dealloc(old_memory)
 	dealloc(old_memory)
 	return new_memory
 	return new_memory
 }
 }
 
 
 
 
-__default_allocator_proc :: proc(allocator_data: rawptr, mode: AllocationMode,
+__default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode,
                                  size, alignment: int,
                                  size, alignment: int,
                                  old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
                                  old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
-	using AllocationMode
+	using Allocation_Mode
 	match mode {
 	match mode {
 	case ALLOC:
 	case ALLOC:
-		return heap_alloc(size)
+		data := heap_alloc(size)
+		memory_zero(data, size)
+		return data
 	case RESIZE:
 	case RESIZE:
 		return default_resize_align(old_memory, old_size, size, alignment)
 		return default_resize_align(old_memory, old_size, size, alignment)
 	case DEALLOC:
 	case DEALLOC:

+ 8 - 0
src/checker/checker.cpp

@@ -145,6 +145,10 @@ enum BuiltinProcId {
 	BuiltinProc_ptr_sub,
 	BuiltinProc_ptr_sub,
 	BuiltinProc_slice_ptr,
 	BuiltinProc_slice_ptr,
 
 
+	BuiltinProc_min,
+	BuiltinProc_max,
+	BuiltinProc_abs,
+
 	BuiltinProc_Count,
 	BuiltinProc_Count,
 };
 };
 struct BuiltinProc {
 struct BuiltinProc {
@@ -178,6 +182,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
 	{STR_LIT("ptr_offset"),       2, false, Expr_Expr},
 	{STR_LIT("ptr_offset"),       2, false, Expr_Expr},
 	{STR_LIT("ptr_sub"),          2, false, Expr_Expr},
 	{STR_LIT("ptr_sub"),          2, false, Expr_Expr},
 	{STR_LIT("slice_ptr"),        2, true,  Expr_Expr},
 	{STR_LIT("slice_ptr"),        2, true,  Expr_Expr},
+
+	{STR_LIT("min"),              2, false, Expr_Expr},
+	{STR_LIT("max"),              2, false, Expr_Expr},
+	{STR_LIT("abs"),              1, false, Expr_Expr},
 };
 };
 
 
 struct CheckerContext {
 struct CheckerContext {

+ 160 - 7
src/checker/expr.cpp

@@ -878,10 +878,11 @@ b32 check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exac
 	} else if (is_type_string(type)) {
 	} else if (is_type_string(type)) {
 		return in_value.kind == ExactValue_String;
 		return in_value.kind == ExactValue_String;
 	} else if (is_type_integer(type)) {
 	} else if (is_type_integer(type)) {
-		if (in_value.kind != ExactValue_Integer)
+		ExactValue v = exact_value_to_integer(in_value);
+		if (v.kind != ExactValue_Integer)
 			return false;
 			return false;
-		if (out_value) *out_value = in_value;
-		i64 i = in_value.value_integer;
+		if (out_value) *out_value = v;
+		i64 i = v.value_integer;
 		i64 s = 8*type_size_of(c->sizes, c->allocator, type);
 		i64 s = 8*type_size_of(c->sizes, c->allocator, type);
 		u64 umax = ~0ull;
 		u64 umax = ~0ull;
 		if (s < 64) {
 		if (s < 64) {
@@ -1282,7 +1283,6 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 
 
 		Type *base_type = get_base_type(type);
 		Type *base_type = get_base_type(type);
 		if (is_const_expr && is_type_constant_type(base_type)) {
 		if (is_const_expr && is_type_constant_type(base_type)) {
-
 			if (base_type->kind == Type_Basic) {
 			if (base_type->kind == Type_Basic) {
 				if (check_value_is_expressible(c, x->value, base_type, &x->value)) {
 				if (check_value_is_expressible(c, x->value, base_type, &x->value)) {
 					can_convert = true;
 					can_convert = true;
@@ -1295,10 +1295,12 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 
 
 		if (!can_convert) {
 		if (!can_convert) {
 			gbString expr_str = expr_to_string(x->expr);
 			gbString expr_str = expr_to_string(x->expr);
-			gbString type_str = type_to_string(type);
+			gbString to_type  = type_to_string(type);
+			gbString from_type = type_to_string(x->type);
 			defer (gb_string_free(expr_str));
 			defer (gb_string_free(expr_str));
-			defer (gb_string_free(type_str));
-			error(&c->error_collector, ast_node_token(x->expr), "Cannot cast `%s` as `%s`", expr_str, type_str);
+			defer (gb_string_free(to_type));
+			defer (gb_string_free(from_type));
+			error(&c->error_collector, ast_node_token(x->expr), "Cannot cast `%s` as `%s` from `%s`", expr_str, to_type, from_type);
 
 
 			x->mode = Addressing_Invalid;
 			x->mode = Addressing_Invalid;
 			return;
 			return;
@@ -2342,6 +2344,157 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		operand->mode = Addressing_Value;
 		operand->mode = Addressing_Value;
 	} break;
 	} break;
 
 
+	case BuiltinProc_min: {
+		// min :: proc(a, b: comparable) -> comparable
+		Type *type = get_base_type(operand->type);
+		if (!is_type_comparable(type) || !is_type_numeric(type)) {
+			gbString type_str = type_to_string(operand->type);
+			defer (gb_string_free(type_str));
+			error(&c->error_collector, ast_node_token(call),
+			      "Expected a comparable numeric type to `min`, got `%s`",
+			      type_str);
+			return false;
+		}
+
+		AstNode *other_arg = ce->arg_list->next;
+		Operand a = *operand;
+		Operand b = {};
+		check_expr(c, &b, other_arg);
+		if (b.mode == Addressing_Invalid)
+			return false;
+		if (!is_type_comparable(b.type) || !is_type_numeric(type)) {
+			gbString type_str = type_to_string(b.type);
+			defer (gb_string_free(type_str));
+			error(&c->error_collector, ast_node_token(call),
+			      "Expected a comparable numeric type to `min`, got `%s`",
+			      type_str);
+			return false;
+		}
+
+
+		if (a.mode == Addressing_Constant &&
+		    b.mode == Addressing_Constant) {
+			ExactValue x = a.value;
+			ExactValue y = b.value;
+			Token lt = {Token_Lt};
+
+			operand->mode = Addressing_Constant;
+			if (compare_exact_values(lt, x, y)) {
+				operand->value = x;
+				operand->type = a.type;
+			} else {
+				operand->value = y;
+				operand->type = b.type;
+			}
+		} else {
+			operand->mode = Addressing_Value;
+			operand->type = type;
+
+			if (!are_types_identical(operand->type, b.type)) {
+				gbString type_a = type_to_string(a.type);
+				gbString type_b = type_to_string(b.type);
+				defer (gb_string_free(type_a));
+				defer (gb_string_free(type_b));
+				error(&c->error_collector, ast_node_token(call),
+				      "Mismatched types to `min`, `%s` vs `%s`",
+				      type_a, type_b);
+				return false;
+			}
+		}
+
+	} break;
+
+	case BuiltinProc_max: {
+		// min :: proc(a, b: comparable) -> comparable
+		Type *type = get_base_type(operand->type);
+		if (!is_type_comparable(type) || !is_type_numeric(type)) {
+			gbString type_str = type_to_string(operand->type);
+			defer (gb_string_free(type_str));
+			error(&c->error_collector, ast_node_token(call),
+			      "Expected a comparable numeric type to `max`, got `%s`",
+			      type_str);
+			return false;
+		}
+
+		AstNode *other_arg = ce->arg_list->next;
+		Operand a = *operand;
+		Operand b = {};
+		check_expr(c, &b, other_arg);
+		if (b.mode == Addressing_Invalid)
+			return false;
+		if (!is_type_comparable(b.type) || !is_type_numeric(type)) {
+			gbString type_str = type_to_string(b.type);
+			defer (gb_string_free(type_str));
+			error(&c->error_collector, ast_node_token(call),
+			      "Expected a comparable numeric type to `max`, got `%s`",
+			      type_str);
+			return false;
+		}
+
+
+		if (a.mode == Addressing_Constant &&
+		    b.mode == Addressing_Constant) {
+			ExactValue x = a.value;
+			ExactValue y = b.value;
+			Token gt = {Token_Gt};
+
+			operand->mode = Addressing_Constant;
+			if (compare_exact_values(gt, x, y)) {
+				operand->value = x;
+				operand->type = a.type;
+			} else {
+				operand->value = y;
+				operand->type = b.type;
+			}
+		} else {
+			operand->mode = Addressing_Value;
+			operand->type = type;
+
+			if (!are_types_identical(operand->type, b.type)) {
+				gbString type_a = type_to_string(a.type);
+				gbString type_b = type_to_string(b.type);
+				defer (gb_string_free(type_a));
+				defer (gb_string_free(type_b));
+				error(&c->error_collector, ast_node_token(call),
+				      "Mismatched types to `max`, `%s` vs `%s`",
+				      type_a, type_b);
+				return false;
+			}
+		}
+
+	} break;
+
+	case BuiltinProc_abs: {
+		// abs :: proc(n: numeric) -> numeric
+		Type *type = get_base_type(operand->type);
+		if (!is_type_numeric(type)) {
+			gbString type_str = type_to_string(operand->type);
+			defer (gb_string_free(type_str));
+			error(&c->error_collector, ast_node_token(call),
+			      "Expected a numeric type to `abs`, got `%s`",
+			      type_str);
+			return false;
+		}
+
+		if (operand->mode == Addressing_Constant) {
+			switch (operand->value.kind) {
+			case ExactValue_Integer:
+				operand->value.value_integer = gb_abs(operand->value.value_integer);
+				break;
+			case ExactValue_Float:
+				operand->value.value_float = gb_abs(operand->value.value_float);
+				break;
+			default:
+				GB_PANIC("Invalid numeric constant");
+				break;
+			}
+		} else {
+			operand->mode = Addressing_Value;
+		}
+
+		operand->type = type;
+	} break;
+
 	}
 	}
 
 
 	return true;
 	return true;

+ 31 - 1
src/codegen/ssa.cpp

@@ -1569,7 +1569,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 	case_ast_node(i, Ident, expr);
 	case_ast_node(i, Ident, expr);
 		Entity *e = *map_get(&proc->module->info->uses, hash_pointer(expr));
 		Entity *e = *map_get(&proc->module->info->uses, hash_pointer(expr));
 		if (e->kind == Entity_Builtin) {
 		if (e->kind == Entity_Builtin) {
-			GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin");
+			GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin `%.*s`", LIT(builtin_procs[e->Builtin.id].name));
 			return NULL;
 			return NULL;
 		}
 		}
 
 
@@ -2068,6 +2068,36 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_two32,  t_int),         cap);
 					ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_two32,  t_int),         cap);
 					return ssa_emit_load(proc, slice);
 					return ssa_emit_load(proc, slice);
 				} break;
 				} break;
+
+				case BuiltinProc_min: {
+					ssaValue *x = ssa_build_expr(proc, ce->arg_list);
+					ssaValue *y = ssa_build_expr(proc, ce->arg_list->next);
+					Type *t = get_base_type(ssa_type(x));
+					Token lt = {Token_Lt};
+					ssaValue *cond = ssa_emit_comp(proc, lt, x, y);
+					return ssa_emit_select(proc, cond, x, y);
+				} break;
+
+				case BuiltinProc_max: {
+					ssaValue *x = ssa_build_expr(proc, ce->arg_list);
+					ssaValue *y = ssa_build_expr(proc, ce->arg_list->next);
+					Type *t = get_base_type(ssa_type(x));
+					Token gt = {Token_Gt};
+					ssaValue *cond = ssa_emit_comp(proc, gt, x, y);
+					return ssa_emit_select(proc, cond, x, y);
+				} break;
+
+				case BuiltinProc_abs: {
+					Token lt = {Token_Lt};
+					Token sub = {Token_Sub};
+
+					ssaValue *x = ssa_build_expr(proc, ce->arg_list);
+					Type *t = ssa_type(x);
+
+					ssaValue *neg_x = ssa_emit_arith(proc, sub, v_zero, x, t);
+					ssaValue *cond = ssa_emit_comp(proc, lt, x, v_zero);
+					return ssa_emit_select(proc, cond, neg_x, x);
+				} break;
 				}
 				}
 			}
 			}
 		}
 		}

+ 4 - 0
src/gb/gb.h

@@ -702,6 +702,10 @@ extern "C++" {
 #define gb_is_between(x, lower, upper) (((x) >= (lower)) && ((x) <= (upper)))
 #define gb_is_between(x, lower, upper) (((x) >= (lower)) && ((x) <= (upper)))
 #endif
 #endif
 
 
+#ifndef gb_abs
+#define gb_abs(x) ((x) < 0 ? -(x) : (x))
+#endif
+
 /* NOTE(bill): Very useful bit setting */
 /* NOTE(bill): Very useful bit setting */
 #ifndef GB_MASK_SET
 #ifndef GB_MASK_SET
 #define GB_MASK_SET(var, set, mask) do { \
 #define GB_MASK_SET(var, set, mask) do { \

+ 78 - 0
src/parser.cpp

@@ -164,6 +164,20 @@ AST_NODE_KIND(_ComplexStmtBegin, "", struct{}) \
 	AST_NODE_KIND(DeferStmt,  "defer statement",  struct { Token token; AstNode *stmt; }) \
 	AST_NODE_KIND(DeferStmt,  "defer statement",  struct { Token token; AstNode *stmt; }) \
 	AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
 	AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
 	AST_NODE_KIND(UsingStmt,  "using statement",  struct { Token token; AstNode *node; }) \
 	AST_NODE_KIND(UsingStmt,  "using statement",  struct { Token token; AstNode *node; }) \
+	AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
+		Token string; \
+		AstNode *operand; \
+	}) \
+	AST_NODE_KIND(AsmStmt,    "assembly statement", struct { \
+		Token token; \
+		b32 is_volatile; \
+		Token open, close; \
+		Token code_string; \
+		AstNode *output_list; \
+		AstNode *input_list; \
+		AstNode *clobber_list; \
+		isize output_count, input_count, clobber_count; \
+	}) \
 \
 \
 AST_NODE_KIND(_ComplexStmtEnd, "", struct{}) \
 AST_NODE_KIND(_ComplexStmtEnd, "", struct{}) \
 AST_NODE_KIND(_StmtEnd,        "", struct{}) \
 AST_NODE_KIND(_StmtEnd,        "", struct{}) \
@@ -348,6 +362,8 @@ Token ast_node_token(AstNode *node) {
 		return node->BranchStmt.token;
 		return node->BranchStmt.token;
 	case AstNode_UsingStmt:
 	case AstNode_UsingStmt:
 		return node->UsingStmt.token;
 		return node->UsingStmt.token;
+	case AstNode_AsmStmt:
+		return node->AsmStmt.token;
 	case AstNode_BadDecl:
 	case AstNode_BadDecl:
 		return node->BadDecl.begin;
 		return node->BadDecl.begin;
 	case AstNode_VarDecl:
 	case AstNode_VarDecl:
@@ -684,6 +700,35 @@ gb_inline AstNode *make_using_stmt(AstFile *f, Token token, AstNode *node) {
 	return result;
 	return result;
 }
 }
 
 
+gb_inline AstNode *make_asm_operand(AstFile *f, Token string, AstNode *operand) {
+	AstNode *result = make_node(f, AstNode_AsmOperand);
+	result->AsmOperand.string  = string;
+	result->AsmOperand.operand = operand;
+	return result;
+
+}
+
+gb_inline AstNode *make_asm_stmt(AstFile *f, Token token, b32 is_volatile, Token open, Token close, Token code_string,
+                                 AstNode *output_list, AstNode *input_list, AstNode *clobber_list,
+                                 isize output_count, isize input_count, isize clobber_count) {
+	AstNode *result = make_node(f, AstNode_AsmStmt);
+	result->AsmStmt.token = token;
+	result->AsmStmt.is_volatile = is_volatile;
+	result->AsmStmt.open  = open;
+	result->AsmStmt.close = close;
+	result->AsmStmt.code_string = code_string;
+	result->AsmStmt.output_list = output_list;
+	result->AsmStmt.input_list = input_list;
+	result->AsmStmt.clobber_list = clobber_list;
+	result->AsmStmt.output_count = output_count;
+	result->AsmStmt.input_count = input_count;
+	result->AsmStmt.clobber_count = clobber_count;
+	return result;
+}
+
+
+
+
 
 
 gb_inline AstNode *make_bad_decl(AstFile *f, Token begin, Token end) {
 gb_inline AstNode *make_bad_decl(AstFile *f, Token begin, Token end) {
 	AstNode *result = make_node(f, AstNode_BadDecl);
 	AstNode *result = make_node(f, AstNode_BadDecl);
@@ -2219,6 +2264,37 @@ AstNode *parse_defer_stmt(AstFile *f) {
 	return make_defer_stmt(f, token, statement);
 	return make_defer_stmt(f, token, statement);
 }
 }
 
 
+AstNode *parse_asm_stmt(AstFile *f) {
+	Token token = expect_token(f, Token_asm);
+	b32 is_volatile = false;
+	if (allow_token(f, Token_volatile)) {
+		is_volatile = true;
+	}
+	Token open, close, code_string;
+	open = expect_token(f, Token_OpenBrace);
+	code_string = expect_token(f, Token_String);
+	AstNode *output_list = NULL;
+	AstNode *input_list = NULL;
+	AstNode *clobber_list = NULL;
+	isize output_count = 0;
+	isize input_count = 0;
+	isize clobber_count = 0;
+
+	// TODO(bill): Finish asm statement and determine syntax
+
+	// if (f->cursor[0].kind != Token_CloseBrace) {
+		// expect_token(f, Token_Colon);
+	// }
+
+	close = expect_token(f, Token_CloseBrace);
+
+	return make_asm_stmt(f, token, is_volatile, open, close, code_string,
+	                     output_list, input_list, clobber_list,
+	                     output_count, input_count, clobber_count);
+
+}
+
+
 
 
 AstNode *parse_stmt(AstFile *f) {
 AstNode *parse_stmt(AstFile *f) {
 	AstNode *s = NULL;
 	AstNode *s = NULL;
@@ -2246,6 +2322,7 @@ AstNode *parse_stmt(AstFile *f) {
 	case Token_for:    return parse_for_stmt(f);
 	case Token_for:    return parse_for_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_break:
 	case Token_break:
 	case Token_continue:
 	case Token_continue:
@@ -2255,6 +2332,7 @@ AstNode *parse_stmt(AstFile *f) {
 		expect_semicolon_after_stmt(f, s);
 		expect_semicolon_after_stmt(f, s);
 		return s;
 		return s;
 
 
+
 	case Token_using: {
 	case Token_using: {
 		AstNode *node = NULL;
 		AstNode *node = NULL;
 
 

+ 8 - 0
src/tokenizer.cpp

@@ -78,6 +78,7 @@ TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \
 	TOKEN_KIND(Token_Period, "."), \
 	TOKEN_KIND(Token_Period, "."), \
 	TOKEN_KIND(Token_Comma, ","), \
 	TOKEN_KIND(Token_Comma, ","), \
 	TOKEN_KIND(Token_Ellipsis, ".."), \
 	TOKEN_KIND(Token_Ellipsis, ".."), \
+	TOKEN_KIND(Token_RangeExclusive, "..<"), \
 TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
 TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
 \
 \
 TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
@@ -93,6 +94,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_if,          "if"), \
 	TOKEN_KIND(Token_if,          "if"), \
 	TOKEN_KIND(Token_else,        "else"), \
 	TOKEN_KIND(Token_else,        "else"), \
 	TOKEN_KIND(Token_for,         "for"), \
 	TOKEN_KIND(Token_for,         "for"), \
+	TOKEN_KIND(Token_range,       "range"), \
 	TOKEN_KIND(Token_defer,       "defer"), \
 	TOKEN_KIND(Token_defer,       "defer"), \
 	TOKEN_KIND(Token_return,      "return"), \
 	TOKEN_KIND(Token_return,      "return"), \
 	TOKEN_KIND(Token_struct,      "struct"), \
 	TOKEN_KIND(Token_struct,      "struct"), \
@@ -100,6 +102,8 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_raw_union,   "raw_union"), \
 	TOKEN_KIND(Token_raw_union,   "raw_union"), \
 	TOKEN_KIND(Token_enum,        "enum"), \
 	TOKEN_KIND(Token_enum,        "enum"), \
 	TOKEN_KIND(Token_using,       "using"), \
 	TOKEN_KIND(Token_using,       "using"), \
+	TOKEN_KIND(Token_asm,         "asm"), \
+	TOKEN_KIND(Token_volatile,    "volatile"), \
 TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
 TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
 	TOKEN_KIND(Token_Count, "")
 	TOKEN_KIND(Token_Count, "")
 
 
@@ -711,6 +715,10 @@ Token tokenizer_get_token(Tokenizer *t) {
 			} else if (t->curr_rune == '.') { // Could be an ellipsis
 			} else if (t->curr_rune == '.') { // Could be an ellipsis
 				advance_to_next_rune(t);
 				advance_to_next_rune(t);
 				token.kind = Token_Ellipsis;
 				token.kind = Token_Ellipsis;
+				if (t->curr_rune == '<') {
+					advance_to_next_rune(t);
+					token.kind = Token_RangeExclusive;
+				}
 			}
 			}
 			break;
 			break;