Browse Source

Allow for either .odin file or directory as the initial start

gingerBill 7 years ago
parent
commit
df578d6ec5
10 changed files with 40 additions and 1495 deletions
  1. 4 11
      core/c/c.odin
  2. 0 753
      examples/demo.odin
  3. 0 20
      examples/factorial.odin
  4. 0 221
      examples/game.odin
  5. 0 5
      examples/hello.odin
  6. 0 479
      examples/punity.odin
  7. 2 2
      src/checker.cpp
  8. 23 3
      src/parser.cpp
  9. 1 1
      src/parser.hpp
  10. 10 0
      src/string.cpp

+ 4 - 11
core/c/c.odin

@@ -1,5 +1,7 @@
 package c
 
+import "core:os"
+
 CHAR_BIT :: 8;
 
 c_bool   :: bool;
@@ -12,17 +14,8 @@ c_ushort :: u16;
 c_int    :: i32;
 c_uint   :: u32;
 
-when ODIN_OS == "windows" || size_of(rawptr) == 4 {
-	c_long :: i32;
-} else {
-	c_long :: i64;
-}
-
-when ODIN_OS == "windows" || size_of(rawptr) == 4 {
-	c_ulong :: u32;
-} else {
-	c_ulong :: u64;
-}
+c_long  :: (os.OS == "windows" || size_of(rawptr) == 4) ? i32 : i64;
+c_ulong :: (os.OS == "windows" || size_of(rawptr) == 4) ? u32 : u64;
 
 c_longlong       :: i64;
 c_ulonglong      :: u64;

+ 0 - 753
examples/demo.odin

