Browse Source

Basic command line flags: e.g. `-opt=0`

Ginger Bill 8 years ago
parent
commit
1d81b73df9
8 changed files with 314 additions and 90 deletions
  1. 1 1
      build.bat
  2. 36 10
      src/build_settings.cpp
  3. 37 16
      src/check_expr.cpp
  4. 1 0
      src/common.cpp
  5. 4 4
      src/gb/gb.h
  6. 221 55
      src/main.cpp
  7. 7 4
      src/parser.cpp
  8. 7 0
      src/string.cpp

+ 1 - 1
build.bat

@@ -44,7 +44,7 @@ del *.ilk > NUL 2> NUL
 
 
 cl %compiler_settings% "src\main.cpp" ^
 cl %compiler_settings% "src\main.cpp" ^
 	/link %linker_settings% -OUT:%exe_name% ^
 	/link %linker_settings% -OUT:%exe_name% ^
-	&& odin run code/demo.odin
+	&& odin run code/demo.odin -opt=0
 	rem && odin docs core/fmt.odin
 	rem && odin docs core/fmt.odin
 
 
 del *.obj > NUL 2> NUL
 del *.obj > NUL 2> NUL

+ 36 - 10
src/build_settings.cpp

@@ -12,10 +12,12 @@ struct BuildContext {
 	i64    word_size; // Size of a pointer, must be >= 4
 	i64    word_size; // Size of a pointer, must be >= 4
 	i64    max_align; // max alignment, must be >= 1 (and typically >= word_size)
 	i64    max_align; // max alignment, must be >= 1 (and typically >= word_size)
 
 
+	String opt_flags;
 	String llc_flags;
 	String llc_flags;
 	String link_flags;
 	String link_flags;
 	bool   is_dll;
 	bool   is_dll;
 	bool   generate_docs;
 	bool   generate_docs;
+	i32    optimization_level;
 };
 };
 
 
 
 
@@ -23,6 +25,8 @@ gb_global BuildContext build_context = {0};
 
 
 
 
 
 
+
+
 // TODO(bill): OS dependent versions for the BuildContext
 // TODO(bill): OS dependent versions for the BuildContext
 // join_path
 // join_path
 // is_dir
 // is_dir
