G

G Programming Language
git clone http://git.omkov.net/G
Log | Tree | Refs | README | Download

AuthorJakob Wakeling <[email protected]>
Date2023-07-14 08:49:59
Commit3c485fcedc4c3a106c6ebe5103abe4db86c2b2f2
Parent1544347804b7a223b8dc012a622ed61ae77eb068

Implicity cast procedure arguments if safe

Diffstat

A examples/Test.sh | 16 ++++++++++++++++
M examples/io.g | 8 ++++----
M examples/main.g | 4 ++--
R examples/map.g -> examples/map.g.theoretical | 0
M src/analyse.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
M src/init.c | 5 +++++
M src/llvm.c | 54 +++++++++++++++++++++++-------------------------------
M src/main.c | 1 +
M src/parse.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
M src/parse.h | 10 +++++++---
M src/type.c | 6 +++---
M src/type.h | 4 +++-

12 files changed, 203 insertions, 94 deletions

diff --git a/examples/Test.sh b/examples/Test.sh
new file mode 100755
index 0000000..bf47ccb
--- /dev/null
+++ b/examples/Test.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+# Shell script to compile all example programs and report on failure
+
+DIR="$(dirname "$(realpath "$0")")"; cd "$DIR"
+
+for f in *.g; do
+	[ -f "$f" ] || break
+	../bin/g "$f" > /dev/null 2>&1
+	if [ $? -eq 0 ]; then
+		printf "\e[1;32m[OKAY]\e[0m $f\n"
+	else
+		printf "\e[1;31m[FAIL]\e[0m $f\n"
+	fi
+done
+
+rm *.o
diff --git a/examples/io.g b/examples/io.g
index a8bfe87..9427d7a 100644
--- a/examples/io.g
+++ b/examples/io.g
@@ -1,10 +1,10 @@
 main :: proc() -> sint {
-	file : [6]u8 = { 'g', '.', 't', 'x', 't', '\0' };
-	buf : [7]u8 = { 'O', 'u', 't', 'p', 'u', 't', '\n' };
+	file : string = "g.txt";
+	body : string = "Output\n";
 
 	/* Open file with O_WRONLY and O_CREAT flags */
-	fd : sint = #syscall(uint(2), ptr(file), s32(0b1000010), u16(0o644));
-	r : sint = #syscall(uint(1), fd, ptr(buf), uint(7));
+	fd : sint = #syscall(uint(2), cstring(file), s32(0b1000010), u16(0o644));
+	r : sint = #syscall(uint(1), fd, cstring(body), len(body));
 	#syscall(uint(3), fd);
 
 	return r;
diff --git a/examples/main.g b/examples/main.g
index 61553d8..4ae7e76 100644
--- a/examples/main.g
+++ b/examples/main.g
@@ -1,7 +1,7 @@
-add3 :: proc(x: u8, y: u8, z: u8) -> u8 {
+add3 :: proc(x: uint, y: uint, z: uint) -> uint {
 	return x + y + z;
 }
 
-main :: proc() -> u8 {
+main :: proc() -> uint {
 	return add3(1, 2, 3);
 }
diff --git a/examples/map.g b/examples/map.g.theoretical
similarity index 100%
rename from examples/map.g
rename to examples/map.g.theoretical
diff --git a/src/analyse.c b/src/analyse.c
index 0178a82..21647a8 100644
--- a/src/analyse.c
+++ b/src/analyse.c
@@ -31,7 +31,7 @@ static void analyse_expr_hash(ast *a, syt *st);
 
 /* Analyse a program. */
 void analyse(ast *a) {
-	for (UINT i = 0; i < A.c.al; i += 1) { analyse_stmt_decl(C[i], &A.st); }
+	for (UINT i = 0; i < CL; i += 1) { analyse_stmt_decl(C[i], &A.st); }
 }
 
 /* Analyse a statement. */
@@ -50,7 +50,7 @@ static void analyse_stmt(ast *a, syt *st) {
 static inline void analyse_stmt_comp(ast *a, syt *st) {
 	assert(A.k == AK_COMP);
 
-	for (UINT i = 0; i < A.c.al; i += 1) { analyse_stmt(C[i], &A.st); }
+	for (UINT i = 0; i < CL; i += 1) { analyse_stmt(C[i], &A.st); }
 }
 
 /* Analyse a declaration statement. */
@@ -61,7 +61,10 @@ static void analyse_stmt_decl(ast *a, syt *st) {
 	analyse_expr(C[0], st);
 	type *value_type = ast_type(C[0], st);
 
-	if (C[0]->k == AK_PROC) { return; /* TODO */ }
+	if (C[0]->k == AK_PROC) {
+		A.t = &TYPE(TY_PROC);
+		return; /* TODO */
+	}
 	else if (is_bool(value_type)) {
 		/* If a type has not been specified, set the type based on the value */
 		if (A.t == NULL) { A.t = C[0]->t; }
@@ -153,12 +156,21 @@ static void analyse_stmt_for(ast *a, syt *st) {
 /* Analyse an expression. */
 static void analyse_expr(ast *a, syt *st) {
 	switch (A.k) {
+	case AK_ID: {
+		ast *sy = syt_search_h(st, A.h, A.s);
+		if (sy == NULL) { note("TODO", A.ln, A.cl, 0, "Use of undeclared identifier \"%s\"", A.s); }
+		else { A.t = sy->t; }
+	} break;
+	case AK_PROC: { analyse_expr_proc(a, st); } break;
+	case AK_HASH: { analyse_expr_hash(a, st); } break;
 	case AK_CALL: {
 		assert(CL >= 1);
 		assert(C[0]->k == AK_ID);
 		assert(C[0]->h != 0);
 		assert(C[0]->s != NULL);
 
+		analyse_expr(C[0], st); type *t = C[0]->t;
+		
 		ast *sym = syt_search_h(st, C[0]->h, C[0]->s);
 		if (sym == NULL) { note("TODO", A.ln, A.cl, 0, "Use of undeclared identifier \"%s\"", C[0]->s); }
 
@@ -167,7 +179,37 @@ static void analyse_expr(ast *a, syt *st) {
 			if (CL > 2) { note("TODO", A.ln, A.cl, 0, "Type casts must only have a single argument"); }
 		}
 		else if (sym->k == AK_PROC) { A.k = AK_BUILTIN; A.t = sym->t; }
-	} goto childs;
+		
+		for (UINT i = 1; i < CL; i += 1) { analyse_expr(C[i], st); }
+		
+		if (is_proc(t)) {
+			register ast **av = sym->c.a[0]->c.a; register UINT ac = sym->c.a[0]->c.al;
+			for (UINT i = 0; i < ac - 1; i += 1) {
+				if (is_equal(C[i + 1]->t, av[i]->t)) { /* Ignored */ }
+				else if (is_com(C[i + 1]->t, av[i]->t)) {
+					if (C[i + 1]->t->l <= av[i]->t->l) {
+						ast *cast = ast_init(AK_CAST, C[i + 1]->ln, C[i + 1]->cl); cast->t = av[i]->t;
+						ast *child = C[i + 1];
+						
+						ast_wrap(a, child, cast);
+					}
+					else {
+						note(
+							"TODO", C[i + 1]->ln, C[i + 1]->cl, 0,
+							"Implicit cast from %s to %s would cause loss of information",
+							C[i + 1]->t->s, av[i]->t->s
+						);
+					}
+				}
+				else {
+					note(
+						"TODO", C[i + 1]->ln, C[i + 1]->cl, 0, "Explicit cast required from %s to %s",
+						C[i + 1]->t->s, av[i]->t->s
+					);
+				}
+			}
+		}
+	} break;
 	case AK_SUBS: {
 		assert(CL == 2);
 
@@ -175,15 +217,25 @@ static void analyse_expr(ast *a, syt *st) {
 			note("TODO", A.ln, A.cl, 0, "Expression must have integral type");
 		}
 	} break;
-	case AK_PROC:   { analyse_expr_proc(a, st);              } break;
-	case AK_HASH:   { analyse_expr_hash(a, st);              } break;
-	case AK_OP_ADO: { A.t = type_ptr(ast_type(C[0], st), 1); } goto childs;
-	case AK_OP_DRF: { A.t = ast_type(C[0], st)->base;        } goto childs;
-	childs: default: { for (UINT i = 0; i < CL; i += 1) { analyse_expr(C[i], st); }} break;
+	case AK_POS: case AK_NEG: case AK_LO_NOT: case AK_BW_NOT: {
+		analyse_expr(C[0], st); A.t = ast_type(C[0], st);
+	} break;
+	case AK_ADO: { analyse_expr(C[0], st); A.t = type_ptr(ast_type(C[0], st), 1); } break;
+	case AK_DRF: { analyse_expr(C[0], st); A.t = ast_type(C[0], st)->base;        } break;
+	case AK_ASSIGN: case AK_AS_ADD: case AK_AS_SUB: case AK_AS_MUL: case AK_AS_DIV: case AK_AS_MOD: {
+		for (UINT i = 0; i < CL; i += 1) { analyse_expr(C[i], st); } A.t = ast_type(C[0], st);
+	} break;
+	case AK_ADD: case AK_SUB: case AK_MUL: case AK_DIV: case AK_MOD: {
+		for (UINT i = 0; i < CL; i += 1) { analyse_expr(C[i], st); } A.t = ast_type(C[0], st);
+	} break;
+	case AK_EQ: case AK_NE: case AK_LT: case AK_LE: case AK_GT: case AK_GE: {
+		for (UINT i = 0; i < CL; i += 1) { analyse_expr(C[i], st); } A.t = &TYPE(TY_BOOL);
+	} break;
+	default: { for (UINT i = 0; i < CL; i += 1) { analyse_expr(C[i], st); }} break;
 	}
 }
 
-/* Analyse a procedure expression. */
+/* Analyse a procedure expression. AK_PROC has a scope. */
 static void analyse_expr_proc(ast *a, syt *st) {
 	assert(A.k == AK_PROC);
 
@@ -193,7 +245,7 @@ static void analyse_expr_proc(ast *a, syt *st) {
 	}
 
 	/* Analyse the procedure body */
-	analyse_stmt_comp(C[CL - 1], st);
+	analyse_stmt_comp(C[CL - 1], &A.st);
 }
 
 /* Analyse a hash expression. */
diff --git a/src/init.c b/src/init.c
index c3a0dc1..f6832d7 100644
--- a/src/init.c
+++ b/src/init.c
@@ -59,6 +59,11 @@ void initialise(void) {
 		kwds[i].h = syt_hash(kwds[i].s, strlen(kwds[i].s));
 		syt_insert_h(&kwt, kwds[i].h, kwds[i].s, &kwds[i]);
 	}
+	
+	/* Set the size of word sized types */
+	/* TODO don't hardcode */
+	TYPE(TY_UINT).l = 8;
+	TYPE(TY_SINT).l = 8;
 }
 
 void uninitialise(void) { syt_free(&kwt); }
diff --git a/src/llvm.c b/src/llvm.c
index 6522299..0d61033 100644
--- a/src/llvm.c
+++ b/src/llvm.c
@@ -68,7 +68,7 @@ void llvm(ast *a, char *file, llvm_action action) {
 
 	switch (action) {
 	case llvm_obj: case llvm_asm: {
-		// if (failed) { break; } /* Don't compile after failure */
+		if (failed) { exit(2); } /* Don't compile after failure */
 
 		char *triple = LLVMGetDefaultTargetTriple();
 		printf("Target: %s\n", triple);
@@ -167,15 +167,7 @@ static LLVMValueRef llvm_stmt_decl(ast *a, syt *st) {
 		}
 		else /* Local */ {
 			LLVMValueRef v = LLVMBuildAlloca(llvm_builder, llvm_type(A.t), "");
-			
-			if (A.t->k == TY_STRING) {
-				LLVMBuildMemCpy(
-					llvm_builder, v, 8, CL ? llvm_expr(C[0], st, true) : llvm_ival(A.t), 8,
-					LLVMConstInt(LLVMIntType(64), 16, false)
-				);
-			}
-			else { LLVMBuildStore(llvm_builder, CL ? llvm_expr(C[0], st, true) : llvm_ival(A.t), v); }
-			
+			LLVMBuildStore(llvm_builder, CL ? llvm_expr(C[0], st, true) : llvm_ival(A.t), v);
 			A.llvm_v = v;
 		}
 	}
@@ -286,8 +278,8 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 		else { return LLVMBuildLoad2(llvm_builder, llvm_type(ast_type(C[0], st)->base), vr, ""); }
 	}
 
-	case AK_OP_POS: { a = C[0]; goto reset; /* no-op */ } break;
-	case AK_OP_NEG: {
+	case AK_POS: { a = C[0]; goto reset; /* no-op */ } break;
+	case AK_NEG: {
 		type *t = ast_type(C[0], st);
 		if (t == NULL) { note(file_name, A.ln, A.cl, -1, "Subtree is missing a type (llvm:llvm_expr)"); }
 
@@ -295,8 +287,8 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 		else if (is_flt(t)) { return LLVMBuildFNeg(llvm_builder, llvm_expr(C[0], st, true), "neg"); }
 		else { note(file_name, A.ln, A.cl, -1, "Expression cannot be made negative (llvm:llvm_expr)"); }
 	} break;
-	case AK_OP_ADO: { return llvm_expr(C[0], st, false); } break;
-	case AK_OP_DRF: {
+	case AK_ADO: { return llvm_expr(C[0], st, false); } break;
+	case AK_DRF: {
 		type *t = ast_type(C[0], st);
 		if (t == NULL) { note(file_name, A.ln, A.cl, -1, "Child is missing a type (llvm:llvm_expr)"); }
 		if (t->base == NULL) { note(file_name, A.ln, A.cl, -1, "Cannot be dereferenced (llvm:llvm_expr)"); }
@@ -305,19 +297,19 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 		else { return LLVMBuildLoad2(llvm_builder, llvm_type(t->base), llvm_expr(C[0], st, true), ""); }
 	}
 
-	case AK_OP_ADD: {
+	case AK_ADD: {
 		if      (is_int(ast_type(C[0], st))) { return LLVMBuildAdd(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 		else if (is_flt(ast_type(C[0], st))) { return LLVMBuildFAdd(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 	} break;
-	case AK_OP_SUB: {
+	case AK_SUB: {
 		if      (is_int(ast_type(C[0], st))) { return LLVMBuildSub(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 		else if (is_flt(ast_type(C[0], st))) { return LLVMBuildFSub(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 	} break;
-	case AK_OP_MUL: {
+	case AK_MUL: {
 		if      (is_int(ast_type(C[0], st))) { return LLVMBuildMul(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 		else if (is_flt(ast_type(C[0], st))) { return LLVMBuildFMul(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 	} break;
-	case AK_OP_DIV: {
+	case AK_DIV: {
 		register type *t = ast_type(C[0], st);
 		if (is_int(t)) {
 			if (is_sign(t)) { return LLVMBuildSDiv(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
@@ -325,7 +317,7 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 		}
 		else if (is_flt(t)) { return LLVMBuildFDiv(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 	} break;
-	case AK_OP_MOD: {
+	case AK_MOD: {
 		register type *t = ast_type(C[0], st);
 		if (is_int(t)) {
 			if (is_sign(t)) { return LLVMBuildSRem(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
@@ -445,36 +437,37 @@ static LLVMValueRef llvm_expr_proc(ast *a, syt *st) {
 /* Generate IR for a type cast. */
 static LLVMValueRef llvm_expr_cast(ast *a, syt *st) {
 	assert(A.t != NULL);
-	assert(CL == 2);
+	assert(CL == 1 || CL == 2);
 
-	type *expr_type = ast_type(C[1], st);
+	ast *c = CL == 1 ? C[0] : C[1];
+	type *expr_type = ast_type(c, st);
 
 	if (is_ptr(expr_type)) {
-		if (is_int(a->t)) { return LLVMBuildPtrToInt(llvm_builder, llvm_expr(C[1], st, true), llvm_type(a->t), ""); }
+		if (is_int(a->t)) { return LLVMBuildPtrToInt(llvm_builder, llvm_expr(c, st, true), llvm_type(a->t), ""); }
 	}
 	else if (is_bool(expr_type)) {
-		if (is_int(a->t)) { return LLVMBuildIntCast2(llvm_builder, llvm_expr(C[1], st, true), llvm_type(a->t), is_sign(a->t), ""); }
+		if (is_int(a->t)) { return LLVMBuildIntCast2(llvm_builder, llvm_expr(c, st, true), llvm_type(a->t), is_sign(a->t), ""); }
 	}
 	else if (is_int(expr_type)) {
 		if (is_bool(a->t) || is_int(a->t)) {
-			return LLVMBuildIntCast2(llvm_builder, llvm_expr(C[1], st, true), llvm_type(a->t), is_sign(a->t), "");
+			return LLVMBuildIntCast2(llvm_builder, llvm_expr(c, st, true), llvm_type(a->t), is_sign(a->t), "");
 		}
 		else if (is_flt(a->t)) {
-			if (is_sign(expr_type)) { return LLVMBuildSIToFP(llvm_builder, llvm_expr(C[1], st, true), llvm_type(a->t), "stof"); }
-			else                    { return LLVMBuildUIToFP(llvm_builder, llvm_expr(C[1], st, true), llvm_type(a->t), "utof"); }
+			if (is_sign(expr_type)) { return LLVMBuildSIToFP(llvm_builder, llvm_expr(c, st, true), llvm_type(a->t), "stof"); }
+			else                    { return LLVMBuildUIToFP(llvm_builder, llvm_expr(c, st, true), llvm_type(a->t), "utof"); }
 		}
 	}
 	else if (is_flt(expr_type)) {
 		if (is_int(a->t)) {
-			if (is_sign(a->t)) { return LLVMBuildFPToSI(llvm_builder, llvm_expr(C[1], st, true), llvm_type(a->t), "ftos"); }
-			else               { return LLVMBuildFPToUI(llvm_builder, llvm_expr(C[1], st, true), llvm_type(a->t), "ftou"); }
+			if (is_sign(a->t)) { return LLVMBuildFPToSI(llvm_builder, llvm_expr(c, st, true), llvm_type(a->t), "ftos"); }
+			else               { return LLVMBuildFPToUI(llvm_builder, llvm_expr(c, st, true), llvm_type(a->t), "ftou"); }
 		}
-		else if (is_flt(a->t)) { return LLVMBuildFPCast(llvm_builder, llvm_expr(C[1], st, true), llvm_type(a->t), "cast"); }
+		else if (is_flt(a->t)) { return LLVMBuildFPCast(llvm_builder, llvm_expr(c, st, true), llvm_type(a->t), "cast"); }
 	}
 	else if (expr_type->k == TY_ARR) {
 		if (is_ptr(a->t)) {
 			a->t->base = expr_type->base;
-			return LLVMBuildBitCast(llvm_builder, llvm_expr(C[1], st, true), llvm_type(a->t), "");
+			return LLVMBuildBitCast(llvm_builder, llvm_expr(c, st, true), llvm_type(a->t), "");
 		}
 	}
 
diff --git a/src/main.c b/src/main.c
index d51477b..cfa2372 100644
--- a/src/main.c
+++ b/src/main.c
@@ -84,6 +84,7 @@ static void compile(const char * file, char *src, UINT len) {
 	else if (Bflag) { llvm(a, strdup(file), llvm_bc); }
 	else if (Sflag) { llvm(a, strdup(file), llvm_asm); }
 	else { llvm(a, strdup(file), llvm_obj); }
+	if (has_error()) { exit(1); }
 
 	end:; return;
 }
diff --git a/src/parse.c b/src/parse.c
index 71035ca..cd94f54 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -19,7 +19,9 @@
 typedef struct { type *t; ast *a; } ta_pair;
 
 char *ast_ks[] = {
-	"AK_VOID", "AK_PROG", "AK_PROC", "AK_TYPE", "AK_CAST",
+	"AK_VOID", "AK_PROG", "AK_ID", "AK_BOOL", "AK_INT", "AK_FLT", "AK_STR",
+	
+	"AK_PROC", "AK_TYPE", "AK_CAST",
 
 	"AK_COMP", "AK_DECL", "AK_RETURN", "AK_IF", "AK_FOR",
 
@@ -33,7 +35,7 @@ char *ast_ks[] = {
 
 	"AK_ASSIGN", "AK_AS_ADD", "AK_AS_SUB", "AK_AS_MUL", "AK_AS_DIV", "AK_AS_MOD",
 
-	"AK_ID", "AK_CALL", "AK_BOOL", "AK_INT", "AK_FLT", "AK_ARR", "AK_SUBS", "AK_STR",
+	"AK_CALL", "AK_ARR", "AK_SUBS",
 
 	"AK_HASH", "AK_HASH_SYSCALL", "AK_BUILTIN"
 };
@@ -53,8 +55,8 @@ static ast *parse_expr_proc(lex *l, syt *st);
 
 static inline ta_pair parse_type(lex *l, syt *st);
 
-static ast *parse_int(lex *l, syt *st);
-static ast *parse_flt(lex *l, syt *st);
+static inline ast *parse_int(lex *l, syt *st);
+static inline ast *parse_flt(lex *l, syt *st);
 
 static s32 tok_precedence(tok_k tk);
 static s32 ast_precedence(ast_k ak);
@@ -63,7 +65,7 @@ static inline char *strdup_or_fail(const char *s);
 
 /* Initialise an AST node. */
 inline ast *ast_init(ast_k kind, UINT ln, UINT cl) {
-	ast *a = calloc(1, sizeof (*a)); if (a == NULL) { error(1, SERR); }
+	ast *a = calloc(1, sizeof (*a)); if (a == NULL) { panic(SERR); }
 	a->k = kind; a->ln = ln; a->cl = cl; return a;
 }
 
@@ -79,7 +81,7 @@ void ast_push(ast *a, ast *c) {
 	assert(a != NULL); assert(c != NULL);
 
 	ast **ca = realloc(a->c.a, (a->c.al += 1) * sizeof (ast *));
-	if (!ca) { error(1, SERR); } else { a->c.a = ca; ca = NULL; }
+	if (!ca) { panic(SERR); } else { a->c.a = ca; ca = NULL; }
 
 	/* Store pointer from parent to child and vice versa */
 	assert(c->p == NULL);
@@ -91,10 +93,10 @@ void ast_displace(ast *a, ast *c) {
 	ast **oa = a->c.a; UINT ol = a->c.al; bool found = false;
 
 	for (UINT i = 0; i < ol; i += 1) { if (oa[i] == c) { found = true; break; }}
-	if (found == false) { return; }
+	if (!found) { return; }
 
 	ast **ca = calloc((a->c.al -= 1), sizeof (ast *));
-	if (!ca) { error(1, SERR); } else { a->c.a = ca; ca = NULL; }
+	if (!ca) { panic(SERR); } else { a->c.a = ca; ca = NULL; }
 
 	for (UINT i = 0, j = 0; i < ol && j < a->c.al; i += 1) {
 		if (oa[i] == c) { continue; } a->c.a[j] = oa[i]; j += 1;
@@ -103,7 +105,22 @@ void ast_displace(ast *a, ast *c) {
 	c->p = NULL; free(oa);
 }
 
+/* Insert a wrapper AST node between a child and its parent. */
+void ast_wrap(ast *const a, ast *const c, ast *const w) {
+	assert(a != NULL); assert(c != NULL); assert(w != NULL);
+	ast **oa = a->c.a; UINT ol = a->c.al, ifound; bool found = false;
+	
+	for (UINT i = 0; i < ol; i += 1) { if (oa[i] == c) { found = true; ifound = i; break; }}
+	if (!found) { panic("ast_wrap called with an unrelated parent and child"); }
+	
+	w->p = a; a->c.a[ifound] = w; c->p = NULL; ast_push(w, c);
+}
+
+/* Get the type of an AST node. */
 type *ast_type(ast *a, syt *st) {
+	/* If the given node has a type, then return that */
+	if (a->t && !is_proc(a->t)) { return a->t; }
+	
 	/* Search the symbol table for a type first */
 	if (a->s) {
 		ast *sym = syt_search(st, a->s);
@@ -113,9 +130,6 @@ type *ast_type(ast *a, syt *st) {
 		}
 	}
 
-	/* If the given node has a type, then return that */
-	if (a->t) { return a->t; }
-	
 	/* Otherwise recurse down the first child */
 	if (a->c.al) {
 		type *rt = ast_type(a->c.a[0], st);
@@ -139,6 +153,8 @@ ast *ast_a_pop(ast_a *aa) {
 }
 
 #define T (l->t) /* lex_peek equivalent */
+#define C(ast) (ast->c.a) /* AST child shorthand "C(a)[i]" */
+#define CL(ast) (ast->c.al) /* AST child array length shorthand */
 
 /* Parse a program. */
 ast *parse(lex *l) {
@@ -196,7 +212,7 @@ static ast *parse_stmt_compound(lex *l, syt *st) {
 /* Parse a declaration statement. */
 static ast *parse_stmt_decl(lex *l, syt *st, bool scolon) {
 	ast *a = ast_init(AK_DECL, T.ln, T.cl); a->h = T.h; a->s = T.s;
-	assert(T.k == TK_ID); lex_next(l); assert(T.k == TK_COLON); lex_next(l);
+	lex_kind(l, TK_ID); lex_kind(l, TK_COLON);
 
 	/* Store the declaration's type if one is specified */
 	if (T.k == TK_ID || T.k == TK_MUL || T.k == TK_LBRACK) {
@@ -208,8 +224,8 @@ static ast *parse_stmt_decl(lex *l, syt *st, bool scolon) {
 	if (T.k == TK_COLON || T.k == TK_ASSIGN) { lex_next(l); ast_push(a, parse_expr(l, st, 0)); }
 
 	/* Ensure that a type is known and consume a semicolon if one is required */
-	if (a->t == NULL && a->c.al == 0) { note(l->n, T.ln, T.cl, 0, "A declaration without a type is invalid"); }
-	if (scolon && (a->c.al < 1 || a->c.a[0]->k != AK_PROC)) { lex_kind(l, TK_SCOLON); }
+	if (a->t == NULL && CL(a) == 0) { note(l->n, T.ln, T.cl, 0, "A declaration without a type is invalid"); }
+	if (scolon && (CL(a) < 1 || C(a)[0]->k != AK_PROC)) { lex_kind(l, TK_SCOLON); }
 
 	/* Insert the new symbol and return */
 	syt_insert_h(st, a->h, a->s, a); return a;
@@ -302,11 +318,16 @@ static ast *parse_expr(lex *l, syt *st, s32 o) {
 		left = ast_init(AK_ID, T.ln, T.cl); left->h = T.h;
 		left->s = strdup_or_fail(T.s); lex_next(l);
 	} break;
-	case TK_TRUE:   { left = ast_init(AK_BOOL, T.ln, T.cl); left->v_bool = true;  } goto boolean;
-	case TK_FALSE:  { left = ast_init(AK_BOOL, T.ln, T.cl); left->v_bool = false; } goto boolean;
-	boolean: { left->t = &TYPE(TY_BOOL); lex_next(l); } break;
-	case TK_INT:    { left = parse_int(l, st);           } break;
-	case TK_FLT:    { left = parse_flt(l, st);           } break;
+	case TK_TRUE:  {
+		left = ast_init(AK_BOOL, T.ln, T.cl);
+		left->t = &TYPE(TY_BOOL); left->v_bool = true; lex_next(l);
+	} break;
+	case TK_FALSE: {
+		left = ast_init(AK_BOOL, T.ln, T.cl);
+		left->t = &TYPE(TY_BOOL); left->v_bool = false; lex_next(l);
+	} break;
+	case TK_INT: { left = parse_int(l, st); } break;
+	case TK_FLT: { left = parse_flt(l, st); } break;
 	case TK_STR: {
 		left = ast_init(AK_STR, T.ln, T.cl); left->h = T.h;
 		left->s = strdup_or_fail(T.s); left->t = &TYPE(TY_STRING); lex_next(l);
@@ -318,12 +339,12 @@ static ast *parse_expr(lex *l, syt *st, s32 o) {
 		left->h = T.h; left->s = strdup_or_fail(T.s); lex_kind(l, TK_ID);
 	} break;
 	case TK_LPAREN: { lex_next(l); left = parse_expr(l, st, 0); lex_kind(l, TK_RPAREN); } break;
-	case TK_ADD:    { left = ast_init(AK_OP_POS, T.ln, T.cl); } goto prefix;
-	case TK_SUB:    { left = ast_init(AK_OP_NEG, T.ln, T.cl); } goto prefix;
+	case TK_ADD:    { left = ast_init(AK_POS, T.ln, T.cl); } goto prefix;
+	case TK_SUB:    { left = ast_init(AK_NEG, T.ln, T.cl); } goto prefix;
 	case TK_EMARK:  { left = ast_init(AK_LO_NOT, T.ln, T.cl); } goto prefix;
 	case TK_TILDE:  { left = ast_init(AK_BW_NOT, T.ln, T.cl); } goto prefix;
-	case TK_BW_AND: { left = ast_init(AK_OP_ADO, T.ln, T.cl); } goto prefix;
-	case TK_MUL:    { left = ast_init(AK_OP_DRF, T.ln, T.cl); } goto prefix;
+	case TK_BW_AND: { left = ast_init(AK_ADO, T.ln, T.cl); } goto prefix;
+	case TK_MUL:    { left = ast_init(AK_DRF, T.ln, T.cl); } goto prefix;
 	prefix: { lex_next(l); ast_push(left, parse_expr(l, st, ast_precedence(left->k))); } break;
 	default: { note(l->n, T.ln, T.cl, 0, "Unexpected \"%s\", was expecting an expression", tok_ks[T.k]); } break;
 	}
@@ -357,17 +378,17 @@ static ast *parse_expr(lex *l, syt *st, s32 o) {
 	case TK_AS_MUL: { a = ast_init(AK_AS_MUL, T.ln, T.cl); } goto infix;
 	case TK_AS_DIV: { a = ast_init(AK_AS_DIV, T.ln, T.cl); } goto infix;
 	case TK_AS_MOD: { a = ast_init(AK_AS_MOD, T.ln, T.cl); } goto infix;
-	case TK_ADD:    { a = ast_init(AK_OP_ADD, T.ln, T.cl); } goto infix;
-	case TK_SUB:    { a = ast_init(AK_OP_SUB, T.ln, T.cl); } goto infix;
-	case TK_MUL:    { a = ast_init(AK_OP_MUL, T.ln, T.cl); } goto infix;
-	case TK_DIV:    { a = ast_init(AK_OP_DIV, T.ln, T.cl); } goto infix;
-	case TK_MOD:    { a = ast_init(AK_OP_MOD, T.ln, T.cl); } goto infix;
-	case TK_EQ:     { a = ast_init(AK_EQ,     T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
-	case TK_NE:     { a = ast_init(AK_NE,     T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
-	case TK_LT:     { a = ast_init(AK_LT,     T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
-	case TK_LE:     { a = ast_init(AK_LE,     T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
-	case TK_GT:     { a = ast_init(AK_GT,     T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
-	case TK_GE:     { a = ast_init(AK_GE,     T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_ADD: { a = ast_init(AK_ADD, T.ln, T.cl); } goto infix;
+	case TK_SUB: { a = ast_init(AK_SUB, T.ln, T.cl); } goto infix;
+	case TK_MUL: { a = ast_init(AK_MUL, T.ln, T.cl); } goto infix;
+	case TK_DIV: { a = ast_init(AK_DIV, T.ln, T.cl); } goto infix;
+	case TK_MOD: { a = ast_init(AK_MOD, T.ln, T.cl); } goto infix;
+	case TK_EQ: { a = ast_init(AK_EQ, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_NE: { a = ast_init(AK_NE, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_LT: { a = ast_init(AK_LT, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_LE: { a = ast_init(AK_LE, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_GT: { a = ast_init(AK_GT, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_GE: { a = ast_init(AK_GE, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
 	case TK_LO_AND: { a = ast_init(AK_LO_AND, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
 	case TK_LO_OR:  { a = ast_init(AK_LO_OR,  T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
 	infix: { lex_next(l); ast_push(a, left); ast_push(a, parse_expr(l, st, ast_precedence(a->k))); } break;
@@ -459,8 +480,8 @@ static inline ta_pair parse_type(lex *l, syt *st) {
 	if (r == NULL) { return (ta_pair){ s->t, a }; } else { c->base = s->t; return (ta_pair){ r, a }; }
 }
 
-/* Parse an integer. */
-static ast *parse_int(lex *l, syt *st) {
+/* Parse an integer. AK_INT is terminal. */
+static inline ast *parse_int(lex *l, syt *st) {
 	assert(T.k == TK_INT);
 
 	tok t = lex_next(l);
@@ -479,8 +500,8 @@ static ast *parse_int(lex *l, syt *st) {
 	return a;
 }
 
-/* Parse a floating-point number. */
-static ast *parse_flt(lex *l, syt *st) {
+/* Parse a floating-point number. AK_FLT is terminal. */
+static inline ast *parse_flt(lex *l, syt *st) {
 	assert(T.k == TK_FLT);
 
 	tok t = lex_next(l);
@@ -527,9 +548,9 @@ static s32 tok_precedence(tok_k tk) {
 static s32 ast_precedence(ast_k ak) {
 	switch (ak) {
 	case AK_CALL: case AK_SUBS: { return 8; }
-	case AK_OP_POS: case AK_OP_NEG: case AK_LO_NOT: case AK_BW_NOT: case AK_OP_ADO: case AK_OP_DRF: { return 7; }
-	case AK_OP_MUL: case AK_OP_DIV: case AK_OP_MOD: { return 6; }
-	case AK_OP_ADD: case AK_OP_SUB: { return 5; }
+	case AK_POS: case AK_NEG: case AK_LO_NOT: case AK_BW_NOT: case AK_ADO: case AK_DRF: { return 7; }
+	case AK_MUL: case AK_DIV: case AK_MOD: { return 6; }
+	case AK_ADD: case AK_SUB: { return 5; }
 	case AK_EQ: case AK_NE: case AK_LT: case AK_LE: case AK_GT: case AK_GE: { return 4; }
 	case AK_LO_AND: { return 3; }
 	case AK_LO_OR: { return 2; }
@@ -584,9 +605,9 @@ void ast_print(ast *a, UINT indent) {
 	for (UINT i = 0; i < a->c.al; i += 1) { ast_print(a->c.a[i], indent + 1); }
 
 	if (a->st.a != NULL) {
-		printf("--- SYT for %s \"%s\" ---\n", ast_ks[a->k], a->s);
+		printf("--- SYT for %s \"%s\"%s ---\n", ast_ks[a->k], a->s, a->st.pt == NULL ? " NO PARENT" : "");
 		syt_print(&a->st);
-		printf("--- SYT for %s \"%s\" ---\n", ast_ks[a->k], a->s);
+		printf("--- SYT for %s \"%s\"%s ---\n", ast_ks[a->k], a->s, a->st.pt == NULL ? " NO PARENT" : "");
 	}
 
 	return;
diff --git a/src/parse.h b/src/parse.h
index fc75a35..0e4716c 100644
--- a/src/parse.h
+++ b/src/parse.h
@@ -15,12 +15,14 @@
 
 /* Remember to update ast_ks in parse.c */
 typedef enum {
-	AK_VOID, AK_PROG, AK_PROC, AK_TYPE, AK_CAST,
+	AK_VOID, AK_PROG, AK_ID, AK_BOOL, AK_INT, AK_FLT, AK_STR,
+	
+	AK_PROC, AK_TYPE, AK_CAST,
 
 	AK_COMP, AK_DECL, AK_RETURN, AK_IF, AK_FOR,
 
-	AK_OP_POS, AK_OP_NEG, AK_OP_ADO, AK_OP_DRF,
-	AK_OP_ADD, AK_OP_SUB, AK_OP_MUL, AK_OP_DIV, AK_OP_MOD,
+	AK_POS, AK_NEG, AK_ADO, AK_DRF,
+	AK_ADD, AK_SUB, AK_MUL, AK_DIV, AK_MOD,
 
 	AK_EQ, AK_NE, AK_LT, AK_LE, AK_GT, AK_GE,
 
@@ -29,7 +31,7 @@ typedef enum {
 
 	AK_ASSIGN, AK_AS_ADD, AK_AS_SUB, AK_AS_MUL, AK_AS_DIV, AK_AS_MOD,
 
-	AK_ID, AK_CALL, AK_BOOL, AK_INT, AK_FLT, AK_ARR, AK_SUBS, AK_STR,
+	AK_CALL, AK_ARR, AK_SUBS,
 
 	AK_HASH, AK_HASH_SYSCALL, AK_BUILTIN
 } ast_k;
@@ -56,6 +58,7 @@ extern void ast_free(ast **a);
 
 extern void ast_push(ast *a, ast *c);
 extern void ast_displace(ast *a, ast *c);
+extern void ast_wrap(ast *const a, ast *const c, ast *const w);
 extern type *ast_type(ast *a, syt *st);
 
 extern void ast_a_push(ast_a *aa, ast *a);
diff --git a/src/type.c b/src/type.c
index 0bf3f62..3d44c1a 100644
--- a/src/type.c
+++ b/src/type.c
@@ -13,7 +13,7 @@ type types[] = {
 	{ TY_PTR,  TF_PTR, -1, "ptr"  },
 	{ TY_ARR,  0,      -1, "arr"  },
 	{ TY_TYPE, 0,      -1, "type" },
-	{ TY_ANY,  0,      -1, "any"  },
+	{ TY_PROC, 0,      -1, "proc" },
 
 	{ TY_BOOL, TF_BOOL, 1, "bool" },
 	{ TY_B8,   TF_BOOL, 1, "b8"   },
@@ -21,14 +21,14 @@ type types[] = {
 	{ TY_B32,  TF_BOOL, 4, "b32"  },
 	{ TY_B64,  TF_BOOL, 8, "b64"  },
 
-	{ TY_UINT, TF_INT, -1, "uint" },
+	{ TY_UINT, TF_INT, -2, "uint" },
 	{ TY_U8,   TF_INT,  1, "u8"   },
 	{ TY_U16,  TF_INT,  2, "u16"  },
 	{ TY_U32,  TF_INT,  4, "u32"  },
 	{ TY_U64,  TF_INT,  8, "u64"  },
 	{ TY_U128, TF_INT, 16, "u128" },
 
-	{ TY_SINT, TF_INT | TF_SIGN, -1, "sint" },
+	{ TY_SINT, TF_INT | TF_SIGN, -2, "sint" },
 	{ TY_S8,   TF_INT | TF_SIGN,  1, "s8"   },
 	{ TY_S16,  TF_INT | TF_SIGN,  2, "s16"  },
 	{ TY_S32,  TF_INT | TF_SIGN,  4, "s32"  },
diff --git a/src/type.h b/src/type.h
index 32d8296..93eeb5f 100644
--- a/src/type.h
+++ b/src/type.h
@@ -11,7 +11,7 @@
 #define TYPE(a) (types[a])
 
 typedef enum {
-	TY_VOID, TY_PTR, TY_ARR, TY_TYPE, TY_ANY,
+	TY_VOID, TY_PTR, TY_ARR, TY_TYPE, TY_PROC,
 
 	TY_BOOL, TY_B8, TY_B16, TY_B32, TY_B64,
 
@@ -71,4 +71,6 @@ extern bool is_sign(type *t);
 extern bool is_equal(type *t1, type *t2);
 extern bool is_com(type *t1, type *t2);
 
+#define is_proc(t) (t->k == TY_PROC)
+
 #endif // G_TYPE_H_QHH0TJJQ