@@ -1,753 +0,0 @@
-import "core:fmt.odin"
-import "core:strconv.odin"
-import "core:mem.odin"
-import "core:bits.odin"
-import "core:hash.odin"
-import "core:math.odin"
-import "core:math/rand.odin"
-import "core:os.odin"
-import "core:raw.odin"
-import "core:sort.odin"
-import "core:strings.odin"
-import "core:types.odin"
-import "core:utf16.odin"
-import "core:utf8.odin"
-
-// File scope `when` statements
-when ODIN_OS == "windows" {
-	import "core:atomics.odin"
-	import "core:thread.odin"
-	import win32 "core:sys/windows.odin"
-}
-
-@(link_name="general_stuff")
-general_stuff :: proc() {
-	fmt.println("# general_stuff");
-	{ // `do` for inline statements rather than block
-		foo :: proc() do fmt.println("Foo!");
-		if   false do foo();
-		for  false do foo();
-		when false do foo();
-
-		if false do foo();
-		else     do foo();
-	}
-
-	{ // Removal of `++` and `--` (again)
-		x: int;
-		x += 1;
-		x -= 1;
-	}
-	{ // Casting syntaxes
-		i := i32(137);
-		ptr := &i;
-
-		_ = (^f32)(ptr);
-		// ^f32(ptr) == ^(f32(ptr))
-		_ = cast(^f32)ptr;
-
-		_ = (^f32)(ptr)^;
-		_ = (cast(^f32)ptr)^;
-
-		// Questions: Should there be two ways to do it?
-	}
-
-	/*
-	 * Remove *_val_of built-in procedures
-	 * size_of, align_of, offset_of
-	 * type_of, type_info_of
-	 */
-
-	{ // `expand_to_tuple` built-in procedure
-		Foo :: struct {
-			x: int,
-			b: bool,
-		}
-		f := Foo{137, true};
-		x, b := expand_to_tuple(f);
-		fmt.println(f);
-		fmt.println(x, b);
-		fmt.println(expand_to_tuple(f));
-	}
-
-	{
-		// ..  half-closed range
-		// ... open range
-
-		for in 0..2  {} // 0, 1
-		for in 0...2 {} // 0, 1, 2
-	}
-
-	{ // Multiple sized booleans
-
-		x0: bool; // default
-		x1: b8  = true;
-		x2: b16 = false;
-		x3: b32 = true;
-		x4: b64 = false;
-
-		fmt.printf("x1: %T = %v;\n", x1, x1);
-		fmt.printf("x2: %T = %v;\n", x2, x2);
-		fmt.printf("x3: %T = %v;\n", x3, x3);
-		fmt.printf("x4: %T = %v;\n", x4, x4);
-
-		// Having specific sized booleans is very useful when dealing with foreign code
-		// and to enforce specific alignment for a boolean, especially within a struct
-	}
-
-	{ // `distinct` types
-		// Originally, all type declarations would create a distinct type unless #type_alias was present.
-		// Now the behaviour has been reversed. All type declarations create a type alias unless `distinct` is present.
-		// If the type expression is `struct`, `union`, `enum`, `proc`, or `bit_field`, the types will always been distinct.
-
-		Int32 :: i32;
-		#assert(Int32 == i32);
-
-		My_Int32 :: distinct i32;
-		#assert(My_Int32 != i32);
-
-		My_Struct :: struct{x: int};
-		#assert(My_Struct != struct{x: int});
-	}
-}
-
-
-union_type :: proc() {
-	fmt.println("\n# union_type");
-	{
-		val: union{int, bool};
-		val = 137;
-		if i, ok := val.(int); ok {
-			fmt.println(i);
-		}
-		val = true;
-		fmt.println(val);
-
-		val = nil;
-
-		switch v in val {
-		case int:  fmt.println("int",  v);
-		case bool: fmt.println("bool", v);
-		case:      fmt.println("nil");
-		}
-	}
-	{
-		// There is a duality between `any` and `union`
-		// An `any` has a pointer to the data and allows for any type (open)
-		// A `union` has as binary blob to store the data and allows only certain types (closed)
-		// The following code is with `any` but has the same syntax
-		val: any;
-		val = 137;
-		if i, ok := val.(int); ok {
-			fmt.println(i);
-		}
-		val = true;
-		fmt.println(val);
-
-		val = nil;
-
-		switch v in val {
-		case int:  fmt.println("int",  v);
-		case bool: fmt.println("bool", v);
-		case:      fmt.println("nil");
-		}
-	}
-
-	Vector3 :: struct {x, y, z: f32};
-	Quaternion :: struct {x, y, z, w: f32};
-
-	// More realistic examples
-	{
-		// NOTE(bill): For the above basic examples, you may not have any
-		// particular use for it. However, my main use for them is not for these
-		// simple cases. My main use is for hierarchical types. Many prefer
-		// subtyping, embedding the base data into the derived types. Below is
-		// an example of this for a basic game Entity.
-
-		Entity :: struct {
-			id:          u64,
-			name:        string,
-			position:    Vector3,
-			orientation: Quaternion,
-
-			derived: any,
-		}
-
-		Frog :: struct {
-			using entity: Entity,
-			jump_height:  f32,
-		}
-
-		Monster :: struct {
-			using entity: Entity,
-			is_robot:     bool,
-			is_zombie:    bool,
-		}
-
-		// See `parametric_polymorphism` procedure for details
-		new_entity :: proc(T: type) -> ^Entity {
-			t := new(T);
-			t.derived = t^;
-			return t;
-		}
-
-		entity := new_entity(Monster);
-
-		switch e in entity.derived {
-		case Frog:
-			fmt.println("Ribbit");
-		case Monster:
-			if e.is_robot  do fmt.println("Robotic");
-			if e.is_zombie do fmt.println("Grrrr!");
-		}
-	}
-
-	{
-		// NOTE(bill): A union can be used to achieve something similar. Instead
-		// of embedding the base data into the derived types, the derived data
-		// in embedded into the base type. Below is the same example of the
-		// basic game Entity but using an union.
-
-		Entity :: struct {
-			id:          u64,
-			name:        string,
-			position:    Vector3,
-			orientation: Quaternion,
-
-			derived: union {Frog, Monster},
-		}
-
-		Frog :: struct {
-			using entity: ^Entity,
-			jump_height:  f32,
-		}
-
-		Monster :: struct {
-			using entity: ^Entity,
-			is_robot:     bool,
-			is_zombie:    bool,
-		}
-
-		// See `parametric_polymorphism` procedure for details
-		new_entity :: proc(T: type) -> ^Entity {
-			t := new(Entity);
-			t.derived = T{entity = t};
-			return t;
-		}
-
-		entity := new_entity(Monster);
-
-		switch e in entity.derived {
-		case Frog:
-			fmt.println("Ribbit");
-		case Monster:
-			if e.is_robot  do fmt.println("Robotic");
-			if e.is_zombie do fmt.println("Grrrr!");
-		}
-
-		// NOTE(bill): As you can see, the usage code has not changed, only its
-		// memory layout. Both approaches have their own advantages but they can
-		// be used together to achieve different results. The subtyping approach
-		// can allow for a greater control of the memory layout and memory
-		// allocation, e.g. storing the derivatives together. However, this is
-		// also its disadvantage. You must either preallocate arrays for each
-		// derivative separation (which can be easily missed) or preallocate a
-		// bunch of "raw" memory; determining the maximum size of the derived
-		// types would require the aid of metaprogramming. Unions solve this
-		// particular problem as the data is stored with the base data.
-		// Therefore, it is possible to preallocate, e.g. [100]Entity.
-
-		// It should be noted that the union approach can have the same memory
-		// layout as the any and with the same type restrictions by using a
-		// pointer type for the derivatives.
-
-		/*
-			Entity :: struct {
-				...
-				derived: union{^Frog, ^Monster},
-			}
-
-			Frog :: struct {
-				using entity: Entity,
-				...
-			}
-			Monster :: struct {
-				using entity: Entity,
-				...
-
-			}
-			new_entity :: proc(T: type) -> ^Entity {
-				t := new(T);
-				t.derived = t;
-				return t;
-			}
-		*/
-	}
-}
-
-parametric_polymorphism :: proc() {
-	fmt.println("# parametric_polymorphism");
-
-	print_value :: proc(value: $T) {
-		fmt.printf("print_value: %T %v\n", value, value);
-	}
-
-	v1: int    = 1;
-	v2: f32    = 2.1;
-	v3: f64    = 3.14;
-	v4: string = "message";
-
-	print_value(v1);
-	print_value(v2);
-	print_value(v3);
-	print_value(v4);
-
-	fmt.println();
-
-	add :: proc(p, q: $T) -> T {
-		x: T = p + q;
-		return x;
-	}
-
-	a := add(3, 4);
-	fmt.printf("a: %T = %v\n", a, a);
-
-	b := add(3.2, 4.3);
-	fmt.printf("b: %T = %v\n", b, b);
-
-	// This is how `new` is implemented
-	alloc_type :: proc(T: type) -> ^T {
-		t := cast(^T)alloc(size_of(T), align_of(T));
-		t^ = T{}; // Use default initialization value
-		return t;
-	}
-
-	copy_slice :: proc(dst, src: []$T) -> int {
-		n := min(len(dst), len(src));
-		if n > 0 {
-			mem.copy(&dst[0], &src[0], n*size_of(T));
-		}
-		return n;
-	}
-
-	double_params :: proc(a: $A, b: $B) -> A {
-		return a + A(b);
-	}
-
-	fmt.println(double_params(12, 1.345));
-
-
-
-	{ // Polymorphic Types and Type Specialization
-		Table_Slot :: struct(Key, Value: type) {
-			occupied: bool,
-			hash:     u32,
-			key:      Key,
-			value:    Value,
-		}
-		TABLE_SIZE_MIN :: 32;
-		Table :: struct(Key, Value: type) {
-			count:     int,
-			allocator: Allocator,
-			slots:     []Table_Slot(Key, Value),
-		}
-
-		// Only allow types that are specializations of a (polymorphic) slice
-		make_slice :: proc(T: type/[]$E, len: int) -> T {
-			return make(T, len);
-		}
-
-
-		// Only allow types that are specializations of `Table`
-		allocate :: proc(table: ^$T/Table, capacity: int) {
-			c := context;
-			if table.allocator.procedure != nil do c.allocator = table.allocator;
-
-			context <- c {
-				table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN));
-			}
-		}
-
-		expand :: proc(table: ^$T/Table) {
-			c := context;
-			if table.allocator.procedure != nil do c.allocator = table.allocator;
-
-			context <- c {
-				old_slots := table.slots;
-
-				cap := max(2*len(table.slots), TABLE_SIZE_MIN);
-				allocate(table, cap);
-
-				for s in old_slots do if s.occupied {
-					put(table, s.key, s.value);
-				}
-
-				free(old_slots);
-			}
-		}
-
-		// Polymorphic determination of a polymorphic struct
-		// put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
-		put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) {
-			hash := get_hash(key); // Ad-hoc method which would fail in a different scope
-			index := find_index(table, key, hash);
-			if index < 0 {
-				if f64(table.count) >= 0.75*f64(len(table.slots)) {
-					expand(table);
-				}
-				assert(table.count <= len(table.slots));
-
-				hash := get_hash(key);
-				index = int(hash % u32(len(table.slots)));
-
-				for table.slots[index].occupied {
-					if index += 1; index >= len(table.slots) {
-						index = 0;
-					}
-				}
-
-				table.count += 1;
-			}
-
-			slot := &table.slots[index];
-			slot.occupied = true;
-			slot.hash     = hash;
-			slot.key      = key;
-			slot.value    = value;
-		}
-
-
-		// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
-		find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
-			hash := get_hash(key);
-			index := find_index(table, key, hash);
-			if index < 0 {
-				return Value{}, false;
-			}
-			return table.slots[index].value, true;
-		}
-
-		find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
-			if len(table.slots) <= 0 do return -1;
-
-			index := int(hash % u32(len(table.slots)));
-			for table.slots[index].occupied {
-				if table.slots[index].hash == hash {
-					if table.slots[index].key == key {
-						return index;
-					}
-				}
-
-				if index += 1; index >= len(table.slots) {
-					index = 0;
-				}
-			}
-
-			return -1;
-		}
-
-		get_hash :: proc(s: string) -> u32 { // fnv32a
-			h: u32 = 0x811c9dc5;
-			for i in 0..len(s) {
-				h = (h ~ u32(s[i])) * 0x01000193;
-			}
-			return h;
-		}
-
-
-		table: Table(string, int);
-
-		for i in 0..36 do put(&table, "Hellope", i);
-		for i in 0..42 do put(&table, "World!",  i);
-
-		found, _ := find(&table, "Hellope");
-		fmt.printf("`found` is %v\n", found);
-
-		found, _ = find(&table, "World!");
-		fmt.printf("`found` is %v\n", found);
-
-		// I would not personally design a hash table like this in production
-		// but this is a nice basic example
-		// A better approach would either use a `u64` or equivalent for the key
-		// and let the user specify the hashing function or make the user store
-		// the hashing procedure with the table
-	}
-}
-
-
-
-
-prefix_table := [?]string{
-	"White",
-	"Red",
-	"Green",
-	"Blue",
-	"Octarine",
-	"Black",
-};
-
-threading_example :: proc() {
-	when ODIN_OS == "windows" {
-		fmt.println("# threading_example");
-
-		unordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) {
-			__bounds_check_error_loc(loc, index, len(array));
-			array[index] = array[len(array)-1];
-			pop(array);
-		}
-		ordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) {
-			__bounds_check_error_loc(loc, index, len(array));
-			copy(array[index..], array[index+1..]);
-			pop(array);
-		}
-
-		worker_proc :: proc(t: ^thread.Thread) -> int {
-			for iteration in 1...5 {
-				fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
-				fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
-				// win32.sleep(1);
-			}
-			return 0;
-		}
-
-		threads := make([dynamic]^thread.Thread, 0, len(prefix_table));
-		defer free(threads);
-
-		for in prefix_table {
-			if t := thread.create(worker_proc); t != nil {
-				t.init_context = context;
-				t.use_init_context = true;
-				t.user_index = len(threads);
-				append(&threads, t);
-				thread.start(t);
-			}
-		}
-
-		for len(threads) > 0 {
-			for i := 0; i < len(threads); /**/ {
-				if t := threads[i]; thread.is_done(t) {
-					fmt.printf("Thread %d is done\n", t.user_index);
-					thread.destroy(t);
-
-					ordered_remove(&threads, i);
-				} else {
-					i += 1;
-				}
-			}
-		}
-	}
-}
-
-array_programming :: proc() {
-	fmt.println("# array_programming");
-	{
-		a := [3]f32{1, 2, 3};
-		b := [3]f32{5, 6, 7};
-		c := a * b;
-		d := a + b;
-		e := 1 +  (c - d) / 2;
-		fmt.printf("%.1f\n", e); // [0.5, 3.0, 6.5]
-	}
-
-	{
-		a := [3]f32{1, 2, 3};
-		b := swizzle(a, 2, 1, 0);
-		assert(b == [3]f32{3, 2, 1});
-
-		c := swizzle(a, 0, 0);
-		assert(c == [2]f32{1, 1});
-		assert(c == 1);
-	}
-
-	{
-		Vector3 :: distinct [3]f32;
-		a := Vector3{1, 2, 3};
-		b := Vector3{5, 6, 7};
-		c := (a * b)/2 + 1;
-		d := c.x + c.y + c.z;
-		fmt.printf("%.1f\n", d); // 22.0
-
-		cross :: proc(a, b: Vector3) -> Vector3 {
-			i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1);
-			j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0);
-			return i - j;
-		}
-
-		blah :: proc(a: Vector3) -> f32 {
-			return a.x + a.y + a.z;
-		}
-
-		x := cross(a, b);
-		fmt.println(x);
-		fmt.println(blah(x));
-	}
-}
-
-
-using println in import "core:fmt.odin"
-
-using_in :: proc() {
-	fmt.println("# using in");
-	using print in fmt;
-
-	println("Hellope1");
-	print("Hellope2\n");
-
-	Foo :: struct {
-		x, y: int,
-		b: bool,
-	}
-	f: Foo;
-	f.x, f.y = 123, 321;
-	println(f);
-	using x, y in f;
-	x, y = 456, 654;
-	println(f);
-}
-
-named_proc_return_parameters :: proc() {
-	fmt.println("# named proc return parameters");
-
-	foo0 :: proc() -> int {
-		return 123;
-	}
-	foo1 :: proc() -> (a: int) {
-		a = 123;
-		return;
-	}
-	foo2 :: proc() -> (a, b: int) {
-		// Named return values act like variables within the scope
-		a = 321;
-		b = 567;
-		return b, a;
-	}
-	fmt.println("foo0 =", foo0()); // 123
-	fmt.println("foo1 =", foo1()); // 123
-	fmt.println("foo2 =", foo2()); // 567 321
-}
-
-
-enum_export :: proc() {
-	fmt.println("# enum #export");
-
-	Foo :: enum #export {A, B, C};
-
-	f0 := A;
-	f1 := B;
-	f2 := C;
-	fmt.println(f0, f1, f2);
-}
-
-explicit_procedure_overloading :: proc() {
-	fmt.println("# explicit procedure overloading");
-
-	add_ints :: proc(a, b: int) -> int {
-		x := a + b;
-		fmt.println("add_ints", x);
-		return x;
-	}
-	add_floats :: proc(a, b: f32) -> f32 {
-		x := a + b;
-		fmt.println("add_floats", x);
-		return x;
-	}
-	add_numbers :: proc(a: int, b: f32, c: u8) -> int {
-		x := int(a) + int(b) + int(c);
-		fmt.println("add_numbers", x);
-		return x;
-	}
-
-	add :: proc[add_ints, add_floats, add_numbers];
-
-	add(int(1), int(2));
-	add(f32(1), f32(2));
-	add(int(1), f32(2), u8(3));
-
-	add(1, 2);     // untyped ints coerce to int tighter than f32
-	add(1.0, 2.0); // untyped floats coerce to f32 tighter than int
-	add(1, 2, 3);  // three parameters
-
-	// Ambiguous answers
-	// add(1.0, 2);
-	// add(1, 2.0);
-}
-
-complete_switch :: proc() {
-	fmt.println("# complete_switch");
-	{ // enum
-		Foo :: enum #export {
-			A,
-			B,
-			C,
-			D,
-		}
-
-		b := Foo.B;
-		f := Foo.A;
-		#complete switch f {
-		case A: fmt.println("A");
-		case B: fmt.println("B");
-		case C: fmt.println("C");
-		case D: fmt.println("D");
-		case:   fmt.println("?");
-		}
-	}
-	{ // union
-		Foo :: union {int, bool};
-		f: Foo = 123;
-		#complete switch in f {
-		case int:  fmt.println("int");
-		case bool: fmt.println("bool");
-		case:
-		}
-	}
-}
-
-
-cstring_example :: proc() {
-	W :: "Hellope";
-	X :: cstring(W);
-	Y :: string(X);
-
-	w := W;
-	x: cstring = X;
-	y: string = Y;
-	z := string(x);
-	fmt.println(x, y, z);
-	fmt.println(len(x), len(y), len(z));
-	fmt.println(len(W), len(X), len(Y));
-	// IMPORTANT NOTE for cstring variables
-	// len(cstring) is O(N)
-	// cast(cstring)string is O(N)
-}
-
-deprecated_attribute :: proc() {
-	@(deprecated="Use foo_v2 instead")
-	foo_v1 :: proc(x: int) {
-		fmt.println("foo_v1");
-	}
-	foo_v2 :: proc(x: int) {
-		fmt.println("foo_v2");
-	}
-
-	// NOTE: Uncomment to see the warning messages
-	// foo_v1(1);
-}
-
-
-main :: proc() {
-	when true {
-		general_stuff();
-		union_type();
-		parametric_polymorphism();
-		threading_example();
-		array_programming();
-		using_in();
-		named_proc_return_parameters();
-		enum_export();
-		explicit_procedure_overloading();
-		complete_switch();
-		cstring_example();
-		deprecated_attribute();
-	}
-}