@@ -272,22 +276,23 @@ void init_build_context(void) {
 
 
 #if defined(GB_SYSTEM_WINDOWS)
 #if defined(GB_SYSTEM_WINDOWS)
 	bc->ODIN_OS      = str_lit("windows");
 	bc->ODIN_OS      = str_lit("windows");
-	#if defined(GB_ARCH_64_BIT)
-		bc->ODIN_ARCH = str_lit("amd64");
-	#else
-		bc->ODIN_ARCH = str_lit("x86");
-	#endif
-	bc->ODIN_ENDIAN  = str_lit("little");
 #elif defined(GB_SYSTEM_OSX)
 #elif defined(GB_SYSTEM_OSX)
 	bc->ODIN_OS      = str_lit("osx");
 	bc->ODIN_OS      = str_lit("osx");
-	bc->ODIN_ARCH    = str_lit("amd64");
-	bc->ODIN_ENDIAN  = str_lit("little");
 #else
 #else
 	bc->ODIN_OS      = str_lit("linux");
 	bc->ODIN_OS      = str_lit("linux");
-	bc->ODIN_ARCH    = str_lit("amd64");
-	bc->ODIN_ENDIAN  = str_lit("little");
 #endif
 #endif
 
 
+#if defined(GB_ARCH_64_BIT)
+	bc->ODIN_ARCH = str_lit("amd64");
+#else
+	bc->ODIN_ARCH = str_lit("x86");
+#endif
+
+	{
+		u16 x = 1;
+		bool big = !(*cast(u8 *)&x);
+		bc->ODIN_ENDIAN = big ? str_lit("big") : str_lit("little");
+	}
 
 
 
 
 	// NOTE(zangent): The linker flags to set the build architecture are different
 	// NOTE(zangent): The linker flags to set the build architecture are different
@@ -317,9 +322,11 @@ void init_build_context(void) {
 		#define LINK_FLAG_X86 "-arch x86"
 		#define LINK_FLAG_X86 "-arch x86"
 	#endif
 	#endif
 
 
+
 	if (bc->ODIN_ARCH == "amd64") {
 	if (bc->ODIN_ARCH == "amd64") {
 		bc->word_size = 8;
 		bc->word_size = 8;
 		bc->max_align = 16;
 		bc->max_align = 16;
+
 		bc->llc_flags = str_lit("-march=x86-64 ");
 		bc->llc_flags = str_lit("-march=x86-64 ");
 		bc->link_flags = str_lit(LINK_FLAG_X64 " ");
 		bc->link_flags = str_lit(LINK_FLAG_X64 " ");
 	} else if (bc->ODIN_ARCH == "x86") {
 	} else if (bc->ODIN_ARCH == "x86") {
@@ -327,8 +334,27 @@ void init_build_context(void) {
 		bc->max_align = 8;
 		bc->max_align = 8;
 		bc->llc_flags = str_lit("-march=x86 ");
 		bc->llc_flags = str_lit("-march=x86 ");
 		bc->link_flags = str_lit(LINK_FLAG_X86 " ");
 		bc->link_flags = str_lit(LINK_FLAG_X86 " ");
+	} else {
+		gb_printf_err("This current architecture is not supported");
+		gb_exit(1);
 	}
 	}
 
 
+
+	isize opt_max = 1023;
+	char *opt_flags_string = gb_alloc_array(heap_allocator(), char, opt_max+1);
+	isize opt_len = 0;
+	bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
+	if (bc->optimization_level != 0) {
+		opt_len = gb_snprintf(opt_flags_string, opt_max, "-O%d", bc->optimization_level);
+	} else {
+		opt_len = gb_snprintf(opt_flags_string, opt_max, "");
+	}
+	if (opt_len > 0) {
+		opt_len--;
+	}
+	bc->opt_flags = make_string(cast(u8 *)opt_flags_string, opt_len);
+
+
 	#undef LINK_FLAG_X64
 	#undef LINK_FLAG_X64
 	#undef LINK_FLAG_X86
 	#undef LINK_FLAG_X86
 }
 }

+ 37 - 16
src/check_expr.cpp

@@ -4840,6 +4840,7 @@ enum CallArgumentError {
 	CallArgumentError_ParameterNotFound,
 	CallArgumentError_ParameterNotFound,
 	CallArgumentError_ParameterMissing,
 	CallArgumentError_ParameterMissing,
 	CallArgumentError_DuplicateParameter,
 	CallArgumentError_DuplicateParameter,
+	CallArgumentError_GenericProcedureNotSupported,
 };
 };
 
 
 enum CallArgumentErrorMode {
 enum CallArgumentErrorMode {
@@ -5010,6 +5011,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 			} else if (o.mode != Addressing_Type) {
 			} else if (o.mode != Addressing_Type) {
 				error(o.expr, "Expected a type for the argument");
 				error(o.expr, "Expected a type for the argument");
 			}
 			}
+
+			score += assign_score_function(1);
 			continue;
 			continue;
 		}
 		}
 		if (variadic) {
 		if (variadic) {
@@ -5150,7 +5153,7 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 			} else if (o->mode != Addressing_Type) {
 			} else if (o->mode != Addressing_Type) {
 				error(o->expr, "Expected a type for the argument");
 				error(o->expr, "Expected a type for the argument");
 			}
 			}
