Browse Source

VarDecl and ConstDecl split; error, warning, et al. now global

Ginger Bill 9 years ago
parent
commit
67694c0df0
13 changed files with 1775 additions and 607 deletions
  1. 1 2
      code/demo.odin
  2. 583 0
      code/fmt.odin
  3. 107 0
      code/os.odin
  4. 484 0
      code/punity.odin
  5. 64 67
      src/checker/checker.cpp
  6. 3 0
      src/checker/entity.cpp
  7. 149 163
      src/checker/expr.cpp
  8. 158 148
      src/checker/stmt.cpp
  9. 1 9
      src/codegen/codegen.cpp
  10. 62 64
      src/codegen/ssa.cpp
  11. 113 138
      src/parser.cpp
  12. 11 4
      src/printer.cpp
  13. 39 12
      src/tokenizer.cpp

+ 1 - 2
code/demo.odin

@@ -2,7 +2,6 @@
 
 
 main :: proc() {
 main :: proc() {
 	init :: proc(c: ^pn.Core) {
 	init :: proc(c: ^pn.Core) {
-
 	}
 	}
 
 
 	step :: proc(c: ^pn.Core) {
 	step :: proc(c: ^pn.Core) {
@@ -11,5 +10,5 @@ main :: proc() {
 		}
 		}
 	}
 	}
 
 
-	// pn.run(init, step)
+	pn.run(init, step)
 }
 }

+ 583 - 0
code/fmt.odin

@@ -0,0 +1,583 @@
+#import "os.odin" as os
+
+print_byte_buffer :: proc(buf: ^[]byte, b: []byte) {
+	if buf.count < buf.capacity {
+		n := min(buf.capacity-buf.count, b.count)
+		if n > 0 {
+			offset := ptr_offset(buf.data, buf.count)
+			memory_copy(offset, ^b[0], n)
+			buf.count += n
+		}
+	}
+}
+
+print_string_to_buffer :: proc(buf: ^[]byte, s: string) {
+	print_byte_buffer(buf, s as []byte)
+}
+
+
+byte_reverse :: proc(b: []byte) {
+	n := b.count
+	for i := 0; i < n/2; i++ {
+		b[i], b[n-1-i] = b[n-1-i], b[i]
+	}
+}
+
+encode_rune :: proc(r: rune) -> ([4]byte, int) {
+	buf: [4]byte
+	i := r as u32
+	mask: byte : 0x3f
+	if i <= 1<<7-1 {
+		buf[0] = r as byte
+		return buf, 1
+	}
+	if i <= 1<<11-1 {
+		buf[0] = 0xc0 | (r>>6) as byte
+		buf[1] = 0x80 | (r)    as byte & mask
+		return buf, 2
+	}
+
+	// Invalid or Surrogate range
+	if i > 0x0010ffff ||
+	   (i >= 0xd800 && i <= 0xdfff) {
+		r = 0xfffd
+	}
+
+	if i <= 1<<16-1 {
+		buf[0] = 0xe0 | (r>>12) as byte
+		buf[1] = 0x80 | (r>>6)  as byte & mask
+		buf[2] = 0x80 | (r)     as byte & mask
+		return buf, 3
+	}
+
+	buf[0] = 0xf0 | (r>>18) as byte
+	buf[1] = 0x80 | (r>>12) as byte & mask
+	buf[2] = 0x80 | (r>>6)  as byte & mask
+	buf[3] = 0x80 | (r)     as byte & mask
+	return buf, 4
+}
+
+print_rune_to_buffer :: proc(buf: ^[]byte, r: rune) {
+	b, n := encode_rune(r)
+	print_string_to_buffer(buf, b[:n] as string)
+}
+
+print_space_to_buffer :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, #rune " ") }
+print_nl_to_buffer    :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, #rune "\n") }
+
+print_int_to_buffer :: proc(buf: ^[]byte, i: int) {
+	print_int_base_to_buffer(buf, i, 10);
+}
+PRINT__NUM_TO_CHAR_TABLE :: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$"
+print_int_base_to_buffer :: proc(buffer: ^[]byte, i, base: int) {
+
+	buf: [65]byte
+	len := 0
+	negative := false
+	if i < 0 {
+		negative = true
+		i = -i
+	}
+	if i == 0 {
+		buf[len] = #rune "0"
+		len++
+	}
+	for i > 0 {
+		buf[len] = PRINT__NUM_TO_CHAR_TABLE[i % base]
+		len++
+		i /= base
+	}
+
+	if negative {
+		buf[len] = #rune "-"
+		len++
+	}
+
+	byte_reverse(buf[:len])
+	print_string_to_buffer(buffer, buf[:len] as string)
+}
+
+print_uint_to_buffer :: proc(buffer: ^[]byte, i: uint) {
+	print_uint_base_to_buffer(buffer, i, 10, 0, #rune " ")
+}
+print_uint_base_to_buffer :: proc(buffer: ^[]byte, i, base: uint, min_width: int, pad_char: byte) {
+	buf: [65]byte
+	len := 0
+	if i == 0 {
+		buf[len] = #rune "0"
+		len++
+	}
+	for i > 0 {
+		buf[len] = PRINT__NUM_TO_CHAR_TABLE[i % base]
+		len++
+		i /= base
+	}
+	for len < min_width {
+		buf[len] = pad_char
+		len++
+	}
+
+	byte_reverse(buf[:len])
+	print_string_to_buffer(buffer, buf[:len] as string)
+}
+
+print_bool_to_buffer :: proc(buffer: ^[]byte, b : bool) {
+	if b { print_string_to_buffer(buffer, "true") }
+	else { print_string_to_buffer(buffer, "false") }
+}
+
+print_pointer_to_buffer :: proc(buffer: ^[]byte, p: rawptr) #inline { print_uint_base_to_buffer(buffer, p as uint, 16, 0, #rune " ") }
+
+print_f32_to_buffer :: proc(buffer: ^[]byte, f: f32) #inline { print__f64(buffer, f as f64, 7) }
+print_f64_to_buffer :: proc(buffer: ^[]byte, f: f64) #inline { print__f64(buffer, f, 10) }
+
+print__f64 :: proc(buffer: ^[]byte, f: f64, decimal_places: int) {
+	if f == 0 {
+		print_rune_to_buffer(buffer, #rune "0")
+		return
+	}
+	if f < 0 {
+		print_rune_to_buffer(buffer, #rune "-")
+		f = -f
+	}
+
+	print_u64_to_buffer :: proc(buffer: ^[]byte, i: u64) {
+		buf: [22]byte
+		len := 0
+		if i == 0 {
+			buf[len] = #rune "0"
+			len++
+		}
+		for i > 0 {
+			buf[len] = PRINT__NUM_TO_CHAR_TABLE[i % 10]
+			len++
+			i /= 10
+		}
+		byte_reverse(buf[:len])
+		print_string_to_buffer(buffer, buf[:len] as string)
+	}
+
+	i := f as u64
+	print_u64_to_buffer(buffer, i)
+	f -= i as f64
+
+	print_rune_to_buffer(buffer, #rune ".")
+
+	mult := 10.0
+	for decimal_places := 6; decimal_places >= 0; decimal_places-- {
+		i = (f * mult) as u64
+		print_u64_to_buffer(buffer, i as u64)
+		f -= i as f64 / mult
+		mult *= 10
+	}
+}
+
+print_type_to_buffer :: proc(buf: ^[]byte, ti: ^Type_Info) {
+	if ti == null { return }
+
+	using Type_Info
+	match type info : ti {
+	case Named:
+		print_string_to_buffer(buf, info.name)
+	case Integer:
+		match {
+		case ti == type_info(int):
+			print_string_to_buffer(buf, "int")
+		case ti == type_info(uint):
+			print_string_to_buffer(buf, "uint")
+		default:
+			if info.signed {
+				print_string_to_buffer(buf, "i")
+			} else {
+				print_string_to_buffer(buf, "u")
+			}
+			print_int_to_buffer(buf, 8*info.size)
+		}
+
+	case Float:
+		match info.size {
+		case 4: print_string_to_buffer(buf, "f32")
+		case 8: print_string_to_buffer(buf, "f64")
+		}
+	case String:  print_string_to_buffer(buf, "string")
+	case Boolean: print_string_to_buffer(buf, "bool")
+	case Pointer:
+		print_string_to_buffer(buf, "^")
+		print_type_to_buffer(buf, info.elem)
+	case Procedure:
+		print_string_to_buffer(buf, "proc")
+		if info.params == null {
+			print_string_to_buffer(buf, "()")
+		} else {
+			count := (info.params as ^Tuple).fields.count
+			if count == 1 { print_string_to_buffer(buf, "(") }
+			print_type_to_buffer(buf, info.params)
+			if count == 1 { print_string_to_buffer(buf, ")") }
+		}
+		if info.results != null {
+			print_string_to_buffer(buf, " -> ")
+			print_type_to_buffer(buf, info.results)
+		}
+	case Tuple:
+		count := info.fields.count
+		if count != 1 { print_string_to_buffer(buf, "(") }
+		for i := 0; i < count; i++ {
+			if i > 0 { print_string_to_buffer(buf, ", ") }
+
+			f := info.fields[i]
+
+			if f.name.count > 0 {
+				print_string_to_buffer(buf, f.name)
+				print_string_to_buffer(buf, ": ")
+			}
+			print_type_to_buffer(buf, f.type_info)
+		}
+		if count != 1 { print_string_to_buffer(buf, ")") }
+
+	case Array:
+		print_string_to_buffer(buf, "[")
+		print_int_to_buffer(buf, info.count)
+		print_string_to_buffer(buf, "]")
+		print_type_to_buffer(buf, info.elem)
+	case Slice:
+		print_string_to_buffer(buf, "[")
+		print_string_to_buffer(buf, "]")
+		print_type_to_buffer(buf, info.elem)
+	case Vector:
+		print_string_to_buffer(buf, "{")
+		print_int_to_buffer(buf, info.count)
+		print_string_to_buffer(buf, "}")
+		print_type_to_buffer(buf, info.elem)
+
+	case Struct:
+		print_string_to_buffer(buf, "struct ")
+		if info.packed  { print_string_to_buffer(buf, "#packed ") }
+		if info.ordered { print_string_to_buffer(buf, "#ordered ") }
+		print_string_to_buffer(buf, "{")
+		for i := 0; i < info.fields.count; i++ {
+			if i > 0 {
+				print_string_to_buffer(buf, ", ")
+			}
+			print_any_to_buffer(buf, info.fields[i].name)
+			print_string_to_buffer(buf, ": ")
+			print_type_to_buffer(buf, info.fields[i].type_info)
+		}
+		print_string_to_buffer(buf, "}")
+
+	case Union:
+		print_string_to_buffer(buf, "union {")
+		for i := 0; i < info.fields.count; i++ {
+			if i > 0 {
+				print_string_to_buffer(buf, ", ")
+			}
+			print_any_to_buffer(buf, info.fields[i].name)
+			print_string_to_buffer(buf, ": ")
+			print_type_to_buffer(buf, info.fields[i].type_info)
+		}
+		print_string_to_buffer(buf, "}")
+
+	case Raw_Union:
+		print_string_to_buffer(buf, "raw_union {")
+		for i := 0; i < info.fields.count; i++ {
+			if i > 0 {
+				print_string_to_buffer(buf, ", ")
+			}
+			print_any_to_buffer(buf, info.fields[i].name)
+			print_string_to_buffer(buf, ": ")
+			print_type_to_buffer(buf, info.fields[i].type_info)
+		}
+		print_string_to_buffer(buf, "}")
+
+	case Enum:
+		print_string_to_buffer(buf, "enum ")
+		print_type_to_buffer(buf, info.base)
+		print_string_to_buffer(buf, "{}")
+	}
+}
+
+
+print_any_to_buffer :: proc(buf: ^[]byte, arg: any)  {
+	using Type_Info
+	match type info : arg.type_info {
+	case Named:
+		a: any
+		a.type_info = info.base
+		a.data = arg.data
+		match type b : info.base {
+		case Struct:
+			print_string_to_buffer(buf, info.name)
+			print_string_to_buffer(buf, "{")
+			for i := 0; i < b.fields.count; i++ {
+				f := b.fields[i];
+				if i > 0 {
+					print_string_to_buffer(buf, ", ")
+				}
+				print_any_to_buffer(buf, f.name)
+				print_string_to_buffer(buf, " = ")
+				v: any
+				v.type_info = f.type_info
+				v.data = ptr_offset(arg.data as ^byte, f.offset)
+				print_any_to_buffer(buf, v)
+			}
+			print_string_to_buffer(buf, "}")
+
+		default:
+			print_any_to_buffer(buf, a)
+		}
+
+	case Integer:
+		if info.signed {
+			i: int = 0;
+			if arg.data != null {
+				match info.size {
+				case 1:  i = (arg.data as ^i8)^   as int
+				case 2:  i = (arg.data as ^i16)^  as int
+				case 4:  i = (arg.data as ^i32)^  as int
+				case 8:  i = (arg.data as ^i64)^  as int
+				case 16: i = (arg.data as ^i128)^ as int
+				}
+			}
+			print_int_to_buffer(buf, i)
+		} else {
+			i: uint = 0;
+			if arg.data != null {
+				match info.size {
+				case 1:  i = (arg.data as ^u8)^   as uint
+				case 2:  i = (arg.data as ^u16)^  as uint
+				case 4:  i = (arg.data as ^u32)^  as uint
+				case 8:  i = (arg.data as ^u64)^  as uint
+				case 16: i = (arg.data as ^u128)^ as uint
+				}
+			}
+			print_uint_to_buffer(buf, i)
+		}
+
+	case Float:
+		f: f64 = 0
+		if arg.data != null {
+			match info.size {
+			case 4: f = (arg.data as ^f32)^ as f64
+			case 8: f = (arg.data as ^f64)^ as f64
+			}
+		}
+		print_f64_to_buffer(buf, f)
+
+	case String:
+		s := ""
+		if arg.data != null {
+			s = (arg.data as ^string)^
+		}
+		print_string_to_buffer(buf, s)
+
+	case Boolean:
+		v := false;
+		if arg.data != null {
+			v = (arg.data as ^bool)^
+		}
+		print_bool_to_buffer(buf, v)
+
+	case Pointer:
+		v := null;
+		if arg.data != null {
+			v = (arg.data as ^rawptr)^
+		}
+		print_pointer_to_buffer(buf, v)
+
+	case Enum:
+		v: any
+		v.data = arg.data
+		v.type_info = info.base
+		print_any_to_buffer(buf, v)
+
+
+	case Array:
+		print_string_to_buffer(buf, "[")
+		defer print_string_to_buffer(buf, "]")
+
+		for i := 0; i < info.count; i++ {
+			if i > 0 {
+				print_string_to_buffer(buf, ", ")
+			}
+
+			elem: any
+			elem.data = (arg.data as int + i*info.elem_size) as rawptr
+			elem.type_info = info.elem
+			print_any_to_buffer(buf, elem)
+		}
+
+	case Slice:
+		slice := arg.data as ^[]byte
+		print_string_to_buffer(buf, "[")
+		defer print_string_to_buffer(buf, "]")
+
+		for i := 0; i < slice.count; i++ {
+			if i > 0 {
+				print_string_to_buffer(buf, ", ")
+			}
+
+			elem: any
+			elem.data = ptr_offset(slice.data, i*info.elem_size)
+			elem.type_info = info.elem
+			print_any_to_buffer(buf, elem)
+		}
+
+	case Vector:
+		print_string_to_buffer(buf, "<")
+		defer print_string_to_buffer(buf, ">")
+
+		for i := 0; i < info.count; i++ {
+			if i > 0 {
+				print_string_to_buffer(buf, ", ")
+			}
+
+			elem: any
+			elem.data = ptr_offset(arg.data as ^byte, i*info.elem_size)
+			elem.type_info = info.elem
+			print_any_to_buffer(buf, elem)
+		}
+
+
+	case Struct:
+		print_string_to_buffer(buf, "struct")
+		print_string_to_buffer(buf, "{")
+		defer print_string_to_buffer(buf, "}")
+
+		for i := 0; i < info.fields.count; i++ {
+			if i > 0 {
+				print_string_to_buffer(buf, ", ")
+			}
+			print_any_to_buffer(buf, info.fields[i].name)
+			print_string_to_buffer(buf, " = ")
+			a: any
+			a.data = ptr_offset(arg.data as ^byte, info.fields[i].offset)
+			a.type_info = info.fields[i].type_info
+			print_any_to_buffer(buf, a)
+		}
+
+	case Union:
+		print_string_to_buffer(buf, "(union)")
+	case Raw_Union:
+		print_string_to_buffer(buf, "(raw_union)")
+	case Procedure:
+		print_type_to_buffer(buf, arg.type_info)
+		print_string_to_buffer(buf, " @ 0x")
+		print_pointer_to_buffer(buf, (arg.data as ^rawptr)^)
+
+	default:
+	}
+}
+
+type_info_is_string :: proc(info: ^Type_Info) -> bool {
+	using Type_Info
+	if info == null {
+		return false
+	}
+
+	for {
+		match type i : info {
+		case Named:
+			info = i.base
+			continue
+		case String:
+			return true
+		default:
+			return false
+		}
+	}
+	return false
+}
+
+
+print_to_buffer :: proc(buf: ^[]byte, fmt: string, args: ..any) {
+	is_digit :: proc(r: rune) -> bool #inline {
+		return r >= #rune "0" && r <= #rune "9"
+	}
+
+	parse_int :: proc(s: string, offset: int) -> (int, int) {
+		result := 0
+
+		for ; offset < s.count; offset++ {
+			c := s[offset] as rune
+			if !is_digit(c) {
+				break
+			}
+
+			result *= 10
+			result += (c - #rune "0") as int
+		}
+
+		return result, offset
+	}
+
+	prev := 0
+	implicit_index := 0
+
+	for i := 0; i < fmt.count; i++ {
+		r := fmt[i] as rune
+		index := implicit_index
+
+		if r != #rune "%" {
+			continue
+		}
+
+		print_string_to_buffer(buf, fmt[prev:i])
+		i++ // Skip %
+		if i < fmt.count {
+			next := fmt[i] as rune
+
+			if next == #rune "%" {
+				print_string_to_buffer(buf, "%")
+				i++
+				prev = i
+				continue
+			}
+
+			if is_digit(next) {
+				index, i = parse_int(fmt, i)
+			}
+		}
+
+		if 0 <= index && index < args.count {
+			print_any_to_buffer(buf, args[index])
+			implicit_index = index+1
+		} else {
+			// TODO(bill): Error check index out bounds
+			print_string_to_buffer(buf, "<invalid>")
+		}
+
+		prev = i
+	}
+
+	print_string_to_buffer(buf, fmt[prev:])
+}
+
+PRINT_BUF_SIZE :: 1<<12
+
+print_to_file :: proc(f: ^os.File, fmt: string, args: ..any) {
+	data: [PRINT_BUF_SIZE]byte
+	buf := data[:0]
+	print_to_buffer(^buf, fmt, ..args)
+	os.write(f, buf)
+}
+
+println_to_file :: proc(f: ^os.File, fmt: string, args: ..any) {
+	data: [PRINT_BUF_SIZE]byte
+	buf := data[:0]
+	print_to_buffer(^buf, fmt, ..args)
+	print_nl_to_buffer(^buf)
+	os.write(f, buf)
+}
+
+
+print :: proc(fmt: string, args: ..any) {
+	print_to_file(os.get_standard_file(os.File_Standard.OUTPUT), fmt, ..args)
+}
+print_err :: proc(fmt: string, args: ..any) {
+	print_to_file(os.get_standard_file(os.File_Standard.ERROR), fmt, ..args)
+}
+println :: proc(fmt: string, args: ..any) {
+	println_to_file(os.get_standard_file(os.File_Standard.OUTPUT), fmt, ..args)
+}
+println_err :: proc(fmt: string, args: ..any) {
+	println_to_file(os.get_standard_file(os.File_Standard.ERROR), fmt, ..args)
+}

+ 107 - 0
code/os.odin

@@ -0,0 +1,107 @@
+#import "runtime.odin" as _ // TODO(bill): make the compile import this automatically
+#import "win32.odin" as win32
+
+File :: type struct {
+	Handle :: type win32.HANDLE
+	handle: Handle
+}
+
+open :: proc(name: string) -> (File, bool) {
+	using win32
+	buf: [300]byte
+	copy(buf[:], name as []byte)
+	f := File{CreateFileA(^buf[0], FILE_GENERIC_READ, FILE_SHARE_READ, null, OPEN_EXISTING, 0, null)}
+	success := f.handle != INVALID_HANDLE_VALUE as File.Handle
+
+	return f, success
+}
+
+create :: proc(name: string) -> (File, bool) {
+	using win32
+	buf: [300]byte
+	copy(buf[:], name as []byte)
+	f := File{
+		handle = CreateFileA(^buf[0], FILE_GENERIC_WRITE, FILE_SHARE_READ, null, CREATE_ALWAYS, 0, null),
+	}
+	success := f.handle != INVALID_HANDLE_VALUE as File.Handle
+	return f, success
+}
+
+
+close :: proc(using f: ^File) {
+	win32.CloseHandle(handle)
+}
+
+write :: proc(using f: ^File, buf: []byte) -> bool {
+	bytes_written: i32
+	return win32.WriteFile(handle, buf.data, buf.count as i32, ^bytes_written, null) != 0
+}
+
+
+File_Standard :: type enum {
+	INPUT,
+	OUTPUT,
+	ERROR,
+	COUNT,
+}
+
+__std_files := __set_file_standards();
+
+__set_file_standards :: proc() -> [File_Standard.COUNT as int]File {
+	return [File_Standard.COUNT as int]File{
+		File{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE)},
+		File{handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)},
+		File{handle = win32.GetStdHandle(win32.STD_ERROR_HANDLE)},
+	}
+}
+
+get_standard_file :: proc(std: File_Standard) -> ^File {
+	return ^__std_files[std]
+}
+
+
+read_entire_file :: proc(name: string) -> (string, bool) {
+	buf: [300]byte
+	copy(buf[:], name as []byte)
+
+	f, file_ok := open(name)
+	if !file_ok {
+		return "", false
+	}
+	defer close(^f)
+
+	length: i64
+	file_size_ok := win32.GetFileSizeEx(f.handle as win32.HANDLE, ^length) != 0
+	if !file_size_ok {
+		return "", false
+	}
+
+	data := new_slice(u8, length)
+	if data.data == null {
+		return "", false
+	}
+
+	single_read_length: i32
+	total_read: i64
+
+	for total_read < length {
+		remaining := length - total_read
+		to_read: u32
+		MAX :: 1<<32-1
+		if remaining <= MAX {
+			to_read = remaining as u32
+		} else {
+			to_read = MAX
+		}
+
+		win32.ReadFile(f.handle as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, null)
+		if single_read_length <= 0 {
+			free(data.data)
+			return "", false
+		}
+
+		total_read += single_read_length as i64
+	}
+
+	return data as string, true
+}