+ 0 - 20
examples/factorial.odin

@@ -1,20 +0,0 @@
-import "core:fmt.odin";
-
-main :: proc() {
-	recursive_factorial :: proc(i: u64) -> u64 {
-		if i < 2 do return 1;
-		return i * recursive_factorial(i-1);
-	}
-
-	loop_factorial :: proc(i: u64) -> u64 {
-		result: u64 = 1;
-		for n in 2..i {
-			result *= n;
-		}
-		return result;
-	}
-
-
-	fmt.println(recursive_factorial(12));
-	fmt.println(loop_factorial(12));
-}

+ 0 - 221
examples/game.odin

@@ -1,221 +0,0 @@
-when ODIN_OS == "windows" do import win32 "core:sys/windows.odin";
-when ODIN_OS == "windows" import wgl "core:sys/wgl.odin";
-import "core:fmt.odin";
-import "core:math.odin";
-import "core:os.odin";
-import gl "core:opengl.odin";
-
-TWO_HEARTS :: '💕';
-
-win32_perf_count_freq := win32.get_query_performance_frequency();
-time_now :: proc() -> f64 {
-	assert(win32_perf_count_freq != 0);
-
-	counter: i64;
-	win32.query_performance_counter(&counter);
-	return f64(counter) / f64(win32_perf_count_freq);
-}
-win32_print_last_error :: proc() {
-	err_code := win32.get_last_error();
-	if err_code != 0 {
-		fmt.println("get_last_error: ", err_code);
-	}
-}
-
-// Yuk!
-to_c_string :: proc(s: string) -> []u8 {
-	c_str := make([]u8, len(s)+1);
-	copy(c_str, cast([]u8)s);
-	c_str[len(s)] = 0;
-	return c_str;
-}
-
-
-Window :: struct {
-	width, height:      int,
-	wc:                 win32.Wnd_Class_Ex_A,
-	dc:                 win32.Hdc,
-	hwnd:               win32.Hwnd,
-	opengl_context, rc: wgl.Hglrc,
-	c_title:            []u8,
-}
-
-make_window :: proc(title: string, msg, height: int, window_proc: win32.Wnd_Proc) -> (Window, bool) {
-	using win32;
-
-	w: Window;
-	w.width, w.height = msg, height;
-
-	class_name := "Win32-Odin-Window\x00";
-	c_class_name := &class_name[0];
-	if title[len(title)-1] != 0 {
-		w.c_title = to_c_string(title);
-	} else {
-		w.c_title = cast([]u8)title;
-	}
-
-	instance := get_module_handle_a(nil);
-
-	w.wc = Wnd_Class_Ex_A{
-		size       = size_of(Wnd_Class_Ex_A),
-		style      = CS_VREDRAW | CS_HREDRAW,
-		instance   = Hinstance(instance),
-		class_name = c_class_name,
-		wnd_proc   = window_proc,
-	};
-
-	if register_class_ex_a(&w.wc) == 0 {
-		win32_print_last_error();
-		return w, false;
-	}
-
-	w.hwnd = create_window_ex_a(0,
-	                            c_class_name, &w.c_title[0],
-	                            WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
-	                            CW_USEDEFAULT, CW_USEDEFAULT,
-	                            i32(w.width), i32(w.height),
-	                            nil, nil, instance, nil);
-
-	if w.hwnd == nil {
-		win32_print_last_error();
-		return w, false;
-	}
-
-	w.dc = get_dc(w.hwnd);
-
-	{
-		pfd := Pixel_Format_Descriptor{
-			size         = size_of(Pixel_Format_Descriptor),
-			version      = 1,
-			flags        = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
-			pixel_type   = PFD_TYPE_RGBA,
-			color_bits   = 32,
-			alpha_bits   = 8,
-			depth_bits   = 24,
-			stencil_bits = 8,
-			layer_type   = PFD_MAIN_PLANE,
-		};
-
-		set_pixel_format(w.dc, choose_pixel_format(w.dc, &pfd), nil);
-		w.opengl_context = wgl.create_context(w.dc);
-		wgl.make_current(w.dc, w.opengl_context);
-
-		attribs := [8]i32{
-			wgl.CONTEXT_MAJOR_VERSION_ARB, 2,
-			wgl.CONTEXT_MINOR_VERSION_ARB, 1,
-			wgl.CONTEXT_PROFILE_MASK_ARB, wgl.CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
-			0, // NOTE(bill): tells the that :: proc this is the end of attribs
-		};
-
-		wgl_str := "wglCreateContextAttribsARB\x00";
-		wglCreateContextAttribsARB := cast(wgl.Create_Context_Attribs_ARB_Type)wgl.get_proc_address(&wgl_str[0]);
-		w.rc = wglCreateContextAttribsARB(w.dc, nil, &attribs[0]);
-		wgl.make_current(w.dc, w.rc);
-		swap_buffers(w.dc);
-	}
-
-	return w, true;
-}
-
-destroy_window :: proc(w: ^Window) {
-	free(w.c_title);
-}
-
-display_window :: proc(w: ^Window) {
-	win32.swap_buffers(w.dc);
-}
-
-
-run :: proc() {
-	using math;
-
-	win32_proc :: proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline {
-		using win32;
-		if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
-			os.exit(0);
-			return 0;
-		}
-		return def_window_proc_a(hwnd, msg, wparam, lparam);
-	}
-
-	window, window_success := make_window("Odin Language Demo", 854, 480, cast(win32.Wnd_Proc)win32_proc);
-	if !window_success {
-		return;
-	}
-	defer destroy_window(&window);
-
-	gl.init();
-
-	using win32;
-
-	prev_time := time_now();
-	running := true;
-
-	pos := Vec2{100, 100};
-
-	for running {
-		curr_time := time_now();
-		dt := f32(curr_time - prev_time);
-		prev_time = curr_time;
-
-		msg: Msg;
-		for peek_message_a(&msg, nil, 0, 0, PM_REMOVE) > 0 {
-			if msg.message == WM_QUIT {
-				running = false;
-			}
-			translate_message(&msg);
-			dispatch_message_a(&msg);
-		}
-
-		if is_key_down(Key_Code.Escape) {
-			running = false;
-		}
-
-		{
-			SPEED :: 500;
-			v: Vec2;
-
-			if is_key_down(Key_Code.Right) do v[0] += 1;
-			if is_key_down(Key_Code.Left)  do v[0] -= 1;
-			if is_key_down(Key_Code.Up)    do v[1] += 1;
-			if is_key_down(Key_Code.Down)  do v[1] -= 1;
-
-			v = norm(v);
-
-			pos += v * Vec2{SPEED * dt};
-		}
-
-
-		gl.ClearColor(0.5, 0.7, 1.0, 1.0);
-		gl.Clear(gl.COLOR_BUFFER_BIT);
-
-		gl.LoadIdentity();
-		gl.Ortho(0, f64(window.width),
-		         0, f64(window.height), 0, 1);
-
-		draw_rect :: proc(x, y, w, h: f32) {
-			gl.Begin(gl.TRIANGLES);
-			defer gl.End();
-
-			gl.Color3f(1, 0, 0); gl.Vertex3f(x,   y,   0);
-			gl.Color3f(0, 1, 0); gl.Vertex3f(x+w, y,   0);
-			gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
-
-			gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
-			gl.Color3f(1, 1, 0); gl.Vertex3f(x,   y+h, 0);
-			gl.Color3f(1, 0, 0); gl.Vertex3f(x,   y,   0);
-		}
-
-		draw_rect(pos.x, pos.y, 50, 50);
-
-		display_window(&window);
-		if ms_to_sleep := i32(16 - 1000*dt); ms_to_sleep > 0 {
-			win32.sleep(ms_to_sleep);
-		}
-	}
-}
-
-
-main :: proc() {
-	run();
-}

