Browse Source

package odin_parser

gingerBill 6 years ago
parent
commit
5f840ea2fc
4 changed files with 3736 additions and 0 deletions
  1. 607 0
      core/odin/ast/ast.odin
  2. 313 0
      core/odin/ast/clone.odin
  3. 40 0
      core/odin/ast/file.odin
  4. 2776 0
      core/odin/parser/parser.odin

+ 607 - 0
core/odin/ast/ast.odin

@@ -0,0 +1,607 @@
+package odin_ast
+
+import "core:odin/token"
+
+Proc_Tag :: enum {
+	Bounds_Check,
+	No_Bounds_Check,
+	Require_Results,
+	No_Context,
+}
+Proc_Tags :: distinct bit_set[Proc_Tag; u32];
+
+Proc_Inlining :: enum u32 {
+	None      = 0,
+	Inline    = 1,
+	No_Inline = 2,
+}
+
+Proc_Calling_Convention :: enum i32 {
+	Invalid = 0,
+	Odin,
+	Contextless,
+	C_Decl,
+	Std_Call,
+	Fast_Call,
+
+	Foreign_Block_Default = -1,
+}
+
+
+Comment_Group :: struct {
+	list: []token.Token,
+}
+
+Node :: struct {
+	pos:     token.Pos,
+	end:     token.Pos,
+	derived: any,
+}
+
+
+Expr :: struct {
+	using expr_base: Node,
+}
+Stmt :: struct {
+	using stmt_base: Node,
+}
+Decl :: struct {
+	using decl_base: Stmt,
+}
+
+// Expressions
+
+Bad_Expr :: struct {
+	using node: Expr,
+}
+
+Ident :: struct {
+	using node: Expr,
+	name: string,
+}
+
+Implicit :: struct {
+	using node: Expr,
+	tok: token.Token,
+}
+
+
+Undef :: struct {
+	using node: Expr,
+	tok:  token.Kind,
+}
+
+Basic_Lit :: struct {
+	using node: Expr,
+	tok: token.Token,
+}
+
+Basic_Directive :: struct {
+	using node: Expr,
+	tok:  token.Token,
+	name: string,
+}
+
+Ellipsis :: struct {
+	using node: Expr,
+	tok:  token.Kind,
+	expr: ^Expr,
+}
+
+Proc_Lit :: struct {
+	using node: Expr,
+	type: ^Proc_Type,
+	body: ^Stmt,
+	tags: Proc_Tags,
+	inlining: Proc_Inlining,
+}
+
+Comp_Lit :: struct {
+	using node: Expr,
+	type: ^Expr,
+	open: token.Pos,
+	elems: []^Expr,
+	close: token.Pos,
+}
+
+
+Tag_Expr :: struct {
+	using node: Expr,
+	op:      token.Token,
+	name:    string,
+	expr:    ^Expr,
+}
+
+Unary_Expr :: struct {
+	using node: Expr,
+	op:   token.Token,
+	expr: ^Expr,
+}
+
+Binary_Expr :: struct {
+	using node: Expr,
+	left:  ^Expr,
+	op:    token.Token,
+	right: ^Expr,
+}
+
+Paren_Expr :: struct {
+	using node: Expr,
+	open:  token.Pos,
+	expr:  ^Expr,
+	close: token.Pos,
+}
+
+Selector_Expr :: struct {
+	using node: Expr,
+	expr:  ^Expr,
+	field: ^Ident,
+}
+
+
+Index_Expr :: struct {
+	using node: Expr,
+	expr:  ^Expr,
+	open:  token.Pos,
+	index: ^Expr,
+	close: token.Pos,
+}
+
+Deref_Expr :: struct {
+	using node: Expr,
+	expr: ^Expr,
+	op:   token.Token,
+}
+
+Slice_Expr :: struct {
+	using node: Expr,
+	expr:     ^Expr,
+	open:     token.Pos,
+	low:      ^Expr,
+	interval: token.Token,
+	high:     ^Expr,
+	close:    token.Pos,
+}
+
+Call_Expr :: struct {
+	using node: Expr,
+	inlining: Proc_Inlining,
+	expr:     ^Expr,
+	open:     token.Pos,
+	args:     []^Expr,
+	ellipsis: token.Token,
+	close:    token.Pos,
+}
+
+Field_Value :: struct {
+	using node: Expr,
+	field: ^Expr,
+	sep:   token.Pos,
+	value: ^Expr,
+}
+
+Ternary_Expr :: struct {
+	using node: Expr,
+	cond: ^Expr,
+	op1:  token.Token,
+	x:    ^Expr,
+	op2:  token.Token,
+	y:    ^Expr,
+}
+
+Type_Assertion :: struct {
+	using node: Expr,
+	expr:  ^Expr,
+	dot:   token.Pos,
+	open:  token.Pos,
+	type:  ^Expr,
+	close: token.Pos,
+}
+
+Type_Cast :: struct {
+	using node: Expr,
+	tok:   token.Token,
+	open:  token.Pos,
+	type:  ^Expr,
+	close: token.Pos,
+	expr:  ^Expr,
+}
+
+Auto_Cast :: struct {
+	using node: Expr,
+	op:   token.Token,
+	expr: ^Expr,
+}
+
+
+
+
+// Statements
+
+Bad_Stmt :: struct {
+	using node: Stmt,
+}
+
+Empty_Stmt :: struct {
+	using node: Stmt,
+	semicolon: token.Pos, // Position of the following ';'
+}
+
+Expr_Stmt :: struct {
+	using node: Stmt,
+	expr: ^Expr,
+}
+
+Tag_Stmt :: struct {
+	using node: Stmt,
+	op:      token.Token,
+	name:    string,
+	stmt:    ^Stmt,
+}
+
+Assign_Stmt :: struct {
+	using node: Stmt,
+	lhs:    []^Expr,
+	op:     token.Token,
+	rhs:    []^Expr,
+}
+
+
+Block_Stmt :: struct {
+	using node: Stmt,
+	label: ^Expr,
+	open:  token.Pos,
+	stmts: []^Stmt,
+	close: token.Pos,
+}
+
+If_Stmt :: struct {
+	using node: Stmt,
+	label:     ^Expr,
+	if_pos:    token.Pos,
+	init:      ^Stmt,
+	cond:      ^Expr,
+	body:      ^Stmt,
+	else_stmt: ^Stmt,
+}
+
+When_Stmt :: struct {
+	using node: Stmt,
+	when_pos:  token.Pos,
+	cond:      ^Expr,
+	body:      ^Stmt,
+	else_stmt: ^Stmt,
+}
+
+Return_Stmt :: struct {
+	using node: Stmt,
+	results: []^Expr,
+}
+
+Defer_Stmt :: struct {
+	using node: Stmt,
+	stmt: ^Stmt,
+}
+
+For_Stmt :: struct {
+	using node: Stmt,
+	label:     ^Expr,
+	for_pos:   token.Pos,
+	init:      ^Stmt,
+	cond:      ^Expr,
+	post:      ^Stmt,
+	body:      ^Stmt,
+}
+
+Range_Stmt :: struct {
+	using node: Stmt,
+	label:     ^Expr,
+	for_pos:   token.Pos,
+	val0:      ^Expr,
+	val1:      ^Expr,
+	in_pos:    token.Pos,
+	expr:      ^Expr,
+	body:      ^Stmt,
+}
+
+
+Case_Clause :: struct {
+	using node: Stmt,
+	case_pos:   token.Pos,
+	list:       []^Expr,
+	terminator: token.Token,
+	body:       []^Stmt,
+}
+
+Switch_Stmt :: struct {
+	using node: Stmt,
+	label:      ^Expr,
+	switch_pos: token.Pos,
+	init:       ^Stmt,
+	cond:       ^Expr,
+	body:       ^Stmt,
+	complete:   bool,
+}
+
+Type_Switch_Stmt :: struct {
+	using node: Stmt,
+	label:      ^Expr,
+	switch_pos: token.Pos,
+	tag:        ^Stmt,
+	expr:       ^Expr,
+	body:       ^Stmt,
+	complete:   bool,
+}
+
+Branch_Stmt :: struct {
+	using node: Stmt,
+	tok:   token.Token,
+	label: ^Ident,
+}
+
+Using_Stmt :: struct {
+	using node: Stmt,
+	list: []^Expr,
+}
+
+
+// Declarations
+
+Bad_Decl :: struct {
+	using node: Decl,
+}
+
+Value_Decl :: struct {
+	using node: Decl,
+	docs:       ^Comment_Group,
+	attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
+	names:      []^Expr,
+	type:       ^Expr,
+	values:     []^Expr,
+	comment:    ^Comment_Group,
+	is_static:  bool,
+	is_using:   bool,
+	is_mutable: bool,
+}
+
+Package_Decl :: struct {
+	using node: Decl,
+	docs:    ^Comment_Group,
+	token:   token.Token,
+	name:    string,
+	comment: ^Comment_Group,
+}
+
+Import_Decl :: struct {
+	using node: Decl,
+	docs:       ^Comment_Group,
+	is_using:    bool,
+	import_tok:  token.Token,
+	name:        token.Token,
+	relpath:     token.Token,
+	fullpath:    string,
+	comment:     ^Comment_Group,
+}
+
+Foreign_Block_Decl :: struct {
+	using node: Decl,
+	docs:            ^Comment_Group,
+	attributes:      [dynamic]^Attribute, // dynamic as parsing will add to them lazily
+	tok:             token.Token,
+	foreign_library: ^Expr,
+	body:            ^Stmt,
+}
+
+Foreign_Import_Decl :: struct {
+	using node: Decl,
+	docs:            ^Comment_Group,
+	foreign_tok:     token.Token,
+	import_tok:      token.Token,
+	name:            ^Ident,
+	collection_name: string,
+	fullpaths:       []string,
+	comment:         ^Comment_Group,
+}
+
+
+
+// Other things
+unparen_expr :: proc(expr: ^Expr) -> ^Expr {
+	if expr == nil {
+		return nil;
+	}
+	for {
+		e, ok := expr.derived.(Paren_Expr);
+		if !ok do break;
+		expr = e.expr;
+	}
+	return expr;
+}
+
+Field_Flag :: enum {
+	Ellipsis,
+	Using,
+	No_Alias,
+	C_Vararg,
+	Auto_Cast,
+	In,
+	Results,
+	Default_Parameters,
+	Typeid_Token,
+}
+
+Field_Flags :: distinct bit_set[Field_Flag];
+
+Field_Flags_Struct :: Field_Flags{
+	Field_Flag.Using,
+};
+Field_Flags_Record_Poly_Params :: Field_Flags{
+	Field_Flag.Typeid_Token,
+};
+Field_Flags_Signature :: Field_Flags{
+	Field_Flag.Ellipsis,
+	Field_Flag.Using,
+	Field_Flag.No_Alias,
+	Field_Flag.C_Vararg,
+	Field_Flag.Auto_Cast,
+	Field_Flag.Default_Parameters,
+};
+
+Field_Flags_Signature_Params  :: Field_Flags_Signature | {Field_Flag.Typeid_Token};
+Field_Flags_Signature_Results :: Field_Flags_Signature;
+
+
+Proc_Group :: struct {
+	using node: Expr,
+	tok:   token.Token,
+	open:  token.Pos,
+	args:  []^Expr,
+	close: token.Pos,
+}
+
+Attribute :: struct {
+	using node: Node,
+	tok:   token.Kind,
+	open:  token.Pos,
+	elems: []^Expr,
+	close: token.Pos,
+}
+
+Field :: struct {
+	using node: Node,
+	docs:          ^Comment_Group,
+	names:         []^Expr, // Could be polymorphic
+	type:          ^Expr,
+	default_value: ^Expr,
+	flags:         Field_Flags,
+	comment:       ^Comment_Group,
+}
+
+Field_List :: struct {
+	using node: Node,
+	open:  token.Pos,
+	list:  []^Field,
+	close: token.Pos,
+}
+
+
+// Types
+Typeid_Type :: struct {
+	using node: Expr,
+	tok:            token.Kind,
+	specialization: ^Expr,
+}
+
+Helper_Type :: struct {
+	using node: Expr,
+	tok:  token.Kind,
+	type: ^Expr,
+}
+
+Distinct_Type :: struct {
+	using node: Expr,
+	tok:  token.Kind,
+	type: ^Expr,
+}
+
+Opaque_Type :: struct {
+	using node: Expr,
+	tok:  token.Kind,
+	type: ^Expr,
+}
+
+Poly_Type :: struct {
+	using node: Expr,
+	dollar:         token.Pos,
+	type:           ^Ident,
+	specialization: ^Expr,
+}
+
+Proc_Type :: struct {
+	using node: Expr,
+	tok:       token.Token,
+	calling_convention: Proc_Calling_Convention,
+	params:    ^Field_List,
+	arrow:     token.Pos,
+	results:   ^Field_List,
+	tags:      Proc_Tags,
+	generic:   bool,
+	diverging: bool,
+}
+
+Pointer_Type :: struct {
+	using node: Expr,
+	pointer: token.Pos,
+	elem:    ^Expr,
+}
+
+Array_Type :: struct {
+	using node: Expr,
+	open:  token.Pos,
+	len:   ^Expr, // Ellipsis node for [?]T arrray types, nil for slice types
+	close: token.Pos,
+	elem:  ^Expr,
+}
+
+Dynamic_Array_Type :: struct {
+	using node: Expr,
+	open:        token.Pos,
+	dynamic_pos: token.Pos,
+	close:       token.Pos,
+	elem:        ^Expr,
+}
+
+Struct_Type :: struct {
+	using node: Expr,
+	tok_pos:   token.Pos,
+	poly_params:  ^Field_List,
+	align:        ^Expr,
+	is_packed:    bool,
+	is_raw_union: bool,
+	fields:       ^Field_List,
+	name_count:  int,
+}
+
+Union_Type :: struct {
+	using node: Expr,
+	tok_pos:     token.Pos,
+	poly_params: ^Field_List,
+	align:       ^Expr,
+	variants:    []^Expr,
+}
+
+Enum_Type :: struct {
+	using node: Expr,
+	tok_pos:  token.Pos,
+	base_type: ^Expr,
+	open:      token.Pos,
+	fields:    []^Expr,
+	close:     token.Pos,
+
+	is_using:  bool,
+}
+
+Bit_Field_Type :: struct {
+	using node: Expr,
+	tok_pos: token.Pos,
+	align:   ^Expr,
+	open:    token.Pos,
+	fields:  []^Field_Value, // Field_Value with ':' rather than '='
+	close:   token.Pos,
+}
+
+Bit_Set_Type :: struct {
+	using node: Expr,
+	tok_pos:    token.Pos,
+	open:       token.Pos,
+	elem:       ^Expr,
+	underlying: ^Expr,
+	close:      token.Pos,
+}
+
+Map_Type :: struct {
+	using node: Expr,
+	tok_pos: token.Pos,
+	key:     ^Expr,
+	value:   ^Expr,
+}

