Browse Source

union_cast

Ginger Bill 8 years ago
parent
commit
a675d3f94d
8 changed files with 292 additions and 53 deletions
  1. 17 5
      code/demo.odin
  2. 19 15
      core/fmt.odin
  3. 98 10
      src/checker/expr.cpp
  4. 117 10
      src/codegen/ssa.cpp
  5. 2 1
      src/gb/gb.h
  6. 3 1
      src/main.cpp
  7. 30 8
      src/parser.cpp
  8. 6 3
      src/tokenizer.cpp

+ 17 - 5
code/demo.odin

@@ -1,13 +1,25 @@
 #import "fmt.odin"
 
 main :: proc() {
-	Thing :: struct {
-		f: f32
-		a: any
+	Entity :: union {
+		Apple: int
+		Banana: f32
+		Goat: struct {
+			x, y: int
+			z, w: f32
+		}
 	}
-	t := Thing{1, "Hello"}
 
+	a := 123 as Entity.Apple
+	e: Entity = a
+	fmt.println(a)
 
-	fmt.printf("Here % %\n", 123, 2.0)
+	if apple, ok := ^e union_cast ^Entity.Apple; ok {
+		apple^ = 321
+		e = apple^
+	}
+
+	apple, ok := e union_cast Entity.Apple
+	fmt.println(apple)
 }
 

+ 19 - 15
core/fmt.odin

@@ -4,35 +4,38 @@
 
 PRINT_BUF_SIZE :: 1<<12
 
-fprint :: proc(f: ^os.File, args: ..any) {
+fprint :: proc(f: ^os.File, args: ..any) -> int {
 	data: [PRINT_BUF_SIZE]byte
 	buf := data[:0]
 	bprint(^buf, ..args)
 	os.write(f, buf)
+	return buf.count
 }
 
-fprintln :: proc(f: ^os.File, args: ..any) {
+fprintln :: proc(f: ^os.File, args: ..any) -> int {
 	data: [PRINT_BUF_SIZE]byte
 	buf := data[:0]
 	bprintln(^buf, ..args)
 	os.write(f, buf)
+	return buf.count
 }
-fprintf :: proc(f: ^os.File, fmt: string, args: ..any) {
+fprintf :: proc(f: ^os.File, fmt: string, args: ..any) -> int {
 	data: [PRINT_BUF_SIZE]byte
 	buf := data[:0]
 	bprintf(^buf, fmt, ..args)
 	os.write(f, buf)
+	return buf.count
 }
 
 
-print :: proc(args: ..any) {
-	fprint(os.stdout, ..args)
+print :: proc(args: ..any) -> int {
+	return fprint(os.stdout, ..args)
 }
-println :: proc(args: ..any) {
-	fprintln(os.stdout, ..args)
+println :: proc(args: ..any) -> int {
+	return fprintln(os.stdout, ..args)
 }
-printf :: proc(fmt: string, args: ..any) {
-	fprintf(os.stdout, fmt, ..args)
+printf :: proc(fmt: string, args: ..any) -> int {
+	return fprintf(os.stdout, fmt, ..args)
 }
 
 
@@ -288,9 +291,7 @@ 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
+		a := make_any(info.base, arg.data)
 		match type b : info.base {
 		case Struct:
 			print_string_to_buffer(buf, info.name)
@@ -491,7 +492,7 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any) {
 }
 
 
-bprintf :: proc(buf: ^[]byte, fmt: string, args: ..any) {
+bprintf :: proc(buf: ^[]byte, fmt: string, args: ..any) -> int {
 	is_digit :: proc(r: rune) -> bool #inline {
 		return r >= #rune "0" && r <= #rune "9"
 	}
@@ -552,10 +553,11 @@ bprintf :: proc(buf: ^[]byte, fmt: string, args: ..any) {
 	}
 
 	print_string_to_buffer(buf, fmt[prev:])
+	return buf.count
 }
 
 
-bprint :: proc(buf: ^[]byte, args: ..any) {
+bprint :: proc(buf: ^[]byte, args: ..any) -> int {
 	is_type_string :: proc(info: ^Type_Info) -> bool {
 		using Type_Info
 		if info == nil {
@@ -580,9 +582,10 @@ bprint :: proc(buf: ^[]byte, args: ..any) {
 		print_any_to_buffer(buf, arg)
 		prev_string = is_string;
 	}
+	return buf.count
 }
 
-bprintln :: proc(buf: ^[]byte, args: ..any) {
+bprintln :: proc(buf: ^[]byte, args: ..any) -> int {
 	for i := 0; i < args.count; i++ {
 		if i > 0 {
 			append(buf, #rune " ")
@@ -590,4 +593,5 @@ bprintln :: proc(buf: ^[]byte, args: ..any) {
 		print_any_to_buffer(buf, args[i])
 	}
 	print_nl_to_buffer(buf)
+	return buf.count
 }

+ 98 - 10
src/checker/expr.cpp

@@ -933,11 +933,15 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
 			o->type = t_invalid;
 			return;
 		}
-		// if (e->Variable.param) {
-			// o->mode = Addressing_Value;
-		// } else {
+	#if 0
+		if (e->Variable.param) {
+			o->mode = Addressing_Value;
+		} else {
 			o->mode = Addressing_Variable;
-		// }
+		}
+	#else
+		o->mode = Addressing_Variable;
+	#endif
 		break;
 
 	case Entity_TypeName: {
@@ -1869,14 +1873,14 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 			return;
 		}
 
-		i64 otz = type_size_of(c->sizes, c->allocator, x->type);
-		i64 ttz = type_size_of(c->sizes, c->allocator, type);
-		if (otz != ttz) {
+		i64 srcz = type_size_of(c->sizes, c->allocator, x->type);
+		i64 dstz = type_size_of(c->sizes, c->allocator, type);
+		if (srcz != dstz) {
 			gbString expr_str = expr_to_string(x->expr);
 			gbString type_str = type_to_string(type);
 			defer (gb_string_free(expr_str));
 			defer (gb_string_free(type_str));
-			error(ast_node_token(x->expr), "Cannot transmute `%s` to `%s`, %lld vs %lld bytes", expr_str, type_str, otz, ttz);
+			error(ast_node_token(x->expr), "Cannot transmute `%s` to `%s`, %lld vs %lld bytes", expr_str, type_str, srcz, dstz);
 			x->mode = Addressing_Invalid;
 			return;
 		}
@@ -1887,8 +1891,9 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 	} else if (be->op.kind == Token_down_cast) {
 		check_expr(c, x, be->left);
 		Type *type = check_type(c, be->right);
-		if (x->mode == Addressing_Invalid)
+		if (x->mode == Addressing_Invalid) {
 			return;
+		}
 
 		if (x->mode == Addressing_Constant) {
 			gbString expr_str = expr_to_string(node);
@@ -1927,7 +1932,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 			return;
 		}
 
-		if (!(is_type_struct(bdst) || is_type_struct(bdst))) {
+		if (!(is_type_struct(bdst) || is_type_raw_union(bdst))) {
 			gbString expr_str = expr_to_string(node);
 			defer (gb_string_free(expr_str));
 			error(ast_node_token(node), "Can only `down_cast` pointer to structs or unions: `%s`", expr_str);
@@ -1947,6 +1952,83 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 		x->mode = Addressing_Value;
 		x->type = type;
 		return;
+	} else if (be->op.kind == Token_union_cast) {
+		check_expr(c, x, be->left);
+		Type *type = check_type(c, be->right);
+		if (x->mode == Addressing_Invalid) {
+			return;
+		}
+
+		if (x->mode == Addressing_Constant) {
+			gbString expr_str = expr_to_string(node);
+			defer (gb_string_free(expr_str));
+			error(ast_node_token(node), "Cannot `union_cast` a constant expression: `%s`", expr_str);
+			x->mode = Addressing_Invalid;
+			return;
+		}
+
+		if (is_type_untyped(x->type)) {
+			gbString expr_str = expr_to_string(node);
+			defer (gb_string_free(expr_str));
+			error(ast_node_token(node), "Cannot `union_cast` an untyped expression: `%s`", expr_str);
+			x->mode = Addressing_Invalid;
+			return;
+		}
+
+		b32 src_is_ptr = is_type_pointer(x->type);
+		b32 dst_is_ptr = is_type_pointer(type);
+		Type *src = type_deref(x->type);
+		Type *dst = type_deref(type);
+		Type *bsrc = base_type(src);
+		Type *bdst = base_type(dst);
+
+		if (src_is_ptr != dst_is_ptr) {
+			gbString src_type_str = type_to_string(x->type);
+			gbString dst_type_str = type_to_string(type);
+			defer (gb_string_free(src_type_str));
+			defer (gb_string_free(dst_type_str));
+			error(ast_node_token(node), "Invalid `union_cast` types: `%s` and `%s`", src_type_str, dst_type_str);
+			x->mode = Addressing_Invalid;
+			return;
+		}
+
+		if (!is_type_union(src)) {
+			error(ast_node_token(node), "`union_cast` can only operate on unions");
+			x->mode = Addressing_Invalid;
+			return;
+		}
+
+		b32 ok = false;
+		for (isize i = 1; i < bsrc->Record.field_count; i++) {
+			Entity *f = bsrc->Record.fields[i];
+			if (are_types_identical(f->type, dst)) {
+				ok = true;
+				break;
+			}
+		}
+
+		if (!ok) {
+			gbString expr_str = expr_to_string(node);
+			gbString dst_type_str = type_to_string(type);
+			defer (gb_string_free(expr_str));
+			defer (gb_string_free(dst_type_str));
+			error(ast_node_token(node), "Cannot `union_cast` `%s` to `%s`", expr_str, dst_type_str);
+			x->mode = Addressing_Invalid;
+			return;
+		}
+
+		Entity **variables = gb_alloc_array(c->allocator, Entity *, 2);
+		Token tok = make_token_ident(make_string(""));
+		variables[0] = make_entity_param(c->allocator, NULL, tok, type, false);
+		variables[1] = make_entity_param(c->allocator, NULL, tok, t_bool, false);
+
+		Type *tuple = make_type_tuple(c->allocator);
+		tuple->Tuple.variables = variables;
+		tuple->Tuple.variable_count = 2;
+
+		x->type = tuple;
+		x->mode = Addressing_Value;
+		return;
 	}
 
 	check_expr(c, x, be->left);
@@ -3723,6 +3805,12 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 		o->expr = node;
 	case_end;
 
+	case_ast_node(re, RunExpr, node);
+		// TODO(bill): Tag expressions
+		kind = check_expr_base(c, o, re->expr, type_hint);
+		o->expr = node;
+	case_end;
+
 
 	case_ast_node(ue, UnaryExpr, node);
 		check_expr(c, o, ue->expr);

+ 117 - 10
src/codegen/ssa.cpp

@@ -840,7 +840,7 @@ ssaValue *ssa_make_instr_get_element_ptr(ssaProcedure *p, ssaValue *address,
 	i->GetElementPtr.indices[1]   = index1;
 	i->GetElementPtr.index_count  = index_count;
 	i->GetElementPtr.elem_type    = ssa_type(address);
-	i->GetElementPtr.inbounds     = inbounds;
+
 	GB_ASSERT_MSG(is_type_pointer(ssa_type(address)),
 	              "%s", type_to_string(ssa_type(address)));
 	return v;
@@ -1169,8 +1169,12 @@ ssaValue *ssa_add_local_generated(ssaProcedure *proc, Type *type) {
 
 ssaValue *ssa_add_param(ssaProcedure *proc, Entity *e) {
 	ssaValue *v = ssa_make_value_param(proc->module->allocator, proc, e);
+#if 1
 	ssaValue *l = ssa_add_local(proc, e);
 	ssa_emit_store(proc, l, v);
+#else
+	ssa_module_add_value(proc->module, e, v);
+#endif
 	return v;
 }
 
@@ -1418,7 +1422,7 @@ void ssa_end_procedure_body(ssaProcedure *proc) {
 	proc->curr_block = proc->decl_block;
 	ssa_emit_jump(proc, proc->entry_block);
 
-#if 0
+#if 1
 	ssa_optimize_blocks(proc);
 	ssa_build_referrers(proc);
 	ssa_build_dom_tree(proc);
@@ -1902,6 +1906,10 @@ String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
 	return make_string("");
 }
 
+ssaValue *ssa_emit_bitcast(ssaProcedure *proc, ssaValue *data, Type *type) {
+	return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, data, ssa_type(data), type));
+}
+
 
 ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_argument) {
 	Type *src_type = ssa_type(value);
@@ -2029,7 +2037,7 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg
 
 				Type *tag_type = src_type;
 				Type *tag_type_ptr = make_type_pointer(allocator, tag_type);
-				ssaValue *underlying = ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, data, t_rawptr, tag_type_ptr));
+				ssaValue *underlying = ssa_emit_bitcast(proc, data, tag_type_ptr);
 				ssa_emit_store(proc, underlying, value);
 
 				return ssa_emit_load(proc, parent);
@@ -2063,23 +2071,23 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg
 
 	// Pointer <-> Pointer
 	if (is_type_pointer(src) && is_type_pointer(dst)) {
-		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, value, src, dst));
+		return ssa_emit_bitcast(proc, value, dst);
 	}
 
 
 
 	// proc <-> proc
 	if (is_type_proc(src) && is_type_proc(dst)) {
-		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, value, src, dst));
+		return ssa_emit_bitcast(proc, value, dst);
 	}
 
 	// pointer -> proc
 	if (is_type_pointer(src) && is_type_proc(dst)) {
-		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, value, src, dst));
+		return ssa_emit_bitcast(proc, value, dst);
 	}
 	// proc -> pointer
 	if (is_type_proc(src) && is_type_pointer(dst)) {
-		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, value, src, dst));
+		return ssa_emit_bitcast(proc, value, dst);
 	}
 
 
@@ -2180,7 +2188,7 @@ ssaValue *ssa_emit_transmute(ssaProcedure *proc, ssaValue *value, Type *t) {
 	i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
 
 	if (sz == dz) {
-		return ssa_emit(proc, ssa_make_instr_conv(proc, ssaConv_bitcast, value, src, dst));
+		return ssa_emit_bitcast(proc, value, dst);
 	}
 
 
@@ -2205,6 +2213,94 @@ ssaValue *ssa_emit_down_cast(ssaProcedure *proc, ssaValue *value, Type *t) {
 	return ssa_emit_conv(proc, head, t);
 }
 
+ssaValue *ssa_emit_union_cast(ssaProcedure *proc, ssaValue *value, Type *tuple) {
+	GB_ASSERT(tuple->kind == Type_Tuple);
+	gbAllocator a = proc->module->allocator;
+
+	Type *src_type = ssa_type(value);
+	b32 is_ptr = is_type_pointer(src_type);
+
+	Type *t_bool_ptr = make_type_pointer(a, t_bool);
+	Type *t_int_ptr  = make_type_pointer(a, t_int);
+
+	ssaValue *v = ssa_add_local_generated(proc, tuple);
+
+	if (is_ptr) {
+		Type *src = base_type(type_deref(src_type));
+		Type *src_ptr = src_type;
+		GB_ASSERT(is_type_union(src));
+		Type *dst_ptr = tuple->Tuple.variables[0]->type;
+		Type *dst = type_deref(dst_ptr);
+
+		ssaValue *tag = ssa_emit_load(proc, ssa_emit_struct_gep(proc, value, v_one32, t_int_ptr));
+		ssaValue *dst_tag = NULL;
+		for (isize i = 1; i < src->Record.field_count; i++) {
+			Entity *f = src->Record.fields[i];
+			if (are_types_identical(f->type, dst)) {
+				dst_tag = ssa_make_const_int(a, i);
+				break;
+			}
+		}
+		GB_ASSERT(dst_tag != NULL);
+
+
+		ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok");
+		ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end");
+		ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag);
+		ssa_emit_if(proc, cond, ok_block, end_block);
+		proc->curr_block = ok_block;
+
+		ssaValue *gep0 = ssa_emit_struct_gep(proc, v, v_zero32, make_type_pointer(a, dst_ptr));
+		ssaValue *gep1 = ssa_emit_struct_gep(proc, v, v_one32,  t_bool_ptr);
+
+		ssaValue *data = ssa_emit_conv(proc, value, dst_ptr);
+		ssa_emit_store(proc, gep0, data);
+		ssa_emit_store(proc, gep1, v_true);
+
+		ssa_emit_jump(proc, end_block);
+		proc->curr_block = end_block;
+
+	} else {
+		Type *src = base_type(src_type);
+		GB_ASSERT(is_type_union(src));
+		Type *dst = tuple->Tuple.variables[0]->type;
+		Type *dst_ptr = make_type_pointer(a, dst);
+
+		ssaValue *tag = ssa_emit_struct_ev(proc, value, 1, t_int);
+		ssaValue *dst_tag = NULL;
+		for (isize i = 1; i < src->Record.field_count; i++) {
+			Entity *f = src->Record.fields[i];
+			if (are_types_identical(f->type, dst)) {
+				dst_tag = ssa_make_const_int(a, i);
+				break;
+			}
+		}
+		GB_ASSERT(dst_tag != NULL);
+
+		// HACK(bill): This is probably not very efficient
+		ssaValue *union_copy = ssa_add_local_generated(proc, src_type);
+		ssa_emit_store(proc, union_copy, value);
+
+		ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok");
+		ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end");
+		ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag);
+		ssa_emit_if(proc, cond, ok_block, end_block);
+		proc->curr_block = ok_block;
+
+		ssaValue *gep0 = ssa_emit_struct_gep(proc, v, v_zero32, dst_ptr);
+		ssaValue *gep1 = ssa_emit_struct_gep(proc, v, v_one32, t_bool_ptr);
+
+		ssaValue *data = ssa_emit_load(proc, ssa_emit_conv(proc, union_copy, dst_ptr));
+		ssa_emit_store(proc, gep0, data);
+		ssa_emit_store(proc, gep1, v_true);
+
+		ssa_emit_jump(proc, end_block);
+		proc->curr_block = end_block;
+
+	}
+	return ssa_emit_load(proc, v);
+}
+
 void ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block);
 
 ssaValue *ssa_emit_logical_binary_expr(ssaProcedure *proc, AstNode *expr) {
@@ -2325,6 +2421,7 @@ ssaValue *ssa_find_implicit_value_backing(ssaProcedure *proc, ImplicitValueId id
 
 
 ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) {
+	expr = unparen_expr(expr);
 	switch (expr->kind) {
 	case_ast_node(bl, BasicLit, expr);
 		GB_PANIC("Non-constant basic literal");
@@ -2350,13 +2447,17 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 			if (v->kind == ssaValue_Proc) {
 				return v;
 			}
+			// if (e->kind == Entity_Variable && e->Variable.param) {
+				// return v;
+			// }
 			return ssa_emit_load(proc, v);
 		}
 		return NULL;
 	case_end;
 
-	case_ast_node(pe, ParenExpr, expr);
-		return ssa_build_single_expr(proc, unparen_expr(expr), tv);
+	case_ast_node(re, RunExpr, expr);
+		// TODO(bill): Run Expression
+		return ssa_build_single_expr(proc, re->expr, tv);
 	case_end;
 
 	case_ast_node(de, DerefExpr, expr);
@@ -2364,6 +2465,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 	case_end;
 
 	case_ast_node(se, SelectorExpr, expr);
+		TypeAndValue *tav = map_get(&proc->module->info->types, hash_pointer(expr));
+		GB_ASSERT(tav != NULL);
 		return ssa_lvalue_load(proc, ssa_build_addr(proc, expr));
 	case_end;
 
@@ -2441,6 +2544,10 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 			ssa_emit_comment(proc, make_string("cast - down_cast"));
 			return ssa_emit_down_cast(proc, ssa_build_expr(proc, be->left), tv->type);
 
+		case Token_union_cast:
+			ssa_emit_comment(proc, make_string("cast - union_cast"));
+			return ssa_emit_union_cast(proc, ssa_build_expr(proc, be->left), tv->type);
+
 		default:
 			GB_PANIC("Invalid binary expression");
 			break;

+ 2 - 1
src/gb/gb.h

@@ -1,4 +1,4 @@
-/* gb.h - v0.26c - Ginger Bill's C Helper Library - public domain
+/* gb.h - v0.26d - Ginger Bill's C Helper Library - public domain
                  - no warranty implied; use at your own risk
 
 	This is a single header file with a bunch of useful stuff
@@ -58,6 +58,7 @@ TODOS
 	- More date & time functions
 
 VERSION HISTORY
+	0.26d - Minor changes to how gbFile works
 	0.26c - gb_str_to_f* fix
 	0.26b - Minor fixes
 	0.26a - gbString Fix

+ 3 - 1
src/main.cpp

@@ -26,6 +26,7 @@ i32 win32_exec_command_line_app(char *name, char *fmt, ...) {
 	va_start(va, fmt);
 	cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
 	va_end(va);
+	// gb_printf("%.*s\n", cast(int)cmd_len, cmd_line);
 
 	tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
 	defer (gb_temp_arena_memory_end(tmp));
@@ -173,7 +174,7 @@ int main(int argc, char **argv) {
 		return exit_code;
 	}
 
-
+	#if 1
 	// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
 	exit_code = win32_exec_command_line_app("llvm-llc",
 		"%.*sbin/llc %.*s.bc -filetype=obj -O%d "
@@ -214,6 +215,7 @@ int main(int argc, char **argv) {
 	if (run_output) {
 		win32_exec_command_line_app("odin run", "%.*s.exe", cast(int)base_name_len, output_name);
 	}
+	#endif
 #endif
 #endif
 #endif

+ 30 - 8
src/parser.cpp

@@ -122,6 +122,7 @@ AstNodeArray make_ast_node_array(AstFile *f) {
 AST_NODE_KIND(_ExprBegin,  "",  struct{}) \
 	AST_NODE_KIND(BadExpr,      "bad expression",         struct { Token begin, end; }) \
 	AST_NODE_KIND(TagExpr,      "tag expression",         struct { Token token, name; AstNode *expr; }) \
+	AST_NODE_KIND(RunExpr,      "run expression",         struct { Token token, name; AstNode *expr; }) \
 	AST_NODE_KIND(UnaryExpr,    "unary expression",       struct { Token op; AstNode *expr; }) \
 	AST_NODE_KIND(BinaryExpr,   "binary expression",      struct { Token op; AstNode *left, *right; } ) \
 	AST_NODE_KIND(ParenExpr,    "parentheses expression", struct { AstNode *expr; Token open, close; }) \
@@ -376,6 +377,8 @@ Token ast_node_token(AstNode *node) {
 		return node->CompoundLit.open;
 	case AstNode_TagExpr:
 		return node->TagExpr.token;
+	case AstNode_RunExpr:
+		return node->RunExpr.token;
 	case AstNode_BadExpr:
 		return node->BadExpr.begin;
 	case AstNode_UnaryExpr:
@@ -512,6 +515,15 @@ AstNode *make_tag_expr(AstFile *f, Token token, Token name, AstNode *expr) {
 	return result;
 }
 
+AstNode *make_run_expr(AstFile *f, Token token, Token name, AstNode *expr) {
+	AstNode *result = make_node(f, AstNode_RunExpr);
+	result->RunExpr.token = token;
+	result->RunExpr.name = name;
+	result->RunExpr.expr = expr;
+	return result;
+}
+
+
 AstNode *make_tag_stmt(AstFile *f, Token token, Token name, AstNode *stmt) {
 	AstNode *result = make_node(f, AstNode_TagStmt);
 	result->TagStmt.token = token;
@@ -1352,9 +1364,9 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
 	}
 
 	case Token_Hash: {
-		operand = parse_tag_expr(f, NULL);
-		String name = operand->TagExpr.name.string;
-		if (name == "rune") {
+		Token token = expect_token(f, Token_Hash);
+		Token name  = expect_token(f, Token_Identifier);
+		if (name.string == "rune") {
 			if (f->curr_token.kind == Token_String) {
 				Token *s = &f->curr_token;
 
@@ -1366,20 +1378,28 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
 				expect_token(f, Token_String);
 			}
 			operand = parse_operand(f, lhs);
-		} else if (name == "file") {
-			Token token = operand->TagExpr.name;
+		} else if (name.string == "file") {
+			Token token = name;
 			token.kind = Token_String;
 			token.string = token.pos.file;
 			return make_basic_lit(f, token);
-		} else if (name == "line") {
-			Token token = operand->TagExpr.name;
+		} else if (name.string == "line") {
+			Token token = name;
 			token.kind = Token_Integer;
 			char *str = gb_alloc_array(gb_arena_allocator(&f->arena), char, 20);
 			gb_i64_to_str(token.pos.line, str, 10);
 			token.string = make_string(str);
 			return make_basic_lit(f, token);
+		} else if (name.string == "run") {
+			AstNode *expr = parse_expr(f, false);
+			operand = make_run_expr(f, token, name, expr);
+			if (unparen_expr(expr)->kind != AstNode_CallExpr) {
+				error(ast_node_token(expr), "#run can only be applied to procedure calls");
+				operand = make_bad_expr(f, token, f->curr_token);
+			}
+			warning(token, "#run is not yet implemented");
 		} else {
-			operand->TagExpr.expr = parse_expr(f, false);
+			operand = make_tag_expr(f, token, name, parse_expr(f, false));
 		}
 		return operand;
 	}
@@ -1658,6 +1678,7 @@ i32 token_precedence(Token t) {
 	case Token_as:
 	case Token_transmute:
 	case Token_down_cast:
+	case Token_union_cast:
 		return 7;
 	}
 
@@ -1705,6 +1726,7 @@ AstNode *parse_binary_expr(AstFile *f, b32 lhs, i32 prec_in) {
 			case Token_as:
 			case Token_transmute:
 			case Token_down_cast:
+			case Token_union_cast:
 				right = parse_type(f);
 				break;
 

+ 6 - 3
src/tokenizer.cpp

@@ -30,9 +30,10 @@ TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \
 	TOKEN_KIND(Token_Shl, "<<"), \
 	TOKEN_KIND(Token_Shr, ">>"), \
 \
-	TOKEN_KIND(Token_as, "as"), \
-	TOKEN_KIND(Token_transmute, "transmute"), \
-	TOKEN_KIND(Token_down_cast, "down_cast"), \
+	TOKEN_KIND(Token_as,         "as"), \
+	TOKEN_KIND(Token_transmute,  "transmute"), \
+	TOKEN_KIND(Token_down_cast,  "down_cast"), \
+	TOKEN_KIND(Token_union_cast, "union_cast"), \
 \
 	TOKEN_KIND(Token_Prime, "'"), \
 	TOKEN_KIND(Token_DoublePrime, "''"), \
@@ -635,6 +636,8 @@ Token tokenizer_get_token(Tokenizer *t) {
 				token.kind = Token_transmute;
 			} else if (token.string == token_strings[Token_down_cast]) {
 				token.kind = Token_down_cast;
+			} else if (token.string == token_strings[Token_union_cast]) {
+				token.kind = Token_union_cast;
 			} else {
 				for (i32 k = Token__KeywordBegin+1; k < Token__KeywordEnd; k++) {
 					if (token.string == token_strings[k]) {