+ 0 - 5
examples/hello.odin

@@ -1,5 +0,0 @@
-import "core:fmt.odin";
-
-main :: proc() {
-	fmt.println("Hellope, world!");
-}

+ 0 - 479
examples/punity.odin

@@ -1,479 +0,0 @@
-import win32 "core:sys/windows.odin";
-import "core:fmt.odin";
-import "core:os.odin";
-import "core:mem.odin";
-
-
-CANVAS_WIDTH  :: 128;
-CANVAS_HEIGHT :: 128;
-CANVAS_SCALE  :: 3;
-FRAME_TIME    :: 1.0/30.0;
-WINDOW_TITLE  :: "Punity\x00";
-
-#assert(CANVAS_WIDTH % 16 == 0);
-
-
-WINDOW_WIDTH  :: CANVAS_WIDTH  * CANVAS_SCALE;
-WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE;
-
-
-STACK_CAPACITY   :: 1<<20;
-STORAGE_CAPACITY :: 1<<20;
-
-DRAW_LIST_RESERVE :: 128;
-
-MAX_KEYS :: 256;
-
-Core :: struct {
-	stack:   ^Bank,
-	storage: ^Bank,
-
-	running:       bool,
-	key_modifiers: u32,
-	key_states:    [MAX_KEYS]u8,
-	key_deltas:    [MAX_KEYS]u8,
-
-	perf_frame,
-	perf_frame_inner,
-	perf_step,
-	perf_audio,
-	perf_blit,
-	perf_blit_cvt,
-	perf_blit_gdi: Perf_Span,
-
-	frame: i64,
-
-	canvas:    Canvas,
-	draw_list: ^Draw_List,
-}
-
-Perf_Span :: struct {
-	stamp: f64,
-	delta: f32,
-}
-
-Bank :: struct {
-	memory: []u8,
-	cursor: int,
-}
-
-Bank_State :: struct {
-	state: Bank,
-	bank: ^Bank,
-}
-
-
-Color :: struct #raw_union {
-	using channels: struct{a, b, g, r: u8},
-	rgba: u32,
-}
-
-Palette :: struct {
-	colors: [256]Color,
-	colors_count: u8,
-}
-
-
-Rect :: struct #raw_union {
-	using minmax: struct {min_x, min_y, max_x, max_y: int},
-	using pos: struct {left, top, right, bottom: int},
-	e: [4]int,
-}
-
-Bitmap :: struct {
-	pixels: []u8,
-	width:  int,
-	height: int,
-}
-
-Font :: struct {
-	using bitmap: Bitmap,
-	char_width:   int,
-	char_height:  int,
-}
-
-Canvas :: struct {
-	using bitmap: ^Bitmap,
-	palette:      Palette,
-	translate_x:  int,
-	translate_y:  int,
-	clip:         Rect,
-	font:         ^Font,
-}
-
-DrawFlag :: enum {
-	NONE   = 0,
-	FLIP_H = 1<<0,
-	FLIP_V = 1<<1,
-	MASK   = 1<<2,
-}
-
-Draw_Item :: struct {}
-Draw_List :: struct {
-	items: []Draw_Item,
-}
-
-Key :: enum {
-	Mod_Shift   = 0x0001,
-	Mod_Control = 0x0002,
-	Mod_Alt     = 0x0004,
-	Mod_Super   = 0x0008,
-
-
-	Unknown            =-1,
-	Invalid            =-2,
-
-
-	Lbutton            = 1,
-	Rbutton            = 2,
-	Cancel             = 3,
-	Mbutton            = 4,
-
-
-	Back               = 8,
-	Tab                = 9,
-	Clear              = 12,
-	Return             = 13,
-	Shift              = 16,
-	Control            = 17,
-	Menu               = 18,
-	Pause              = 19,
-	Capital            = 20,
-	Kana               = 0x15,
-	Hangeul            = 0x15,
-	Hangul             = 0x15,
-	Junja              = 0x17,
-	Final              = 0x18,
-	Hanja              = 0x19,
-	Kanji              = 0x19,
-	Escape             = 0x1B,
-	Convert            = 0x1C,
-	Non_Convert        = 0x1D,
-	Accept             = 0x1E,
-	Mode_Change        = 0x1F,
-	Space              = 32,
-	Prior              = 33,
-	Next               = 34,
-	End                = 35,
-	Home               = 36,
-	Left               = 37,
-	Up                 = 38,
-	Right              = 39,
-	Down               = 40,
-	Select             = 41,
-	Print              = 42,
-	Exec               = 43,
-	Snapshot           = 44,
-	Insert             = 45,
-	Delete             = 46,
-	Help               = 47,
-	Lwin               = 0x5B,
-	Rwin               = 0x5C,
-	Apps               = 0x5D,
-	Sleep              = 0x5F,
-	Numpad0            = 0x60,
-	Numpad1            = 0x61,
-	Numpad2            = 0x62,
-	Numpad3            = 0x63,
-	Numpad4            = 0x64,
-	Numpad5            = 0x65,
-	Numpad6            = 0x66,
-	Numpad7            = 0x67,
-	Numpad8            = 0x68,
-	Numpad9            = 0x69,
-	Multiply           = 0x6A,
-	Add                = 0x6B,
-	Separator          = 0x6C,
-	Subtract           = 0x6D,
-	Decimal            = 0x6E,
-	Divide             = 0x6F,
-	F1                 = 0x70,
-	F2                 = 0x71,
-	F3                 = 0x72,
-	F4                 = 0x73,
-	F5                 = 0x74,
-	F6                 = 0x75,
-	F7                 = 0x76,
-	F8                 = 0x77,
-	F9                 = 0x78,
-	F10                = 0x79,
-	F11                = 0x7A,
-	F12                = 0x7B,
-	F13                = 0x7C,
-	F14                = 0x7D,
-	F15                = 0x7E,
-	F16                = 0x7F,
-	F17                = 0x80,
-	F18                = 0x81,
-	F19                = 0x82,
-	F20                = 0x83,
-	F21                = 0x84,
-	F22                = 0x85,
-	F23                = 0x86,
-	F24                = 0x87,
-	Numlock            = 0x90,
-	Scroll             = 0x91,
-	Lshift             = 0xA0,
-	Rshift             = 0xA1,
-	Lcontrol           = 0xA2,
-	Rcontrol           = 0xA3,
-	Lmenu              = 0xA4,
-	Rmenu              = 0xA5,
-
-
-	Apostrophe         = 39,  /* ' */
-	Comma              = 44,  /* , */
-	Minus              = 45,  /* - */
-	Period             = 46,  /* . */
-	Slash              = 47,  /* / */
-	Num0               = 48,
-	Num1               = 49,
-	Num2               = 50,
-	Num3               = 51,
-	Num4               = 52,
-	Num5               = 53,
-	Num6               = 54,
-	Num7               = 55,
-	Num8               = 56,
-	Num9               = 57,
-	Semicolon          = 59,  /* ; */
-	Equal              = 61,  /* = */
-	A                  = 65,
-	B                  = 66,
-	C                  = 67,
-	D                  = 68,
-	E                  = 69,
-	F                  = 70,
-	G                  = 71,
-	H                  = 72,
-	I                  = 73,
-	J                  = 74,
-	K                  = 75,
-	L                  = 76,
-	M                  = 77,
-	N                  = 78,
-	O                  = 79,
-	P                  = 80,
-	Q                  = 81,
-	R                  = 82,
-	S                  = 83,
-	T                  = 84,
-	U                  = 85,
-	V                  = 86,
-	W                  = 87,
-	X                  = 88,
-	Y                  = 89,
-	Z                  = 90,
-	Left_Bracket       = 91,  /* [ */
-	Backslash          = 92,  /* \ */
-	Right_Bracket      = 93,  /* ] */
-	Grave_Accent       = 96,  /* ` */
-};
-
-
-key_down :: proc(k: Key) -> bool {
-	return _core.key_states[k] != 0;
-}
-
-key_pressed :: proc(k: Key) -> bool {
-	return (_core.key_deltas[k] != 0) && key_down(k);
-}
-
-
-
-
-win32_perf_count_freq := win32.get_query_performance_frequency();
-time_now :: proc() -> f64 {
-	assert(win32_perf_count_freq != 0);
-
-	counter: i64;
-	win32.query_performance_counter(&counter);
-	return f64(counter) / f64(win32_perf_count_freq);
-}
-
-_core: Core;
-
-run :: proc(user_init, user_step: proc(c: ^Core)) {
-	using win32;
-
-	_core.running = true;
-
-	win32_proc :: proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline #cc_c {
-		win32_app_key_mods :: proc() -> u32 {
-			mods: u32 = 0;
-
-			if is_key_down(Key_Code.Shift)   do mods |= u32(Key.Mod_Shift);
-			if is_key_down(Key_Code.Control) do mods |= u32(Key.Mod_Control);
-			if is_key_down(Key_Code.Menu)    do mods |= u32(Key.Mod_Alt);
-			if is_key_down(Key_Code.Lwin)    do mods |= u32(Key.Mod_Super);
-			if is_key_down(Key_Code.Rwin)    do mods |= u32(Key.Mod_Super);
-
-			return mods;
-		}
-
-		match msg {
-		case WM_KEYDOWN:
-			_core.key_modifiers = win32_app_key_mods();
-			if wparam < MAX_KEYS {
-				_core.key_states[wparam] = 1;
-				_core.key_deltas[wparam] = 1;
-			}
-			return 0;
-
-		case WM_KEYUP:
-			_core.key_modifiers = win32_app_key_mods();
-			if wparam < MAX_KEYS {
-				_core.key_states[wparam] = 0;
-				_core.key_deltas[wparam] = 1;
-			}
-			return 0;
-
-		case WM_CLOSE:
-			post_quit_message(0);
-			_core.running = false;
-			return 0;
-		}
-
-		return def_window_proc_a(hwnd, msg, wparam, lparam);
-	}
-
-
-	class_name := "Punity\x00";
-	window_class := Wnd_Class_Ex_A{
-		class_name = &class_name[0],
-		size       = size_of(Wnd_Class_Ex_A),
-		style      = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
-		instance   = Hinstance(get_module_handle_a(nil)),
-		wnd_proc   = win32_proc,
-		background = Hbrush(get_stock_object(BLACK_BRUSH)),
-	};
-
-	if register_class_ex_a(&window_class) == 0 {
-		fmt.fprintln(os.stderr, "register_class_ex_a failed");
-		return;
-	}
-
-	screen_width  := get_system_metrics(SM_CXSCREEN);
-	screen_height := get_system_metrics(SM_CYSCREEN);
-
-	rc: Rect;
-	rc.left   = (screen_width - WINDOW_WIDTH)   / 2;
-	rc.top    = (screen_height - WINDOW_HEIGHT) / 2;
-	rc.right  = rc.left + WINDOW_WIDTH;
-	rc.bottom = rc.top + WINDOW_HEIGHT;
-
-	style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
-	assert(adjust_window_rect(&rc, style, 0) != 0);
-
-	wt := WINDOW_TITLE;
-
-	win32_window := create_window_ex_a(0,
-	                                   window_class.class_name,
-	                                   &wt[0],
-	                                   style,
-	                                   rc.left, rc.top,
-	                                   rc.right-rc.left, rc.bottom-rc.top,
-	                                   nil, nil, window_class.instance,
-	                                   nil);
-
-	if win32_window == nil {
-		fmt.fprintln(os.stderr, "create_window_ex_a failed");
-		return;
-	}
-
-
-	window_bmi: Bitmap_Info;
-	window_bmi.size        = size_of(Bitmap_Info_Header);
-	window_bmi.width       = CANVAS_WIDTH;
-	window_bmi.height      = CANVAS_HEIGHT;
-	window_bmi.planes      = 1;
-	window_bmi.bit_count   = 32;
-	window_bmi.compression = BI_RGB;
-
-
-	user_init(&_core);
-
-	show_window(win32_window, SW_SHOW);
-
-	window_buffer := make([]u32, CANVAS_WIDTH * CANVAS_HEIGHT);
-	defer free(window_buffer);
-
-	for _, i in window_buffer do window_buffer[i] = 0xff00ff;
-
-	dt: f64;
-	prev_time := time_now();
-	curr_time := time_now();
-	total_time: f64 = 0;
-	offset_x := 0;
-	offset_y := 0;
-
-	message: Msg;
-	for _core.running {
-		curr_time = time_now();
-		dt = curr_time - prev_time;
-		prev_time = curr_time;
-		total_time += dt;
-
-		offset_x += 1;
-		offset_y += 2;
-
-		{
-			buf: [128]u8;
-			s := fmt.bprintf(buf[..], "Punity: %.4f ms\x00", dt*1000);
-			win32.set_window_text_a(win32_window, &s[0]);
-		}
-
-
-		for y in 0..CANVAS_HEIGHT {
-			for x in 0..CANVAS_WIDTH {
-				g := (x % 32) * 8;
-				b := (y % 32) * 8;
-				window_buffer[x + y*CANVAS_WIDTH] = u32(g << 8 | b);
-			}
-		}
-
-		mem.zero(&_core.key_deltas[0], size_of(_core.key_deltas));
-
-		for peek_message_a(&message, nil, 0, 0, PM_REMOVE) != 0 {
-			if message.message == WM_QUIT {
-				_core.running = false;
-			}
-			translate_message(&message);
-			dispatch_message_a(&message);
-		}
-
-		user_step(&_core);
-
-		dc := get_dc(win32_window);
-		stretch_dibits(dc,
-		               0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
-		               0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
-		               &window_buffer[0],
-		               &window_bmi,
-		               DIB_RGB_COLORS,
-		               SRCCOPY);
-		release_dc(win32_window, dc);
-
-
-
-		delta := time_now() - prev_time;
-		if ms := i32((FRAME_TIME - delta) * 1000); ms > 0 {
-			win32.sleep(ms);
-		}
-
-		_core.frame += 1;
-	}
-}
-
-
-main :: proc() {
-	user_init :: proc(c: ^Core) {
-
-	}
-
-	user_step :: proc(c: ^Core) {
-
-	}
-
-	run(user_init, user_step);
-}