+ 313 - 0
core/odin/ast/clone.odin

@@ -0,0 +1,313 @@
+package odin_ast
+
+import "core:mem"
+import "core:fmt"
+import "core:odin/token"
+
+new :: proc($T: typeid, pos, end: token.Pos) -> ^T {
+	n := mem.new(T);
+	n.pos = pos;
+	n.end = end;
+	n.derived = n^;
+	base: ^Node = n; // dummy check
+	return n;
+}
+
+clone :: proc{
+	clone_node,
+	clone_expr,
+	clone_stmt,
+	clone_decl,
+	clone_array,
+	clone_dynamic_array,
+};
+
+clone_array :: proc(array: $A/[]^$T) -> A {
+	if len(array) == 0 {
+		return nil;
+	}
+	res := make(A, len(array));
+	for elem, i in array {
+		res[i] = auto_cast clone(elem);
+	}
+	return res;
+}
+
+clone_dynamic_array :: proc(array: $A/[dynamic]^$T) -> A {
+	if len(array) == 0 {
+		return nil;
+	}
+	res := make(A, len(array));
+	for elem, i in array {
+		res[i] = auto_cast clone(elem);
+	}
+	return res;
+}
+
+clone_expr :: proc(node: ^Expr) -> ^Expr {
+	return cast(^Expr)clone_node(node);
+}
+clone_stmt :: proc(node: ^Stmt) -> ^Stmt {
+	return cast(^Stmt)clone_node(node);
+}
+clone_decl :: proc(node: ^Decl) -> ^Decl {
+	return cast(^Decl)clone_node(node);
+}
+clone_node :: proc(node: ^Node) -> ^Node {
+	if node == nil {
+		return nil;
+	}
+
+	size := size_of(Node);
+	align := align_of(Node);
+	ti := type_info_of(node.derived.id);
+	if ti != nil {
+		size = ti.size;
+		align = ti.align;
+	}
+
+	res := cast(^Node)mem.alloc(size, align);
+	src: rawptr = node;
+	if node.derived != nil {
+		src = node.derived.data;
+	}
+	mem.copy(res, src, size);
+	res.derived.data = rawptr(res);
+
+	switch n in node.derived {
+	case Bad_Expr:
+	case Ident:
+	case Implicit:
+	case Undef:
+	case Basic_Lit:
+
+	case Ellipsis:
+		r := cast(^Ellipsis)res;
+		r.expr = clone(r.expr);
+	case Proc_Lit:
+		r := cast(^Proc_Lit)res;
+		r.type = auto_cast clone(r.type);
+		r.body = clone(r.body);
+	case Comp_Lit:
+		r := cast(^Comp_Lit)res;
+		r.type  = clone(r.type);
+		r.elems = clone(r.elems);
+
+	case Tag_Expr:
+		r := cast(^Tag_Expr)res;
+		r.expr = clone(r.expr);
+	case Unary_Expr:
+		r := cast(^Unary_Expr)res;
+		r.expr = clone(r.expr);
+	case Binary_Expr:
+		r := cast(^Binary_Expr)res;
+		r.left  = clone(r.left);
+		r.right = clone(r.right);
+	case Paren_Expr:
+		r := cast(^Paren_Expr)res;
+		r.expr = clone(r.expr);
+	case Selector_Expr:
+		r := cast(^Selector_Expr)res;
+		r.expr = clone(r.expr);
+		r.field = auto_cast clone(r.field);
+	case Index_Expr:
+		r := cast(^Index_Expr)res;
+		r.expr = clone(r.expr);
+		r.index = clone(r.index);
+	case Deref_Expr:
+		r := cast(^Deref_Expr)res;
+		r.expr = clone(r.expr);
+	case Slice_Expr:
+		r := cast(^Slice_Expr)res;
+		r.expr = clone(r.expr);
+		r.low  = clone(r.low);
+		r.high = clone(r.high);
+	case Call_Expr:
+		r := cast(^Call_Expr)res;
+		r.expr = clone(r.expr);
+		r.args = clone(r.args);
+	case Field_Value:
+		r := cast(^Field_Value)res;
+		r.field = clone(r.field);
+		r.value = clone(r.value);
+	case Ternary_Expr:
+		r := cast(^Ternary_Expr)res;
+		r.cond = clone(r.cond);
+		r.x    = clone(r.x);
+		r.y    = clone(r.y);
+	case Type_Assertion:
+		r := cast(^Type_Assertion)res;
+		r.expr = clone(r.expr);
+		r.type = clone(r.type);
+	case Type_Cast:
+		r := cast(^Type_Cast)res;
+		r.type = clone(r.type);
+		r.expr = clone(r.expr);
+	case Auto_Cast:
+		r := cast(^Auto_Cast)res;
+		r.expr = clone(r.expr);
+
+	case Bad_Stmt:
+	case Empty_Stmt:
+	case Expr_Stmt:
+		r := cast(^Expr_Stmt)res;
+		r.expr = clone(r.expr);
+	case Tag_Stmt:
+		r := cast(^Expr_Stmt)res;
+		r.expr = clone(r.expr);
+
+	case Assign_Stmt:
+		r := cast(^Assign_Stmt)res;
+		r.lhs = clone(r.lhs);
+		r.rhs = clone(r.rhs);
+	case Block_Stmt:
+		r := cast(^Block_Stmt)res;
+		r.label = auto_cast clone(r.label);
+		r.stmts = clone(r.stmts);
+	case If_Stmt:
+		r := cast(^If_Stmt)res;
+		r.label     = auto_cast clone(r.label);
+		r.init      = clone(r.init);
+		r.cond      = clone(r.cond);
+		r.body      = clone(r.body);
+		r.else_stmt = clone(r.else_stmt);
+	case When_Stmt:
+		r := cast(^When_Stmt)res;
+		r.cond      = clone(r.cond);
+		r.body      = clone(r.body);
+		r.else_stmt = clone(r.else_stmt);
+	case Return_Stmt:
+		r := cast(^Return_Stmt)res;
+		r.results = clone(r.results);
+	case Defer_Stmt:
+		r := cast(^Defer_Stmt)res;
+		r.stmt = clone(r.stmt);
+	case For_Stmt:
+		r := cast(^For_Stmt)res;
+		r.label = auto_cast clone(r.label);
+		r.init = clone(r.init);
+		r.cond = clone(r.cond);
+		r.post = clone(r.post);
+		r.body = clone(r.body);
+	case Range_Stmt:
+		r := cast(^Range_Stmt)res;
+		r.label = auto_cast clone(r.label);
+		r.val0 = clone(r.val0);
+		r.val1 = clone(r.val1);
+		r.expr = clone(r.expr);
+		r.body = clone(r.body);
+	case Case_Clause:
+		r := cast(^Case_Clause)res;
+		r.list = clone(r.list);
+		r.body = clone(r.body);
+	case Switch_Stmt:
+		r := cast(^Switch_Stmt)res;
+		r.label = auto_cast clone(r.label);
+		r.init = clone(r.init);
+		r.cond = clone(r.cond);
+		r.body = clone(r.body);
+	case Type_Switch_Stmt:
+		r := cast(^Type_Switch_Stmt)res;
+		r.label = auto_cast clone(r.label);
+		r.tag  = clone(r.tag);
+		r.expr = clone(r.expr);
+		r.body = clone(r.body);
+	case Branch_Stmt:
+		r := cast(^Branch_Stmt)res;
+		r.label = auto_cast clone(r.label);
+	case Using_Stmt:
+		r := cast(^Using_Stmt)res;
+		r.list = clone(r.list);
+	case Bad_Decl:
+	case Value_Decl:
+		r := cast(^Value_Decl)res;
+		r.attributes = clone(r.attributes);
+		r.names      = clone(r.names);
+		r.type       = clone(r.type);
+		r.values     = clone(r.values);
+	case Package_Decl:
+	case Import_Decl:
+	case Foreign_Block_Decl:
+		r := cast(^Foreign_Block_Decl)res;
+		r.attributes      = clone(r.attributes);
+		r.foreign_library = clone(r.foreign_library);
+		r.body            = clone(r.body);
+	case Foreign_Import_Decl:
+		r := cast(^Foreign_Import_Decl)res;
+		r.name = auto_cast clone(r.name);
+	case Proc_Group:
+		r := cast(^Proc_Group)res;
+		r.args = clone(r.args);
+	case Attribute:
+		r := cast(^Attribute)res;
+		r.elems = clone(r.elems);
+	case Field:
+		r := cast(^Field)res;
+		r.names         = clone(r.names);
+		r.type          = clone(r.type);
+		r.default_value = clone(r.default_value);
+	case Field_List:
+		r := cast(^Field_List)res;
+		r.list = clone(r.list);
+	case Typeid_Type:
+		r := cast(^Typeid_Type)res;
+		r.specialization = clone(r.specialization);
+	case Helper_Type:
+		r := cast(^Helper_Type)res;
+		r.type = clone(r.type);
+	case Distinct_Type:
+		r := cast(^Distinct_Type)res;
+		r.type = clone(r.type);
+	case Opaque_Type:
+		r := cast(^Opaque_Type)res;
+		r.type = clone(r.type);
+	case Poly_Type:
+		r := cast(^Poly_Type)res;
+		r.type = auto_cast clone(r.type);
+		r.specialization = clone(r.specialization);
+	case Proc_Type:
+		r := cast(^Proc_Type)res;
+		r.params  = auto_cast clone(r.params);
+		r.results = auto_cast clone(r.results);
+	case Pointer_Type:
+		r := cast(^Pointer_Type)res;
+		r.elem = clone(r.elem);
+	case Array_Type:
+		r := cast(^Array_Type)res;
+		r.len  = clone(r.len);
+		r.elem = clone(r.elem);
+	case Dynamic_Array_Type:
+		r := cast(^Dynamic_Array_Type)res;
+		r.elem = clone(r.elem);
+	case Struct_Type:
+		r := cast(^Struct_Type)res;
+		r.poly_params = auto_cast clone(r.poly_params);
+		r.align = clone(r.align);
+		r.fields = auto_cast clone(r.fields);
+	case Union_Type:
+		r := cast(^Union_Type)res;
+		r.poly_params = auto_cast clone(r.poly_params);
+		r.align = clone(r.align);
+		r.variants = clone(r.variants);
+	case Enum_Type:
+		r := cast(^Enum_Type)res;
+		r.base_type = clone(r.base_type);
+		r.fields = clone(r.fields);
+	case Bit_Field_Type:
+		r := cast(^Bit_Field_Type)res;
+		r.fields = clone(r.fields);
+	case Bit_Set_Type:
+		r := cast(^Bit_Set_Type)res;
+		r.elem = clone(r.elem);
+		r.underlying = clone(r.underlying);
+	case Map_Type:
+		r := cast(^Map_Type)res;
+		r.key = clone(r.key);
+		r.value = clone(r.value);
+
+	case:
+		fmt.panicf("Unhandled node kind: %T", n);
+	}
+
+	return res;
+}

+ 40 - 0
core/odin/ast/file.odin

@@ -0,0 +1,40 @@
+package odin_ast
+
+import "core:odin/token"
+
+Package_Kind :: enum {
+	Normal,
+	Runtime,
+	Init,
+}
+
+Package :: struct {
+	kind:     Package_Kind,
+	id:       int,
+	name:     string,
+	fullpath: string,
+	files:    []^File,
+
+	user_data: rawptr,
+}
+
+File :: struct {
+	id: int,
+	pkg: ^Package,
+
+	fullpath: string,
+	src:      []byte,
+
+	pkg_decl:  ^Package_Decl,
+	pkg_token: token.Token,
+	pkg_name:  string,
+
+	decls:   [dynamic]^Stmt,
+	imports: [dynamic]^Import_Decl,
+	directive_count: int,
+
+	comments: [dynamic]^Comment_Group,
+
+	syntax_warning_count: int,
+	syntax_error_count:   int,
+}

+ 2776 - 0
core/odin/parser/parser.odin