-			score += 1;
+			score += assign_score_function(1);
 		} else {
 		} else {
 			i64 s = 0;
 			i64 s = 0;
 			if (!check_is_assignable_to_with_score(c, o, e->type, &s)) {
 			if (!check_is_assignable_to_with_score(c, o, e->type, &s)) {
@@ -5197,6 +5200,13 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 	}
 	}
 #endif
 #endif
 
 
+	if (pt->is_generic) {
+		if (show_error) {
+			error(call, "Generic procedures do not yet support named arguments");
+		}
+		err = CallArgumentError_GenericProcedureNotSupported;
+	}
+
 	if (score_) *score_ = score;
 	if (score_) *score_ = score;
 
 
 	return err;
 	return err;
@@ -5210,6 +5220,8 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
 	Array<Operand> operands = {};
 	Array<Operand> operands = {};
 	defer (array_free(&operands));
 	defer (array_free(&operands));
 
 
+	Type *result_type = t_invalid;
+
 	if (is_call_expr_field_value(ce)) {
 	if (is_call_expr_field_value(ce)) {
 		call_checker = check_named_call_arguments;
 		call_checker = check_named_call_arguments;
 
 
@@ -5252,10 +5264,10 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
 
 
 		for (isize i = 0; i < overload_count; i++) {
 		for (isize i = 0; i < overload_count; i++) {
 			Entity *p = procs[i];
 			Entity *p = procs[i];
-			Type *proc_type = base_type(p->type);
-			if (proc_type != NULL && is_type_proc(proc_type)) {
+			Type *pt = base_type(p->type);
+			if (pt != NULL && is_type_proc(pt)) {
 				i64 score = 0;
 				i64 score = 0;
-				CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_NoErrors, &score);
+				CallArgumentError err = call_checker(c, call, pt, operands, CallArgumentMode_NoErrors, &score);
 				if (err == CallArgumentError_None) {
 				if (err == CallArgumentError_None) {
 					valids[valid_count].index = i;
 					valids[valid_count].index = i;
 					valids[valid_count].score = score;
 					valids[valid_count].score = score;
@@ -5279,7 +5291,7 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
 
 
 		if (valid_count == 0) {
 		if (valid_count == 0) {
 			error(operand->expr, "No overloads for `%.*s` that match with the given arguments", LIT(name));
 			error(operand->expr, "No overloads for `%.*s` that match with the given arguments", LIT(name));
-			proc_type = t_invalid;
+			result_type = t_invalid;
 		} else if (valid_count > 1) {
 		} else if (valid_count > 1) {
 			error(operand->expr, "Ambiguous procedure call `%.*s`, could be:", LIT(name));
 			error(operand->expr, "Ambiguous procedure call `%.*s`, could be:", LIT(name));
 			for (isize i = 0; i < valid_count; i++) {
 			for (isize i = 0; i < valid_count; i++) {
@@ -5289,7 +5301,7 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
 				gb_printf_err("\t%.*s of type %s at %.*s(%td:%td) with score %lld\n", LIT(name), pt, LIT(pos.file), pos.line, pos.column, cast(long long)valids[i].score);
 				gb_printf_err("\t%.*s of type %s at %.*s(%td:%td) with score %lld\n", LIT(name), pt, LIT(pos.file), pos.line, pos.column, cast(long long)valids[i].score);
 				gb_string_free(pt);
 				gb_string_free(pt);
 			}
 			}
-			proc_type = t_invalid;
+			result_type = t_invalid;
 		} else {
 		} else {
 			AstNode *expr = operand->expr;
 			AstNode *expr = operand->expr;
 			while (expr->kind == AstNode_SelectorExpr) {
 			while (expr->kind == AstNode_SelectorExpr) {
@@ -5301,14 +5313,20 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
 			proc_type = e->type;
 			proc_type = e->type;
 			i64 score = 0;
 			i64 score = 0;
 			CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score);
 			CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score);
+
+			if (proc_type != NULL && is_type_proc(proc_type)) {
+				result_type = base_type(proc_type)->Proc.results;
+			}
 		}
 		}
 	} else {
 	} else {
 		i64 score = 0;
 		i64 score = 0;
 		CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score);
 		CallArgumentError err = call_checker(c, call, proc_type, operands, CallArgumentMode_ShowErrors, &score);
+		if (proc_type != NULL && is_type_proc(proc_type)) {
+			result_type = base_type(proc_type)->Proc.results;
+		}
 	}
 	}
 
 
-
-	return proc_type;
+	return result_type;
 }
 }
 
 
 
 
@@ -5443,36 +5461,39 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 		}
 		}
 	}
 	}
 
 