+ 484 - 0
code/punity.odin

@@ -0,0 +1,484 @@
+#import "win32.odin" as win32
+#import "fmt.odin" as _
+
+CANVAS_WIDTH  :: 128
+CANVAS_HEIGHT :: 128
+CANVAS_SCALE  :: 3
+FRAME_TIME    :: 1.0/30.0
+WINDOW_TITLE  : string : "Punity\x00"
+
+_ := compile_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]byte
+	key_deltas:    [MAX_KEYS]byte
+
+	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: []byte
+	cursor: int
+}
+
+Bank_State :: struct {
+	state: Bank
+	bank: ^Bank
+}
+
+
+Color :: raw_union {
+	using channels: struct{ a, b, g, r: byte }
+	rgba: u32
+}
+
+Palette :: struct {
+	colors: [256]Color
+	colors_count: byte
+}
+
+
+Rect :: 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: []byte
+	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_List :: struct {
+	Item :: struct {
+
+	}
+	items: []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,
+	NONCONVERT         = 0x1D,
+	ACCEPT             = 0x1E,
+	MODECHANGE         = 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.GetQueryPerformanceFrequency()
+time_now :: proc() -> f64 {
+	assert(win32_perf_count_freq != 0)
+
+	counter: i64
+	win32.QueryPerformanceCounter(^counter)
+	result := counter as f64 / win32_perf_count_freq as f64
+	return result
+}
+
+_core: Core
+
+run :: proc(user_init, user_step: proc(c: ^Core)) {
+	using win32
+
+
+	_core.running = true
+
+	win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline #stdcall {
+		win32_app_key_mods :: proc() -> u32 {
+			mods: u32 = 0
+
+			if is_key_down(Key_Code.SHIFT) {
+				mods |= Key.MOD_SHIFT as u32;
+			}
+			if is_key_down(Key_Code.CONTROL) {
+				mods |= Key.MOD_CONTROL as u32;
+			}
+			if is_key_down(Key_Code.MENU) {
+				mods |= Key.MOD_ALT as u32;
+			}
+			if is_key_down(Key_Code.LWIN) || is_key_down(Key_Code.RWIN) {
+				mods |= Key.MOD_SUPER as u32;
+			}
+
+			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:
+			PostQuitMessage(0)
+			_core.running = false
+			return 0
+		}
+
+		return DefWindowProcA(hwnd, msg, wparam, lparam)
+	}
+
+
+	window_class := WNDCLASSEXA{
+		class_name = ("Punity\x00" as string).data, // C-style string
+		size       = size_of(WNDCLASSEXA) as u32,
+		style      = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
+		instance   = GetModuleHandleA(null) as HINSTANCE,
+		wnd_proc   = win32_proc,
+		// wnd_proc   = DefWindowProcA,
+		background = GetStockObject(BLACK_BRUSH) as HBRUSH,
+	}
+
+	if RegisterClassExA(^window_class) == 0 {
+		/*fmt.*/println_err("RegisterClassExA failed")
+		return
+	}
+
+	screen_width  := GetSystemMetrics(SM_CXSCREEN)
+	screen_height := GetSystemMetrics(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(AdjustWindowRect(^rc, style, 0) != 0)
+
+	wt := WINDOW_TITLE
+
+	win32_window := CreateWindowExA(0,
+	                                window_class.class_name,
+	                                WINDOW_TITLE.data,
+	                                // wt.data,
+	                                style,
+	                                rc.left, rc.top,
+	                                rc.right-rc.left, rc.bottom-rc.top,
+	                                null, null, window_class.instance,
+	                                null);
+
+	if win32_window == null {
+		/*fmt.*/println_err("CreateWindowExA failed")
+		return
+	}
+
+
+	window_bmi: BITMAPINFO;
+	window_bmi.size        = size_of(BITMAPINFO.HEADER) as u32
+	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)
+
+
+	ShowWindow(win32_window, SW_SHOW)
+
+	window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT);
+	assert(window_buffer.data != null)
+	defer free(window_buffer.data)
+
+	for i := 0; i < window_buffer.count; i++ {
+		window_buffer[i] = 0xff00ff
+	}
+
+
+	prev_time, curr_time,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
+
+		{
+			data: [128]byte
+			buf := data[:0]
+			/*fmt.*/print_to_buffer(^buf, "Punity: % ms\x00", dt*1000)
+			win32.SetWindowTextA(win32_window, buf.data)
+		}
+
+
+		for y := 0; y < CANVAS_HEIGHT; y++ {
+			for x := 0; x < CANVAS_WIDTH; x++ {
+				g := (x % 32) * 8
+				b := (y % 32) * 8
+				window_buffer[x + y*CANVAS_WIDTH] = (g << 8 | b) as u32
+			}
+		}
+
+		memory_zero(^_core.key_deltas[0], size_of(_core.key_deltas[0]))
+
+
+		for PeekMessageA(^message, null, 0, 0, PM_REMOVE) != 0 {
+			if message.message == WM_QUIT {
+				_core.running = false
+			}
+			TranslateMessage(^message)
+			DispatchMessageA(^message)
+		}
+
+		user_step(^_core)
+
+		dc := GetDC(win32_window);
+		StretchDIBits(dc,
+		              0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
+		              0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+		              window_buffer.data,
+		              ^window_bmi,
+		              DIB_RGB_COLORS,
+		              SRCCOPY)
+		ReleaseDC(win32_window, dc)
+
+
+		{
+			delta := time_now() - prev_time
+			ms := ((FRAME_TIME - delta) * 1000) as i32
+			if ms > 0 {
+				win32.Sleep(ms)
+			}
+		}
+
+		_core.frame++
+	}
+}

+ 64 - 67
src/checker/checker.cpp

@@ -237,8 +237,6 @@ struct Checker {
 
 
 	gbArray(Type *) proc_stack;
 	gbArray(Type *) proc_stack;
 	b32 in_defer; // TODO(bill): Actually handle correctly
 	b32 in_defer; // TODO(bill): Actually handle correctly
-
-	ErrorCollector error_collector;
 };
 };
 
 
 gb_global Scope *universal_scope = NULL;
 gb_global Scope *universal_scope = NULL;
@@ -635,18 +633,23 @@ b32 add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
 		if (insert_entity) {
 		if (insert_entity) {
 			Entity *up = insert_entity->using_parent;
 			Entity *up = insert_entity->using_parent;
 			if (up != NULL) {
 			if (up != NULL) {
-				error(&c->error_collector, entity->token,
+				error(entity->token,
 				      "Redeclararation of `%.*s` in this scope through `using`\n"
 				      "Redeclararation of `%.*s` in this scope through `using`\n"
 				      "\tat %.*s(%td:%td)",
 				      "\tat %.*s(%td:%td)",
 				      LIT(entity->token.string),
 				      LIT(entity->token.string),
 				      LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
 				      LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
 				return false;
 				return false;
 			} else {
 			} else {
-				error(&c->error_collector, entity->token,
+				TokenPos pos = insert_entity->token.pos;
+				if (token_pos_are_equal(pos, entity->token.pos)) {
+					// NOTE(bill): Error should have been handled already
+					return false;
+				}
+				error(entity->token,
 				      "Redeclararation of `%.*s` in this scope\n"
 				      "Redeclararation of `%.*s` in this scope\n"
 				      "\tat %.*s(%td:%td)",
 				      "\tat %.*s(%td:%td)",
 				      LIT(entity->token.string),
 				      LIT(entity->token.string),
-				      LIT(entity->token.pos.file), entity->token.pos.line, entity->token.pos.column);
+				      LIT(pos.file), pos.line, pos.column);
 				return false;
 				return false;
 			}
 			}
 		}
 		}
@@ -797,7 +800,7 @@ Type *const curr_procedure(Checker *c) {
 
 
 void add_curr_ast_file(Checker *c, AstFile *file) {
 void add_curr_ast_file(Checker *c, AstFile *file) {
 	TokenPos zero_pos = {};
 	TokenPos zero_pos = {};
-	c->error_collector.prev = zero_pos;
+	global_error_collector.prev = zero_pos;
 	c->curr_ast_file = file;
 	c->curr_ast_file = file;
 }
 }
 
 
@@ -924,64 +927,60 @@ void check_parsed_files(Checker *c) {
 				// NOTE(bill): ignore
 				// NOTE(bill): ignore
 			case_end;
 			case_end;
 
 
-			case_ast_node(vd, VarDecl, decl);
-				switch (vd->kind) {
-				case Declaration_Immutable: {
-					gb_for_array(i, vd->values) {
-						AstNode *name = vd->names[i];
-						AstNode *value = vd->values[i];
-						ExactValue v = {ExactValue_Invalid};
-						Entity *e = make_entity_constant(c->allocator, file_scope, name->Ident, NULL, v);
-						DeclInfo *di = make_declaration_info(c->allocator, file_scope);
-						di->type_expr = vd->type;
-						di->init_expr = value;
-						add_file_entity(c, file_scope, name, e, di);
-					}
+			case_ast_node(cd, ConstDecl, decl);
+				gb_for_array(i, cd->values) {
+					AstNode *name = cd->names[i];
+					AstNode *value = cd->values[i];
+					ExactValue v = {ExactValue_Invalid};
+					Entity *e = make_entity_constant(c->allocator, file_scope, name->Ident, NULL, v);
+					DeclInfo *di = make_declaration_info(c->allocator, file_scope);
+					di->type_expr = cd->type;
+					di->init_expr = value;
+					add_file_entity(c, file_scope, name, e, di);
+				}
 
 
-					isize lhs_count = gb_array_count(vd->names);
-					isize rhs_count = gb_array_count(vd->values);
+				isize lhs_count = gb_array_count(cd->names);
+				isize rhs_count = gb_array_count(cd->values);
 
 
-					if (rhs_count == 0 && vd->type == NULL) {
-						error(&c->error_collector, ast_node_token(decl), "Missing type or initial expression");
-					} else if (lhs_count < rhs_count) {
-						error(&c->error_collector, ast_node_token(decl), "Extra initial expression");
-					}
-				} break;
-
-				case Declaration_Mutable: {
-					isize entity_count = gb_array_count(vd->names);
-					isize entity_index = 0;
-					Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
-					DeclInfo *di = NULL;
-					if (gb_array_count(vd->values) > 0) {
-						di = make_declaration_info(gb_heap_allocator(), file_scope);
-						di->entities = entities;
-						di->entity_count = entity_count;
-						di->type_expr = vd->type;
-						di->init_expr = vd->values[0]; // TODO(bill): Is this correct?
-					}
+				if (rhs_count == 0 && cd->type == NULL) {
+					error(ast_node_token(decl), "Missing type or initial expression");
+				} else if (lhs_count < rhs_count) {
+					error(ast_node_token(decl), "Extra initial expression");
+				}
+			case_end;
 
 
-					gb_for_array(i, vd->names) {
-						AstNode *name = vd->names[i];
-						AstNode *value = NULL;
-						if (i < gb_array_count(vd->values)) {
-							value = vd->values[i];
-						}
-						Entity *e = make_entity_variable(c->allocator, file_scope, name->Ident, NULL);
-						entities[entity_index++] = e;
-
-						DeclInfo *d = di;
-						if (d == NULL) {
-							AstNode *init_expr = value;
-							d = make_declaration_info(gb_heap_allocator(), file_scope);
-							d->type_expr = vd->type;
-							d->init_expr = init_expr;
-							d->var_decl_tags = vd->tags;
-						}
+			case_ast_node(vd, VarDecl, decl);
+				isize entity_count = gb_array_count(vd->names);
+				isize entity_index = 0;
+				Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
+				DeclInfo *di = NULL;
+				if (gb_array_count(vd->values) > 0) {
+					di = make_declaration_info(gb_heap_allocator(), file_scope);
+					di->entities = entities;
+					di->entity_count = entity_count;
+					di->type_expr = vd->type;
+					di->init_expr = vd->values[0]; // TODO(bill): Is this correct?
+				}
 
 
-						add_file_entity(c, file_scope, name, e, d);
+				gb_for_array(i, vd->names) {
+					AstNode *name = vd->names[i];
+					AstNode *value = NULL;
+					if (i < gb_array_count(vd->values)) {
+						value = vd->values[i];
+					}
+					Entity *e = make_entity_variable(c->allocator, file_scope, name->Ident, NULL);
+					entities[entity_index++] = e;
+
+					DeclInfo *d = di;
+					if (d == NULL) {
+						AstNode *init_expr = value;
+						d = make_declaration_info(gb_heap_allocator(), file_scope);
+						d->type_expr = vd->type;
+						d->init_expr = init_expr;
+						d->var_decl_tags = vd->tags;
 					}
 					}
-				} break;
+
+					add_file_entity(c, file_scope, name, e, d);
 				}
 				}
 			case_end;
 			case_end;
 
 
@@ -1003,7 +1002,7 @@ void check_parsed_files(Checker *c) {
 			case_end;
 			case_end;
 
 
 			default:
 			default:
-				error(&c->error_collector, ast_node_token(decl), "Only declarations are allowed at file scope");
+				error(ast_node_token(decl), "Only declarations are allowed at file scope");
 				break;
 				break;
 			}
 			}
 		}
 		}
@@ -1046,13 +1045,11 @@ void check_parsed_files(Checker *c) {
 						continue;
 						continue;
 					}
 					}
 					// NOTE(bill): Do not add other imported entities
 					// NOTE(bill): Do not add other imported entities