@@ -0,0 +1,2776 @@
+package odin_parser
+
+import "core:odin/ast"
+import "core:odin/token"
+import "core:odin/tokenizer"
+
+import "core:strings"
+import "core:fmt"
+
+Warning_Handler :: #type proc(pos: token.Pos, fmt: string, args: ..any);
+Error_Handler   :: #type proc(pos: token.Pos, fmt: string, args: ..any);
+
+Parser :: struct {
+	file: ^ast.File,
+	tok: tokenizer.Tokenizer,
+
+	warn: Warning_Handler,
+	err:  Error_Handler,
+
+	prev_tok: token.Token,
+	curr_tok: token.Token,
+
+	// >= 0: In Expression
+	// <  0: In Control Clause
+	// NOTE(bill): Used to prevent type literals in control clauses
+	expr_level:       int,
+	allow_range:      bool, // NOTE(bill): Ranges are only allowed in certain cases
+	allow_in_expr:    bool, // NOTE(bill): in expression are only allowed in certain cases
+	in_foreign_block: bool,
+	allow_type:       bool,
+
+	lead_comment: ^ast.Comment_Group,
+	line_comment: ^ast.Comment_Group,
+
+	curr_proc: ^ast.Node,
+
+	error_count: int,
+}
+
+Stmt_Allow_Flag :: enum {
+	In,
+	Label,
+}
+Stmt_Allow_Flags :: distinct bit_set[Stmt_Allow_Flag];
+
+
+Import_Decl_Kind :: enum {
+	Standard,
+	Using,
+}
+
+
+
+default_warning_handler :: proc(pos: token.Pos, msg: string, args: ..any) {
+	fmt.printf_err("%s(%d:%d): Warning: ", pos.file, pos.line, pos.column);
+	fmt.printf_err(msg, ..args);
+	fmt.printf_err("\n");
+}
+default_error_handler :: proc(pos: token.Pos, msg: string, args: ..any) {
+	fmt.printf_err("%s(%d:%d): ", pos.file, pos.line, pos.column);
+	fmt.printf_err(msg, ..args);
+	fmt.printf_err("\n");
+}
+
+warn :: proc(p: ^Parser, pos: token.Pos, msg: string, args: ..any) {
+	if p.warn != nil {
+		p.warn(pos, msg, ..args);
+	}
+	p.file.syntax_warning_count += 1;
+}
+
+error :: proc(p: ^Parser, pos: token.Pos, msg: string, args: ..any) {
+	if p.err != nil {
+		p.err(pos, msg, ..args);
+	}
+	p.file.syntax_error_count += 1;
+	p.error_count += 1;
+}
+
+
+end_pos :: proc(tok: token.Token) -> token.Pos {
+	// TODO(bill): Correct this for multiline tokens (comments)
+	pos := tok.pos;
+	pos.offset += len(tok.text);
+	pos.column += len(tok.text);
+	return pos;
+}
+
+parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
+	zero_parser: {
+		p.prev_tok         = {};
+		p.curr_tok         = {};
+		p.expr_level       = 0;
+		p.allow_range      = false;
+		p.allow_in_expr    = false;
+		p.in_foreign_block = false;
+		p.allow_type       = false;
+		p.lead_comment     = nil;
+		p.line_comment     = nil;
+	}
+
+	p.file = file;
+	tokenizer.init(&p.tok, file.src, file.fullpath);
+	if p.tok.ch <= 0 {
+		return true;
+	}
+
+
+	advance_token(p);
+	consume_comment_groups(p, p.prev_tok);
+
+	docs := p.lead_comment;
+
+	p.file.pkg_token = expect_token(p, token.Package);
+	if p.file.pkg_token.kind != token.Package {
+		return false;
+	}
+
+	pkg_name := expect_token_after(p, token.Ident, "package");
+	if pkg_name.kind == token.Ident {
+		if is_blank_ident(pkg_name) {
+			error(p, pkg_name.pos, "invalid package name '_'");
+		}
+		// TODO(bill): Reserved package names
+	}
+	p.file.pkg_name = pkg_name.text;
+
+	pd := ast.new(ast.Package_Decl, pkg_name.pos, end_pos(p.prev_tok));
+	pd.docs    = docs;
+	pd.token   = p.file.pkg_token;
+	pd.name    = pkg_name.text;
+	pd.comment = p.line_comment;
+	p.file.pkg_decl = pd;
+
+	expect_semicolon(p, pd);
+
+	if p.file.syntax_error_count > 0 {
+		return false;
+	}
+
+	p.file.decls = make([dynamic]^ast.Stmt);
+
+	for p.curr_tok.kind != token.EOF {
+		stmt := parse_stmt(p);
+		if stmt != nil {
+			if _, ok := stmt.derived.(ast.Empty_Stmt); !ok {
+				append(&p.file.decls, stmt);
+				if es, ok := stmt.derived.(ast.Expr_Stmt); ok && es.expr != nil {
+					if _, ok := es.expr.derived.(ast.Proc_Lit); ok {
+						error(p, stmt.pos, "procedure literal evaluated but not used");
+					}
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
+
+next_token0 :: proc(p: ^Parser) -> bool {
+	p.curr_tok = tokenizer.scan(&p.tok);
+	if p.curr_tok.kind == token.EOF {
+		// error(p, p.curr_tok.pos, "token is EOF");
+		return false;
+	}
+	return true;
+}
+
+consume_comment :: proc(p: ^Parser) -> (tok: token.Token, end_line: int) {
+	tok = p.curr_tok;
+	assert(tok.kind == token.Comment);
+	end_line = tok.pos.line;
+
+	if tok.text[1] == '*' {
+		for c in tok.text {
+			if c == '\n' {
+				end_line += 1;
+			}
+		}
+	}
+
+	end := next_token0(p);
+	if p.curr_tok.pos.line > tok.pos.line {
+		end_line += 1;
+	}
+
+	return;
+}
+
+consume_comment_group :: proc(p: ^Parser, n: int) -> (comments: ^ast.Comment_Group, end_line: int) {
+	list: [dynamic]token.Token;
+	end_line = p.curr_tok.pos.line;
+	for p.curr_tok.kind == token.Comment &&
+	    p.curr_tok.pos.line <= end_line+n {
+	    comment: token.Token;
+    	comment, end_line = consume_comment(p);
+		append(&list, comment);
+    }
+
+    if len(list) > 0 {
+    	comments = new(ast.Comment_Group);
+    	comments.list = list[:];
+    	append(&p.file.comments, comments);
+    }
+
+    return;
+}
+
+consume_comment_groups :: proc(p: ^Parser, prev: token.Token) {
+	if p.curr_tok.kind == token.Comment {
+		comment: ^ast.Comment_Group;
+		end_line := 0;
+
+		if p.curr_tok.pos.line == prev.pos.line {
+			comment, end_line = consume_comment_group(p, 0);
+			if p.curr_tok.pos.line != end_line || p.curr_tok.kind == token.EOF {
+				p.line_comment = comment;
+			}
+		}
+
+		end_line = -1;
+		for p.curr_tok.kind == token.Comment {
+			comment, end_line = consume_comment_group(p, 1);
+		}
+		if end_line+1 == p.curr_tok.pos.line || end_line < 0 {
+			p.lead_comment = comment;
+		}
+
+		assert(p.curr_tok.kind != token.Comment);
+	}
+}
+
+advance_token :: proc(p: ^Parser) -> token.Token {
+	p.lead_comment = nil;
+	p.line_comment = nil;
+	p.prev_tok = p.curr_tok;
+	prev := p.prev_tok;
+
+	if next_token0(p) {
+		consume_comment_groups(p, prev);
+	}
+	return prev;
+}
+
+expect_token :: proc(p: ^Parser, kind: token.Kind) -> token.Token {
+	prev := p.curr_tok;
+	if prev.kind != kind {
+		e := token.to_string(kind);
+		g := token.to_string(prev.kind);
+		error(p, prev.pos, "expected '%s', got '%s'", e, g);
+		if prev.kind == token.EOF {
+			// TODO(bill): Handle catastropic errors?
+		}
+	}
+	advance_token(p);
+	return prev;
+}
+
+expect_token_after :: proc(p: ^Parser, kind: token.Kind, msg: string) -> token.Token {
+	prev := p.curr_tok;
+	if prev.kind != kind {
+		e := token.to_string(kind);
+		g := token.to_string(prev.kind);
+		error(p, prev.pos, "expected '%s' after %s, got '%s'", e, msg, g);
+	}
+	advance_token(p);
+	return prev;
+}
+
+expect_operator :: proc(p: ^Parser) -> token.Token {
+	prev := p.curr_tok;
+	if !token.is_operator(prev.kind) {
+		g := token.to_string(prev.kind);
+		error(p, prev.pos, "expected an operator, got '%s'", g);
+	}
+	advance_token(p);
+	return prev;
+}
+
+allow_token :: proc(p: ^Parser, kind: token.Kind) -> bool {
+	if p.curr_tok.kind == kind {
+		advance_token(p);
+		return true;
+	}
+	return false;
+}
+
+
+is_blank_ident :: proc{
+	is_blank_ident_string,
+	is_blank_ident_token,
+	is_blank_ident_node,
+};
+is_blank_ident_string :: inline proc(str: string) -> bool {
+	return str == "_";
+}
+is_blank_ident_token :: inline proc(tok: token.Token) -> bool {
+	if tok.kind == token.Ident {
+		return is_blank_ident_string(tok.text);
+	}
+	return false;
+}
+is_blank_ident_node :: inline proc(node: ^ast.Node) -> bool {
+	if ident, ok := node.derived.(ast.Ident); ok {
+		return is_blank_ident(ident.name);
+	}
+	return true;
+}
+
+
+is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
+	if node == nil {
+		return false;
+	}
+	switch n in node.derived {
+	case ast.Empty_Stmt, ast.Block_Stmt:
+		return true;
+
+	case ast.If_Stmt, ast.When_Stmt,
+	     ast.For_Stmt, ast.Range_Stmt,
+	     ast.Switch_Stmt, ast.Type_Switch_Stmt:
+		return true;
+
+	case ast.Helper_Type:
+		return is_semicolon_optional_for_node(p, n.type);
+	case ast.Distinct_Type:
+		return is_semicolon_optional_for_node(p, n.type);
+	case ast.Pointer_Type:
+		return is_semicolon_optional_for_node(p, n.elem);
+	case ast.Struct_Type, ast.Union_Type, ast.Enum_Type, ast.Bit_Field_Type:
+		return true;
+	case ast.Proc_Lit:
+		return true;
+
+	case ast.Package_Decl, ast.Import_Decl, ast.Foreign_Import_Decl:
+		return true;
+
+	case ast.Foreign_Block_Decl:
+		return is_semicolon_optional_for_node(p, n.body);
+
+	case ast.Value_Decl:
+		if n.is_mutable {
+			return false;
+		}
+		if len(n.values) > 0 {
+			return is_semicolon_optional_for_node(p, n.values[len(n.values)-1]);
+		}
+	}
+
+	return false;
+}
+
+
+expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
+	if allow_token(p, token.Semicolon) {
+		return true;
+	}
+
+	prev := p.prev_tok;
+	if prev.kind == token.Semicolon {
+		return true;
+	}
+
+	if p.curr_tok.kind == token.EOF {
+		return true;
+	}
+
+	if node != nil {
+		if prev.pos.line != p.curr_tok.pos.line {
+			if is_semicolon_optional_for_node(p, node) {
+				return true;
+			}
+		} else {
+			switch p.curr_tok.kind {
+			case token.Close_Brace:
+			case token.Close_Paren:
+			case token.Else:
+				return true;
+			}
+		}
+	}
+
+	error(p, prev.pos, "expected ';', got %s", token.to_string(prev.kind));
+	return false;
+}
+
+new_blank_ident :: proc(p: ^Parser, pos: token.Pos) -> ^ast.Ident {
+	tok: token.Token;
+	tok.pos = pos;
+	i := ast.new(ast.Ident, pos, end_pos(tok));
+	i.name = "_";
+	return i;
+}
+
+parse_ident :: proc(p: ^Parser) -> ^ast.Ident {
+	tok := p.curr_tok;
+	pos := tok.pos;
+	name := "_";
+	if tok.kind == token.Ident {
+		name = tok.text;
+		advance_token(p);
+	} else {
+		expect_token(p, token.Ident);
+	}
+	i := ast.new(ast.Ident, pos, end_pos(tok));
+	i.name = name;
+	return i;
+}
+
+parse_stmt_list :: proc(p: ^Parser) -> []^ast.Stmt {
+	list: [dynamic]^ast.Stmt;
+	for p.curr_tok.kind != token.Case &&
+	    p.curr_tok.kind != token.Close_Brace &&
+	    p.curr_tok.kind != token.EOF  {
+		stmt := parse_stmt(p);
+		if stmt != nil {
+			if _, ok := stmt.derived.(ast.Empty_Stmt); !ok {
+				append(&list, stmt);
+				if es, ok := stmt.derived.(ast.Expr_Stmt); ok && es.expr != nil {
+					if _, ok := es.expr.derived.(ast.Proc_Lit); ok {
+						error(p, stmt.pos, "procedure literal evaluated but not used");
+					}
+				}
+			}
+		}
+	}
+	return list[:];
+}
+
+parse_block_stmt :: proc(p: ^Parser, is_when: bool) -> ^ast.Stmt {
+	if !is_when && p.curr_proc == nil {
+		error(p, p.curr_tok.pos, "you cannot use a block statement in the file scope");
+	}
+	return parse_body(p);
+}
+
+parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
+	tok := expect_token(p, token.When);
+
+	cond: ^ast.Expr;
+	body: ^ast.Stmt;
+	else_stmt: ^ast.Stmt;
+
+	prev_level := p.expr_level;
+	p.expr_level = -1;
+	cond = parse_expr(p, false);
+	p.expr_level = prev_level;
+
+	if cond == nil {
+		error(p, p.curr_tok.pos, "expected a condition for when statement");
+	}
+	if allow_token(p, token.Do) {
+		body = convert_stmt_to_body(p, parse_stmt(p));
+	} else {
+		body = parse_block_stmt(p, true);
+	}
+
+	if allow_token(p, token.Else) {
+		switch p.curr_tok.kind {
+		case token.When:
+			else_stmt = parse_when_stmt(p);
+		case token.Open_Brace:
+			else_stmt = parse_block_stmt(p, true);
+		case token.Do:
+			expect_token(p, token.Do);
+			else_stmt = convert_stmt_to_body(p, parse_stmt(p));
+		case:
+			error(p, p.curr_tok.pos, "expected when statement block statement");
+			else_stmt = ast.new(ast.Bad_Stmt, p.curr_tok.pos, end_pos(p.curr_tok));
+		}
+	}
+
+	end := body.end;
+	if else_stmt != nil {
+		end = else_stmt.end;
+	}
+	when_stmt := ast.new(ast.When_Stmt, tok.pos, end);
+	when_stmt.when_pos  = tok.pos;
+	when_stmt.cond      = cond;
+	when_stmt.body      = body;
+	when_stmt.else_stmt = else_stmt;
+	return when_stmt;
+}
+
+convert_stmt_to_expr :: proc(p: ^Parser, stmt: ^ast.Stmt, kind: string) -> ^ast.Expr {
+	if stmt == nil {
+		return nil;
+	}
+	if es, ok := stmt.derived.(ast.Expr_Stmt); ok {
+		return es.expr;
+	}
+	error(p, stmt.pos, "expected %s, found a simple statement", kind);
+	return ast.new(ast.Bad_Expr, p.curr_tok.pos, end_pos(p.curr_tok));
+}
+
+parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
+	tok := expect_token(p, token.If);
+
+	init: ^ast.Stmt;
+	cond: ^ast.Expr;
+	body: ^ast.Stmt;
+	else_stmt: ^ast.Stmt;
+
+	prev_level := p.expr_level;
+	p.expr_level = -1;
+	prev_allow_in_expr := p.allow_in_expr;
+	p.allow_in_expr = true;
+	if allow_token(p, token.Semicolon) {
+		cond = parse_expr(p, false);
+	} else {
+		init = parse_simple_stmt(p, nil);
+		if allow_token(p, token.Semicolon) {
+			cond = parse_expr(p, false);
+		} else {
+			cond = convert_stmt_to_expr(p, init, "boolean expression");
+			init = nil;
+		}
+	}
+
+	p.expr_level = prev_level;
+	p.allow_in_expr = prev_allow_in_expr;
+
+	if cond == nil {
+		error(p, p.curr_tok.pos, "expected a condition for if statement");
+
+	}
+	if allow_token(p, token.Do) {
+		body = convert_stmt_to_body(p, parse_stmt(p));
+	} else {
+		body = parse_block_stmt(p, false);
+	}
+
+	if allow_token(p, token.Else) {
+		switch p.curr_tok.kind {
+		case token.If:
+			else_stmt = parse_when_stmt(p);
+		case token.Open_Brace:
+			else_stmt = parse_block_stmt(p, false);
+		case token.Do:
+			expect_token(p, token.Do);
+			else_stmt = convert_stmt_to_body(p, parse_stmt(p));
+		case:
+			error(p, p.curr_tok.pos, "expected if statement block statement");
+			else_stmt = ast.new(ast.Bad_Stmt, p.curr_tok.pos, end_pos(p.curr_tok));
+		}
+	}
+
+	end := body.end;
+	if else_stmt != nil {
+		end = else_stmt.end;
+	}
+	if_stmt := ast.new(ast.If_Stmt, tok.pos, end);
+	if_stmt.if_pos  = tok.pos;
+	if_stmt.init      = init;
+	if_stmt.cond      = cond;
+	if_stmt.body      = body;
+	if_stmt.else_stmt = else_stmt;
+	return if_stmt;
+}
+
+parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
+	if p.curr_proc == nil {
+		error(p, p.curr_tok.pos, "you cannot use a for statement in the file scope");
+	}
+
+	tok := expect_token(p, token.For);
+
+	init: ^ast.Stmt;
+	cond: ^ast.Stmt;
+	post: ^ast.Stmt;
+	body: ^ast.Stmt;
+	is_range := false;
+
+	if p.curr_tok.kind != token.Open_Brace && p.curr_tok.kind != token.Do {
+		prev_level := p.expr_level;
+		defer p.expr_level = prev_level;
+		p.expr_level = -1;
+
+		if p.curr_tok.kind == token.In {
+			in_tok := expect_token(p, token.In);
+			rhs: ^ast.Expr;
+
+			prev_allow_range := p.allow_range;
+			p.allow_range = true;
+			rhs = parse_expr(p, false);
+			p.allow_range = prev_allow_range;
+
+			if allow_token(p, token.Do) {
+				body = convert_stmt_to_body(p, parse_stmt(p));
+			} else {
+				body = parse_body(p);
+			}
+
+			range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end);
+			range_stmt.for_pos = tok.pos;
+			range_stmt.in_pos = in_tok.pos;
+			range_stmt.expr = rhs;
+			range_stmt.body = body;
+			return range_stmt;
+		}
+
+		if p.curr_tok.kind != token.Semicolon {
+			cond = parse_simple_stmt(p, {Stmt_Allow_Flag.In});
+			if as, ok := cond.derived.(ast.Assign_Stmt); ok && as.op.kind == token.In {
+				is_range = true;
+			}
+		}
+
+		if !is_range && allow_token(p, token.Semicolon) {
+			init = cond;
+			cond = nil;
+			if p.curr_tok.kind != token.Semicolon {
+				cond = parse_simple_stmt(p, nil);
+			}
+			expect_semicolon(p, cond);
+			if p.curr_tok.kind != token.Open_Brace && p.curr_tok.kind != token.Do {
+				post = parse_simple_stmt(p, nil);
+			}
+		}
+	}
+
+	if allow_token(p, token.Do) {
+		body = convert_stmt_to_body(p, parse_stmt(p));
+	} else {
+		body = parse_body(p);
+	}
+
+
+	if is_range {
+		assign_stmt := cond.derived.(ast.Assign_Stmt);
+		val0, val1: ^ast.Expr;
+
+		switch len(assign_stmt.lhs) {
+		case 1:
+			val0 = assign_stmt.lhs[0];
+		case 2:
+			val0 = assign_stmt.lhs[0];
+			val1 = assign_stmt.lhs[1];
+		case:
+			error(p, cond.pos, "expected either 1 or 2 identifiers");
+			return ast.new(ast.Bad_Stmt, tok.pos, body.end);
+		}
+
+		rhs: ^ast.Expr;
+		if len(assign_stmt.rhs) > 0 {
+			rhs = assign_stmt.rhs[0];
+		}
+
+		range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end);
+		range_stmt.for_pos = tok.pos;
+		range_stmt.val0 = val0;
+		range_stmt.val1 = val1;
+		range_stmt.in_pos = assign_stmt.op.pos;
+		range_stmt.expr = rhs;
+		range_stmt.body = body;
+		return range_stmt;
+	}
+
+	cond_expr := convert_stmt_to_expr(p, cond, "boolean expression");
+	for_stmt := ast.new(ast.For_Stmt, tok.pos, body.end);
+	for_stmt.for_pos = tok.pos;
+	for_stmt.init = init;
+	for_stmt.cond = cond_expr;
+	for_stmt.post = post;
+	for_stmt.body = body;
+	return for_stmt;
+}
+
+parse_case_clause :: proc(p: ^Parser, is_type_switch: bool) -> ^ast.Case_Clause {
+	tok := expect_token(p, token.Case);
+
+	list: []^ast.Expr;
+
+	if p.curr_tok.kind != token.Colon {
+		prev_allow_range, prev_allow_in_expr := p.allow_range, p.allow_in_expr;
+		defer p.allow_range, p.allow_in_expr = prev_allow_range, prev_allow_in_expr;
+		p.allow_range, p.allow_in_expr = !is_type_switch, !is_type_switch;
+
+		list = parse_rhs_expr_list(p);
+	}
+
+	terminator := expect_token(p, token.Colon);
+
+	stmts := parse_stmt_list(p);
+
+	cc := ast.new(ast.Case_Clause, tok.pos, end_pos(p.prev_tok));
+	cc.list = list;
+	cc.terminator = terminator;
+	cc.body = stmts;
+	return cc;
+}
+
+parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
+	tok := expect_token(p, token.Switch);
+
+	init: ^ast.Stmt;
+	tag:  ^ast.Stmt;
+	is_type_switch := false;
+	clauses: [dynamic]^ast.Stmt;
+
+	if p.curr_tok.kind != token.Open_Brace {
+		prev_level := p.expr_level;
+		defer p.expr_level = prev_level;
+		p.expr_level = -1;
+
+		if p.curr_tok.kind == token.In {
+			in_tok := expect_token(p, token.In);
+			is_type_switch = true;
+
+			lhs := make([]^ast.Expr, 1);
+			rhs := make([]^ast.Expr, 1);
+			lhs[0] = new_blank_ident(p, tok.pos);
+			rhs[0] = parse_expr(p, true);
+
+			as := ast.new(ast.Assign_Stmt, tok.pos, rhs[0].end);
+			as.lhs = lhs;
+			as.op  = in_tok;
+			as.rhs = rhs;
+			tag = as;
+		} else {
+			tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In});
+			if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == token.In {
+				is_type_switch = true;
+			} else if allow_token(p, token.Semicolon) {
+				init = tag;
+				tag = nil;
+				if p.curr_tok.kind != token.Open_Brace {
+					tag = parse_simple_stmt(p, nil);
+				}
+			}
+		}
+	}
+
+
+	open := expect_token(p, token.Open_Brace);
+
+	for p.curr_tok.kind == token.Case {
+		clause := parse_case_clause(p, is_type_switch);
+		append(&clauses, clause);
+	}
+
+	close := expect_token(p, token.Close_Brace);
+
+	body := ast.new(ast.Block_Stmt, open.pos, end_pos(close));
+	body.stmts = clauses[:];
+
+	if is_type_switch {
+		ts := ast.new(ast.Type_Switch_Stmt, tok.pos, body.end);
+		ts.tag  = tag;
+		ts.body = body;
+		return ts;
+	} else {
+		cond := convert_stmt_to_expr(p, tag, "switch expression");
+		ts := ast.new(ast.Switch_Stmt, tok.pos, body.end);
+		ts.init = init;
+		ts.cond = cond;
+		ts.body = body;
+		return ts;
+	}
+}
+
+parse_attribute :: proc(p: ^Parser, tok: token.Token, open_kind, close_kind: token.Kind) -> ^ast.Stmt {
+	elems: [dynamic]^ast.Expr;
+
+	open := expect_token(p, open_kind);
+	p.expr_level += 1;
+	for p.curr_tok.kind != close_kind &&
+		p.curr_tok.kind != token.EOF {
+		elem: ^ast.Expr;
+		elem = parse_ident(p);
+		if p.curr_tok.kind == token.Eq {
+			eq := expect_token(p, token.Eq);
+			value := parse_value(p);
+			fv := ast.new(ast.Field_Value, elem.pos, value.end);
+			fv.field = elem;
+			fv.sep   = eq.pos;
+			fv.value = value;
+
+			elem = fv;
+		}
+		append(&elems, elem);
+
+		if !allow_token(p, token.Comma) {
+			break;
+		}
+	}
+
+	p.expr_level -= 1;
+	close := expect_token_after(p, close_kind, "attribute");
+
+	attribute := ast.new(ast.Attribute, tok.pos, end_pos(close));
+	attribute.tok   = tok.kind;
+	attribute.open  = open.pos;
+	attribute.elems = elems[:];
+	attribute.close = close.pos;
+
+	decl := parse_stmt(p);
+	switch d in &decl.derived {
+	case ast.Value_Decl:
+		append(&d.attributes, attribute);
+	case ast.Foreign_Block_Decl:
+		append(&d.attributes, attribute);
+	case:
+		error(p, decl.pos, "expected a value or foreign declaration after an attribute");
+		free(attribute);
+		delete(elems);
+	}
+	return decl;
+
+}
+
+parse_foreign_block_decl :: proc(p: ^Parser) -> ^ast.Stmt {
+	decl := parse_stmt(p);
+	switch in decl.derived {
+	case ast.Empty_Stmt, ast.Bad_Stmt, ast.Bad_Decl:
+		// Ignore
+		return nil;
+	case ast.When_Stmt, ast.Value_Decl:
+		return decl;
+	}
+
+	error(p, decl.pos, "foreign blocks only allow procedure and variable declarations");
+
+	return nil;
+
+}
+
+parse_foreign_block :: proc(p: ^Parser, tok: token.Token) -> ^ast.Foreign_Block_Decl {
+	docs := p.lead_comment;
+
+	foreign_library: ^ast.Expr;
+	switch p.curr_tok.kind {
+	case token.Export:
+		i := ast.new(ast.Implicit, tok.pos, end_pos(tok));
+		i.tok = expect_token(p, token.Export);
+		foreign_library = i;
+	case token.Open_Brace:
+		i := ast.new(ast.Ident, tok.pos, end_pos(tok));
+		i.name = "_";
+		foreign_library = i;
+	case:
+		foreign_library = parse_ident(p);
+	}
+
+	decls: [dynamic]^ast.Stmt;
+
+	prev_in_foreign_block := p.in_foreign_block;
+	defer p.in_foreign_block = prev_in_foreign_block;
+	p.in_foreign_block = true;
+
+	open := expect_token(p, token.Open_Brace);
+	for p.curr_tok.kind != token.Close_Brace && p.curr_tok.kind != token.EOF {
+		decl := parse_foreign_block_decl(p);
+		if decl != nil {
+			append(&decls, decl);
+		}
+	}
+	close := expect_token(p, token.Close_Brace);
+
+	body := ast.new(ast.Block_Stmt, open.pos, end_pos(close));
+	body.open = open.pos;
+	body.stmts = decls[:];
+	body.close = close.pos;
+
+	decl := ast.new(ast.Foreign_Block_Decl, tok.pos, body.end);
+	decl.docs            = docs;
+	decl.tok             = tok;
+	decl.foreign_library = foreign_library;
+	decl.body            = body;
+	return decl;
+}
+
+
+parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl {
+	docs := p.lead_comment;
+	tok := expect_token(p, token.Foreign);
+
+	switch p.curr_tok.kind {
+	case token.Export, token.Ident, token.Open_Brace:
+		return parse_foreign_block(p, tok);
+
+	case token.Import:
+		import_tok := expect_token(p, token.Import);
+		name: ^ast.Ident;
+		if p.curr_tok.kind == token.Ident {
+			name = parse_ident(p);
+		}
+
+		if name != nil && is_blank_ident(name) {
+			error(p, name.pos, "illegal foreign import name: '_'");
+		}
+
+		fullpaths: [dynamic]string;
+		if allow_token(p, token.Open_Brace) {
+			for p.curr_tok.kind != token.Close_Brace &&
+				p.curr_tok.kind != token.EOF {
+				path := expect_token(p, token.String);
+				append(&fullpaths, path.text);
+
+				if !allow_token(p, token.Comma) {
+					break;
+				}
+			}
+			expect_token(p, token.Close_Brace);
+		} else {
+			path := expect_token(p, token.String);
+			reserve(&fullpaths, 1);
+			append(&fullpaths, path.text);
+		}
+
+		if len(fullpaths) == 0 {
+			error(p, import_tok.pos, "foreign import without any paths");
+		}
+
+		decl := ast.new(ast.Foreign_Import_Decl, tok.pos, end_pos(p.prev_tok));
+		decl.docs            = docs;
+		decl.foreign_tok     = tok;
+		decl.import_tok      = import_tok;
+		decl.name            = name;
+		decl.fullpaths       = fullpaths[:];
+		expect_semicolon(p, decl);
+		decl.comment = p.line_comment;
+		return decl;
+	}
+
+	error(p, tok.pos, "invalid foreign declaration");
+	return ast.new(ast.Bad_Decl, tok.pos, end_pos(tok));
+}
+
+
+parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
+	tok := p.curr_tok;
+	switch tok.kind {
+	// Operands
+	case token.Context, // Also allows for 'context = '
+	     token.Inline, token.No_Inline,
+	     token.Ident,
+	     token.Integer, token.Float, token.Imag,
+	     token.Rune, token.String,
+	     token.Open_Paren,
+	     token.Pointer,
+	     // Unary Expressions
+	     token.Add, token.Sub, token.Xor, token.Not, token.And:
+	    s := parse_simple_stmt(p, {Stmt_Allow_Flag.Label});
+	    expect_semicolon(p, s);
+		return s;
+
+
+	case token.Import:  return parse_import_decl(p);
+	case token.Foreign: return parse_foreign_decl(p);
+	case token.If:      return parse_if_stmt(p);
+	case token.When:    return parse_when_stmt(p);
+	case token.For:     return parse_for_stmt(p);
+	case token.Switch:  return parse_switch_stmt(p);
+
+	case token.Defer:
+		tok := advance_token(p);
+		stmt := parse_stmt(p);
+		switch s in stmt.derived {
+		case ast.Empty_Stmt:
+			error(p, s.pos, "empty statement after defer (e.g. ';')");
+		case ast.Defer_Stmt:
+			error(p, s.pos, "you cannot defer a defer statement");
+			stmt = s.stmt;
+		case ast.Return_Stmt:
+			error(p, s.pos, "you cannot defer a return statement");
+		}
+		ds := ast.new(ast.Defer_Stmt, tok.pos, stmt.end);
+		ds.stmt = stmt;
+		return ds;
+
+	case token.Return:
+		tok := advance_token(p);
+
+		if p.expr_level > 0 {
+			error(p, tok.pos, "you cannot use a return statement within an expression");
+		}
+
+		results: [dynamic]^ast.Expr;
+		for p.curr_tok.kind != token.Semicolon {
+			result := parse_expr(p, false);
+			append(&results, result);
+			if p.curr_tok.kind != token.Comma ||
+			   p.curr_tok.kind == token.EOF {
+				break;
+			}
+			advance_token(p);
+		}
+
+		end := end_pos(tok);
+		if len(results) > 0 {
+			end = results[len(results)-1].pos;
+		}
+
+		rs := ast.new(ast.Return_Stmt, tok.pos, end);
+		rs.results = results[:];
+		return rs;
+
+	case token.Break, token.Continue, token.Fallthrough:
+		tok := advance_token(p);
+		label: ^ast.Expr;
+		if tok.kind != token.Fallthrough && p.curr_tok.kind == token.Ident {
+			label = parse_ident(p);
+		}
+		end := label != nil ? label.end : end_pos(tok);
+		s := ast.new(ast.Branch_Stmt, tok.pos, end);
+		expect_semicolon(p, s);
+		return s;
+
+	case token.Static:
+		docs := p.lead_comment;
+		tok := expect_token(p, token.Static);
+
+		list := parse_lhs_expr_list(p);
+		if len(list) == 0 {
+			error(p, tok.pos, "illegal use of 'static' statement");
+			expect_semicolon(p, nil);
+			return ast.new(ast.Bad_Stmt, tok.pos, end_pos(p.prev_tok));
+		}
+
+		expect_token_after(p, token.Colon, "identifier list");
+		decl := parse_value_decl(p, list, docs);
+		if decl != nil do switch d in &decl.derived {
+		case ast.Value_Decl:
+			if d.is_mutable {
+				d.is_static = true;
+			} else {
+				error(p, tok.pos, "'static' may only be currently used with variable declarations");
+			}
+		case:
+			error(p, tok.pos, "illegal use of 'static' statement");
+		}
+
+		error(p, tok.pos, "illegal use of 'static' statement");
+		if decl != nil {
+			return decl;
+		}
+
+		return ast.new(ast.Bad_Stmt, tok.pos, end_pos(p.prev_tok));
+
+	case token.Using:
+		docs := p.lead_comment;
+		tok := expect_token(p, token.Using);
+
+		if p.curr_tok.kind == token.Import {
+			return parse_import_decl(p, Import_Decl_Kind.Using);
+		}
+
+		list := parse_lhs_expr_list(p);
+		if len(list) == 0 {
+			error(p, tok.pos, "illegal use of 'using' statement");
+			expect_semicolon(p, nil);
+			return ast.new(ast.Bad_Stmt, tok.pos, end_pos(p.prev_tok));
+		}
+
+		if p.curr_tok.kind != token.Colon {
+			end := list[len(list)-1];
+			expect_semicolon(p, end);
+			us := ast.new(ast.Using_Stmt, tok.pos, end.end);
+			us.list = list;
+			return us;
+		}
+		expect_token_after(p, token.Colon, "identifier list");
+		decl := parse_value_decl(p, list, docs);
+		if decl != nil do switch d in &decl.derived {
+		case ast.Value_Decl:
+			d.is_using = true;
+			return decl;
+		}
+
+		error(p, tok.pos, "illegal use of 'using' statement");
+		return ast.new(ast.Bad_Stmt, tok.pos, end_pos(p.prev_tok));
+
+	case token.At:
+		tok := advance_token(p);
+		return parse_attribute(p, tok, token.Open_Paren, token.Close_Paren);
+
+	case token.Hash:
+		tok := expect_token(p, token.Hash);
+		tag := expect_token(p, token.Ident);
+		name := tag.text;
+
+		switch name {
+		case "bounds_check", "no_bounds_check":
+			// TODO(bill): Handle stmt state flags
+			return parse_stmt(p);
+		case "complete":
+			stmt := parse_stmt(p);
+			switch s in &stmt.derived {
+			case ast.Switch_Stmt:      s.complete = true;
+			case ast.Type_Switch_Stmt: s.complete = true;
+			case: error(p, stmt.pos, "#complete can only be applied to a swtich statement");
+			}
+			return stmt;
+		case "assert":
+			bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(tag));
+			bd.tok  = tok;
+			bd.name = name;
+			ce := parse_call_expr(p, bd);
+			es := ast.new(ast.Expr_Stmt, ce.pos, ce.end);
+			es.expr = ce;
+			return es;
+		case "include":
+			error(p, tag.pos, "#include is not a valid import declaration kind. Did you meant 'import'?");
+			return ast.new(ast.Bad_Stmt, tok.pos, end_pos(tag));
+		case:
+			stmt := parse_stmt(p);
+			te := ast.new(ast.Tag_Stmt, tok.pos, stmt.pos);
+			te.op   = tok;
+			te.name = name;
+			te.stmt = stmt;
+			return te;
+		}
+	case token.Open_Brace:
+		return parse_block_stmt(p, false);
+
+	case token.Semicolon:
+		s := ast.new(ast.Empty_Stmt, tok.pos, end_pos(tok));
+		advance_token(p);
+		return s;
+	}
+
+	advance_token(p);
+	error(p, tok.pos, "expected a statement, got %s", token.to_string(tok.kind));
+	s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok));
+	return s;
+}
+
+
+token_precedence :: proc(p: ^Parser, kind: token.Kind) -> int {
+	switch kind {
+	case token.Question:
+		return 1;
+	case token.Ellipsis:
+		if !p.allow_range {
+			return 0;
+		}
+		return 2;
+	case token.Cmp_Or:
+		return 3;
+	case token.Cmp_And:
+		return 4;
+	case token.Cmp_Eq, token.Not_Eq,
+	     token.Lt, token.Gt,
+	     token.Lt_Eq, token.Gt_Eq:
+		return 5;
+	case token.In, token.Notin:
+		if p.expr_level >= 0 || p.allow_in_expr {
+			return 6;
+		}
+		return 0;
+	case token.Add, token.Sub, token.Or, token.Xor:
+		return 7;
+	case token.Mul, token.Quo,
+	     token.Mod, token.Mod_Mod,
+	     token.And, token.And_Not,
+	     token.Shl, token.Shr:
+		return 8;
+	}
+	return 0;
+}
+
+parse_type_or_ident :: proc(p: ^Parser) -> ^ast.Expr {
+	prev_allow_type := p.allow_type;
+	prev_expr_level := p.expr_level;
+	defer {
+		p.allow_type = prev_allow_type;
+		p.expr_level = prev_expr_level;
+	}
+
+	p.allow_type = true;
+	p.expr_level = -1;
+
+	lhs := true;
+	return parse_atom_expr(p, parse_operand(p, lhs), lhs);
+}
+parse_type :: proc(p: ^Parser) -> ^ast.Expr {
+	type := parse_type_or_ident(p);
+	if type == nil {
+		error(p, p.curr_tok.pos, "expected a type");
+		return ast.new(ast.Bad_Expr, p.curr_tok.pos, end_pos(p.curr_tok));
+	}
+	return type;
+}
+
+parse_body :: proc(p: ^Parser) -> ^ast.Block_Stmt {
+	prev_expr_level := p.expr_level;
+	defer p.expr_level = prev_expr_level;
+
+	p.expr_level = 0;
+	open := expect_token(p, token.Open_Brace);
+	stmts := parse_stmt_list(p);
+	close := expect_token(p, token.Close_Brace);
+
+	bs := ast.new(ast.Block_Stmt, open.pos, end_pos(close));
+	bs.open = open.pos;
+	bs.stmts = stmts;
+	bs.close = close.pos;
+	return bs;
+}
+
+convert_stmt_to_body :: proc(p: ^Parser, stmt: ^ast.Stmt) -> ^ast.Stmt {
+	switch s in stmt.derived {
+	case ast.Block_Stmt:
+		error(p, stmt.pos, "expected a normal statement rather than a block statement");
+		return stmt;
+	case ast.Empty_Stmt:
+		error(p, stmt.pos, "expected a non-empty statement");
+	}
+
+	bs := ast.new(ast.Block_Stmt, stmt.pos, stmt.end);
+	bs.open = stmt.pos;
+	bs.stmts = make([]^ast.Stmt, 1);
+	bs.stmts[0] = stmt;
+	bs.close = stmt.end;
+	return bs;
+}
+
+new_ast_field :: proc(names: []^ast.Expr, type: ^ast.Expr, default_value: ^ast.Expr) -> ^ast.Field {
+	pos, end: token.Pos;
+
+	if len(names) > 0 {
+		pos = names[0].pos;
+		if default_value != nil {
+			end = default_value.end;
+		} else if type != nil {
+			end = type.end;
+		} else {
+			end = names[len(names)-1].pos;
+		}
+	} else {
+		if type != nil {
+			pos = type.pos;
+		} else if default_value != nil {
+			pos = default_value.pos;
+		}
+
+		if default_value != nil {
+			end = default_value.end;
+		} else if type != nil {
+			end = type.end;
+		}
+	}
+
+	field := ast.new(ast.Field, pos, end);
+	field.names = names;
+	field.type  = type;
+	field.default_value = default_value;
+	return field;
+}
+
+
+Field_Prefix :: enum {
+	Invalid,
+	Unknown,
+
+	Using,
+	No_Alias,
+	C_Vararg,
+	In,
+	Auto_Cast,
+}
+
+Field_Prefixes :: distinct bit_set[Field_Prefix];
+
+Expr_And_Flags :: struct {
+	expr:  ^ast.Expr,
+	flags: ast.Field_Flags,
+}
+
+convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags, allow_poly_names: bool) -> []^ast.Expr {
+	idents := make([dynamic]^ast.Expr, 0, len(list));
+
+	for ident, i in list {
+		if !ignore_flags {
+			if i != 0 {
+				error(p, ident.expr.pos, "illegal use of prefixes in parameter list");
+			}
+		}
+
+		id: ^ast.Expr = ident.expr;
+
+		switch n in ident.expr.derived {
+		case ast.Ident:
+		case ast.Bad_Expr:
+		case ast.Poly_Type:
+			if allow_poly_names {
+				if n.specialization == nil {
+					break;
+				} else {
+					error(p, ident.expr.pos, "expected a polymorphic identifier without an specialization");
+				}
+			} else {
+				error(p, ident.expr.pos, "expected a non-polymorphic identifier");
+			}
+		case:
+			error(p, ident.expr.pos, "expected an identifier");
+			id = ast.new(ast.Ident, ident.expr.pos, ident.expr.end);
+		}
+
+		append(&idents, id);
+	}
+
+	return idents[:];
+}
+
+is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
+	using Field_Prefix;
+	switch p.curr_tok.kind {
+	case token.EOF:
+		return Invalid;
+	case token.Using:
+		advance_token(p);
+		return Using;
+	case token.In:
+		advance_token(p);
+		return In;
+	case token.Auto_Cast:
+		advance_token(p);
+		return Auto_Cast;
+	case token.Hash:
+		advance_token(p);
+		defer advance_token(p);
+		switch p.curr_tok.kind {
+		case token.Ident:
+			switch p.curr_tok.text {
+			case "no_alias":
+				return No_Alias;
+			case "c_vararg":
+				return C_Vararg;
+			}
+		}
+		return Unknown;
+	}
+	return Invalid;
+}
+
+parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
+	counts: [len(Field_Prefix)]int;
+
+	for {
+		kind := is_token_field_prefix(p);
+		if kind == Field_Prefix.Invalid {
+			break;
+		}
+
+		if kind == Field_Prefix.Unknown {
+			error(p, p.curr_tok.pos, "unknown prefix kind '#%s'", p.curr_tok.text);
+			continue;
+		}
+
+		counts[kind] += 1;
+	}
+
+	flags: ast.Field_Flags;
+
+	for kind in Field_Prefix {
+		count := counts[kind];
+		using Field_Prefix;
+		#complete switch kind {
+		case Invalid, Unknown: // Ignore
+		case Using:
+			if count > 1 do error(p, p.curr_tok.pos, "multiple 'using' in this field list");
+			if count > 0 do flags |= {ast.Field_Flag.Using};
+		case No_Alias:
+			if count > 1 do error(p, p.curr_tok.pos, "multiple '#no_alias' in this field list");
+			if count > 0 do flags |= {ast.Field_Flag.No_Alias};
+		case C_Vararg:
+			if count > 1 do error(p, p.curr_tok.pos, "multiple '#c_vararg' in this field list");
+			if count > 0 do flags |= {ast.Field_Flag.C_Vararg};
+		case In:
+			if count > 1 do error(p, p.curr_tok.pos, "multiple 'in' in this field list");
+			if count > 0 do flags |= {ast.Field_Flag.In};
+		case Auto_Cast:
+			if count > 1 do error(p, p.curr_tok.pos, "multiple 'auto_cast' in this field list");
+			if count > 0 do flags |= {ast.Field_Flag.Auto_Cast};
+		}
+	}
+
+	return flags;
+}
+
+check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, set_flags: ast.Field_Flags) -> ast.Field_Flags {
+	if name_count > 1 && ast.Field_Flag.Using in set_flags {
+		error(p, p.curr_tok.pos, "cannot apply 'using' to more than one of the same type");
+		set_flags &~= {ast.Field_Flag.Using};
+	}
+
+	for flag in ast.Field_Flag {
+		if flag notin allowed_flags && flag in set_flags {
+			using ast.Field_Flag;
+			#complete switch flag {
+			case Using:
+				error(p, p.curr_tok.pos, "'using' is not allowed within this field list");
+			case No_Alias:
+				error(p, p.curr_tok.pos, "'#no_alias' is not allowed within this field list");
+			case C_Vararg:
+				error(p, p.curr_tok.pos, "'#c_vararg' is not allowed within this field list");
+			case Auto_Cast:
+				error(p, p.curr_tok.pos, "'auto_cast' is not allowed within this field list");
+			case In:
+				error(p, p.curr_tok.pos, "'in' is not allowed within this field list");
+			case Ellipsis, Results, Default_Parameters, Typeid_Token:
+				panic("Impossible prefixes");
+			}
+			set_flags &~= {flag};
+		}
+	}
+
+	if ast.Field_Flag.Using in allowed_flags && ast.Field_Flag.Using in set_flags {
+		set_flags &~= {ast.Field_Flag.Using};
+	}
+
+	return set_flags;
+}
+
+parse_var_type :: proc(p: ^Parser, flags: ast.Field_Flags) -> ^ast.Expr {
+	if ast.Field_Flag.Ellipsis in flags && p.curr_tok.kind == token.Ellipsis {
+		tok := advance_token(p);
+		type := parse_type_or_ident(p);
+		if type == nil {
+			error(p, tok.pos, "variadic field missing type after '..'");
+			type = ast.new(ast.Bad_Expr, tok.pos, end_pos(tok));
+		}
+		e := ast.new(ast.Ellipsis, type.pos, type.end);
+		e.expr = type;
+		return e;
+	}
+	type: ^ast.Expr;
+	if ast.Field_Flag.Typeid_Token in flags && p.curr_tok.kind == token.Typeid {
+		tok := expect_token(p, token.Typeid);
+		specialization: ^ast.Expr;
+		end := tok.pos;
+		if allow_token(p, token.Quo) {
+			specialization = parse_type(p);
+			end = specialization.end;
+		}
+
+		ti := ast.new(ast.Typeid_Type, tok.pos, end);
+		ti.tok = tok.kind;
+		ti.specialization = specialization;
+		type = ti;
+	} else {
+		type = parse_type(p);
+	}
+
+	return type;
+}
+
+check_procedure_name_list :: proc(p: ^Parser, names: []^ast.Expr) -> bool {
+	if len(names) == 0 {
+		return false;
+	}
+
+	_, first_is_polymorphic := names[0].derived.(ast.Poly_Type);
+	any_polymorphic_names := first_is_polymorphic;
+
+	for i := 1; i < len(names); i += 1 {
+		name := names[i];
+
+		if first_is_polymorphic {
+			if _, ok := name.derived.(ast.Poly_Type); ok {
+				any_polymorphic_names = true;
+			} else {
+				error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers");
+				return any_polymorphic_names;
+			}
+		} else {
+			if _, ok := name.derived.(ast.Poly_Type); ok {
+				any_polymorphic_names = true;
+				error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers");
+				return any_polymorphic_names;
+			} else {
+				// Okay
+			}
+		}
+	}
+
+	return any_polymorphic_names;
+}
+
+parse_ident_list :: proc(p: ^Parser, allow_poly_names: bool) -> []^ast.Expr {
+	list: [dynamic]^ast.Expr;
+
+	for {
+		if allow_poly_names && p.curr_tok.kind == token.Dollar {
+			tok := expect_token(p, token.Dollar);
+			ident := parse_ident(p);
+			poly_name := ast.new(ast.Poly_Type, tok.pos, ident.end);
+			poly_name.type = ident;
+			append(&list, poly_name);
+		} else {
+			ident := parse_ident(p);
+			append(&list, ident);
+		}
+		if p.curr_tok.kind != token.Comma ||
+		   p.curr_tok.kind == token.EOF {
+		   	break;
+		}
+		advance_token(p);
+	}
+
+	return list[:];
+}
+
+
+
+parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Field_Flags) -> (field_list: ^ast.Field_List, total_name_count: int) {
+	handle_field :: proc(p: ^Parser,
+	                     seen_ellipsis: ^bool, fields: ^[dynamic]^ast.Field,
+	                     docs: ^ast.Comment_Group,
+	                     names: []^ast.Expr,
+	                     allowed_flags, set_flags: ast.Field_Flags
+	                     ) -> bool {
+
+		expect_field_separator :: proc(p: ^Parser, param: ^ast.Expr) -> bool {
+			tok := p.curr_tok;
+			if allow_token(p, token.Comma) {
+				return true;
+			}
+			if allow_token(p, token.Semicolon) {
+				error(p, tok.pos, "expected a comma, got a semicolon");
+				return true;
+			}
+			return false;
+		}
+		is_type_ellipsis :: proc(type: ^ast.Expr) -> bool {
+			if type == nil do return false;
+			_, ok := type.derived.(ast.Ellipsis);
+			return ok;
+		}
+
+		any_polymorphic_names := check_procedure_name_list(p, names);
+		set_flags = check_field_flag_prefixes(p, len(names), allowed_flags, set_flags);
+
+		type:          ^ast.Expr;
+		default_value: ^ast.Expr;
+
+		expect_token_after(p, token.Colon, "field list");
+		if p.curr_tok.kind != token.Eq {
+			type = parse_var_type(p, allowed_flags);
+			tt := ast.unparen_expr(type);
+			if !any_polymorphic_names {
+				if ti, ok := type.derived.(ast.Typeid_Type); ok && ti.specialization != nil {
+					error(p, type.pos, "specialization of typeid is not allowed without polymorphic names");
+				}
+			}
+		}
+
+		if allow_token(p, token.Eq) {
+			default_value = parse_expr(p, false);
+			if ast.Field_Flag.Default_Parameters notin allowed_flags {
+				error(p, p.curr_tok.pos, "default parameters are only allowed for procedures");
+				default_value = nil;
+			}
+		}
+
+		if default_value != nil && len(names) > 1 {
+			error(p, p.curr_tok.pos, "default parameters can only be applied to single values");
+		}
+
+		if allowed_flags == ast.Field_Flags_Struct && default_value != nil {
+			error(p, default_value.pos, "default parameters are not allowed for structs");
+			default_value = nil;
+		}
+
+		if is_type_ellipsis(type) {
+			if seen_ellipsis^ do error(p, type.pos, "extra variadic parameter after ellipsis");
+			seen_ellipsis^ = true;
+			if len(names) != 1 {
+				error(p, type.pos, "variadic parameters can only have one field name");
+			}
+		} else if seen_ellipsis^ && default_value == nil {
+			error(p, p.curr_tok.pos, "extra parameter after ellipsis without a default value");
+		}
+
+		ok := expect_field_separator(p, type);
+
+		field := new_ast_field(names, type, default_value);
+		field.docs    = docs;
+		field.flags   = set_flags;
+		field.comment = p.line_comment;
+		append(fields, field);
+
+		return ok;
+	}
+
+
+	start_tok := p.curr_tok;
+
+	docs := p.lead_comment;
+
+	fields: [dynamic]^ast.Field;
+
+	list: [dynamic]Expr_And_Flags;
+	defer delete(list);
+
+	seen_ellipsis := false;
+
+	allow_typeid_token := ast.Field_Flag.Typeid_Token in allowed_flags;
+	allow_poly_names := allow_typeid_token;
+
+	for p.curr_tok.kind != follow &&
+	    p.curr_tok.kind != token.Colon &&
+	    p.curr_tok.kind != token.EOF {
+		prefix_flags := parse_field_prefixes(p);
+		param := parse_var_type(p, allowed_flags & {ast.Field_Flag.Typeid_Token, ast.Field_Flag.Ellipsis});
+		if _, ok := param.derived.(ast.Ellipsis); ok {
+			if seen_ellipsis {
+				error(p, param.pos, "extra variadic parameter after ellipsis");
+			}
+			seen_ellipsis = true;
+		} else if seen_ellipsis {
+			error(p, param.pos, "extra parameter after ellipsis");
+		}
+
+		eaf := Expr_And_Flags{param, prefix_flags};
+		append(&list, eaf);
+		if !allow_token(p, token.Comma) {
+			break;
+		}
+	}
+
+	if p.curr_tok.kind != token.Colon {
+		for eaf in list {
+			type := eaf.expr;
+			tok: token.Token;
+			tok.pos = type.pos;
+			if ast.Field_Flag.Results notin allowed_flags {
+				tok.text = "_";
+			}
+
+			names := make([]^ast.Expr, 1);
+			names[0] = ast.new(ast.Ident, tok.pos, end_pos(tok));
+			names[0].derived.(ast.Ident).name = tok.text;
+
+			flags := check_field_flag_prefixes(p, len(list), allowed_flags, eaf.flags);
+
+			field := new_ast_field(names, type, nil);
+			field.docs    = docs;
+			field.flags   = flags;
+			field.comment = p.line_comment;
+			append(&fields, field);
+		}
+	} else {
+		names := convert_to_ident_list(p, list[:], true, allow_poly_names);
+		if len(names) == 0 {
+			error(p, p.curr_tok.pos, "empty field declaration");
+		}
+
+		set_flags: ast.Field_Flags;
+		if len(list) > 0 {
+			set_flags = list[0].flags;
+		}
+		total_name_count += len(names);
+		handle_field(p, &seen_ellipsis, &fields, docs, names, allowed_flags, set_flags);
+
+		for p.curr_tok.kind != follow && p.curr_tok.kind != token.EOF {
+			docs := p.lead_comment;
+			set_flags := parse_field_prefixes(p);
+			names := parse_ident_list(p, allow_poly_names);
+
+			total_name_count += len(names);
+			ok := handle_field(p, &seen_ellipsis, &fields, docs, names, allowed_flags, set_flags);
+			if !ok {
+				break;
+			}
+		}
+	}
+
+	field_list = ast.new(ast.Field_List, start_tok.pos, p.curr_tok.pos);
+	field_list.list = fields[:];
+	return;
+}
+
+
+parse_results :: proc(p: ^Parser) -> (list: ^ast.Field_List, diverging: bool) {
+	if !allow_token(p, token.Arrow_Right) {
+		return;
+	}
+
+	if allow_token(p, token.Not) {
+		diverging = true;
+		return;
+	}
+
+	prev_level := p.expr_level;
+	defer p.expr_level = prev_level;
+
+	if p.curr_tok.kind != token.Open_Paren {
+		type := parse_type(p);
+		field := new_ast_field(nil, type, nil);
+
+		list = ast.new(ast.Field_List, field.pos, field.end);
+		list.list = make([]^ast.Field, 1);
+		list.list[0] = field;
+		return;
+	}
+
+	expect_token(p, token.Open_Paren);
+	list, _ = parse_field_list(p, token.Close_Paren, ast.Field_Flags_Signature_Results);
+	expect_token_after(p, token.Close_Paren, "parameter list");
+	return;
+}
+
+
+string_to_calling_convention :: proc(s: string) -> ast.Proc_Calling_Convention {
+	using ast.Proc_Calling_Convention;
+	switch s {
+	case "odin":
+		return Odin;
+	case "contextless":
+		return Contextless;
+	case "cdecl", "c":
+		return C_Decl;
+	case "stdcall", "std":
+		return Std_Call;
+	case "fast", "fastcall":
+		return Fast_Call;
+	}
+	return Invalid;
+}
+
+parse_proc_type :: proc(p: ^Parser, tok: token.Token) -> ^ast.Proc_Type {
+	cc := ast.Proc_Calling_Convention.Invalid;
+	if p.curr_tok.kind == token.String {
+		str := expect_token(p, token.String);
+		cc = string_to_calling_convention(str.text);
+		if cc == ast.Proc_Calling_Convention.Invalid {
+			error(p, str.pos, "unknown calling convention '%s'", str.text);
+		}
+	}
+
+	if cc == ast.Proc_Calling_Convention.Invalid {
+		if p.in_foreign_block {
+			cc = ast.Proc_Calling_Convention.Foreign_Block_Default;
+		} else {
+			cc = ast.Proc_Calling_Convention.Odin;
+		}
+	}
+
+	expect_token(p, token.Open_Paren);
+	params, _ := parse_field_list(p, token.Close_Paren, ast.Field_Flags_Signature_Params);
+	expect_token(p, token.Close_Paren);
+	results, diverging := parse_results(p);
+
+	is_generic := false;
+
+	loop: for param in params.list {
+		if param.type != nil {
+
+		}
+	}
+
+	end := end_pos(p.prev_tok);
+	pt := ast.new(ast.Proc_Type, tok.pos, end);
+	pt.tok = tok;
+	pt.calling_convention = cc;
+	pt.params = params;
+	pt.results = results;
+	pt.diverging = diverging;
+	pt.generic = is_generic;
+	return pt;
+}
+
+check_poly_params_for_type :: proc(p: ^Parser, poly_params: ^ast.Field_List, tok: token.Token) {
+
+}
+
+
+
+parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
+	switch p.curr_tok.kind {
+	case token.Ident:
+		return parse_ident(p);
+
+	case token.Undef:
+		tok := expect_token(p, token.Undef);
+		undef := ast.new(ast.Undef, tok.pos, end_pos(tok));
+		undef.tok = tok.kind;
+		return undef;
+
+	case token.Context:
+		tok := expect_token(p, token.Context);
+		ctx := ast.new(ast.Implicit, tok.pos, end_pos(tok));
+		ctx.tok = tok;
+		return ctx;
+
+	case token.Integer, token.Float, token.Imag,
+	     token.Rune, token.String:
+	     tok := advance_token(p);
+	     bl := ast.new(ast.Basic_Lit, tok.pos, end_pos(tok));
+	     bl.tok = tok;
+	     return bl;
+
+
+	case token.Size_Of, token.Align_Of, token.Offset_Of:
+		tok := advance_token(p);
+		expr := ast.new(ast.Implicit, tok.pos, end_pos(tok));
+		expr.tok = tok;
+		return parse_call_expr(p, expr);
+
+	case token.Open_Brace:
+		if !lhs {
+			return parse_literal_value(p, nil);
+		}
+
+	case token.Open_Paren:
+		open := expect_token(p, token.Open_Paren);
+		p.expr_level += 1;
+		expr := parse_expr(p, false);
+		p.expr_level -= 1;
+		close := expect_token(p, token.Close_Paren);
+
+		pe := ast.new(ast.Paren_Expr, open.pos, end_pos(close));
+		pe.open  = open.pos;
+		pe.expr  = expr;
+		pe.close = close.pos;
+		return pe;
+
+	case token.Distinct:
+		tok := advance_token(p);
+		type := parse_type(p);
+		dt := ast.new(ast.Distinct_Type, tok.pos, type.end);
+		dt.tok  = tok.kind;
+		dt.type = type;
+		return dt;
+
+	case token.Opaque:
+		tok := advance_token(p);
+		type := parse_type(p);
+		ot := ast.new(ast.Opaque_Type, tok.pos, type.end);
+		ot.tok  = tok.kind;
+		ot.type = type;
+		return ot;
+	case token.Hash:
+		tok := expect_token(p, token.Hash);
+		name := expect_token(p, token.Ident);
+		switch name.text {
+		case "type":
+			type := parse_type(p);
+			hp := ast.new(ast.Helper_Type, tok.pos, type.end);
+			hp.tok  = tok.kind;
+			hp.type = type;
+			return hp;
+
+		case "file", "line", "procedure", "caller_location":
+			bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name));
+			bd.tok  = tok;
+			bd.name = name.text;
+			return bd;
+		case "location", "assert", "defined":
+			bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name));
+			bd.tok  = tok;
+			bd.name = name.text;
+			return parse_call_expr(p, bd);
+		case:
+			expr := parse_expr(p, lhs);
+			te := ast.new(ast.Tag_Expr, tok.pos, expr.pos);
+			te.op   = tok;
+			te.name = name.text;
+			te.expr = expr;
+			return te;
+		}
+
+	case token.Inline, token.No_Inline:
+		tok := advance_token(p);
+
+	case token.Proc:
+		tok := expect_token(p, token.Proc);
+
+		if p.curr_tok.kind == token.Open_Brace {
+			open := expect_token(p, token.Open_Brace);
+
+			args: [dynamic]^ast.Expr;
+
+			for p.curr_tok.kind != token.Close_Brace &&
+			    p.curr_tok.kind != token.EOF {
+				elem := parse_expr(p, false);
+				append(&args, elem);
+
+				if !allow_token(p, token.Comma) {
+					break;
+				}
+			}
+
+			close := expect_token(p, token.Close_Brace);
+
+			if len(args) == 0 {
+				error(p, tok.pos, "expected at least 1 argument in procedure group");
+			}
+
+			pg := ast.new(ast.Proc_Group, tok.pos, end_pos(close));
+			pg.tok   = tok;
+			pg.open  = open.pos;
+			pg.args  = args[:];
+			pg.close = close.pos;
+			return pg;
+		}
+
+		type := parse_proc_type(p, tok);
+
+		if p.allow_type && p.expr_level < 0 {
+			return type;
+		}
+		body: ^ast.Stmt;
+
+		if allow_token(p, token.Undef) {
+			// Okay
+		} else if p.curr_tok.kind == token.Open_Brace {
+			prev_proc := p.curr_proc;
+			p.curr_proc = type;
+			body = parse_body(p);
+			p.curr_proc = prev_proc;
+		} else if allow_token(p, token.Do) {
+			prev_proc := p.curr_proc;
+			p.curr_proc = type;
+			body = convert_stmt_to_body(p, parse_stmt(p));
+			p.curr_proc = prev_proc;
+		} else {
+			return type;
+		}
+
+		pl := ast.new(ast.Proc_Lit, tok.pos, end_pos(p.prev_tok));
+		pl.type = type;
+		pl.body = body;
+		return pl;
+
+	case token.Dollar:
+		tok := advance_token(p);
+		type := parse_ident(p);
+		end := type.end;
+
+		specialization: ^ast.Expr;
+		if allow_token(p, token.Quo) {
+			specialization = parse_type(p);
+			end = specialization.pos;
+		}
+		pt := ast.new(ast.Poly_Type, tok.pos, end);
+		pt.dollar = tok.pos;
+		pt.type = type;
+		pt.specialization = specialization;
+		return pt;
+
+	case token.Typeid:
+		tok := advance_token(p);
+		ti := ast.new(ast.Typeid_Type, tok.pos, end_pos(tok));
+		ti.tok = tok.kind;
+		ti.specialization = nil;
+		return ti;
+
+	case token.Type_Of:
+		tok := advance_token(p);
+		i := ast.new(ast.Implicit, tok.pos, end_pos(tok));
+		i.tok = tok;
+		type: ^ast.Expr = parse_call_expr(p, i);
+		for p.curr_tok.kind == token.Period {
+			tok := advance_token(p);
+
+			field := parse_ident(p);
+			sel := ast.new(ast.Selector_Expr, tok.pos, field.end);
+			sel.expr = type;
+			sel.field = field;
+
+			type = sel;
+		}
+
+		return type;
+
+
+	case token.Pointer:
+		tok := expect_token(p, token.Pointer);
+		elem := parse_type(p);
+		ptr := ast.new(ast.Pointer_Type, tok.pos, elem.end);
+		ptr.elem = elem;
+		return ptr;
+
+	case token.Open_Bracket:
+		open := expect_token(p, token.Open_Bracket);
+		count: ^ast.Expr;
+		if p.curr_tok.kind == token.Question {
+			tok := expect_token(p, token.Question);
+			q := ast.new(ast.Unary_Expr, tok.pos, end_pos(tok));
+			q.op = tok;
+			count = q;
+		} else if p.curr_tok.kind == token.Dynamic {
+			tok := expect_token(p, token.Dynamic);
+			close := expect_token(p, token.Close_Bracket);
+			elem := parse_type(p);
+			da := ast.new(ast.Dynamic_Array_Type, open.pos, elem.end);
+			da.open = open.pos;
+			da.dynamic_pos = tok.pos;
+			da.close = close.pos;
+			da.elem = elem;
+
+			return da;
+		} else if p.curr_tok.kind != token.Close_Bracket {
+			p.expr_level += 1;
+			count = parse_expr(p, false);
+			p.expr_level -= 1;
+		}
+		close := expect_token(p, token.Close_Bracket);
+		elem := parse_type(p);
+		at := ast.new(ast.Array_Type, open.pos, elem.end);
+		at.open  = open.pos;
+		at.len   = count;
+		at.close = close.pos;
+		at.elem  = elem;
+		return at;
+
+	case token.Map:
+		tok := expect_token(p, token.Map);
+		open := expect_token(p, token.Open_Bracket);
+		key := parse_type(p);
+		close := expect_token(p, token.Close_Bracket);
+		value := parse_type(p);
+
+		mt := ast.new(ast.Map_Type, tok.pos, value.end);
+		mt.tok_pos = tok.pos;
+		mt.key = key;
+		mt.value = value;
+		return mt;
+
+	case token.Struct:
+		tok := expect_token(p, token.Struct);
+
+		poly_params: ^ast.Field_List;
+		align:        ^ast.Expr;
+		is_packed:    bool;
+		is_raw_union: bool;
+		fields:       ^ast.Field_List;
+		name_count:   int;
+
+		if allow_token(p, token.Open_Paren) {
+			param_count: int;
+			poly_params, param_count = parse_field_list(p, token.Close_Paren, ast.Field_Flags_Record_Poly_Params);
+			if param_count == 0 {
+				error(p, poly_params.pos, "expected at least 1 polymorphic parameter");
+				poly_params = nil;
+			}
+			expect_token_after(p, token.Close_Paren, "parameter list");
+			check_poly_params_for_type(p, poly_params, tok);
+		}
+
+		prev_level := p.expr_level;
+		p.expr_level = -1;
+		for allow_token(p, token.Hash) {
+			tag := expect_token_after(p, token.Ident, "#");
+			switch tag.text {
+			case "packed":
+				if is_packed do error(p, tag.pos, "duplicate struct tag '#%s'", tag.text);
+				is_packed = true;
+			case "align":
+				if align != nil do error(p, tag.pos, "duplicate struct tag '#%s'", tag.text);
+				align = parse_expr(p, true);
+			case "raw_union":
+				if is_raw_union do error(p, tag.pos, "duplicate struct tag '#%s'", tag.text);
+				is_raw_union = true;
+			case:
+				error(p, tag.pos, "invalid struct tag '#%s", tag.text);
+			}
+		}
+		p.expr_level = prev_level;
+
+		if is_raw_union && is_packed {
+			is_packed = false;
+			error(p, tok.pos, "'#raw_union' cannot also be '#packed");
+		}
+
+		open := expect_token(p, token.Open_Brace);
+		fields, name_count = parse_field_list(p, token.Close_Brace, ast.Field_Flags_Struct);
+		close := expect_token(p, token.Close_Brace);
+
+		st := ast.new(ast.Struct_Type, tok.pos, end_pos(close));
+		st.poly_params  = poly_params;
+		st.align        = align;
+		st.is_packed    = is_packed;
+		st.is_raw_union = is_raw_union;
+		st.fields       = fields;
+		st.name_count   = name_count;
+		return st;
+
+	case token.Union:
+		tok := expect_token(p, token.Union);
+		poly_params: ^ast.Field_List;
+		align:       ^ast.Expr;
+
+		if allow_token(p, token.Open_Paren) {
+			param_count: int;
+			poly_params, param_count = parse_field_list(p, token.Close_Paren, ast.Field_Flags_Record_Poly_Params);
+			if param_count == 0 {
+				error(p, poly_params.pos, "expected at least 1 polymorphic parameter");
+				poly_params = nil;
+			}
+			expect_token_after(p, token.Close_Paren, "parameter list");
+			check_poly_params_for_type(p, poly_params, tok);
+		}
+
+		prev_level := p.expr_level;
+		p.expr_level = -1;
+		for allow_token(p, token.Hash) {
+			tag := expect_token_after(p, token.Ident, "#");
+			switch tag.text {
+			case "align":
+				if align != nil do error(p, tag.pos, "duplicate union tag '#%s'", tag.text);
+				align = parse_expr(p, true);
+			case:
+				error(p, tag.pos, "invalid union tag '#%s", tag.text);
+			}
+		}
+		p.expr_level = prev_level;
+
+		variants: [dynamic]^ast.Expr;
+
+		open := expect_token_after(p, token.Open_Brace, "union");
+
+		for p.curr_tok.kind != token.Close_Brace && p.curr_tok.kind != token.EOF {
+			type := parse_type(p);
+			if _, ok := type.derived.(ast.Bad_Expr); !ok {
+				append(&variants, type);
+			}
+			if !allow_token(p, token.Comma) {
+				break;
+			}
+		}
+
+		close := expect_token(p, token.Close_Brace);
+
+		ut := ast.new(ast.Union_Type, tok.pos, end_pos(close));
+		ut.poly_params = poly_params;
+		ut.variants    = variants[:];
+		ut.align       = align;
+
+		return ut;
+
+	case token.Enum:
+		tok := expect_token(p, token.Enum);
+		base_type: ^ast.Expr;
+		if p.curr_tok.kind != token.Open_Brace {
+			base_type = parse_type(p);
+		}
+		open := expect_token(p, token.Open_Brace);
+		fields := parse_elem_list(p);
+		close := expect_token(p, token.Close_Brace);
+
+		et := ast.new(ast.Enum_Type, tok.pos, end_pos(close));
+		et.base_type = base_type;
+		et.open = open.pos;
+		et.fields = fields;
+		et.close = close.pos;
+		return et;
+
+	case token.Bit_Field:
+		tok := expect_token(p, token.Bit_Field);
+
+		fields: [dynamic]^ast.Field_Value;
+		align: ^ast.Expr;
+
+		prev_level := p.expr_level;
+		p.expr_level = -1;
+
+		for allow_token(p, token.Hash) {
+			tag := expect_token_after(p, token.Ident, "#");
+			switch tag.text {
+			case "align":
+				if align != nil {
+					error(p, tag.pos, "duplicate bit_field tag '#%s", tag.text);
+				}
+				align = parse_expr(p, true);
+			case:
+				error(p, tag.pos, "invalid bit_field tag '#%s", tag.text);
+			}
+		}
+
+		p.expr_level = prev_level;
+
+		open := expect_token_after(p, token.Open_Brace, "bit_field");
+
+		for p.curr_tok.kind != token.Close_Brace && p.curr_tok.kind != token.EOF {
+			name := parse_ident(p);
+			colon := expect_token(p, token.Colon);
+			value := parse_expr(p, true);
+
+			fv := ast.new(ast.Field_Value, name.pos, value.end);
+			fv.field = name;
+			fv.sep   = colon.pos;
+			fv.value = value;
+			append(&fields, fv);
+
+			if !allow_token(p, token.Comma) {
+				break;
+			}
+		}
+
+		close := expect_token(p, token.Close_Brace);
+
+		bft := ast.new(ast.Bit_Field_Type, tok.pos, end_pos(close));
+		bft.tok_pos = tok.pos;
+		bft.open    = open.pos;
+		bft.fields  = fields[:];
+		bft.close   = close.pos;
+		bft.align   = align;
+
+		return bft;
+
+	case token.Bit_Set:
+		tok := expect_token(p, token.Bit_Set);
+		open := expect_token(p, token.Open_Bracket);
+		elem, underlying: ^ast.Expr;
+
+		prev_allow_range := p.allow_range;
+		p.allow_range = true;
+		elem = parse_expr(p, false);
+		p.allow_range = prev_allow_range;
+
+		if allow_token(p, token.Semicolon) {
+			underlying = parse_type(p);
+		}
+
+
+		close := expect_token(p, token.Close_Bracket);
+
+		bst := ast.new(ast.Bit_Set_Type, tok.pos, end_pos(close));
+		bst.tok_pos = tok.pos;
+		bst.open = open.pos;
+		bst.elem = elem;
+		bst.underlying = underlying;
+		bst.close = close.pos;
+		return bst;
+
+	}
+
+	return nil;
+}
+
+is_literal_type :: proc(expr: ^ast.Expr) -> bool {
+	expr = ast.unparen_expr(expr);
+	if expr == nil {
+		return false;
+	}
+	switch _ in expr.derived {
+	case ast.Bad_Expr,
+		ast.Ident,
+		ast.Selector_Expr,
+		ast.Array_Type,
+		ast.Struct_Type,
+		ast.Union_Type,
+		ast.Enum_Type,
+		ast.Dynamic_Array_Type,
+		ast.Map_Type,
+		ast.Bit_Field_Type,
+		ast.Bit_Set_Type,
+		ast.Call_Expr:
+		return true;
+	}
+	return false;
+}
+
+parse_value :: proc(p: ^Parser) -> ^ast.Expr {
+	if p.curr_tok.kind == token.Open_Brace {
+		return parse_literal_value(p, nil);
+	}
+	return parse_expr(p, false);
+}
+
+parse_elem_list :: proc(p: ^Parser) -> []^ast.Expr {
+	elems: [dynamic]^ast.Expr;
+
+	for p.curr_tok.kind != token.Close_Brace && p.curr_tok.kind != token.EOF {
+		elem := parse_value(p);
+		if p.curr_tok.kind == token.Eq {
+			eq := expect_token(p, token.Eq);
+			value := parse_value(p);
+
+			fv := ast.new(ast.Field_Value, elem.pos, value.end);
+			fv.field = elem;
+			fv.sep   = eq.pos;
+			fv.value = value;
+
+			elem = fv;
+		}
+
+		append(&elems, elem);
+
+		if !allow_token(p, token.Comma) {
+			break;
+		}
+	}
+
+	return elems[:];
+}
+
+parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit {
+	elems: []^ast.Expr;
+	open := expect_token(p, token.Open_Brace);
+	p.expr_level += 1;
+	if p.curr_tok.kind != token.Close_Brace {
+		elems = parse_elem_list(p);
+	}
+	p.expr_level -= 1;
+
+	close := expect_token_after(p, token.Close_Brace, "compound literal");
+
+	pos := type != nil ? type.pos : open.pos;
+	lit := ast.new(ast.Comp_Lit, pos, end_pos(close));
+	lit.type  = type;
+	lit.open  = open.pos;
+	lit.elems = elems;
+	lit.close = close.pos;
+	return lit;
+}
+
+parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr {
+	args: [dynamic]^ast.Expr;
+
+	ellipsis: token.Token;
+
+	p.expr_level += 1;
+	open := expect_token(p, token.Open_Paren);
+
+	for p.curr_tok.kind != token.Close_Paren &&
+	    p.curr_tok.kind != token.EOF &&
+	    ellipsis.pos.line == 0 {
+
+		if p.curr_tok.kind == token.Comma {
+			error(p, p.curr_tok.pos, "expected an expression not ,");
+		} else if p.curr_tok.kind == token.Eq {
+			error(p, p.curr_tok.pos, "expected an expression not =");
+		}
+
+		prefix_ellipsis := false;
+		if p.curr_tok.kind == token.Ellipsis {
+			prefix_ellipsis = true;
+			ellipsis = expect_token(p, token.Ellipsis);
+		}
+
+		arg := parse_expr(p, false);
+		if p.curr_tok.kind == token.Eq {
+			eq := expect_token(p, token.Eq);
+
+			if prefix_ellipsis {
+				error(p, ellipsis.pos, "'..' must be applied to value rather than a field name");
+			}
+
+			value := parse_value(p);
+			fv := ast.new(ast.Field_Value, arg.pos, value.end);
+			fv.field = arg;
+			fv.sep   = eq.pos;
+			fv.value = value;
+
+			arg = fv;
+		}
+
+		append(&args, arg);
+
+		if !allow_token(p, token.Comma) {
+			break;
+		}
+	}
+
+	close := expect_token_after(p, token.Close_Paren, "argument list");
+	p.expr_level -= 1;
+
+	ce := ast.new(ast.Call_Expr, operand.pos, end_pos(close));
+	ce.expr     = operand;
+	ce.open     = open.pos;
+	ce.args     = args[:];
+	ce.ellipsis = ellipsis;
+	ce.close    = close.pos;
+
+	return ce;
+}
+
+
+parse_atom_expr :: proc(p: ^Parser, operand: ^ast.Expr, lhs: bool) -> ^ast.Expr {
+	if operand == nil {
+		if p.allow_type do return nil;
+		error(p, p.curr_tok.pos, "expected an operand");
+		be := ast.new(ast.Bad_Expr, p.curr_tok.pos, end_pos(p.curr_tok));
+		advance_token(p);
+		operand = be;
+	}
+
+	loop := true;
+	for loop {
+		switch p.curr_tok.kind {
+		case:
+			loop = false;
+
+		case token.Open_Paren:
+			operand = parse_call_expr(p, operand);
+
+		case token.Open_Bracket:
+			prev_allow_range := p.allow_range;
+			defer p.allow_range = prev_allow_range;
+			p.allow_range = false;
+
+			indicies: [2]^ast.Expr;
+			interval: token.Token;
+			is_slice_op := false;
+
+			p.expr_level += 1;
+			open := expect_token(p, token.Open_Bracket);
+
+			if p.curr_tok.kind != token.Colon {
+				indicies[0] = parse_expr(p, false);
+			}
+
+			if p.curr_tok.kind == token.Colon {
+				interval = advance_token(p);
+				is_slice_op = true;
+				if (p.curr_tok.kind != token.Close_Bracket && p.curr_tok.kind != token.EOF) {
+					indicies[1] = parse_expr(p, false);
+				}
+			}
+
+			close := expect_token(p, token.Close_Bracket);
+			p.expr_level -= 1;
+
+			if is_slice_op {
+				se := ast.new(ast.Slice_Expr, operand.pos, end_pos(close));
+				se.expr = operand;
+				se.open = open.pos;
+				se.low = indicies[0];
+				se.interval = interval;
+				se.high = indicies[1];
+				se.close = close.pos;
+
+				operand = se;
+			} else {
+				ie := ast.new(ast.Index_Expr, operand.pos, end_pos(close));
+				ie.expr = operand;
+				ie.open = open.pos;
+				ie.index = indicies[0];
+				ie.close = close.pos;
+
+				operand = ie;
+			}
+
+
+		case token.Period:
+			tok := expect_token(p, token.Period);
+			switch p.curr_tok.kind {
+			case token.Ident:
+				field := parse_ident(p);
+
+				sel := ast.new(ast.Selector_Expr, operand.pos, field.end);
+				sel.expr  = operand;
+				sel.field = field;
+
+				operand = sel;
+
+			case token.Open_Paren:
+				open := expect_token(p, token.Open_Paren);
+				type := parse_type(p);
+				close := expect_token(p, token.Close_Paren);
+
+				ta := ast.new(ast.Type_Assertion, operand.pos, end_pos(close));
+				ta.expr  = operand;
+				ta.open  = open.pos;
+				ta.type  = type;
+				ta.close = close.pos;
+
+				operand = ta;
+
+			case:
+				error(p, p.curr_tok.pos, "expected a selector");
+				advance_token(p);
+				operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok));
+			}
+
+		case token.Pointer:
+			op := expect_token(p, token.Pointer);
+			deref := ast.new(ast.Deref_Expr, operand.pos, end_pos(op));
+			deref.expr = operand;
+			deref.op   = op;
+
+			operand = deref;
+
+		case token.Open_Brace:
+			if !lhs && is_literal_type(operand) && p.expr_level >= 0 {
+				operand = parse_literal_value(p, operand);
+			} else {
+				loop = false;
+			}
+
+		}
+
+		lhs = false;
+	}
+
+	return operand;
+
+}
+
+parse_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
+	return parse_binary_expr(p, lhs, 0+1);
+}
+parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
+	switch p.curr_tok.kind {
+	case token.Transmute, token.Cast:
+		tok := advance_token(p);
+		open := expect_token(p, token.Open_Paren);
+		type := parse_type(p);
+		close := expect_token(p, token.Close_Paren);
+		expr := parse_unary_expr(p, lhs);
+
+		tc := ast.new(ast.Type_Cast, tok.pos, expr.end);
+		tc.tok   = tok;
+		tc.open  = open.pos;
+		tc.type  = type;
+		tc.close = close.pos;
+		tc.expr  = expr;
+		return tc;
+
+	case token.Auto_Cast:
+		op := advance_token(p);
+		expr := parse_unary_expr(p, lhs);
+
+		ac := ast.new(ast.Auto_Cast, op.pos, expr.end);
+		ac.op   = op;
+		ac.expr = expr;
+		return ac;
+
+	case token.Add, token.Sub,
+	     token.Not, token.Xor,
+	     token.And:
+		op := advance_token(p);
+		expr := parse_unary_expr(p, lhs);
+
+		ue := ast.new(ast.Unary_Expr, op.pos, expr.end);
+		ue.op   = op;
+		ue.expr = expr;
+		return ue;
+
+	}
+	return parse_atom_expr(p, parse_operand(p, lhs), lhs);
+}
+parse_binary_expr :: proc(p: ^Parser, lhs: bool, prec_in: int) -> ^ast.Expr {
+	expr := parse_unary_expr(p, lhs);
+	for prec := token_precedence(p, p.curr_tok.kind); prec >= prec_in; prec -= 1 {
+		for {
+			op := p.curr_tok;
+			op_prec := token_precedence(p, op.kind);
+			if op_prec != prec {
+				break;
+			}
+			expect_operator(p);
+
+			if op.kind == token.Question {
+				cond := expr;
+				x := parse_expr(p, lhs);
+				colon := expect_token(p, token.Colon);
+				y := parse_expr(p, lhs);
+				te := ast.new(ast.Ternary_Expr, expr.pos, end_pos(p.prev_tok));
+				te.cond = cond;
+				te.op1  = op;
+				te.x    = x;
+				te.op2  = colon;
+				te.y    = y;
+
+				expr = te;
+			} else {
+				right := parse_binary_expr(p, false, prec+1);
+				if right == nil {
+					error(p, op.pos, "expected expression on the right-hand side of the binary operator");
+				}
+				be := ast.new(ast.Binary_Expr, expr.pos, end_pos(p.prev_tok));
+				be.left  = expr;
+				be.op    = op;
+				be.right = right;
+
+				expr = be;
+			}
+		}
+	}
+
+	return expr;
+}
+
+
+parse_expr_list :: proc(p: ^Parser, lhs: bool) -> ([]^ast.Expr) {
+	list: [dynamic]^ast.Expr;
+	for {
+		expr := parse_expr(p, lhs);
+		append(&list, expr);
+		if p.curr_tok.kind != token.Comma || p.curr_tok.kind == token.EOF {
+			break;
+		}
+		advance_token(p);
+	}
+
+	return list[:];
+}
+parse_lhs_expr_list :: proc(p: ^Parser) -> []^ast.Expr {
+	return parse_expr_list(p, true);
+}
+parse_rhs_expr_list :: proc(p: ^Parser) -> []^ast.Expr {
+	return parse_expr_list(p, false);
+}
+
+parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
+	start_tok := p.curr_tok;
+	docs := p.lead_comment;
+
+	lhs := parse_lhs_expr_list(p);
+	op := p.curr_tok;
+	switch {
+	case token.is_assignment_operator(op.kind):
+		// if p.curr_proc == nil {
+		// 	error(p, p.curr_tok.pos, "simple statements are not allowed at the file scope");
+		// 	return ast.new(ast.Bad_Stmt, start_tok.pos, end_pos(p.curr_tok));
+		// }
+		advance_token(p);
+		rhs := parse_rhs_expr_list(p);
+		if len(rhs) == 0 {
+			error(p, p.curr_tok.pos, "no right-hand side in assignment statement");
+			return ast.new(ast.Bad_Stmt, start_tok.pos, end_pos(p.curr_tok));
+		}
+		stmt := ast.new(ast.Assign_Stmt, lhs[0].pos, rhs[len(rhs)-1].end);
+		stmt.lhs = lhs;
+		stmt.op = op;
+		stmt.rhs = rhs;
+		return stmt;
+
+	case op.kind == token.In:
+		if Stmt_Allow_Flag.In in flags {
+			allow_token(p, token.In);
+			prev_allow_range := p.allow_range;
+			p.allow_range = true;
+			expr := parse_expr(p, false);
+			p.allow_range = prev_allow_range;
+
+			rhs := make([]^ast.Expr, 1);
+			rhs[0] = expr;
+
+			stmt := ast.new(ast.Assign_Stmt, lhs[0].pos, rhs[len(rhs)-1].end);
+			stmt.lhs = lhs;
+			stmt.op = op;
+			stmt.rhs = rhs;
+			return stmt;
+		}
+	case op.kind == token.Colon:
+		expect_token_after(p, token.Colon, "identifier list");
+		if Stmt_Allow_Flag.Label in flags && len(lhs) == 1 {
+			switch p.curr_tok.kind {
+			case token.Open_Brace, token.If, token.For, token.Switch:
+				label := lhs[0];
+				stmt := parse_stmt(p);
+
+				if stmt != nil do switch n in &stmt.derived {
+				case ast.Block_Stmt:       n.label = label;
+				case ast.If_Stmt:          n.label = label;
+				case ast.For_Stmt:         n.label = label;
+				case ast.Switch_Stmt:      n.label = label;
+				case ast.Type_Switch_Stmt: n.label = label;
+				}
+
+				return stmt;
+			}
+		}
+
+		return parse_value_decl(p, lhs, docs);
+	}
+
+	if len(lhs) > 1 {
+		error(p, op.pos, "expected 1 expression, got %d", len(lhs));
+		return ast.new(ast.Bad_Stmt, start_tok.pos, end_pos(p.curr_tok));
+	}
+
+	es := ast.new(ast.Expr_Stmt, lhs[0].pos, lhs[0].end);
+	es.expr = lhs[0];
+	return es;
+}
+
+parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Group) -> ^ast.Decl {
+	is_mutable := true;
+
+	values: []^ast.Expr;
+	type := parse_type_or_ident(p);
+
+	switch p.curr_tok.kind {
+	case token.Eq, token.Colon:
+		sep := advance_token(p);
+		is_mutable = sep.kind != token.Colon;
+
+		values = parse_rhs_expr_list(p);
+		if len(values) > len(names) {
+			error(p, p.curr_tok.pos, "too many values on the right-hand side of the declaration");
+		} else if len(values) < len(names) && !is_mutable {
+			error(p, p.curr_tok.pos, "all constant declarations must be defined");
+		} else if len(values) == 0 {
+			error(p, p.curr_tok.pos, "expected an expression for this declaration");
+		}
+	}
+
+	if is_mutable {
+		if type == nil && len(values) == 0 {
+			error(p, p.curr_tok.pos, "missing variable type or initialization");
+			return ast.new(ast.Bad_Decl, names[0].pos, end_pos(p.curr_tok));
+		}
+	} else {
+		if type == nil && len(values) == 0 && len(names) > 0 {
+			error(p, p.curr_tok.pos, "missing constant value");
+			return ast.new(ast.Bad_Decl, names[0].pos, end_pos(p.curr_tok));
+		}
+	}
+
+	if p.expr_level >= 0 {
+		end: ^ast.Expr;
+		if !is_mutable && len(values) > 0 {
+			end = values[len(values)-1];
+		}
+		if p.curr_tok.kind == token.Close_Brace &&
+		   p.curr_tok.pos.line == p.prev_tok.pos.line {
+
+		} else {
+			expect_semicolon(p, end);
+		}
+	}
+
+	if p.curr_proc == nil {
+		if len(values) > 0 && len(names) != len(values) {
+			error(p, values[0].pos, "expected %d expressions on the right-hand side, got %d", len(names), len(values));
+		}
+	}
+
+	decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(p.prev_tok));
+	decl.docs = docs;
+	decl.names = names;
+	decl.type = type;
+	decl.values = values;
+	decl.is_mutable = is_mutable;
+	return decl;
+}
+
+
+parse_import_decl :: proc(p: ^Parser, kind := Import_Decl_Kind.Standard) -> ^ast.Import_Decl {
+	docs := p.lead_comment;
+	tok := expect_token(p, token.Import);
+
+	import_name: token.Token;
+	is_using := kind != Import_Decl_Kind.Standard;
+
+	switch p.curr_tok.kind {
+	case token.Ident:
+		import_name = advance_token(p);
+	case:
+		import_name.pos = p.curr_tok.pos;
+	}
+
+	if !is_using && is_blank_ident(import_name) {
+		error(p, import_name.pos, "illegal import name: '_'");
+	}
+
+	path := expect_token_after(p, token.String, "import");
+
+	decl := ast.new(ast.Import_Decl, tok.pos, end_pos(path));
+	decl.docs       = docs;
+	decl.is_using   = is_using;
+	decl.import_tok = tok;
+	decl.name       = import_name;
+	decl.relpath    = path;
+	decl.fullpath   = path.text;
+
+	if p.curr_proc != nil {
+		error(p, decl.pos, "import declarations cannot be used within a procedure, it must be done at the file scope");
+	} else {
+		append(&p.file.imports, decl);
+	}
+	expect_semicolon(p, decl);
+	decl.comment = p.line_comment;
+
+	return decl;
+}