-	proc_type = check_call_arguments(c, operand, proc_type, call);
+	Type *result_type = check_call_arguments(c, operand, proc_type, call);
 
 
 	gb_zero_item(operand);
 	gb_zero_item(operand);
+	operand->expr = call;
 
 
-	Type *pt = base_type(proc_type);
-	if (pt == NULL || !is_type_proc(pt)) {
+	if (result_type == t_invalid) {
 		operand->mode = Addressing_Invalid;
 		operand->mode = Addressing_Invalid;
 		operand->type = t_invalid;
 		operand->type = t_invalid;
-		operand->expr = call;
 		return Expr_Stmt;
 		return Expr_Stmt;
 	}
 	}
 
 
+	Type *pt = base_type(proc_type);
 	bool results_are_generic = false;
 	bool results_are_generic = false;
-	if (pt->Proc.results != NULL) {
+	if (is_type_proc(pt) && pt->Proc.results != NULL) {
 		results_are_generic = is_type_generic(pt->Proc.results);
 		results_are_generic = is_type_generic(pt->Proc.results);
 	}
 	}
 	if (results_are_generic) {
 	if (results_are_generic) {
 		operand->mode = Addressing_NoValue;
 		operand->mode = Addressing_NoValue;
+	} else if (result_type == NULL) {
+		operand->mode = Addressing_NoValue;
 	} else {
 	} else {
-		switch (pt->Proc.result_count) {
+		GB_ASSERT(is_type_tuple(result_type));
+		switch (result_type->Tuple.variable_count) {
 		case 0:
 		case 0:
 			operand->mode = Addressing_NoValue;
 			operand->mode = Addressing_NoValue;
 			break;
 			break;
 		case 1:
 		case 1:
 			operand->mode = Addressing_Value;
 			operand->mode = Addressing_Value;
-			operand->type = pt->Proc.results->Tuple.variables[0]->type;
+			operand->type = result_type->Tuple.variables[0]->type;
 			break;
 			break;
 		default:
 		default:
 			operand->mode = Addressing_Value;
 			operand->mode = Addressing_Value;
-			operand->type = pt->Proc.results;
+			operand->type = result_type;
 			break;
 			break;
 		}
 		}
 	}
 	}

+ 1 - 0
src/common.cpp

@@ -12,6 +12,7 @@
 
 
 #include <math.h>
 #include <math.h>
 
 
+
 gbAllocator heap_allocator(void) {
 gbAllocator heap_allocator(void) {
 	return gb_heap_allocator();
 	return gb_heap_allocator();
 }
 }

+ 4 - 4
src/gb/gb.h