-					if (e->kind != Entity_ImportName) {
-						if (is_entity_exported(e)) {
-							add_entity(c, file_scope, NULL, e);
-							if (!id->is_load) {
-								HashKey key = hash_string(e->token.string);
-								map_set(&file_scope->implicit, key, e);
-							}
+					if (is_entity_exported(e)) {
+						add_entity(c, file_scope, NULL, e);
+						if (!id->is_load) { // `#import`ed entities don't get exported
+							HashKey key = hash_string(e->token.string);
+							map_set(&file_scope->implicit, key, e);
 						}
 						}
 					}
 					}
 				}
 				}

+ 3 - 0
src/checker/entity.cpp

@@ -67,6 +67,9 @@ struct Entity {
 };
 };
 
 
 b32 is_entity_exported(Entity *e) {
 b32 is_entity_exported(Entity *e) {
+	if (e->kind == Entity_ImportName) {
+		return false;
+	}
 	// TODO(bill): Do I really want non-exported entities?
 	// TODO(bill): Do I really want non-exported entities?
 	// if (e->token.string.len >= 1 &&
 	// if (e->token.string.len >= 1 &&
 	    // e->token.string.text[0] == '_') {
 	    // e->token.string.text[0] == '_') {

File diff suppressed because it is too large
+ 149 - 163
src/checker/expr.cpp


+ 158 - 148
src/checker/stmt.cpp

@@ -203,7 +203,7 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
 
 
 		gbString str = expr_to_string(op_b.expr);
 		gbString str = expr_to_string(op_b.expr);
 		defer (gb_string_free(str));
 		defer (gb_string_free(str));
-		error(&c->error_collector, ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
+		error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
 	} break;
 	} break;
 	}
 	}
 
 
@@ -225,7 +225,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
 			defer (gb_string_free(expr_str));
 			defer (gb_string_free(expr_str));
 
 
 			// TODO(bill): is this a good enough error message?
 			// TODO(bill): is this a good enough error message?
-			error(&c->error_collector, ast_node_token(operand->expr),
+			error(ast_node_token(operand->expr),
 			      "Cannot assign builtin procedure `%s` in %.*s",
 			      "Cannot assign builtin procedure `%s` in %.*s",
 			      expr_str,
 			      expr_str,
 			      LIT(context_name));
 			      LIT(context_name));
@@ -244,7 +244,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
 		Type *t = operand->type;
 		Type *t = operand->type;
 		if (is_type_untyped(t)) {
 		if (is_type_untyped(t)) {
 			if (t == t_invalid) {
 			if (t == t_invalid) {
-				error(&c->error_collector, e->token, "Use of untyped thing in %.*s", LIT(context_name));
+				error(e->token, "Use of untyped thing in %.*s", LIT(context_name));
 				e->type = t_invalid;
 				e->type = t_invalid;
 				return NULL;
 				return NULL;
 			}
 			}
@@ -285,6 +285,12 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
 	}
 	}
 
 
 	isize rhs_count = gb_array_count(operands);
 	isize rhs_count = gb_array_count(operands);
+	gb_for_array(i, operands) {
+		if (operands[i].mode == Addressing_Invalid) {
+			rhs_count--;
+		}
+	}
+
 
 
 	isize max = gb_min(lhs_count, rhs_count);
 	isize max = gb_min(lhs_count, rhs_count);
 	for (isize i = 0; i < max; i++) {
 	for (isize i = 0; i < max; i++) {
@@ -292,7 +298,7 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
 	}
 	}
 
 
 	if (rhs_count > 0 && lhs_count != rhs_count) {
 	if (rhs_count > 0 && lhs_count != rhs_count) {
-		error(&c->error_collector, lhs[0]->token, "Assignment count mismatch `%td` := `%td`", lhs_count, rhs_count);
+		error(lhs[0]->token, "Assignment count mismatch `%td` := `%td`", lhs_count, rhs_count);
 	}
 	}
 }
 }
 
 
@@ -307,7 +313,7 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) {
 
 
 	if (operand->mode != Addressing_Constant) {
 	if (operand->mode != Addressing_Constant) {
 		// TODO(bill): better error
 		// TODO(bill): better error
-		error(&c->error_collector, ast_node_token(operand->expr),
+		error(ast_node_token(operand->expr),
 		      "`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string));
 		      "`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string));
 		if (e->type == NULL)
 		if (e->type == NULL)
 			e->type = t_invalid;
 			e->type = t_invalid;
@@ -343,7 +349,7 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_e
 		if (!is_type_constant_type(t)) {
 		if (!is_type_constant_type(t)) {
 			gbString str = type_to_string(t);
 			gbString str = type_to_string(t);
 			defer (gb_string_free(str));
 			defer (gb_string_free(str));
-			error(&c->error_collector, ast_node_token(type_expr),
+			error(ast_node_token(type_expr),
 			      "Invalid constant type `%s`", str);
 			      "Invalid constant type `%s`", str);
 			e->type = t_invalid;
 			e->type = t_invalid;
 			return;
 			return;
@@ -411,13 +417,13 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
 						Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
 						Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
 						Entity *prev = scope_insert_entity(c->context.scope, uvar);
 						Entity *prev = scope_insert_entity(c->context.scope, uvar);
 						if (prev != NULL) {
 						if (prev != NULL) {
-							error(&c->error_collector, e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
+							error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
 							break;
 							break;
 						}
 						}
 					}
 					}
 				}
 				}
 			} else {
 			} else {
-				error(&c->error_collector, e->token, "`using` can only be applied to variables of type struct or raw_union");
+				error(e->token, "`using` can only be applied to variables of type struct or raw_union");
 				break;
 				break;
 			}
 			}
 		}
 		}
@@ -429,7 +435,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
 	check_stmt_list(c, bs->stmts, 0);
 	check_stmt_list(c, bs->stmts, 0);
 	if (type->Proc.result_count > 0) {
 	if (type->Proc.result_count > 0) {
 		if (!check_is_terminating(body)) {
 		if (!check_is_terminating(body)) {
-			error(&c->error_collector, bs->close, "Missing return statement at the end of the procedure");
+			error(bs->close, "Missing return statement at the end of the procedure");
 		}
 		}
 	}
 	}
 	pop_procedure(c);
 	pop_procedure(c);
@@ -501,20 +507,20 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
 				gbString str = type_to_string(proc_type);
 				gbString str = type_to_string(proc_type);
 				defer (gb_string_free(str));
 				defer (gb_string_free(str));
 
 
-				error(&c->error_collector, e->token,
+				error(e->token,
 				      "Procedure type of `main` was expected to be `proc()`, got %s", str);
 				      "Procedure type of `main` was expected to be `proc()`, got %s", str);
 			}
 			}
 		}
 		}
 	}
 	}
 
 
 	if (is_inline && is_no_inline) {
 	if (is_inline && is_no_inline) {
-		error(&c->error_collector, ast_node_token(pd->type),
+		error(ast_node_token(pd->type),
 		      "You cannot apply both `inline` and `no_inline` to a procedure");
 		      "You cannot apply both `inline` and `no_inline` to a procedure");
 	}
 	}
 
 
 	if (pd->body != NULL) {
 	if (pd->body != NULL) {
 		if (is_foreign) {
 		if (is_foreign) {
-			error(&c->error_collector, ast_node_token(pd->body),
+			error(ast_node_token(pd->body),
 			      "A procedure tagged as `#foreign` cannot have a body");
 			      "A procedure tagged as `#foreign` cannot have a body");
 		}
 		}
 
 
@@ -543,7 +549,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
 			Type *this_type = get_base_type(e->type);
 			Type *this_type = get_base_type(e->type);
 			Type *other_type = get_base_type(f->type);
 			Type *other_type = get_base_type(f->type);
 			if (!are_signatures_similar_enough(this_type, other_type)) {
 			if (!are_signatures_similar_enough(this_type, other_type)) {
-				error(&c->error_collector, ast_node_token(d->proc_decl),
+				error(ast_node_token(d->proc_decl),
 				      "Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
 				      "Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
 				      "\tat %.*s(%td:%td)",
 				      "\tat %.*s(%td:%td)",
 				      LIT(name), LIT(pos.file), pos.line, pos.column);
 				      LIT(name), LIT(pos.file), pos.line, pos.column);
@@ -662,118 +668,115 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, Cyc
 
 
 
 
 
 
-void check_var_decl_node(Checker *c, AstNode *node) {
+void check_var_decl(Checker *c, AstNode *node) {
 	ast_node(vd, VarDecl, node);
 	ast_node(vd, VarDecl, node);
 	isize entity_count = gb_array_count(vd->names);
 	isize entity_count = gb_array_count(vd->names);
 	isize entity_index = 0;
 	isize entity_index = 0;
 	Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
 	Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
-	switch (vd->kind) {
-	case Declaration_Mutable: {
-		Entity **new_entities = gb_alloc_array(c->allocator, Entity *, entity_count);
-		isize new_entity_count = 0;
-
-		gb_for_array(i, vd->names) {
-			AstNode *name = vd->names[i];
-			Entity *entity = NULL;
-			Token token = name->Ident;
-			if (name->kind == AstNode_Ident) {
-				String str = token.string;
-				Entity *found = NULL;
-				// NOTE(bill): Ignore assignments to `_`
-				b32 can_be_ignored = are_strings_equal(str, make_string("_"));
-				if (!can_be_ignored) {
-					found = current_scope_lookup_entity(c->context.scope, str);
-				}
-				if (found == NULL) {
-					entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
-					if (!can_be_ignored) {
-						new_entities[new_entity_count++] = entity;
-					}
-					add_entity_definition(&c->info, name, entity);
-				} else {
-					TokenPos pos = found->token.pos;
-					error(&c->error_collector, token,
-					      "Redeclaration of `%.*s` in this scope\n"
-					      "\tat %.*s(%td:%td)",
-					      LIT(str), LIT(pos.file), pos.line, pos.column);
-					entity = found;
-				}
+
+	gb_for_array(i, vd->names) {
+		AstNode *name = vd->names[i];
+		Entity *entity = NULL;
+		Token token = name->Ident;
+		if (name->kind == AstNode_Ident) {
+			String str = token.string;
+			Entity *found = NULL;
+			// NOTE(bill): Ignore assignments to `_`
+			b32 can_be_ignored = are_strings_equal(str, make_string("_"));
+			if (!can_be_ignored) {
+				found = current_scope_lookup_entity(c->context.scope, str);
+			}
+			if (found == NULL) {
+				entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
+				add_entity_definition(&c->info, name, entity);
 			} else {
 			} else {
-				error(&c->error_collector, token, "A variable declaration must be an identifier");
+				TokenPos pos = found->token.pos;
+				error(token,
+				      "Redeclaration of `%.*s` in this scope\n"
+				      "\tat %.*s(%td:%td)",
+				      LIT(str), LIT(pos.file), pos.line, pos.column);
+				entity = found;
 			}
 			}
-			if (entity == NULL)
-				entity = make_entity_dummy_variable(c->allocator, c->global_scope, token);
-			entities[entity_index++] = entity;
-		}
-
-		Type *init_type = NULL;
-		if (vd->type) {
-			init_type = check_type(c, vd->type, NULL);
-			if (init_type == NULL)
-				init_type = t_invalid;
+		} else {
+			error(token, "A variable declaration must be an identifier");
 		}
 		}
+		if (entity == NULL)
+			entity = make_entity_dummy_variable(c->allocator, c->global_scope, token);
+		entities[entity_index++] = entity;
+	}
 
 
-		for (isize i = 0; i < entity_count; i++) {
-			Entity *e = entities[i];
-			GB_ASSERT(e != NULL);
-			if (e->Variable.visited) {
-				e->type = t_invalid;
-				continue;
-			}
-			e->Variable.visited = true;
+	Type *init_type = NULL;
+	if (vd->type) {
+		init_type = check_type(c, vd->type, NULL);
+		if (init_type == NULL)
+			init_type = t_invalid;
+	}
 
 
-			if (e->type == NULL)
-				e->type = init_type;
+	for (isize i = 0; i < entity_count; i++) {
+		Entity *e = entities[i];
+		GB_ASSERT(e != NULL);
+		if (e->Variable.visited) {
+			e->type = t_invalid;
+			continue;
 		}
 		}
+		e->Variable.visited = true;
 
 
-		check_init_variables(c, entities, entity_count, vd->values, make_string("variable declaration"));
+		if (e->type == NULL)
+			e->type = init_type;
+	}
 
 
-		gb_for_array(i, vd->names) {
-			add_entity(c, c->context.scope, vd->names[i], new_entities[i]);
-		}
+	check_init_variables(c, entities, entity_count, vd->values, make_string("variable declaration"));
 
 
-	} break;
+	gb_for_array(i, vd->names) {
+		if (entities[i] != NULL) {
+			add_entity(c, c->context.scope, vd->names[i], entities[i]);
+		}
+	}
 
 
-	case Declaration_Immutable: {
-		gb_for_array(i, vd->values) {
-			AstNode *name = vd->names[i];
-			AstNode *value = vd->values[i];
+}
 
 
-			GB_ASSERT(name->kind == AstNode_Ident);
-			ExactValue v = {ExactValue_Invalid};
-			String str = name->Ident.string;
-			Entity *found = current_scope_lookup_entity(c->context.scope, str);
-			if (found == NULL) {
-				Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
-				entities[entity_index++] = e;
-				check_const_decl(c, e, vd->type, value);
-			} else {
-				entities[entity_index++] = found;
-			}
-		}
 
 
-		isize lhs_count = gb_array_count(vd->names);
-		isize rhs_count = gb_array_count(vd->values);
+void check_const_decl(Checker *c, AstNode *node) {
+	ast_node(vd, ConstDecl, node);
+	isize entity_count = gb_array_count(vd->names);
+	isize entity_index = 0;
+	Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
 
 
-		// TODO(bill): Better error messages or is this good enough?
-		if (rhs_count == 0 && vd->type == NULL) {
-			error(&c->error_collector, ast_node_token(node), "Missing type or initial expression");
-		} else if (lhs_count < rhs_count) {
-			error(&c->error_collector, ast_node_token(node), "Extra initial expression");
+	gb_for_array(i, vd->values) {
+		AstNode *name = vd->names[i];
+		AstNode *value = vd->values[i];
+
+		GB_ASSERT(name->kind == AstNode_Ident);
+		ExactValue v = {ExactValue_Invalid};
+		String str = name->Ident.string;
+		Entity *found = current_scope_lookup_entity(c->context.scope, str);
+		if (found == NULL) {
+			Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
+			entities[entity_index++] = e;
+			check_const_decl(c, e, vd->type, value);
+		} else {
+			entities[entity_index++] = found;
 		}
 		}
+	}
 
 
-		gb_for_array(i, vd->names) {
-			add_entity(c, c->context.scope, vd->names[i], entities[i]);
-		}
-	} break;
+	isize lhs_count = gb_array_count(vd->names);
+	isize rhs_count = gb_array_count(vd->values);
 
 
-	default:
-		error(&c->error_collector, ast_node_token(node), "Unknown variable declaration kind. Probably an invalid AST.");
-		return;
+	// TODO(bill): Better error messages or is this good enough?
+	if (rhs_count == 0 && vd->type == NULL) {
+		error(ast_node_token(node), "Missing type or initial expression");
+	} else if (lhs_count < rhs_count) {
+		error(ast_node_token(node), "Extra initial expression");
+	}
+
+	gb_for_array(i, vd->names) {
+		add_entity(c, c->context.scope, vd->names[i], entities[i]);
 	}
 	}
 }
 }
 
 
 
 
+
+
 void check_stmt(Checker *c, AstNode *node, u32 flags) {
 void check_stmt(Checker *c, AstNode *node, u32 flags) {
 	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
 	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
 	switch (node->kind) {
 	switch (node->kind) {
@@ -786,7 +789,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		ExprKind kind = check_expr_base(c, &operand, es->expr);
 		ExprKind kind = check_expr_base(c, &operand, es->expr);
 		switch (operand.mode) {
 		switch (operand.mode) {
 		case Addressing_Type:
 		case Addressing_Type:
-			error(&c->error_collector, ast_node_token(node), "Is not an expression");
+			error(ast_node_token(node), "Is not an expression");
 			break;
 			break;
 		case Addressing_NoValue:
 		case Addressing_NoValue:
 			return;
 			return;
@@ -800,14 +803,14 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				return;
 				return;
 			}
 			}
 
 
-			error(&c->error_collector, ast_node_token(node), "Expression is not used: `%s`", expr_str);
+			error(ast_node_token(node), "Expression is not used: `%s`", expr_str);
 		} break;
 		} break;
 		}
 		}
 	case_end;
 	case_end;
 
 
 	case_ast_node(ts, TagStmt, node);
 	case_ast_node(ts, TagStmt, node);
 		// TODO(bill): Tag Statements
 		// TODO(bill): Tag Statements
-		error(&c->error_collector, ast_node_token(node), "Tag statements are not supported yet");
+		error(ast_node_token(node), "Tag statements are not supported yet");
 		check_stmt(c, ts->stmt, flags);
 		check_stmt(c, ts->stmt, flags);
 	case_end;
 	case_end;
 
 
@@ -823,7 +826,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			op.string.len = 1;
 			op.string.len = 1;
 			break;
 			break;
 		default:
 		default:
-			error(&c->error_collector, ids->op, "Unknown inc/dec operation %.*s", LIT(ids->op.string));
+			error(ids->op, "Unknown inc/dec operation %.*s", LIT(ids->op.string));
 			return;
 			return;
 		}
 		}
 
 
@@ -832,7 +835,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		if (operand.mode == Addressing_Invalid)
 		if (operand.mode == Addressing_Invalid)
 			return;
 			return;
 		if (!is_type_numeric(operand.type)) {
 		if (!is_type_numeric(operand.type)) {
-			error(&c->error_collector, ids->op, "Non numeric type");
+			error(ids->op, "Non numeric type");
 			return;
 			return;
 		}
 		}
 
 
@@ -855,7 +858,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		case Token_Eq: {
 		case Token_Eq: {
 			// a, b, c = 1, 2, 3;  // Multisided
 			// a, b, c = 1, 2, 3;  // Multisided
 			if (gb_array_count(as->lhs) == 0) {
 			if (gb_array_count(as->lhs) == 0) {
-				error(&c->error_collector, as->op, "Missing lhs in assignment statement");
+				error(as->op, "Missing lhs in assignment statement");
 				return;
 				return;
 			}
 			}
 
 
@@ -888,7 +891,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				check_assignment_variable(c, &operands[i], lhs);
 				check_assignment_variable(c, &operands[i], lhs);
 			}
 			}
 			if (lhs_count != rhs_count) {
 			if (lhs_count != rhs_count) {
-				error(&c->error_collector, ast_node_token(as->lhs[0]), "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
+				error(ast_node_token(as->lhs[0]), "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
 			}
 			}
 		} break;
 		} break;
 
 
@@ -896,11 +899,11 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			// a += 1; // Single-sided
 			// a += 1; // Single-sided
 			Token op = as->op;
 			Token op = as->op;
 			if (gb_array_count(as->lhs) != 1 || gb_array_count(as->rhs) != 1) {
 			if (gb_array_count(as->lhs) != 1 || gb_array_count(as->rhs) != 1) {
-				error(&c->error_collector, op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string));
+				error(op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string));
 				return;
 				return;
 			}
 			}
 			if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) {
 			if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) {
-				error(&c->error_collector, op, "Unknown Assignment operation `%.*s`", LIT(op.string));
+				error(op, "Unknown Assignment operation `%.*s`", LIT(op.string));
 				return;
 				return;
 			}
 			}
 			// TODO(bill): Check if valid assignment operator
 			// TODO(bill): Check if valid assignment operator