+ 2 - 2
src/checker.cpp

@@ -2532,7 +2532,7 @@ void check_add_import_decl(Checker *c, AstNodeImportDecl *id) {
 	GB_ASSERT(scope->is_package && scope->package != nullptr);
 
 	if (scope->is_global) {
-		error(token, "Importing a runtime package is disallowed and unnecessary");
+		error(token, "Importing a built-in package is disallowed and unnecessary");
 		return;
 	}
 
@@ -2567,7 +2567,7 @@ void check_add_import_decl(Checker *c, AstNodeImportDecl *id) {
 
 	if (id->is_using) {
 		if (parent_scope->is_global) {
-			error(id->import_name, "'runtime' package imports cannot use using");
+			error(id->import_name, "'builtin' package imports cannot use using");
 			return;
 		}
 

+ 23 - 3
src/parser.cpp

@@ -374,6 +374,9 @@ void error(AstNode *node, char *fmt, ...) {
 	va_start(va, fmt);
 	error_va(token, fmt, va);
 	va_end(va);
+	if (node != nullptr && node->file != nullptr) {
+		node->file->error_count += 1;
+	}
 }
 
 void error_no_newline(AstNode *node, char *fmt, ...) {
@@ -385,6 +388,9 @@ void error_no_newline(AstNode *node, char *fmt, ...) {
 	va_start(va, fmt);
 	error_no_newline_va(token, fmt, va);
 	va_end(va);
+	if (node != nullptr && node->file != nullptr) {
+		node->file->error_count += 1;
+	}
 }
 
 void warning(AstNode *node, char *fmt, ...) {
@@ -399,6 +405,9 @@ void syntax_error(AstNode *node, char *fmt, ...) {
 	va_start(va, fmt);
 	syntax_error_va(ast_node_token(node), fmt, va);
 	va_end(va);
+	if (node != nullptr && node->file != nullptr) {
+		node->file->error_count += 1;
+	}
 }
 
 
@@ -1227,6 +1236,7 @@ void fix_advance_to_next_stmt(AstFile *f) {
 			return;
 
 
+		case Token_package:
 		case Token_foreign:
 		case Token_import:
 		case Token_export:
@@ -4144,7 +4154,6 @@ void parse_file(Parser *p, AstFile *f) {
 
 	comsume_comment_groups(f, f->prev_token);
 
-
 	f->package_token = expect_token(f, Token_package);
 	Token package_name = expect_token_after(f, Token_Ident, "package");
 	if (package_name.kind == Token_Ident) {
@@ -4156,6 +4165,10 @@ void parse_file(Parser *p, AstFile *f) {
 	}
 	f->package_name = package_name.string;
 
+	if (f->error_count > 0) {
+		return;
+	}
+
 	f->decls = parse_stmt_list(f);
 	parse_setup_file_decls(p, f, base_dir, f->decls);
 }
@@ -4218,7 +4231,6 @@ skip:
 	parse_file(p, file);
 
 	gb_mutex_lock(&p->file_add_mutex);
-	// file->id = imported_package.index;
 	HashKey key = hash_string(fi->fullpath);
 	map_set(&package->files, key, file);
 
@@ -4332,6 +4344,14 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
 
 	char *fullpath_str = gb_path_get_full_name(heap_allocator(), cast(char *)&init_filename[0]);
 	String init_fullpath = string_trim_whitespace(make_string_c(fullpath_str));
+	if (!path_is_directory(init_fullpath)) {
+		String const ext = str_lit(".odin");
+		if (!string_ends_with(init_fullpath, ext)) {
+			gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
+			return ParseFile_WrongExtension;
+		}
+		init_fullpath = directory_from_path(init_fullpath);
+	}
 
 	TokenPos init_pos = {};
 	ImportedPackage init_imported_package = {ImportedPackage_Init, init_fullpath, init_fullpath, init_pos};
@@ -4347,7 +4367,7 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
 	p->init_fullpath = init_fullpath;
 
 	// IMPORTANT TODO(bill): Figure out why this doesn't work on *nix sometimes
-#if 0 && defined(GB_SYSTEM_WINDOWS)
+#if defined(GB_SYSTEM_WINDOWS)
 	isize thread_count = gb_max(build_context.thread_count, 1);
 	if (thread_count > 1) {
 		isize volatile curr_import_index = 0;

+ 1 - 1
src/parser.hpp

@@ -70,7 +70,7 @@ struct AstFile {
 	AstNode *           curr_proc;
 	isize               scope_level;
 	// DeclInfo *          decl_info;   // NOTE(bill): Created in checker
-
+	isize               error_count;
 
 	CommentGroup        lead_comment; // Comment (block) before the decl
 	CommentGroup        line_comment; // Comment after the semicolon

+ 10 - 0
src/string.cpp

@@ -287,6 +287,16 @@ String remove_directory_from_path(String const &s) {
 	}
 	return substring(s, s.len-len, s.len);
 }
+String directory_from_path(String const &s) {
+	isize i = s.len-1;
+	for (; i >= 0; i--) {
+		if (s[i] == '/' ||
+		    s[i] == '\\') {
+			break;
+		}
+	}
+	return substring(s, 0, i);
+}
 
 
 String concatenate_strings(gbAllocator a, String const &x, String const &y) {