@@ -164,11 +164,11 @@ extern "C" {
 #endif
 #endif
 
 
 
 
-#ifndef GB_EDIAN_ORDER
-#define GB_EDIAN_ORDER
+#ifndef GB_ENDIAN_ORDER
+#define GB_ENDIAN_ORDER
 	// TODO(bill): Is the a good way or is it better to test for certain compilers and macros?
 	// TODO(bill): Is the a good way or is it better to test for certain compilers and macros?
-	#define GB_IS_BIG_EDIAN    (!*(u8*)&(u16){1})
-	#define GB_IS_LITTLE_EDIAN (!GB_IS_BIG_EDIAN)
+	#define GB_IS_BIG_ENDIAN    (!*(u8*)&(u16){1})
+	#define GB_IS_LITTLE_ENDIAN (!GB_IS_BIG_ENDIAN)
 #endif
 #endif
 
 
 #if defined(_WIN32) || defined(_WIN64)
 #if defined(_WIN32) || defined(_WIN64)

+ 221 - 55
src/main.cpp

@@ -33,7 +33,9 @@ i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
 	va_start(va, fmt);
 	va_start(va, fmt);
 	cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
 	cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
 	va_end(va);
 	va_end(va);
-	// gb_printf("%.*s\n", cast(int)cmd_len, cmd_line);
+	if (!is_silent) {
+		// gb_printf("%.*s\n", cast(int)cmd_len, cmd_line);
+	}
 
 
 	tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
 	tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
 
 
@@ -118,13 +120,19 @@ Array<String> setup_args(int argc, char **argv) {
 		wchar_t *warg = wargv[i];
 		wchar_t *warg = wargv[i];
 		isize wlen = string16_len(warg);
 		isize wlen = string16_len(warg);
 		String16 wstr = make_string16(warg, wlen);
 		String16 wstr = make_string16(warg, wlen);
-		array_add(&args, string16_to_string(a, wstr));
+		String arg = string16_to_string(a, wstr);
+		if (arg.len > 0) {
+			array_add(&args, arg);
+		}
 	}
 	}
 
 
 #else
 #else
 	array_init(&args, a, argc);
 	array_init(&args, a, argc);
 	for (i = 0; i < argc; i++) {
 	for (i = 0; i < argc; i++) {
-		array_add(&args, make_string_c(argv[i]));
+		String arg = make_string_c(argv[i]);
+		if (arg.len > 0) {
+			array_add(&args, arg);
+		}
 	}
 	}
 #endif
 #endif
 	return args;
 	return args;