@@ -938,7 +941,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		check_expr(c, &operand, is->cond);
 		check_expr(c, &operand, is->cond);
 		if (operand.mode != Addressing_Invalid &&
 		if (operand.mode != Addressing_Invalid &&
 		    !is_type_boolean(operand.type)) {
 		    !is_type_boolean(operand.type)) {
-			error(&c->error_collector, ast_node_token(is->cond),
+			error(ast_node_token(is->cond),
 			            "Non-boolean condition in `if` statement");
 			            "Non-boolean condition in `if` statement");
 		}
 		}
 
 
@@ -951,7 +954,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				check_stmt(c, is->else_stmt, mod_flags);
 				check_stmt(c, is->else_stmt, mod_flags);
 				break;
 				break;
 			default:
 			default:
-				error(&c->error_collector, ast_node_token(is->else_stmt),
+				error(ast_node_token(is->else_stmt),
 				            "Invalid `else` statement in `if` statement");
 				            "Invalid `else` statement in `if` statement");
 				break;
 				break;
 			}
 			}
@@ -962,23 +965,28 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		GB_ASSERT(gb_array_count(c->proc_stack) > 0);
 		GB_ASSERT(gb_array_count(c->proc_stack) > 0);
 
 
 		if (c->in_defer) {
 		if (c->in_defer) {
-			error(&c->error_collector, rs->token, "You cannot `return` within a defer statement");
+			error(rs->token, "You cannot `return` within a defer statement");
 			// TODO(bill): Should I break here?
 			// TODO(bill): Should I break here?
 			break;
 			break;
 		}
 		}
 
 
 		Type *proc_type = c->proc_stack[gb_array_count(c->proc_stack)-1];
 		Type *proc_type = c->proc_stack[gb_array_count(c->proc_stack)-1];
 		isize result_count = 0;
 		isize result_count = 0;
-		if (proc_type->Proc.results)
+		if (proc_type->Proc.results) {
 			result_count = proc_type->Proc.results->Tuple.variable_count;
 			result_count = proc_type->Proc.results->Tuple.variable_count;
+		}
 		if (result_count != gb_array_count(rs->results)) {
 		if (result_count != gb_array_count(rs->results)) {
-			error(&c->error_collector, rs->token, "Expected %td return %s, got %td",
+			error(rs->token, "Expected %td return %s, got %td",
 			      result_count,
 			      result_count,
 			      (result_count != 1 ? "values" : "value"),
 			      (result_count != 1 ? "values" : "value"),
 			      gb_array_count(rs->results));
 			      gb_array_count(rs->results));
 		} else if (result_count > 0) {
 		} else if (result_count > 0) {
-			auto *tuple = &proc_type->Proc.results->Tuple;
-			check_init_variables(c, tuple->variables, tuple->variable_count,
+			Entity **variables = NULL;
+			if (proc_type->Proc.results != NULL) {
+				auto *tuple = &proc_type->Proc.results->Tuple;
+				variables = tuple->variables;
+			}
+			check_init_variables(c, variables, result_count,
 			                     rs->results, make_string("return statement"));
 			                     rs->results, make_string("return statement"));
 		}
 		}
 	case_end;
 	case_end;
@@ -995,7 +1003,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			check_expr(c, &operand, fs->cond);
 			check_expr(c, &operand, fs->cond);
 			if (operand.mode != Addressing_Invalid &&
 			if (operand.mode != Addressing_Invalid &&
 			    !is_type_boolean(operand.type)) {
 			    !is_type_boolean(operand.type)) {
-				error(&c->error_collector, ast_node_token(fs->cond),
+				error(ast_node_token(fs->cond),
 				      "Non-boolean condition in `for` statement");
 				      "Non-boolean condition in `for` statement");
 			}
 			}
 		}
 		}
@@ -1040,13 +1048,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 					default_stmt = stmt;
 					default_stmt = stmt;
 				}
 				}
 			} else {
 			} else {
-				error(&c->error_collector, ast_node_token(stmt), "Invalid AST - expected case clause");
+				error(ast_node_token(stmt), "Invalid AST - expected case clause");
 			}
 			}
 
 
 			if (default_stmt != NULL) {
 			if (default_stmt != NULL) {
 				if (first_default != NULL) {
 				if (first_default != NULL) {
 					TokenPos pos = ast_node_token(first_default).pos;
 					TokenPos pos = ast_node_token(first_default).pos;
-					error(&c->error_collector, ast_node_token(stmt),
+					error(ast_node_token(stmt),
 					      "multiple `default` clauses\n"
 					      "multiple `default` clauses\n"
 					      "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
 					      "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
 				} else {
 				} else {
@@ -1114,8 +1122,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 							if (are_types_identical(y.type, tap.type)) {
 							if (are_types_identical(y.type, tap.type)) {
 								TokenPos pos = tap.token.pos;
 								TokenPos pos = tap.token.pos;
 								gbString expr_str = expr_to_string(y.expr);
 								gbString expr_str = expr_to_string(y.expr);
-								error(&c->error_collector,
-								      ast_node_token(y.expr),
+								error(ast_node_token(y.expr),
 								      "Duplicate case `%s`\n"
 								      "Duplicate case `%s`\n"
 								      "\tprevious case at %.*s(%td:%td)",
 								      "\tprevious case at %.*s(%td:%td)",
 								      expr_str,
 								      expr_str,
@@ -1158,7 +1165,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		if (!is_type_pointer(x.type) || !is_type_union(type_deref(x.type))) {
 		if (!is_type_pointer(x.type) || !is_type_union(type_deref(x.type))) {
 			gbString str = type_to_string(x.type);
 			gbString str = type_to_string(x.type);
 			defer (gb_string_free(str));
 			defer (gb_string_free(str));
-			error(&c->error_collector, ast_node_token(x.expr),
+			error(ast_node_token(x.expr),
 			      "Expected a pointer to a union for this type match expression, got `%s`", str);
 			      "Expected a pointer to a union for this type match expression, got `%s`", str);
 			break;
 			break;
 		}
 		}
@@ -1177,13 +1184,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 					default_stmt = stmt;
 					default_stmt = stmt;
 				}
 				}
 			} else {
 			} else {
-				error(&c->error_collector, ast_node_token(stmt), "Invalid AST - expected case clause");
+				error(ast_node_token(stmt), "Invalid AST - expected case clause");
 			}
 			}
 
 
 			if (default_stmt != NULL) {
 			if (default_stmt != NULL) {
 				if (first_default != NULL) {
 				if (first_default != NULL) {
 					TokenPos pos = ast_node_token(first_default).pos;
 					TokenPos pos = ast_node_token(first_default).pos;
-					error(&c->error_collector, ast_node_token(stmt),
+					error(ast_node_token(stmt),
 					      "multiple `default` clauses\n"
 					      "multiple `default` clauses\n"
 					      "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
 					      "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
 				} else {
 				} else {
@@ -1226,7 +1233,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				if (!tag_type_found) {
 				if (!tag_type_found) {
 					gbString type_str = type_to_string(y.type);
 					gbString type_str = type_to_string(y.type);
 					defer (gb_string_free(type_str));
 					defer (gb_string_free(type_str));
-					error(&c->error_collector, ast_node_token(y.expr),
+					error(ast_node_token(y.expr),
 					      "Unknown tag type, got `%s`", type_str);
 					      "Unknown tag type, got `%s`", type_str);
 					continue;
 					continue;
 				}
 				}
@@ -1237,8 +1244,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				if (found) {
 				if (found) {
 					TokenPos pos = cc->token.pos;
 					TokenPos pos = cc->token.pos;
 					gbString expr_str = expr_to_string(y.expr);
 					gbString expr_str = expr_to_string(y.expr);
-					error(&c->error_collector,
-					      ast_node_token(y.expr),
+					error(ast_node_token(y.expr),
 					      "Duplicate type case `%s`\n"
 					      "Duplicate type case `%s`\n"
 					      "\tprevious type case at %.*s(%td:%td)",
 					      "\tprevious type case at %.*s(%td:%td)",
 					      expr_str,
 					      expr_str,
@@ -1266,7 +1272,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 
 
 	case_ast_node(ds, DeferStmt, node);
 	case_ast_node(ds, DeferStmt, node);
 		if (is_ast_node_decl(ds->stmt)) {
 		if (is_ast_node_decl(ds->stmt)) {
-			error(&c->error_collector, ds->token, "You cannot defer a declaration");
+			error(ds->token, "You cannot defer a declaration");
 		} else {
 		} else {
 			b32 out_in_defer = c->in_defer;
 			b32 out_in_defer = c->in_defer;
 			c->in_defer = true;
 			c->in_defer = true;
@@ -1280,18 +1286,18 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		switch (token.kind) {
 		switch (token.kind) {
 		case Token_break:
 		case Token_break:
 			if ((flags & Stmt_BreakAllowed) == 0)
 			if ((flags & Stmt_BreakAllowed) == 0)
-				error(&c->error_collector, token, "`break` only allowed in `for` or `match` statements");
+				error(token, "`break` only allowed in `for` or `match` statements");
 			break;
 			break;
 		case Token_continue:
 		case Token_continue:
 			if ((flags & Stmt_ContinueAllowed) == 0)
 			if ((flags & Stmt_ContinueAllowed) == 0)
-				error(&c->error_collector, token, "`continue` only allowed in `for` statements");
+				error(token, "`continue` only allowed in `for` statements");
 			break;
 			break;
 		case Token_fallthrough:
 		case Token_fallthrough:
 			if ((flags & Stmt_FallthroughAllowed) == 0)
 			if ((flags & Stmt_FallthroughAllowed) == 0)
-				error(&c->error_collector, token, "`fallthrough` statement in illegal position");
+				error(token, "`fallthrough` statement in illegal position");
 			break;
 			break;
 		default:
 		default:
-			error(&c->error_collector, token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
+			error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
 			break;
 			break;
 		}
 		}
 	case_end;
 	case_end;
@@ -1315,7 +1321,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			}
 			}
 
 
 			if (e == NULL) {
 			if (e == NULL) {
-				error(&c->error_collector, us->token, "`using` applied to an unknown entity");
+				error(us->token, "`using` applied to an unknown entity");
 				return;
 				return;
 			}
 			}
 
 
@@ -1330,7 +1336,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 						Entity *f = t->Record.other_fields[i];
 						Entity *f = t->Record.other_fields[i];
 						Entity *found = scope_insert_entity(c->context.scope, f);
 						Entity *found = scope_insert_entity(c->context.scope, f);
 						if (found != NULL) {
 						if (found != NULL) {
-							error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
+							error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
 							return;
 							return;
 						}
 						}
 						f->using_parent = e;
 						f->using_parent = e;
@@ -1340,7 +1346,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 						Entity *f = t->Record.fields[i];
 						Entity *f = t->Record.fields[i];
 						Entity *found = scope_insert_entity(c->context.scope, f);
 						Entity *found = scope_insert_entity(c->context.scope, f);
 						if (found != NULL) {
 						if (found != NULL) {
-							error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
+							error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
 							return;
 							return;
 						}
 						}
 						f->using_parent = e;
 						f->using_parent = e;
@@ -1349,7 +1355,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 						Entity *f = t->Record.other_fields[i];
 						Entity *f = t->Record.other_fields[i];
 						Entity *found = scope_insert_entity(c->context.scope, f);
 						Entity *found = scope_insert_entity(c->context.scope, f);
 						if (found != NULL) {
 						if (found != NULL) {
-							error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
+							error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
 							return;
 							return;
 						}
 						}
 						f->using_parent = e;
 						f->using_parent = e;
@@ -1363,7 +1369,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 					Entity *decl = scope->elements.entries[i].value;
 					Entity *decl = scope->elements.entries[i].value;
 					Entity *found = scope_insert_entity(c->context.scope, decl);
 					Entity *found = scope_insert_entity(c->context.scope, decl);
 					if (found != NULL) {
 					if (found != NULL) {
-						error(&c->error_collector, us->token,
+						error(us->token,
 						      "Namespace collision while `using` `%s` of: %.*s\n"
 						      "Namespace collision while `using` `%s` of: %.*s\n"
 						      "\tat %.*s(%td:%td)\n"
 						      "\tat %.*s(%td:%td)\n"
 						      "\tat %.*s(%td:%td)",
 						      "\tat %.*s(%td:%td)",
@@ -1377,12 +1383,12 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			} break;
 			} break;
 
 
 			case Entity_Constant:
 			case Entity_Constant:
-				error(&c->error_collector, us->token, "`using` cannot be applied to a constant");
+				error(us->token, "`using` cannot be applied to a constant");
 				break;
 				break;
 
 
 			case Entity_Procedure:
 			case Entity_Procedure:
 			case Entity_Builtin:
 			case Entity_Builtin:
-				error(&c->error_collector, us->token, "`using` cannot be applied to a procedure");
+				error(us->token, "`using` cannot be applied to a procedure");
 				break;
 				break;
 
 
 			case Entity_Variable: {
 			case Entity_Variable: {
@@ -1399,13 +1405,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 							}
 							}
 							Entity *prev = scope_insert_entity(c->context.scope, uvar);
 							Entity *prev = scope_insert_entity(c->context.scope, uvar);
 							if (prev != NULL) {
 							if (prev != NULL) {
-								error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
+								error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
 								return;
 								return;
 							}
 							}
 						}
 						}
 					}
 					}
 				} else {
 				} else {
-					error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or raw_union");
+					error(us->token, "`using` can only be applied to variables of type struct or raw_union");
 					return;
 					return;
 				}
 				}
 			} break;
 			} break;
@@ -1417,9 +1423,9 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 
 
 		case_ast_node(vd, VarDecl, us->node);
 		case_ast_node(vd, VarDecl, us->node);
 			if (gb_array_count(vd->names) > 1 && vd->type != NULL) {
 			if (gb_array_count(vd->names) > 1 && vd->type != NULL) {
-				error(&c->error_collector, us->token, "`using` can only be applied to one variable of the same type");
+				error(us->token, "`using` can only be applied to one variable of the same type");
 			}
 			}
-			check_var_decl_node(c, us->node);
+			check_var_decl(c, us->node);
 
 
 			gb_for_array(name_index, vd->names) {
 			gb_for_array(name_index, vd->names) {
 				AstNode *item = vd->names[name_index];
 				AstNode *item = vd->names[name_index];
@@ -1436,13 +1442,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 							Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
 							Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
 							Entity *prev = scope_insert_entity(c->context.scope, uvar);
 							Entity *prev = scope_insert_entity(c->context.scope, uvar);
 							if (prev != NULL) {
 							if (prev != NULL) {
-								error(&c->error_collector, us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
+								error(us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
 								return;
 								return;
 							}
 							}
 						}
 						}
 					}
 					}
 				} else {
 				} else {
-					error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or raw_union");
+					error(us->token, "`using` can only be applied to variables of type struct or raw_union");
 					return;
 					return;
 				}
 				}
 			}
 			}
@@ -1450,7 +1456,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 
 
 
 
 		default:
 		default:
-			error(&c->error_collector, us->token, "Invalid AST: Using Statement");
+			error(us->token, "Invalid AST: Using Statement");
 			break;
 			break;
 		}
 		}
 	case_end;
 	case_end;
@@ -1461,7 +1467,11 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 
 
 
 
 	case_ast_node(vd, VarDecl, node);
 	case_ast_node(vd, VarDecl, node);
-		check_var_decl_node(c, node);
+		check_var_decl(c, node);
+	case_end;
+
+	case_ast_node(cd, ConstDecl, node);
+		check_const_decl(c, node);
 	case_end;
 	case_end;
 
 
 	case_ast_node(pd, ProcDecl, node);
 	case_ast_node(pd, ProcDecl, node);

+ 1 - 9
src/codegen/codegen.cpp

@@ -7,17 +7,9 @@ struct ssaGen {
 };
 };
 
 
 b32 ssa_gen_init(ssaGen *s, Checker *c) {
 b32 ssa_gen_init(ssaGen *s, Checker *c) {
-	if (c->error_collector.count != 0)
+	if (global_error_collector.count != 0)
 		return false;
 		return false;
 
 
-	gb_for_array(i, c->parser->files) {
-		AstFile *f = &c->parser->files[i];
-		if (f->error_collector.count != 0)
-			return false;
-		if (f->tokenizer.error_count != 0)
-			return false;
-	}
-
 	isize tc = c->parser->total_token_count;
 	isize tc = c->parser->total_token_count;
 	if (tc < 2) {
 	if (tc < 2) {
 		return false;
 		return false;

+ 62 - 64
src/codegen/ssa.cpp

@@ -3053,82 +3053,80 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 	case_end;
 	case_end;
 
 
 	case_ast_node(vd, VarDecl, node);
 	case_ast_node(vd, VarDecl, node);
-		if (vd->kind == Declaration_Mutable) {
-			if (gb_array_count(vd->names) == gb_array_count(vd->values)) { // 1:1 assigment
-				gbArray(ssaAddr)  lvals;
-				gbArray(ssaValue *) inits;
-				gb_array_init_reserve(lvals, gb_heap_allocator(), gb_array_count(vd->names));
-				gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(vd->names));
-				defer (gb_array_free(lvals));
-				defer (gb_array_free(inits));
+		if (gb_array_count(vd->names) == gb_array_count(vd->values)) { // 1:1 assigment
+			gbArray(ssaAddr)  lvals;
+			gbArray(ssaValue *) inits;
+			gb_array_init_reserve(lvals, gb_heap_allocator(), gb_array_count(vd->names));
+			gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(vd->names));
+			defer (gb_array_free(lvals));
+			defer (gb_array_free(inits));
+
+			gb_for_array(i, vd->names) {
+				AstNode *name = vd->names[i];
+				ssaAddr lval = ssa_make_addr(NULL, NULL);
+				if (!ssa_is_blank_ident(name)) {
+					ssa_add_local_for_identifier(proc, name, false);
+					lval = ssa_build_addr(proc, name);
+					GB_ASSERT(lval.addr != NULL);
+				}
 
 
-				gb_for_array(i, vd->names) {
-					AstNode *name = vd->names[i];
-					ssaAddr lval = ssa_make_addr(NULL, NULL);
-					if (!ssa_is_blank_ident(name)) {
-						ssa_add_local_for_identifier(proc, name, false);
-						lval = ssa_build_addr(proc, name);
-						GB_ASSERT(lval.addr != NULL);
-					}
+				gb_array_append(lvals, lval);
+			}
+			gb_for_array(i, vd->values) {
+				ssaValue *init = ssa_build_expr(proc, vd->values[i]);
+				gb_array_append(inits, init);
+			}
 
 
-					gb_array_append(lvals, lval);
-				}
-				gb_for_array(i, vd->values) {
-					ssaValue *init = ssa_build_expr(proc, vd->values[i]);
-					gb_array_append(inits, init);
-				}
 
 
+			gb_for_array(i, inits) {
+				ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_addr_type(lvals[i]));
+				ssa_lvalue_store(proc, lvals[i], v);
+			}
 
 
-				gb_for_array(i, inits) {
-					ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_addr_type(lvals[i]));
-					ssa_lvalue_store(proc, lvals[i], v);
+		} else if (gb_array_count(vd->values) == 0) { // declared and zero-initialized
+			gb_for_array(i, vd->names) {
+				AstNode *name = vd->names[i];
+				if (!ssa_is_blank_ident(name)) {
+					ssa_add_local_for_identifier(proc, name, true);
 				}
 				}
-
-			} else if (gb_array_count(vd->values) == 0) { // declared and zero-initialized
-				gb_for_array(i, vd->names) {
-					AstNode *name = vd->names[i];
-					if (!ssa_is_blank_ident(name)) {
-						ssa_add_local_for_identifier(proc, name, true);
-					}
+			}
+		} else { // Tuple(s)
+			gbArray(ssaAddr)  lvals;
+			gbArray(ssaValue *) inits;
+			gb_array_init_reserve(lvals, gb_heap_allocator(), gb_array_count(vd->names));
+			gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(vd->names));
+			defer (gb_array_free(lvals));
+			defer (gb_array_free(inits));
+
+			gb_for_array(i, vd->names) {
+				AstNode *name = vd->names[i];
+				ssaAddr lval = ssa_make_addr(NULL, NULL);
+				if (!ssa_is_blank_ident(name)) {
+					ssa_add_local_for_identifier(proc, name, false);
+					lval = ssa_build_addr(proc, name);
 				}
 				}
-			} else { // Tuple(s)
-				gbArray(ssaAddr)  lvals;
-				gbArray(ssaValue *) inits;
-				gb_array_init_reserve(lvals, gb_heap_allocator(), gb_array_count(vd->names));
-				gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(vd->names));
-				defer (gb_array_free(lvals));
-				defer (gb_array_free(inits));
 
 
-				gb_for_array(i, vd->names) {
-					AstNode *name = vd->names[i];
-					ssaAddr lval = ssa_make_addr(NULL, NULL);
-					if (!ssa_is_blank_ident(name)) {
-						ssa_add_local_for_identifier(proc, name, false);
-						lval = ssa_build_addr(proc, name);
-					}
-
-					gb_array_append(lvals, lval);
-				}
+				gb_array_append(lvals, lval);
+			}
 
 
-				gb_for_array(i, vd->values) {
-					ssaValue *init = ssa_build_expr(proc, vd->values[i]);
-					Type *t = ssa_type(init);
-					if (t->kind == Type_Tuple) {
-						for (isize i = 0; i < t->Tuple.variable_count; i++) {
-							Entity *e = t->Tuple.variables[i];
-							ssaValue *v = ssa_emit_struct_ev(proc, init, i, e->type);
-							gb_array_append(inits, v);
-						}
-					} else {
-						gb_array_append(inits, init);
+			gb_for_array(i, vd->values) {
+				ssaValue *init = ssa_build_expr(proc, vd->values[i]);
+				Type *t = ssa_type(init);
+				if (t->kind == Type_Tuple) {
+					for (isize i = 0; i < t->Tuple.variable_count; i++) {
+						Entity *e = t->Tuple.variables[i];
+						ssaValue *v = ssa_emit_struct_ev(proc, init, i, e->type);
+						gb_array_append(inits, v);
 					}
 					}
+				} else {
+					gb_array_append(inits, init);
 				}
 				}
+			}
 
 
 
 
-				gb_for_array(i, inits) {
-					ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_addr_type(lvals[i]));
-					ssa_lvalue_store(proc, lvals[i], v);
-				}
+			gb_for_array(i, inits) {
+				ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_addr_type(lvals[i]));
+				ssa_lvalue_store(proc, lvals[i], v);
 			}
 			}
 		}
 		}
 	case_end;
 	case_end;

+ 113 - 138
src/parser.cpp

@@ -34,8 +34,6 @@ struct AstFile {
 	isize    scope_level;
 	isize    scope_level;
 	Scope *  scope; // NOTE(bill): Created in checker
 	Scope *  scope; // NOTE(bill): Created in checker
 
 
-	ErrorCollector error_collector;
-
 	// TODO(bill): Error recovery
 	// TODO(bill): Error recovery
 	// NOTE(bill): Error recovery
 	// NOTE(bill): Error recovery
 #define PARSER_MAX_FIX_COUNT 6
 #define PARSER_MAX_FIX_COUNT 6
@@ -59,13 +57,6 @@ struct Parser {
 	isize total_token_count;
 	isize total_token_count;
 };
 };
 
 