@@ -132,6 +140,8 @@ Array<String> setup_args(int argc, char **argv) {
 
 
 
 
 
 
+
+
 void print_usage_line(i32 indent, char *fmt, ...) {
 void print_usage_line(i32 indent, char *fmt, ...) {
 	while (indent --> 0) {
 	while (indent --> 0) {
 		gb_printf_err("\t");
 		gb_printf_err("\t");
@@ -155,6 +165,163 @@ void usage(String argv0) {
 	print_usage_line(1, "version      print version");
 	print_usage_line(1, "version      print version");
 }
 }
 
 
+
+
+enum BuildFlagKind {
+	BuildFlag_Invalid,
+
+	BuildFlag_OptimizationLevel,
+
+	BuildFlag_COUNT,
+};
+
+enum BuildFlagParamKind {
+	BuildFlagParam_None,
+
+	BuildFlagParam_Boolean,
+	BuildFlagParam_Integer,
+	BuildFlagParam_Float,
+	BuildFlagParam_String,
+
+	BuildFlagParam_COUNT,
+};
+
+struct BuildFlag {
+	BuildFlagKind      kind;
+	String             name;
+	BuildFlagParamKind param_kind;
+};
+
+
+void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) {
+	BuildFlag flag = {kind, name, param_kind};
+	array_add(build_flags, flag);
+}
+
+bool parse_build_flags(Array<String> args) {
+	Array<BuildFlag> build_flags = {};
+	array_init(&build_flags, heap_allocator(), BuildFlag_COUNT);
+	add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer);
+
+	Array<String> flag_args = args;
+	flag_args.data  += 3;
+	flag_args.count -= 3;
+
+	bool set_flags[BuildFlag_COUNT] = {};
+
+	bool bad_flags = false;
+	for_array(i, flag_args) {
+		String flag = flag_args[i];
+		if (flag[0] != '-') {
+			gb_printf_err("Invalid flag: %.*s\n", LIT(flag));
+		} else {
+			String name = substring(flag, 1, flag.len);
+			isize end = 0;
+			for (; end < name.len; end++) {
+				if (name[end] == '=') {
+					break;
+				}
+			}
+			name.len = end;
+			String param = substring(flag, 2+end, flag.len);
+
+			bool found = false;
+			for_array(build_flag_index, build_flags) {
+				BuildFlag bf = build_flags[build_flag_index];
+				if (bf.name == name) {
+					found = true;
+					if (set_flags[bf.kind]) {
+						gb_printf_err("Previous flag set: `%.*s`\n", LIT(name));
+						bad_flags = true;
+					} else {
+						ExactValue value = {};
+						bool ok = false;
+						if (bf.param_kind == BuildFlagParam_None) {
+							if (param.len == 0) {
+								ok = true;
+							} else {
+								gb_printf_err("Flag `%.*s` was not expecting a parameter `%.*s`\n", LIT(name), LIT(param));
+								bad_flags = true;
+							}
+						} else {
+							if (param.len == 0) {
+								gb_printf_err("Flag missing for `%.*s`\n", LIT(name));
+								bad_flags = true;
+							} else {
+								ok = true;
+								switch (bf.param_kind) {
+								default: ok = false; break;
+								case BuildFlagParam_Boolean: {
+									if (param == "t") {
+										value = exact_value_bool(true);
+									} else if (param == "T") {
+										value = exact_value_bool(true);
+									} else if (param == "true") {
+										value = exact_value_bool(true);
+									} else if (param == "TRUE") {
+										value = exact_value_bool(true);
+									} else if (param == "1") {
+										value = exact_value_bool(true);
+									} else if (param == "f") {
+										value = exact_value_bool(false);
+									} else if (param == "F") {
+										value = exact_value_bool(false);
+									} else if (param == "false") {
+										value = exact_value_bool(false);
+									} else if (param == "FALSE") {
+										value = exact_value_bool(false);
+									} else if (param == "0") {
+										value = exact_value_bool(false);
+									} else {
+										gb_printf_err("Invalid flag parameter for `%.*s` = `%.*s`\n", LIT(name), LIT(param));
+									}
+								} break;
+								case BuildFlagParam_Integer:
+									value = exact_value_integer_from_string(param);
+									break;
+								case BuildFlagParam_Float:
+									value = exact_value_float_from_string(param);
+									break;
+								case BuildFlagParam_String:
+									value = exact_value_string(param);
+									break;
+								}
+							}
+
+						}
+						if (ok) {
+							switch (bf.kind) {
+							case BuildFlag_OptimizationLevel:
+								if (value.kind == ExactValue_Integer) {
+									build_context.optimization_level = cast(i32)i128_to_i64(value.value_integer);
+								} else {
+									gb_printf_err("%.*s expected an integer, got %.*s", LIT(name), LIT(param));
+									bad_flags = true;
+									ok = false;
+								}
+								break;
+							}
+						}
+
+
+						set_flags[bf.kind] = ok;
+					}
+					break;
+				}
+			}
+			if (!found) {
+				gb_printf_err("Unknown flag: `%.*s`\n", LIT(name));
+				bad_flags = true;
+			}
+		}
+	}
+
+	return !bad_flags;
+}
+
+
+
+
 int main(int arg_count, char **arg_ptr) {
 int main(int arg_count, char **arg_ptr) {
 	if (arg_count < 2) {
 	if (arg_count < 2) {
 		usage(make_string_c(arg_ptr[0]));
 		usage(make_string_c(arg_ptr[0]));
@@ -172,39 +339,31 @@ int main(int arg_count, char **arg_ptr) {
 
 
 
 
 #if 1
 #if 1
-	init_build_context();
-
-	if (build_context.word_size == 4) {
-		print_usage_line(0, "%s 32-bit is not yet supported", args[0]);
-		return 1;
-	}
-
-	init_universal_scope();
 
 
 	String init_filename = {};
 	String init_filename = {};
 	bool run_output = false;
 	bool run_output = false;
 	if (args[1] == "run") {
 	if (args[1] == "run") {
-		if (args.count != 3) {
+		if (args.count < 3) {
 			usage(args[0]);
 			usage(args[0]);
 			return 1;
 			return 1;
 		}
 		}
 		init_filename = args[2];
 		init_filename = args[2];
 		run_output = true;
 		run_output = true;
 	} else if (args[1] == "build_dll") {
 	} else if (args[1] == "build_dll") {
-		if (args.count != 3) {
+		if (args.count < 3) {
 			usage(args[0]);
 			usage(args[0]);
 			return 1;
 			return 1;
 		}
 		}
 		init_filename = args[2];
 		init_filename = args[2];
 		build_context.is_dll = true;
 		build_context.is_dll = true;
 	} else if (args[1] == "build") {
 	} else if (args[1] == "build") {
-		if (args.count != 3) {
+		if (args.count < 3) {
 			usage(args[0]);
 			usage(args[0]);
 			return 1;
 			return 1;
 		}
 		}
 		init_filename = args[2];
 		init_filename = args[2];
 	} else if (args[1] == "docs") {
 	} else if (args[1] == "docs") {
-		if (args.count != 3) {
+		if (args.count < 3) {
 			usage(args[0]);
 			usage(args[0]);
 			return 1;
 			return 1;
 		}
 		}
@@ -223,6 +382,18 @@ int main(int arg_count, char **arg_ptr) {
 		return 1;
 		return 1;
 	}
 	}
 
 
+	if (!parse_build_flags(args)) {
+		return 1;
+	}
+
+
+	init_build_context();
+	if (build_context.word_size == 4) {
+		print_usage_line(0, "%s 32-bit is not yet supported", args[0]);
+		return 1;
+	}
+	init_universal_scope();
+
 	// TODO(bill): prevent compiling without a linker
 	// TODO(bill): prevent compiling without a linker
 
 
 	timings_start_section(&timings, str_lit("parse files"));
 	timings_start_section(&timings, str_lit("parse files"));
@@ -291,49 +462,44 @@ int main(int arg_count, char **arg_ptr) {
 	String output_base = ir_gen.output_base;
 	String output_base = ir_gen.output_base;
 	int base_name_len = output_base.len;
 	int base_name_len = output_base.len;
 
 
-	i32 optimization_level = 0;
-	optimization_level = gb_clamp(optimization_level, 0, 3);
+	build_context.optimization_level = gb_clamp(build_context.optimization_level, 0, 3);
 
 
 	i32 exit_code = 0;
 	i32 exit_code = 0;
 
 
 	#if defined(GB_SYSTEM_WINDOWS)
 	#if defined(GB_SYSTEM_WINDOWS)
-	// For more passes arguments: http://llvm.org/docs/Passes.html
-	exit_code = system_exec_command_line_app("llvm-opt", false,
-		"\"%.*sbin/opt\" \"%.*s\".ll -o \"%.*s\".bc "
-		"-mem2reg "
-		"-memcpyopt "
-		"-die "
-		// "-dse "
-		// "-dce "
-		// "-S "
-		"",
-		LIT(build_context.ODIN_ROOT),
-		LIT(output_base), LIT(output_base));
-	if (exit_code != 0) {
-		return exit_code;
-	}
+		// For more passes arguments: http://llvm.org/docs/Passes.html
+		exit_code = system_exec_command_line_app("llvm-opt", false,
+			"\"%.*sbin/opt\" \"%.*s\".ll -o \"%.*s\".bc %.*s "
+			"-mem2reg "
+			"-memcpyopt "
+			"-die "
+			"",
+			LIT(build_context.ODIN_ROOT),
+			LIT(output_base), LIT(output_base),
+			LIT(build_context.opt_flags));
+		if (exit_code != 0) {
+			return exit_code;
+		}
 	#else
 	#else
-	// NOTE(zangent): This is separate because it seems that LLVM tools are packaged
-	//   with the Windows version, while they will be system-provided on MacOS and GNU/Linux
-	exit_code = system_exec_command_line_app("llvm-opt", false,
-		"opt \"%.*s\".ll -o \"%.*s\".bc "
-		"-mem2reg "
-		"-memcpyopt "
-		"-die "
-		#if defined(GB_SYSTEM_OSX)
-			// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
-			// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
-			//       make sure to also change the `macosx_version_min` param passed to `llc`
-			"-mtriple=x86_64-apple-macosx10.8 "
-		#endif
-		// "-dse "
-		// "-dce "
-		// "-S "
-		"",
-		LIT(output_base), LIT(output_base));
-	if (exit_code != 0) {
-		return exit_code;
-	}
+		// NOTE(zangent): This is separate because it seems that LLVM tools are packaged
+		//   with the Windows version, while they will be system-provided on MacOS and GNU/Linux
+		exit_code = system_exec_command_line_app("llvm-opt", false,
+			"opt \"%.*s\".ll -o \"%.*s\".bc %.*s "
+			"-mem2reg "
+			"-memcpyopt "
+			"-die "
+			#if defined(GB_SYSTEM_OSX)
+				// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
+				// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
+				//       make sure to also change the `macosx_version_min` param passed to `llc`
+				"-mtriple=x86_64-apple-macosx10.8 "
+			#endif
+			"",
+			LIT(output_base), LIT(output_base),
+			LIT(build_context.opt_flags));
+		if (exit_code != 0) {
+			return exit_code;
+		}
 	#endif
 	#endif
 
 
 	#if defined(GB_SYSTEM_WINDOWS)
 	#if defined(GB_SYSTEM_WINDOWS)
@@ -346,7 +512,7 @@ int main(int arg_count, char **arg_ptr) {
 			"",
 			"",
 			LIT(build_context.ODIN_ROOT),
 			LIT(build_context.ODIN_ROOT),
 			LIT(output_base),
 			LIT(output_base),
-			optimization_level,
+			build_context.optimization_level,
 			LIT(build_context.llc_flags));
 			LIT(build_context.llc_flags));
 		if (exit_code != 0) {
 		if (exit_code != 0) {
 			return exit_code;
 			return exit_code;
@@ -408,7 +574,7 @@ int main(int arg_count, char **arg_ptr) {
 			// "-debug-pass=Arguments "
 			// "-debug-pass=Arguments "
 			"",
 			"",
 			LIT(output_base),
 			LIT(output_base),
-			optimization_level,
+			build_context.optimization_level,
 			LIT(build_context.llc_flags));
 			LIT(build_context.llc_flags));
 		if (exit_code != 0) {
 		if (exit_code != 0) {
 			return exit_code;
 			return exit_code;

+ 7 - 4
src/parser.cpp

@@ -475,7 +475,7 @@ String const ast_node_strings[] = {
 
 
 struct AstNode {
 struct AstNode {
 	AstNodeKind kind;
 	AstNodeKind kind;
-	u32 stmt_state_flags;
+	u32         stmt_state_flags;
 	union {
 	union {
 #define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_;
 #define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_;
 	AST_NODE_KINDS
 	AST_NODE_KINDS
@@ -629,9 +629,12 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
 	gb_memmove(n, node, gb_size_of(AstNode));
 	gb_memmove(n, node, gb_size_of(AstNode));
 
 
 	switch (n->kind) {
 	switch (n->kind) {
-	case AstNode_Ident: break;
-	case AstNode_Implicit: break;
-	case AstNode_BasicLit: break;
+	default: GB_PANIC("Unhandled AstNode %.*s", LIT(ast_node_strings[n->kind])); break;
+
+	case AstNode_Invalid:        break;
+	case AstNode_Ident:          break;
+	case AstNode_Implicit:       break;
+	case AstNode_BasicLit:       break;
 	case AstNode_BasicDirective: break;
 	case AstNode_BasicDirective: break;
 	case AstNode_Ellipsis:
 	case AstNode_Ellipsis:
 		n->Ellipsis.expr = clone_ast_node(a, n->Ellipsis.expr);
 		n->Ellipsis.expr = clone_ast_node(a, n->Ellipsis.expr);

+ 7 - 0
src/string.cpp

@@ -80,6 +80,13 @@ gb_inline String make_string_c(char *text) {
 	return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
 	return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
 }
 }
 
 
+String substring(String s, isize lo, isize hi) {
+	isize max = s.len;
+	GB_ASSERT(lo <= hi && hi <= max);
+
+	return make_string(s.text+lo, hi-lo);
+}
+