-enum DeclKind {
-	Declaration_Invalid,
-	Declaration_Mutable,
-	Declaration_Immutable,
-	Declaration_Count,
-};
-
 enum ProcTag : u64 {
 enum ProcTag : u64 {
 	ProcTag_bounds_check    = GB_BIT(0),
 	ProcTag_bounds_check    = GB_BIT(0),
 	ProcTag_no_bounds_check = GB_BIT(1),
 	ProcTag_no_bounds_check = GB_BIT(1),
@@ -227,20 +218,25 @@ AST_NODE_KIND(_StmtEnd,        "", struct{}) \
 AST_NODE_KIND(_DeclBegin,      "", struct{}) \
 AST_NODE_KIND(_DeclBegin,      "", struct{}) \
 	AST_NODE_KIND(BadDecl,  "bad declaration", struct { Token begin, end; }) \
 	AST_NODE_KIND(BadDecl,  "bad declaration", struct { Token begin, end; }) \
 	AST_NODE_KIND(VarDecl,  "variable declaration", struct { \
 	AST_NODE_KIND(VarDecl,  "variable declaration", struct { \
-			DeclKind kind; \
 			u64      tags; \
 			u64      tags; \
 			b32      is_using; \
 			b32      is_using; \
 			AstNodeArray names; \
 			AstNodeArray names; \
 			AstNode *type; \
 			AstNode *type; \
 			AstNodeArray values; \
 			AstNodeArray values; \
-		}) \
+	}) \
+	AST_NODE_KIND(ConstDecl,  "constant declaration", struct { \
+			u64      tags; \
+			AstNodeArray names; \
+			AstNode *type; \
+			AstNodeArray values; \
+	}) \
 	AST_NODE_KIND(ProcDecl, "procedure declaration", struct { \
 	AST_NODE_KIND(ProcDecl, "procedure declaration", struct { \
 			AstNode *name;        \
 			AstNode *name;        \
 			AstNode *type;        \
 			AstNode *type;        \
 			AstNode *body;        \
 			AstNode *body;        \
 			u64     tags;         \
 			u64     tags;         \
 			String  foreign_name; \
 			String  foreign_name; \
-		}) \
+	}) \
 	AST_NODE_KIND(TypeDecl,   "type declaration",   struct { Token token; AstNode *name, *type; }) \
 	AST_NODE_KIND(TypeDecl,   "type declaration",   struct { Token token; AstNode *name, *type; }) \
 	AST_NODE_KIND(ImportDecl, "import declaration", struct { \
 	AST_NODE_KIND(ImportDecl, "import declaration", struct { \
 		Token token, relpath; \
 		Token token, relpath; \
@@ -419,6 +415,8 @@ Token ast_node_token(AstNode *node) {
 		return node->BadDecl.begin;
 		return node->BadDecl.begin;
 	case AstNode_VarDecl:
 	case AstNode_VarDecl:
 		return ast_node_token(node->VarDecl.names[0]);
 		return ast_node_token(node->VarDecl.names[0]);
+	case AstNode_ConstDecl:
+		return ast_node_token(node->ConstDecl.names[0]);
 	case AstNode_ProcDecl:
 	case AstNode_ProcDecl:
 		return node->ProcDecl.name->Ident;
 		return node->ProcDecl.name->Ident;
 	case AstNode_TypeDecl:
 	case AstNode_TypeDecl:
@@ -458,26 +456,6 @@ HashKey hash_token(Token t) {
 	return hash_string(t.string);
 	return hash_string(t.string);
 }
 }
 
 
-#define ast_file_err(f, token, fmt, ...) ast_file_err_(f, __FUNCTION__, token, fmt, ##__VA_ARGS__)
-void ast_file_err_(AstFile *file, char *function, Token token, char *fmt, ...) {
-	// NOTE(bill): Duplicate error, skip it
-	if (!token_pos_are_equal(file->error_collector.prev, token.pos)) {
-		va_list va;
-
-		file->error_collector.prev = token.pos;
-
-	#if 0
-		gb_printf_err("%s()\n", function);
-	#endif
-		va_start(va, fmt);
-		gb_printf_err("%.*s(%td:%td) Syntax error: %s\n",
-		              LIT(token.pos.file), token.pos.line, token.pos.column,
-		              gb_bprintf_va(fmt, va));
-		va_end(va);
-	}
-	file->error_collector.count++;
-}
-
 
 
 // NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
 // NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
 gb_inline AstNode *make_node(AstFile *f, AstNodeKind kind) {
 gb_inline AstNode *make_node(AstFile *f, AstNodeKind kind) {
@@ -525,11 +503,11 @@ gb_inline AstNode *make_binary_expr(AstFile *f, Token op, AstNode *left, AstNode
 	AstNode *result = make_node(f, AstNode_BinaryExpr);
 	AstNode *result = make_node(f, AstNode_BinaryExpr);
 
 
 	if (left == NULL) {
 	if (left == NULL) {
-		ast_file_err(f, op, "No lhs expression for binary expression `%.*s`", LIT(op.string));
+		syntax_error(op, "No lhs expression for binary expression `%.*s`", LIT(op.string));
 		left = make_bad_expr(f, op, op);
 		left = make_bad_expr(f, op, op);
 	}
 	}
 	if (right == NULL) {
 	if (right == NULL) {
-		ast_file_err(f, op, "No rhs expression for binary expression `%.*s`", LIT(op.string));
+		syntax_error(op, "No rhs expression for binary expression `%.*s`", LIT(op.string));
 		right = make_bad_expr(f, op, op);
 		right = make_bad_expr(f, op, op);
 	}
 	}
 
 
@@ -795,15 +773,22 @@ gb_inline AstNode *make_bad_decl(AstFile *f, Token begin, Token end) {
 	return result;
 	return result;
 }
 }
 
 
-gb_inline AstNode *make_var_decl(AstFile *f, DeclKind kind, AstNodeArray names, AstNode *type, AstNodeArray values) {
+gb_inline AstNode *make_var_decl(AstFile *f, AstNodeArray names, AstNode *type, AstNodeArray values) {
 	AstNode *result = make_node(f, AstNode_VarDecl);
 	AstNode *result = make_node(f, AstNode_VarDecl);
-	result->VarDecl.kind = kind;
 	result->VarDecl.names = names;
 	result->VarDecl.names = names;
 	result->VarDecl.type = type;
 	result->VarDecl.type = type;
 	result->VarDecl.values = values;
 	result->VarDecl.values = values;
 	return result;
 	return result;
 }
 }
 
 
+gb_inline AstNode *make_const_decl(AstFile *f, AstNodeArray names, AstNode *type, AstNodeArray values) {
+	AstNode *result = make_node(f, AstNode_ConstDecl);
+	result->ConstDecl.names = names;
+	result->ConstDecl.type = type;
+	result->ConstDecl.values = values;
+	return result;
+}
+
 gb_inline AstNode *make_field(AstFile *f, AstNodeArray names, AstNode *type, b32 is_using) {
 gb_inline AstNode *make_field(AstFile *f, AstNodeArray names, AstNode *type, b32 is_using) {
 	AstNode *result = make_node(f, AstNode_Field);
 	AstNode *result = make_node(f, AstNode_Field);
 	result->Field.names = names;
 	result->Field.names = names;
@@ -918,7 +903,7 @@ gb_inline b32 next_token(AstFile *f) {
 		f->cursor++;
 		f->cursor++;
 		return true;
 		return true;
 	} else {
 	} else {
-		ast_file_err(f, f->cursor[0], "Token is EOF");
+		syntax_error(f->cursor[0], "Token is EOF");
 		return false;
 		return false;
 	}
 	}
 }
 }
@@ -926,7 +911,7 @@ gb_inline b32 next_token(AstFile *f) {
 gb_inline Token expect_token(AstFile *f, TokenKind kind) {
 gb_inline Token expect_token(AstFile *f, TokenKind kind) {
 	Token prev = f->cursor[0];
 	Token prev = f->cursor[0];
 	if (prev.kind != kind) {
 	if (prev.kind != kind) {
-		ast_file_err(f, f->cursor[0], "Expected `%.*s`, got `%.*s`",
+		syntax_error(f->cursor[0], "Expected `%.*s`, got `%.*s`",
 		             LIT(token_strings[kind]),
 		             LIT(token_strings[kind]),
 		             LIT(token_strings[prev.kind]));
 		             LIT(token_strings[prev.kind]));
 	}
 	}
@@ -937,7 +922,7 @@ gb_inline Token expect_token(AstFile *f, TokenKind kind) {
 gb_inline Token expect_operator(AstFile *f) {
 gb_inline Token expect_operator(AstFile *f) {
 	Token prev = f->cursor[0];
 	Token prev = f->cursor[0];
 	if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
 	if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
-		ast_file_err(f, f->cursor[0], "Expected an operator, got `%.*s`",
+		syntax_error(f->cursor[0], "Expected an operator, got `%.*s`",
 		             LIT(token_strings[prev.kind]));
 		             LIT(token_strings[prev.kind]));
 	}
 	}
 	next_token(f);
 	next_token(f);
@@ -947,7 +932,7 @@ gb_inline Token expect_operator(AstFile *f) {
 gb_inline Token expect_keyword(AstFile *f) {
 gb_inline Token expect_keyword(AstFile *f) {
 	Token prev = f->cursor[0];
 	Token prev = f->cursor[0];
 	if (!gb_is_between(prev.kind, Token__KeywordBegin+1, Token__KeywordEnd-1)) {
 	if (!gb_is_between(prev.kind, Token__KeywordBegin+1, Token__KeywordEnd-1)) {
-		ast_file_err(f, f->cursor[0], "Expected a keyword, got `%.*s`",
+		syntax_error(f->cursor[0], "Expected a keyword, got `%.*s`",
 		             LIT(token_strings[prev.kind]));
 		             LIT(token_strings[prev.kind]));
 	}
 	}
 	next_token(f);
 	next_token(f);
@@ -1027,7 +1012,7 @@ b32 expect_semicolon_after_stmt(AstFile *f, AstNode *s) {
 		if (f->cursor[0].pos.line == f->cursor[-1].pos.line) {
 		if (f->cursor[0].pos.line == f->cursor[-1].pos.line) {
 			if (f->cursor[0].kind != Token_CloseBrace) {
 			if (f->cursor[0].kind != Token_CloseBrace) {
 				// CLEANUP(bill): Semicolon handling in parser
 				// CLEANUP(bill): Semicolon handling in parser
-				ast_file_err(f, f->cursor[0],
+				syntax_error(f->cursor[0],
 				             "Expected `;` after %.*s, got `%.*s`",
 				             "Expected `;` after %.*s, got `%.*s`",
 				             LIT(ast_node_strings[s->kind]), LIT(token_strings[f->cursor[0].kind]));
 				             LIT(ast_node_strings[s->kind]), LIT(token_strings[f->cursor[0].kind]));
 				return false;
 				return false;
@@ -1126,7 +1111,7 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags = 0);
 
 
 void check_proc_add_tag(AstFile *f, AstNode *tag_expr, u64 *tags, ProcTag tag, String tag_name) {
 void check_proc_add_tag(AstFile *f, AstNode *tag_expr, u64 *tags, ProcTag tag, String tag_name) {
 	if (*tags & tag) {
 	if (*tags & tag) {
-		ast_file_err(f, ast_node_token(tag_expr), "Procedure tag already used: %.*s", LIT(tag_name));
+		syntax_error(ast_node_token(tag_expr), "Procedure tag already used: %.*s", LIT(tag_name));
 	}
 	}
 	*tags |= tag;
 	*tags |= tag;
 }
 }
@@ -1200,7 +1185,7 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name) {
 				*foreign_name = f->cursor[0].string;
 				*foreign_name = f->cursor[0].string;
 				// TODO(bill): Check if valid string
 				// TODO(bill): Check if valid string
 				if (!is_foreign_name_valid(*foreign_name)) {
 				if (!is_foreign_name_valid(*foreign_name)) {
-					ast_file_err(f, ast_node_token(tag_expr), "Invalid alternative foreign procedure name");
+					syntax_error(ast_node_token(tag_expr), "Invalid alternative foreign procedure name");
 				}
 				}
 
 
 				next_token(f);
 				next_token(f);
@@ -1216,22 +1201,22 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name) {
 		ELSE_IF_ADD_TAG(fastcall)
 		ELSE_IF_ADD_TAG(fastcall)
 		// ELSE_IF_ADD_TAG(cdecl)
 		// ELSE_IF_ADD_TAG(cdecl)
 		else {
 		else {
-			ast_file_err(f, ast_node_token(tag_expr), "Unknown procedure tag");
+			syntax_error(ast_node_token(tag_expr), "Unknown procedure tag");
 		}
 		}
 
 
 		#undef ELSE_IF_ADD_TAG
 		#undef ELSE_IF_ADD_TAG
 	}
 	}
 
 
 	if ((*tags & ProcTag_inline) && (*tags & ProcTag_no_inline)) {
 	if ((*tags & ProcTag_inline) && (*tags & ProcTag_no_inline)) {
-		ast_file_err(f, f->cursor[0], "You cannot apply both #inline and #no_inline to a procedure");
+		syntax_error(f->cursor[0], "You cannot apply both #inline and #no_inline to a procedure");
 	}
 	}
 
 
 	if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) {
 	if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) {
-		ast_file_err(f, f->cursor[0], "You cannot apply both #bounds_check and #no_bounds_check to a procedure");
+		syntax_error(f->cursor[0], "You cannot apply both #bounds_check and #no_bounds_check to a procedure");
 	}
 	}
 
 
 	if (((*tags & ProcTag_bounds_check) || (*tags & ProcTag_no_bounds_check)) && (*tags & ProcTag_foreign)) {
 	if (((*tags & ProcTag_bounds_check) || (*tags & ProcTag_no_bounds_check)) && (*tags & ProcTag_foreign)) {
-		ast_file_err(f, f->cursor[0], "You cannot apply both #bounds_check or #no_bounds_check to a procedure without a body");
+		syntax_error(f->cursor[0], "You cannot apply both #bounds_check or #no_bounds_check to a procedure without a body");
 	}
 	}
 }
 }
 
 
@@ -1272,7 +1257,7 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
 				Token *s = &f->cursor[0];
 				Token *s = &f->cursor[0];
 
 
 				if (gb_utf8_strnlen(s->string.text, s->string.len) != 1) {
 				if (gb_utf8_strnlen(s->string.text, s->string.len) != 1) {
-					ast_file_err(f, *s, "Invalid rune literal %.*s", LIT(s->string));
+					syntax_error(*s, "Invalid rune literal %.*s", LIT(s->string));
 				}
 				}
 				s->kind = Token_Rune; // NOTE(bill): Change it
 				s->kind = Token_Rune; // NOTE(bill): Change it
 			} else {
 			} else {
@@ -1308,7 +1293,7 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
 		String foreign_name = {};
 		String foreign_name = {};
 		parse_proc_tags(f, &tags, &foreign_name);
 		parse_proc_tags(f, &tags, &foreign_name);
 		if (tags & ProcTag_foreign) {
 		if (tags & ProcTag_foreign) {
-			ast_file_err(f, f->cursor[0], "#foreign cannot be applied to procedure literals");
+			syntax_error(f->cursor[0], "#foreign cannot be applied to procedure literals");
 		}
 		}
 
 
 		if (f->cursor[0].kind != Token_OpenBrace) {
 		if (f->cursor[0].kind != Token_OpenBrace) {
@@ -1335,7 +1320,7 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
 	}
 	}
 
 
 	Token begin = f->cursor[0];
 	Token begin = f->cursor[0];
-	ast_file_err(f, begin, "Expected an operand");
+	syntax_error(begin, "Expected an operand");
 	fix_advance_to_next_stmt(f);
 	fix_advance_to_next_stmt(f);
 	return make_bad_expr(f, begin, f->cursor[0]);
 	return make_bad_expr(f, begin, f->cursor[0]);
 }
 }
@@ -1365,7 +1350,7 @@ AstNode *parse_call_expr(AstFile *f, AstNode *operand) {
 	       f->cursor[0].kind != Token_EOF &&
 	       f->cursor[0].kind != Token_EOF &&
 	       ellipsis.pos.line == 0) {
 	       ellipsis.pos.line == 0) {
 		if (f->cursor[0].kind == Token_Comma)
 		if (f->cursor[0].kind == Token_Comma)
-			ast_file_err(f, f->cursor[0], "Expected an expression not a ,");
+			syntax_error(f->cursor[0], "Expected an expression not a ,");
 
 
 		if (f->cursor[0].kind == Token_Ellipsis) {
 		if (f->cursor[0].kind == Token_Ellipsis) {
 			ellipsis = f->cursor[0];
 			ellipsis = f->cursor[0];
@@ -1425,7 +1410,7 @@ AstNode *parse_atom_expr(AstFile *f, b32 lhs) {
 				operand = make_selector_expr(f, token, operand, parse_identifier(f));
 				operand = make_selector_expr(f, token, operand, parse_identifier(f));
 				break;
 				break;
 			default: {
 			default: {
-				ast_file_err(f, f->cursor[0], "Expected a selector");
+				syntax_error(f->cursor[0], "Expected a selector");
 				next_token(f);
 				next_token(f);
 				operand = make_selector_expr(f, f->cursor[0], operand, NULL);
 				operand = make_selector_expr(f, f->cursor[0], operand, NULL);
 			} break;
 			} break;
@@ -1467,11 +1452,11 @@ AstNode *parse_atom_expr(AstFile *f, b32 lhs) {
 				if (colon_count == 2) {
 				if (colon_count == 2) {
 					triple_indexed = true;
 					triple_indexed = true;
 					if (indices[1] == NULL) {
 					if (indices[1] == NULL) {
-						ast_file_err(f, colons[0], "Second index is required in a triple indexed slice");
+						syntax_error(colons[0], "Second index is required in a triple indexed slice");
 						indices[1] = make_bad_expr(f, colons[0], colons[1]);
 						indices[1] = make_bad_expr(f, colons[0], colons[1]);
 					}
 					}
 					if (indices[2] == NULL) {
 					if (indices[2] == NULL) {
-						ast_file_err(f, colons[1], "Third index is required in a triple indexed slice");
+						syntax_error(colons[1], "Third index is required in a triple indexed slice");
 						indices[2] = make_bad_expr(f, colons[1], close);
 						indices[2] = make_bad_expr(f, colons[1], close);
 					}
 					}
 				}
 				}
@@ -1558,7 +1543,7 @@ AstNode *parse_binary_expr(AstFile *f, b32 lhs, i32 prec_in) {
 			default:
 			default:
 				right = parse_binary_expr(f, false, prec+1);
 				right = parse_binary_expr(f, false, prec+1);
 				if (!right) {
 				if (!right) {
-					ast_file_err(f, op, "Expected expression on the right hand side of the binary operator");
+					syntax_error(op, "Expected expression on the right hand side of the binary operator");
 				}
 				}
 				break;
 				break;
 			}
 			}
@@ -1622,13 +1607,13 @@ AstNode *parse_simple_stmt(AstFile *f) {
 	case Token_CmpOrEq:
 	case Token_CmpOrEq:
 	{
 	{
 		if (f->curr_proc == NULL) {
 		if (f->curr_proc == NULL) {
-			ast_file_err(f, f->cursor[0], "You cannot use a simple statement in the file scope");
+			syntax_error(f->cursor[0], "You cannot use a simple statement in the file scope");
 			return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 			return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 		}
 		}
 		next_token(f);
 		next_token(f);
 		AstNodeArray rhs = parse_rhs_expr_list(f);
 		AstNodeArray rhs = parse_rhs_expr_list(f);
 		if (gb_array_count(rhs) == 0) {
 		if (gb_array_count(rhs) == 0) {
-			ast_file_err(f, token, "No right-hand side in assignment statement.");
+			syntax_error(token, "No right-hand side in assignment statement.");
 			return make_bad_stmt(f, token, f->cursor[0]);
 			return make_bad_stmt(f, token, f->cursor[0]);
 		}
 		}
 		return make_assign_stmt(f, token, lhs, rhs);
 		return make_assign_stmt(f, token, lhs, rhs);
@@ -1639,7 +1624,7 @@ AstNode *parse_simple_stmt(AstFile *f) {
 	}
 	}
 
 
 	if (lhs_count > 1) {
 	if (lhs_count > 1) {
-		ast_file_err(f, token, "Expected 1 expression");
+		syntax_error(token, "Expected 1 expression");
 		return make_bad_stmt(f, token, f->cursor[0]);
 		return make_bad_stmt(f, token, f->cursor[0]);
 	}
 	}
 
 
@@ -1648,7 +1633,7 @@ AstNode *parse_simple_stmt(AstFile *f) {
 	case Token_Increment:
 	case Token_Increment:
 	case Token_Decrement:
 	case Token_Decrement:
 		if (f->curr_proc == NULL) {
 		if (f->curr_proc == NULL) {
-			ast_file_err(f, f->cursor[0], "You cannot use a simple statement in the file scope");
+			syntax_error(f->cursor[0], "You cannot use a simple statement in the file scope");
 			return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 			return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 		}
 		}
 		statement = make_inc_dec_stmt(f, token, lhs[0]);
 		statement = make_inc_dec_stmt(f, token, lhs[0]);
@@ -1663,7 +1648,7 @@ AstNode *parse_simple_stmt(AstFile *f) {
 
 
 AstNode *parse_block_stmt(AstFile *f) {
 AstNode *parse_block_stmt(AstFile *f) {
 	if (f->curr_proc == NULL) {
 	if (f->curr_proc == NULL) {
-		ast_file_err(f, f->cursor[0], "You cannot use a block statement in the file scope");
+		syntax_error(f->cursor[0], "You cannot use a block statement in the file scope");
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 	}
 	}
 	AstNode *block_stmt = parse_body(f);
 	AstNode *block_stmt = parse_body(f);
@@ -1677,7 +1662,7 @@ AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) {
 	if (statement->kind == AstNode_ExprStmt)
 	if (statement->kind == AstNode_ExprStmt)
 		return statement->ExprStmt.expr;
 		return statement->ExprStmt.expr;
 
 
-	ast_file_err(f, f->cursor[0], "Expected `%.*s`, found a simple statement.", LIT(kind));
+	syntax_error(f->cursor[0], "Expected `%.*s`, found a simple statement.", LIT(kind));
 	return make_bad_expr(f, f->cursor[0], f->cursor[1]);
 	return make_bad_expr(f, f->cursor[0], f->cursor[1]);
 }
 }
 
 
@@ -1710,7 +1695,7 @@ AstNode *parse_type(AstFile *f) {
 	AstNode *type = parse_type_attempt(f);
 	AstNode *type = parse_type_attempt(f);
 	if (type == NULL) {
 	if (type == NULL) {
 		Token token = f->cursor[0];
 		Token token = f->cursor[0];
-		ast_file_err(f, token, "Expected a type");
+		syntax_error(token, "Expected a type");
 		next_token(f);
 		next_token(f);
 		return make_bad_expr(f, token, f->cursor[0]);
 		return make_bad_expr(f, token, f->cursor[0]);
 	}
 	}
@@ -1738,11 +1723,11 @@ AstNode *parse_field_decl(AstFile *f) {
 
 
 	AstNodeArray names = parse_lhs_expr_list(f);
 	AstNodeArray names = parse_lhs_expr_list(f);
 	if (gb_array_count(names) == 0) {
 	if (gb_array_count(names) == 0) {
-		ast_file_err(f, f->cursor[0], "Empty field declaration");
+		syntax_error(f->cursor[0], "Empty field declaration");
 	}
 	}
 
 
 	if (gb_array_count(names) > 1 && is_using) {
 	if (gb_array_count(names) > 1 && is_using) {
-		ast_file_err(f, f->cursor[0], "Cannot apply `using` to more than one of the same type");
+		syntax_error(f->cursor[0], "Cannot apply `using` to more than one of the same type");
 		is_using = false;
 		is_using = false;
 	}
 	}
 
 
@@ -1755,11 +1740,11 @@ AstNode *parse_field_decl(AstFile *f) {
 		next_token(f);
 		next_token(f);
 		type = parse_type_attempt(f);
 		type = parse_type_attempt(f);
 		if (type == NULL) {
 		if (type == NULL) {
-			ast_file_err(f, f->cursor[0], "variadic parameter is missing a type after `..`");
+			syntax_error(f->cursor[0], "variadic parameter is missing a type after `..`");
 			type = make_bad_expr(f, ellipsis, f->cursor[0]);
 			type = make_bad_expr(f, ellipsis, f->cursor[0]);
 		} else {
 		} else {
 			if (gb_array_count(names) > 1) {
 			if (gb_array_count(names) > 1) {
-				ast_file_err(f, f->cursor[0], "mutliple variadic parameters, only  `..`");
+				syntax_error(f->cursor[0], "mutliple variadic parameters, only  `..`");
 			} else {
 			} else {
 				type = make_ellipsis(f, ellipsis, type);
 				type = make_ellipsis(f, ellipsis, type);
 			}
 			}
@@ -1769,7 +1754,7 @@ AstNode *parse_field_decl(AstFile *f) {
 	}
 	}
 
 
 	if (type == NULL) {
 	if (type == NULL) {
-		ast_file_err(f, f->cursor[0], "Expected a type for this field declaration");
+		syntax_error(f->cursor[0], "Expected a type for this field declaration");
 	}
 	}
 
 
 	AstNode *field = make_field(f, names, type, is_using);
 	AstNode *field = make_field(f, names, type, is_using);
@@ -1804,15 +1789,15 @@ AstNodeArray parse_struct_params(AstFile *f, isize *decl_count_, b32 using_allow
 		}
 		}
 		AstNodeArray names = parse_lhs_expr_list(f);
 		AstNodeArray names = parse_lhs_expr_list(f);
 		if (gb_array_count(names) == 0) {
 		if (gb_array_count(names) == 0) {
-			ast_file_err(f, f->cursor[0], "Empty field declaration");
+			syntax_error(f->cursor[0], "Empty field declaration");
 		}
 		}
 
 
 		if (!using_allowed && is_using) {
 		if (!using_allowed && is_using) {
-			ast_file_err(f, f->cursor[0], "Cannot apply `using` to members of a union");
+			syntax_error(f->cursor[0], "Cannot apply `using` to members of a union");
 			is_using = false;
 			is_using = false;
 		}
 		}
 		if (gb_array_count(names) > 1 && is_using) {
 		if (gb_array_count(names) > 1 && is_using) {
-			ast_file_err(f, f->cursor[0], "Cannot apply `using` to more than one of the same type");
+			syntax_error(f->cursor[0], "Cannot apply `using` to more than one of the same type");
 		}
 		}
 
 
 		AstNode *decl = NULL;
 		AstNode *decl = NULL;
@@ -1821,11 +1806,11 @@ AstNodeArray parse_struct_params(AstFile *f, isize *decl_count_, b32 using_allow
 			decl = parse_decl(f, names);
 			decl = parse_decl(f, names);
 
 
 			if (decl->kind == AstNode_ProcDecl) {
 			if (decl->kind == AstNode_ProcDecl) {
-				ast_file_err(f, f->cursor[0], "Procedure declarations are not allowed within a structure");
+				syntax_error(f->cursor[0], "Procedure declarations are not allowed within a structure");
 				decl = make_bad_decl(f, ast_node_token(names[0]), f->cursor[0]);
 				decl = make_bad_decl(f, ast_node_token(names[0]), f->cursor[0]);
 			}
 			}
 		} else {
 		} else {
-			ast_file_err(f, f->cursor[0], "Illegal structure field");
+			syntax_error(f->cursor[0], "Illegal structure field");
 			decl = make_bad_decl(f, ast_node_token(names[0]), f->cursor[0]);
 			decl = make_bad_decl(f, ast_node_token(names[0]), f->cursor[0]);
 		}
 		}
 
 
@@ -1835,13 +1820,9 @@ AstNodeArray parse_struct_params(AstFile *f, isize *decl_count_, b32 using_allow
 			gb_array_append(decls, decl);
 			gb_array_append(decls, decl);
 			if (decl->kind == AstNode_VarDecl) {
 			if (decl->kind == AstNode_VarDecl) {
 				decl->VarDecl.is_using = is_using && using_allowed;
 				decl->VarDecl.is_using = is_using && using_allowed;
-
-				if (decl->VarDecl.kind == Declaration_Mutable) {
-					if (gb_array_count(decl->VarDecl.values) > 0) {
-						ast_file_err(f, f->cursor[0], "Default variable assignments within a structure will be ignored (at the moment)");
-					}
+				if (gb_array_count(decl->VarDecl.values) > 0) {
+					syntax_error(f->cursor[0], "Default variable assignments within a structure will be ignored (at the moment)");
 				}
 				}
-
 			} else {
 			} else {
 				decl_count += 1;
 				decl_count += 1;
 			}
 			}
@@ -1925,12 +1906,12 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
 			} else if (are_strings_equal(tag.string, make_string("ordered"))) {
 			} else if (are_strings_equal(tag.string, make_string("ordered"))) {
 				is_ordered = true;
 				is_ordered = true;
 			}  else {
 			}  else {
-				ast_file_err(f, tag, "Expected a `#packed` or `#ordered` tag");
+				syntax_error(tag, "Expected a `#packed` or `#ordered` tag");
 			}
 			}
 		}
 		}
 
 
 		if (is_packed && is_ordered) {
 		if (is_packed && is_ordered) {
-			ast_file_err(f, token, "`#ordered` is not needed with `#packed` which implies ordering");
+			syntax_error(token, "`#ordered` is not needed with `#packed` which implies ordering");
 		}
 		}
 
 
 		Token open = expect_token(f, Token_OpenBrace);
 		Token open = expect_token(f, Token_OpenBrace);
@@ -2034,8 +2015,8 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
 			break;
 			break;
 		// fallthrough
 		// fallthrough
 	default:
 	default:
-		ast_file_err(f, f->cursor[0],
-		             "Expected a type after `%.*s`, got `%.*s`", LIT(f->cursor[-1].string), LIT(f->cursor[0].string));
+		syntax_error(f->cursor[0],
+		             "Expected a type or identifier after `%.*s`, got `%.*s`", LIT(f->cursor[-1].string), LIT(f->cursor[0].string));
 		break;
 		break;
 	}
 	}
 
 
@@ -2109,7 +2090,7 @@ AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) {
 
 
 	if (f->cursor[0].kind == Token_OpenBrace) {
 	if (f->cursor[0].kind == Token_OpenBrace) {
 		if ((tags & ProcTag_foreign) != 0) {
 		if ((tags & ProcTag_foreign) != 0) {
-			ast_file_err(f, f->cursor[0], "A procedure tagged as `#foreign` cannot have a body");
+			syntax_error(f->cursor[0], "A procedure tagged as `#foreign` cannot have a body");
 		}
 		}
 		body = parse_body(f);
 		body = parse_body(f);
 	}
 	}
@@ -2127,7 +2108,7 @@ AstNode *parse_decl(AstFile *f, AstNodeArray names) {
 			String n = name->Ident.string;
 			String n = name->Ident.string;
 			// NOTE(bill): Check for reserved identifiers
 			// NOTE(bill): Check for reserved identifiers
 			if (are_strings_equal(n, make_string("context"))) {
 			if (are_strings_equal(n, make_string("context"))) {
-				ast_file_err(f, ast_node_token(name), "`context` is a reserved identifier");
+				syntax_error(ast_node_token(name), "`context` is a reserved identifier");
 				break;
 				break;
 			}
 			}
 		}
 		}
@@ -2138,15 +2119,16 @@ AstNode *parse_decl(AstFile *f, AstNodeArray names) {
 			type = parse_identifier_or_type(f);
 			type = parse_identifier_or_type(f);
 		}
 		}
 	} else if (f->cursor[0].kind != Token_Eq && f->cursor[0].kind != Token_Semicolon) {
 	} else if (f->cursor[0].kind != Token_Eq && f->cursor[0].kind != Token_Semicolon) {
-		ast_file_err(f, f->cursor[0], "Expected type separator `:` or `=`");
+		syntax_error(f->cursor[0], "Expected type separator `:` or `=`");
 	}
 	}
 
 
-	DeclKind declaration_kind = Declaration_Mutable;
+	b32 is_mutable = true;
 
 
 	if (f->cursor[0].kind == Token_Eq ||
 	if (f->cursor[0].kind == Token_Eq ||
 	    f->cursor[0].kind == Token_Colon) {
 	    f->cursor[0].kind == Token_Colon) {
-		if (f->cursor[0].kind == Token_Colon)
-			declaration_kind = Declaration_Immutable;
+		if (f->cursor[0].kind == Token_Colon) {
+			is_mutable = false;
+		}
 		next_token(f);
 		next_token(f);
 
 
 		if (f->cursor[0].kind == Token_type ||
 		if (f->cursor[0].kind == Token_type ||
@@ -2159,71 +2141,67 @@ AstNode *parse_decl(AstFile *f, AstNodeArray names) {
 				next_token(f);
 				next_token(f);
 			}
 			}
 			if (gb_array_count(names) != 1) {
 			if (gb_array_count(names) != 1) {
-				ast_file_err(f, ast_node_token(names[0]), "You can only declare one type at a time");
+				syntax_error(ast_node_token(names[0]), "You can only declare one type at a time");
 				return make_bad_decl(f, names[0]->Ident, token);
 				return make_bad_decl(f, names[0]->Ident, token);
 			}
 			}
 
 
 			if (type != NULL) {
 			if (type != NULL) {
-				ast_file_err(f, f->cursor[-1], "Expected either `type` or nothing between : and :");
+				syntax_error(f->cursor[-1], "Expected either `type` or nothing between : and :");
 				// NOTE(bill): Do not fail though
 				// NOTE(bill): Do not fail though
 			}
 			}
 
 
 			AstNode *type = parse_type(f);
 			AstNode *type = parse_type(f);
 			return make_type_decl(f, token, names[0], type);
 			return make_type_decl(f, token, names[0], type);
 		} else if (f->cursor[0].kind == Token_proc &&
 		} else if (f->cursor[0].kind == Token_proc &&
-		    declaration_kind == Declaration_Immutable) {
+		    is_mutable == false) {
 		    // NOTE(bill): Procedure declarations
 		    // NOTE(bill): Procedure declarations
 			Token proc_token = f->cursor[0];
 			Token proc_token = f->cursor[0];
 			AstNode *name = names[0];
 			AstNode *name = names[0];
 			if (gb_array_count(names) != 1) {
 			if (gb_array_count(names) != 1) {
-				ast_file_err(f, proc_token, "You can only declare one procedure at a time");
+				syntax_error(proc_token, "You can only declare one procedure at a time");
 				return make_bad_decl(f, name->Ident, proc_token);
 				return make_bad_decl(f, name->Ident, proc_token);
 			}
 			}
 
 
-			AstNode *proc_decl = parse_proc_decl(f, proc_token, name);
-			return proc_decl;
+			return parse_proc_decl(f, proc_token, name);
 
 
 		} else {
 		} else {
 			values = parse_rhs_expr_list(f);
 			values = parse_rhs_expr_list(f);
 			if (gb_array_count(values) > gb_array_count(names)) {
 			if (gb_array_count(values) > gb_array_count(names)) {
-				ast_file_err(f, f->cursor[0], "Too many values on the right hand side of the declaration");
-			} else if (gb_array_count(values) < gb_array_count(names) &&
-			           declaration_kind == Declaration_Immutable) {
-				ast_file_err(f, f->cursor[0], "All constant declarations must be defined");
+				syntax_error(f->cursor[0], "Too many values on the right hand side of the declaration");
+			} else if (gb_array_count(values) < gb_array_count(names) && !is_mutable) {
+				syntax_error(f->cursor[0], "All constant declarations must be defined");
 			} else if (gb_array_count(values) == 0) {
 			} else if (gb_array_count(values) == 0) {
-				ast_file_err(f, f->cursor[0], "Expected an expression for this declaration");
+				syntax_error(f->cursor[0], "Expected an expression for this declaration");
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	if (declaration_kind == Declaration_Mutable) {
+	if (is_mutable) {
 		if (type == NULL && gb_array_count(values) == 0) {
 		if (type == NULL && gb_array_count(values) == 0) {
-			ast_file_err(f, f->cursor[0], "Missing variable type or initialization");
+			syntax_error(f->cursor[0], "Missing variable type or initialization");
 			return make_bad_decl(f, f->cursor[0], f->cursor[0]);
 			return make_bad_decl(f, f->cursor[0], f->cursor[0]);
 		}
 		}
-	} else if (declaration_kind == Declaration_Immutable) {
+	} else {
 		if (type == NULL && gb_array_count(values) == 0 && gb_array_count(names) > 0) {
 		if (type == NULL && gb_array_count(values) == 0 && gb_array_count(names) > 0) {
-			ast_file_err(f, f->cursor[0], "Missing constant value");
+			syntax_error(f->cursor[0], "Missing constant value");
 			return make_bad_decl(f, f->cursor[0], f->cursor[0]);
 			return make_bad_decl(f, f->cursor[0], f->cursor[0]);
 		}
 		}
-	} else {
-		Token begin = f->cursor[0];
-		ast_file_err(f, begin, "Unknown type of variable declaration");
-		fix_advance_to_next_stmt(f);
-		return make_bad_decl(f, begin, f->cursor[0]);
 	}
 	}
 
 
 	if (values == NULL) {
 	if (values == NULL) {
 		values = make_ast_node_array(f);
 		values = make_ast_node_array(f);
 	}
 	}
 
 
-	return make_var_decl(f, declaration_kind, names, type, values);
+	if (is_mutable) {
+		return make_var_decl(f, names, type, values);
+	}
+	return make_const_decl(f, names, type, values);
 }
 }
 
 
 
 
 AstNode *parse_if_stmt(AstFile *f) {
 AstNode *parse_if_stmt(AstFile *f) {
 	if (f->curr_proc == NULL) {
 	if (f->curr_proc == NULL) {
-		ast_file_err(f, f->cursor[0], "You cannot use an if statement in the file scope");
+		syntax_error(f->cursor[0], "You cannot use an if statement in the file scope");
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 	}
 	}
 
 
@@ -2252,7 +2230,7 @@ AstNode *parse_if_stmt(AstFile *f) {
 	f->expr_level = prev_level;
 	f->expr_level = prev_level;
 
 
 	if (cond == NULL) {
 	if (cond == NULL) {
-		ast_file_err(f, f->cursor[0], "Expected condition for if statement");
+		syntax_error(f->cursor[0], "Expected condition for if statement");
 	}
 	}
 
 
 	body = parse_block_stmt(f);
 	body = parse_block_stmt(f);
@@ -2266,7 +2244,7 @@ AstNode *parse_if_stmt(AstFile *f) {
 			else_stmt = parse_block_stmt(f);
 			else_stmt = parse_block_stmt(f);
 			break;
 			break;
 		default:
 		default:
-			ast_file_err(f, f->cursor[0], "Expected if statement block statement");
+			syntax_error(f->cursor[0], "Expected if statement block statement");
 			else_stmt = make_bad_stmt(f, f->cursor[0], f->cursor[1]);
 			else_stmt = make_bad_stmt(f, f->cursor[0], f->cursor[1]);
 			break;
 			break;
 		}
 		}
@@ -2277,7 +2255,7 @@ AstNode *parse_if_stmt(AstFile *f) {
 
 
 AstNode *parse_return_stmt(AstFile *f) {
 AstNode *parse_return_stmt(AstFile *f) {
 	if (f->curr_proc == NULL) {
 	if (f->curr_proc == NULL) {
-		ast_file_err(f, f->cursor[0], "You cannot use a return statement in the file scope");
+		syntax_error(f->cursor[0], "You cannot use a return statement in the file scope");
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 	}
 	}
 
 
@@ -2297,7 +2275,7 @@ AstNode *parse_return_stmt(AstFile *f) {
 
 
 AstNode *parse_for_stmt(AstFile *f) {
 AstNode *parse_for_stmt(AstFile *f) {
 	if (f->curr_proc == NULL) {
 	if (f->curr_proc == NULL) {
-		ast_file_err(f, f->cursor[0], "You cannot use a for statement in the file scope");
+		syntax_error(f->cursor[0], "You cannot use a for statement in the file scope");
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 	}
 	}
 
 
@@ -2314,7 +2292,7 @@ AstNode *parse_for_stmt(AstFile *f) {
 		if (f->cursor[0].kind != Token_Semicolon) {
 		if (f->cursor[0].kind != Token_Semicolon) {
 			cond = parse_simple_stmt(f);
 			cond = parse_simple_stmt(f);
 			if (is_ast_node_complex_stmt(cond)) {
 			if (is_ast_node_complex_stmt(cond)) {
-				ast_file_err(f, f->cursor[0],
+				syntax_error(f->cursor[0],
 				             "You are not allowed that type of statement in a for statement, it is too complex!");
 				             "You are not allowed that type of statement in a for statement, it is too complex!");
 			}
 			}
 		}
 		}
@@ -2371,7 +2349,7 @@ AstNode *parse_type_case_clause(AstFile *f) {
 
 
 AstNode *parse_match_stmt(AstFile *f) {
 AstNode *parse_match_stmt(AstFile *f) {
 	if (f->curr_proc == NULL) {
 	if (f->curr_proc == NULL) {
-		ast_file_err(f, f->cursor[0], "You cannot use a match statement in the file scope");
+		syntax_error(f->cursor[0], "You cannot use a match statement in the file scope");
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 	}
 	}
 
 
@@ -2444,7 +2422,7 @@ AstNode *parse_match_stmt(AstFile *f) {
 
 
 AstNode *parse_defer_stmt(AstFile *f) {
 AstNode *parse_defer_stmt(AstFile *f) {
 	if (f->curr_proc == NULL) {
 	if (f->curr_proc == NULL) {
-		ast_file_err(f, f->cursor[0], "You cannot use a defer statement in the file scope");
+		syntax_error(f->cursor[0], "You cannot use a defer statement in the file scope");
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 		return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
 	}
 	}
 
 
@@ -2452,13 +2430,13 @@ AstNode *parse_defer_stmt(AstFile *f) {
 	AstNode *statement = parse_stmt(f);
 	AstNode *statement = parse_stmt(f);
 	switch (statement->kind) {
 	switch (statement->kind) {
 	case AstNode_EmptyStmt:
 	case AstNode_EmptyStmt:
-		ast_file_err(f, token, "Empty statement after defer (e.g. `;`)");
+		syntax_error(token, "Empty statement after defer (e.g. `;`)");
 		break;
 		break;
 	case AstNode_DeferStmt:
 	case AstNode_DeferStmt:
-		ast_file_err(f, token, "You cannot defer a defer statement");
+		syntax_error(token, "You cannot defer a defer statement");
 		break;
 		break;
 	case AstNode_ReturnStmt:
 	case AstNode_ReturnStmt:
-		ast_file_err(f, token, "You cannot a return statement");
+		syntax_error(token, "You cannot a return statement");
 		break;
 		break;
 	}
 	}
 
 
@@ -2554,14 +2532,12 @@ AstNode *parse_stmt(AstFile *f) {
 			}
 			}
 		} break;
 		} break;
 		case AstNode_VarDecl:
 		case AstNode_VarDecl:
-			if (node->VarDecl.kind == Declaration_Mutable) {
-				valid = true;
-			}
+			valid = true;
 			break;
 			break;
 		}
 		}
 
 
 		if (!valid) {
 		if (!valid) {
-			ast_file_err(f, token, "Illegal use of `using` statement.");
+			syntax_error(token, "Illegal use of `using` statement.");
 			return make_bad_stmt(f, token, f->cursor[0]);
 			return make_bad_stmt(f, token, f->cursor[0]);
 		}
 		}
 
 
@@ -2577,7 +2553,7 @@ AstNode *parse_stmt(AstFile *f) {
 				f->is_global_scope = true;
 				f->is_global_scope = true;
 				return make_empty_stmt(f, f->cursor[0]);
 				return make_empty_stmt(f, f->cursor[0]);
 			}
 			}
-			ast_file_err(f, token, "You cannot use #shared_global_scope within a procedure. This must be done at the file scope.");
+			syntax_error(token, "You cannot use #shared_global_scope within a procedure. This must be done at the file scope.");
 			return make_bad_decl(f, token, f->cursor[0]);
 			return make_bad_decl(f, token, f->cursor[0]);
 		} else if (are_strings_equal(tag, make_string("import"))) {
 		} else if (are_strings_equal(tag, make_string("import"))) {
 			// TODO(bill): better error messages
 			// TODO(bill): better error messages
@@ -2589,7 +2565,7 @@ AstNode *parse_stmt(AstFile *f) {
 			if (f->curr_proc == NULL) {
 			if (f->curr_proc == NULL) {
 				return make_import_decl(f, s->TagStmt.token, file_path, import_name, false);
 				return make_import_decl(f, s->TagStmt.token, file_path, import_name, false);
 			}
 			}
-			ast_file_err(f, token, "You cannot use #import within a procedure. This must be done at the file scope.");
+			syntax_error(token, "You cannot use #import within a procedure. This must be done at the file scope.");
 			return make_bad_decl(f, token, file_path);
 			return make_bad_decl(f, token, file_path);
 		} else if (are_strings_equal(tag, make_string("load"))) {
 		} else if (are_strings_equal(tag, make_string("load"))) {
 			// TODO(bill): better error messages
 			// TODO(bill): better error messages
@@ -2600,24 +2576,23 @@ AstNode *parse_stmt(AstFile *f) {
 			if (f->curr_proc == NULL) {
 			if (f->curr_proc == NULL) {
 				return make_import_decl(f, s->TagStmt.token, file_path, import_name, true);
 				return make_import_decl(f, s->TagStmt.token, file_path, import_name, true);
 			}
 			}
-			ast_file_err(f, token, "You cannot use #load within a procedure. This must be done at the file scope.");
+			syntax_error(token, "You cannot use #load within a procedure. This must be done at the file scope.");
 			return make_bad_decl(f, token, file_path);
 			return make_bad_decl(f, token, file_path);
 		} else if (are_strings_equal(tag, make_string("foreign_system_library"))) {
 		} else if (are_strings_equal(tag, make_string("foreign_system_library"))) {
 			Token file_path = expect_token(f, Token_String);
 			Token file_path = expect_token(f, Token_String);
 			if (f->curr_proc == NULL) {
 			if (f->curr_proc == NULL) {
 				return make_foreign_system_library(f, s->TagStmt.token, file_path);
 				return make_foreign_system_library(f, s->TagStmt.token, file_path);
 			}
 			}
-			ast_file_err(f, token, "You cannot use #foreign_system_library within a procedure. This must be done at the file scope.");
+			syntax_error(token, "You cannot use #foreign_system_library within a procedure. This must be done at the file scope.");
 			return make_bad_decl(f, token, file_path);
 			return make_bad_decl(f, token, file_path);
 		} else if (are_strings_equal(tag, make_string("thread_local"))) {
 		} else if (are_strings_equal(tag, make_string("thread_local"))) {
 			AstNode *var_decl = parse_simple_stmt(f);
 			AstNode *var_decl = parse_simple_stmt(f);
-			if (var_decl->kind != AstNode_VarDecl ||
-			    var_decl->VarDecl.kind != Declaration_Mutable) {
-				ast_file_err(f, token, "#thread_local may only be applied to variable declarations");
+			if (var_decl->kind != AstNode_VarDecl) {
+				syntax_error(token, "#thread_local may only be applied to variable declarations");
 				return make_bad_decl(f, token, ast_node_token(var_decl));
 				return make_bad_decl(f, token, ast_node_token(var_decl));
 			}
 			}
 			if (f->curr_proc != NULL) {
 			if (f->curr_proc != NULL) {
-				ast_file_err(f, token, "#thread_local is only allowed at the file scope.");
+				syntax_error(token, "#thread_local is only allowed at the file scope.");
 				return make_bad_decl(f, token, ast_node_token(var_decl));
 				return make_bad_decl(f, token, ast_node_token(var_decl));
 			}
 			}
 			var_decl->VarDecl.tags |= VarDeclTag_thread_local;
 			var_decl->VarDecl.tags |= VarDeclTag_thread_local;
@@ -2626,14 +2601,14 @@ AstNode *parse_stmt(AstFile *f) {
 			s = parse_stmt(f);
 			s = parse_stmt(f);
 			s->stmt_state_flags |= StmtStateFlag_bounds_check;
 			s->stmt_state_flags |= StmtStateFlag_bounds_check;
 			if ((s->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
 			if ((s->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
-				ast_file_err(f, token, "#bounds_check and #no_bounds_check cannot be applied together");
+				syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
 			}
 			}
 			return s;
 			return s;
 		} else if (are_strings_equal(tag, make_string("no_bounds_check"))) {
 		} else if (are_strings_equal(tag, make_string("no_bounds_check"))) {
 			s = parse_stmt(f);
 			s = parse_stmt(f);
 			s->stmt_state_flags |= StmtStateFlag_no_bounds_check;
 			s->stmt_state_flags |= StmtStateFlag_no_bounds_check;
 			if ((s->stmt_state_flags & StmtStateFlag_bounds_check) != 0) {
 			if ((s->stmt_state_flags & StmtStateFlag_bounds_check) != 0) {
-				ast_file_err(f, token, "#bounds_check and #no_bounds_check cannot be applied together");
+				syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
 			}
 			}
 			return s;
 			return s;
 		}
 		}
@@ -2651,7 +2626,7 @@ AstNode *parse_stmt(AstFile *f) {
 		return s;
 		return s;
 	}
 	}
 
 
-	ast_file_err(f, token,
+	syntax_error(token,
 	             "Expected a statement, got `%.*s`",
 	             "Expected a statement, got `%.*s`",
 	             LIT(token_strings[token.kind]));
 	             LIT(token_strings[token.kind]));
 	fix_advance_to_next_stmt(f);
 	fix_advance_to_next_stmt(f);
@@ -2837,14 +2812,14 @@ void parse_file(Parser *p, AstFile *f) {
 		    node->kind != AstNode_BadStmt &&
 		    node->kind != AstNode_BadStmt &&
 		    node->kind != AstNode_EmptyStmt) {
 		    node->kind != AstNode_EmptyStmt) {
 			// NOTE(bill): Sanity check
 			// NOTE(bill): Sanity check
-			ast_file_err(f, ast_node_token(node), "Only declarations are allowed at file scope");
+			syntax_error(ast_node_token(node), "Only declarations are allowed at file scope");
 		} else {
 		} else {
 			if (node->kind == AstNode_ImportDecl) {
 			if (node->kind == AstNode_ImportDecl) {
 				auto *id = &node->ImportDecl;
 				auto *id = &node->ImportDecl;
 				String file_str = id->relpath.string;
 				String file_str = id->relpath.string;
 
 
 				if (!is_import_path_valid(file_str)) {
 				if (!is_import_path_valid(file_str)) {
-					ast_file_err(f, ast_node_token(node), "Invalid `load` path");
+					syntax_error(ast_node_token(node), "Invalid `load` path");
 					continue;
 					continue;
 				}
 				}
 
 
@@ -2868,7 +2843,7 @@ void parse_file(Parser *p, AstFile *f) {
 				String file_str = id->filepath.string;
 				String file_str = id->filepath.string;
 
 
 				if (!is_import_path_valid(file_str)) {
 				if (!is_import_path_valid(file_str)) {
-					ast_file_err(f, ast_node_token(node), "Invalid `foreign_system_library` path");
+					syntax_error(ast_node_token(node), "Invalid `foreign_system_library` path");
 					continue;
 					continue;
 				}
 				}
 
 

+ 11 - 4
src/printer.cpp

@@ -142,11 +142,18 @@ void print_ast(AstNode *node, isize indent) {
 
 
 	case AstNode_VarDecl:
 	case AstNode_VarDecl:
 		print_indent(indent);
 		print_indent(indent);
-		if (node->VarDecl.kind == Declaration_Mutable) {
-			gb_printf("(decl:var,mutable)\n");
-		} else if (node->VarDecl.kind == Declaration_Immutable) {
-			gb_printf("(decl:var,immutable)\n");
+		gb_printf("(decl:var)\n");
+		gb_for_array(i, node->VarDecl.names) {
+			print_ast(node->VarDecl.names[i], indent+1);
+		}
+		print_ast(node->VarDecl.type, indent+1);
+		gb_for_array(i, node->VarDecl.values) {
+			print_ast(node->VarDecl.values[i], indent+1);
 		}
 		}
+		break;
+	case AstNode_ConstDecl:
+		print_indent(indent);
+		gb_printf("(decl:const)\n");
 		gb_for_array(i, node->VarDecl.names) {
 		gb_for_array(i, node->VarDecl.names) {
 			print_ast(node->VarDecl.names[i], indent+1);
 			print_ast(node->VarDecl.names[i], indent+1);
 		}
 		}

+ 39 - 12
src/tokenizer.cpp

@@ -155,31 +155,58 @@ Token empty_token = {Token_Invalid};
 struct ErrorCollector {
 struct ErrorCollector {
 	TokenPos prev;
 	TokenPos prev;
 	i64 count;
 	i64 count;
+	i64 warning_count;
 };
 };
 
 
-gb_no_inline void error(ErrorCollector *ec, Token token, char *fmt, ...) {
-	ec->count++;
+gb_global ErrorCollector global_error_collector;
+
+
+void warning(Token token, char *fmt, ...) {
+	global_error_collector.warning_count++;
 	// NOTE(bill): Duplicate error, skip it
 	// NOTE(bill): Duplicate error, skip it
-	if (!token_pos_are_equal(ec->prev, token.pos)) {
-		ec->prev = token.pos;
+	if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
+		va_list va;
+
+		global_error_collector.prev = token.pos;
 
 
+		va_start(va, fmt);
+		gb_printf_err("%.*s(%td:%td) Warning: %s\n",
+		              LIT(token.pos.file), token.pos.line, token.pos.column,
+		              gb_bprintf_va(fmt, va));
+		va_end(va);
+	}
+}
+
+void error(Token token, char *fmt, ...) {
+	global_error_collector.count++;
+	// NOTE(bill): Duplicate error, skip it
+	if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
 		va_list va;
 		va_list va;
+
+		global_error_collector.prev = token.pos;
+
 		va_start(va, fmt);
 		va_start(va, fmt);
 		gb_printf_err("%.*s(%td:%td) %s\n",
 		gb_printf_err("%.*s(%td:%td) %s\n",
 		              LIT(token.pos.file), token.pos.line, token.pos.column,
 		              LIT(token.pos.file), token.pos.line, token.pos.column,
 		              gb_bprintf_va(fmt, va));
 		              gb_bprintf_va(fmt, va));
 		va_end(va);
 		va_end(va);
-
 	}
 	}
 }
 }
 
 
-gb_no_inline void warning(Token token, char *fmt, ...) {
-	va_list va;
-	va_start(va, fmt);
-	gb_printf_err("%.*s(%td:%td) Warning: %s\n",
-	              LIT(token.pos.file), token.pos.line, token.pos.column,
-	              gb_bprintf_va(fmt, va));
-	va_end(va);
+void syntax_error(Token token, char *fmt, ...) {
+	global_error_collector.count++;
+	// NOTE(bill): Duplicate error, skip it
+	if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
+		va_list va;
+
+		global_error_collector.prev = token.pos;
+
+		va_start(va, fmt);
+		gb_printf_err("%.*s(%td:%td) Syntax Error: %s\n",
+		              LIT(token.pos.file), token.pos.line, token.pos.column,
+		              gb_bprintf_va(fmt, va));
+		va_end(va);
+	}
 }
 }
 
 
 
 

Some files were not shown because too many files changed in this diff