G

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

AuthorJakob Wakeling <[email protected]>
Date2023-06-29 05:38:34
Commit6a79f8726e30559134fbd692dff366db7dd748d1
Parentf718ad0837fb154deaacf843c679f8b6975d3e50

Implement arrays, integrate values into AST nodes

Diffstat

M README.md | 6 ++++--
M examples/hello.g | 16 +---------------
M src/init.c | 2 ++
M src/lex.c | 19 ++++++++++++++-----
M src/lex.h | 4 ++--
M src/llvm.c | 90 +++++++++++++++++++++++++++++++++++++++++++++----------------------------------
M src/parse.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
M src/parse.h | 9 +++++----
M src/symbol.c | 11 ++++++++++-
M src/symbol.h | 1 -
M src/type.c | 12 ++++++++----
M src/type.h | 7 ++-----
D src/value.c | 50 --------------------------------------------------
D src/value.h | 23 -----------------------

14 files changed, 187 insertions, 193 deletions

diff --git a/README.md b/README.md
index ca35bcf..164afd1 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,8 @@ command. The second command will output an executable file, *a.out* by default.
 
 ## Todo
 
-> Not all todo items will necesarilly be implemented
+> Not all todo items will necesarilly be implemented and sometimes I am pretty
+> liberal with what justifies crossing something off
 
 - [x] Implement procedure declarations
 - [x] Implement procedure calls
@@ -65,7 +66,8 @@ command. The second command will output an executable file, *a.out* by default.
 - [x] Implement integers
 - [x] Implement reals
 - [x] Implement pointers
-- [ ] Implement arrays
+- [x] Implement arrays
+- [ ] Implement variable length arrays
 - [x] Implement expressions
 - [x] Implement type casting
 - [ ] Implement type casting to pointers and arrays
diff --git a/examples/hello.g b/examples/hello.g
index 70b57ac..3525d82 100644
--- a/examples/hello.g
+++ b/examples/hello.g
@@ -1,20 +1,5 @@
 main :: proc() -> u64 {
-	c : u8;
-	
-	c = u8('H'); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8('e'); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8('l'); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8('l'); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8('o'); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8(','); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8(' '); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8('W'); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8('o'); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8('r'); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8('l'); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8('d'); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8('!'); #syscall(u64(1), u64(1), &c, u64(1));
-	c = u8('\n'); #syscall(u64(1), u64(1), &c, u64(1));
-	
+	hello : [14]u8 = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\n' };
+	#syscall(u64(1), u64(1), hello, u64(14));
 	return u64(0);
 }
diff --git a/src/init.c b/src/init.c
index c9587b5..1b82d1d 100644
--- a/src/init.c
+++ b/src/init.c
@@ -11,6 +11,8 @@
 #include <string.h>
 
 static ast kwds[] = {
+	{ AK_TYPE, 0, 0, 0, "ptr",  &TYPE(TY_PTR),  { 0 }, NULL },
+	
 	/* Boolean Types */
 	{ AK_TYPE, 0, 0, 0, "b8",   &TYPE(TY_B8),   { 0 }, NULL },
 	{ AK_TYPE, 0, 0, 0, "b16",  &TYPE(TY_B16),  { 0 }, NULL },
diff --git a/src/lex.c b/src/lex.c
index c27fc8c..00352bc 100644
--- a/src/lex.c
+++ b/src/lex.c
@@ -16,7 +16,7 @@
 #include <stdio.h>
 
 char *tok_ks[] = {
-	"TK_VOID", "TK_EOF", "TK_ID", "TK_INT", "TK_NUM", "TK_STR", "TK_HASH",
+	"TK_VOID", "TK_EOF", "TK_ID", "TK_INT", "TK_FLT", "TK_STR", "TK_HASH",
 
 	"TK_NULL", "TK_TRUE", "TK_FALSE", "TK_RETURN", "TK_IF", "TK_ELSE", "TK_FOR", "TK_PROC",
 
@@ -128,13 +128,17 @@ tok lex_next(lex *l) {
 		T.k = TK_INT; char *s = P;
 
 		for (P += 1; is_alpha(C) || is_digit_dec(C); P += 1);
-		if (C == '.') { T.k = TK_NUM; P += 1; for (P += 1; is_digit_dec(C); P += 1); }
+		if (C == '.') { T.k = TK_FLT; P += 1; for (P += 1; is_digit_dec(C); P += 1); }
 		/* TODO lex exponent part of real numbers */
 
 		UINT sl = P - s; CL += sl;
 
 		if (!(T.s = strndup(s, sl))) { error(1, SERR); }
-		if (T.k == TK_INT) { T.v_int = parse_int(T.s); }
+		
+		switch (T.k) {
+		case TK_INT: { T.v_int = parse_int(T.s); } break;
+		case TK_FLT: { T.v_flt = parse_flt(T.s); } break;
+		}
 	}
 
 	/* Handle hash procedures */
@@ -327,5 +331,11 @@ static inline u128 parse_int(char *s) {
 }
 
 static inline f128 parse_flt(char *s) {
-	return 0.0;
+	register f128 v = 0; u64 c; char *endptr;
+	
+	v = strtold(s, &endptr);
+	/* TODO better error handling */
+	if (*endptr != '\0') { return 0; }
+	
+	return v;
 }
diff --git a/src/lex.h b/src/lex.h
index 0f22a2e..770d35a 100644
--- a/src/lex.h
+++ b/src/lex.h
@@ -10,7 +10,7 @@
 
 /* Remember to update tok_ks in lex.c */
 typedef enum {
-	TK_VOID, TK_EOF, TK_ID, TK_INT, TK_NUM, TK_STR, TK_HASH,
+	TK_VOID, TK_EOF, TK_ID, TK_INT, TK_FLT, TK_STR, TK_HASH,
 
 	TK_NULL, TK_TRUE, TK_FALSE, TK_RETURN, TK_IF, TK_ELSE, TK_FOR, TK_PROC,
 
@@ -30,7 +30,7 @@ typedef enum {
 	k : Kind, ln : Line, cl : Column, h : Hash, s : String,
 	v_int : Int Value, v_flt : Flt Value
 */
-typedef struct { tok_k k; UINT ln, cl; u64 h; char *s; union { u64 v_int; f128 v_flt; }; } tok;
+typedef struct { tok_k k; UINT ln, cl; u64 h; char *s; union { u128 v_int; f128 v_flt; }; } tok;
 typedef struct { tok *a; UINT al; } tok_a;
 
 /*
diff --git a/src/llvm.c b/src/llvm.c
index b3e5e02..852823b 100644
--- a/src/llvm.c
+++ b/src/llvm.c
@@ -40,9 +40,7 @@ static LLVMValueRef llvm_expr(ast *a, syt *st);
 static LLVMValueRef llvm_expr_proc(ast *a, syt *st);
 static LLVMValueRef llvm_expr_cast(ast *a, syt *st);
 
-static LLVMValueRef llvm_bool(ast *a);
-static LLVMValueRef llvm_int(ast *a);
-static LLVMValueRef llvm_flt(ast *a);
+static LLVMValueRef llvm_arr(ast *a, syt *st);
 
 static LLVMValueRef llvm_hash(ast *a, syt *st);
 
@@ -143,26 +141,26 @@ static LLVMValueRef llvm_stmt_compound(ast *a, syt *st) {
 static LLVMValueRef llvm_stmt_decl(ast *a, syt *st) {
 	assert(a->k == AK_DECL);
 
-	if (a->c.al && C[0]->k == AK_PROC) {
+	if (CL && C[0]->k == AK_PROC) {
 		/* TODO handle procedure arguments if present */
 		LLVMTypeRef ft = LLVMFunctionType(llvm_type(C[0]->t), NULL, 0, 0);
-		LLVMValueRef f = LLVMAddFunction(llvm_module, a->s, ft);
+		LLVMValueRef f = LLVMAddFunction(llvm_module, A.s, ft);
 
 		LLVMBasicBlockRef bb = LLVMAppendBasicBlock(f, "entry");
 		LLVMPositionBuilderAtEnd(llvm_builder, bb);
 
-		llvm_expr_proc(C[0], st); a->llvm_t = ft; a->llvm_v = f;
+		llvm_expr_proc(C[0], st); A.llvm_t = ft; A.llvm_v = f;
 	}
 	else {
-		if (a->p->k == AK_PROG) { /* Global */
+		if (A.p->k == AK_PROG) { /* Global */
 			LLVMValueRef v = LLVMAddGlobal(llvm_module, llvm_type(A.t), "");
-			LLVMSetInitializer(v, a->c.al ? llvm_expr(C[0], st) : llvm_ival(a->t));
-			a->llvm_v = v;
+			LLVMSetInitializer(v, CL ? llvm_expr(C[0], st) : llvm_ival(A.t));
+			A.llvm_v = v;
 		}
 		else { /* Local */
 			LLVMValueRef v = LLVMBuildAlloca(llvm_builder, llvm_type(A.t), "");
-			LLVMBuildStore(llvm_builder, a->c.al ? llvm_expr(C[0], st) : llvm_ival(a->t), v);
-			a->llvm_v = v;
+			LLVMBuildStore(llvm_builder, CL ? llvm_expr(C[0], st) : llvm_ival(A.t), v);
+			A.llvm_v = v;
 		}
 	}
 
@@ -238,16 +236,19 @@ static LLVMValueRef llvm_stmt_for(ast *a, syt *st) {
 /* Generate IR for an expression. */
 static LLVMValueRef llvm_expr(ast *a, syt *st) {
 	reset:; switch (A.k) {
+	case AK_BOOL: { return LLVMConstInt(llvm_type(a->t), a->v_bool, false); } break;
+	case AK_INT:  { return LLVMConstInt(llvm_type(a->t), a->v_int, false);  } break;
+	case AK_FLT:  { return LLVMConstReal(llvm_type(a->t), a->v_flt);        } break;
+	case AK_ARR:  { return llvm_arr(a, st);       } break;
 	case AK_PROC: { return llvm_expr_proc(a, st); } break;
 	case AK_CAST: { return llvm_expr_cast(a, st); } break;
-	case AK_BOOL: { return llvm_bool(a); } break;
-	case AK_INT:  { return llvm_int(a); } break;
-	case AK_FLT:  { return llvm_flt(a); } break;
 	case AK_ID_VAR: {
-		ast *v = syt_search(st, a->s);
-		if (v == NULL) { note(file_name, A.ln, A.cl, 0, "Undefined variable %s", A.s); }
+		ast *sym = syt_search(st, a->s);
+		if (sym == NULL) { note(file_name, A.ln, A.cl, 0, "Undefined variable %s", A.s); }
+		
+		if (sym->t->k == TY_ARR) { return sym->llvm_v; }
 
-		return LLVMBuildLoad2(llvm_builder, llvm_type(v->t), v->llvm_v, "");
+		return LLVMBuildLoad2(llvm_builder, llvm_type(sym->t), sym->llvm_v, "");
 	} break;
 	case AK_CALL: {
 		ast *v = syt_search(st, a->s);
@@ -360,23 +361,25 @@ static LLVMValueRef llvm_expr_cast(ast *a, syt *st) {
 			}
 		}
 	}
+	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[0], st), llvm_type(a->t), "");
+		}
+	}
 
 	note(file_name, A.ln, A.cl, -1, "unhandled type %s or %s (llvm:llvm_expr_cast)", expr_type->s, a->t->s);
 }
 
-/* Generate IR for a boolean. */
-static LLVMValueRef llvm_bool(ast *a) {
-	return LLVMConstInt(llvm_type(a->t), a->v.v_bool, false);
-}
-
-/* Generate IR for an integer. */
-static LLVMValueRef llvm_int(ast *a) {
-	return LLVMConstInt(llvm_type(a->t), a->v.v_int, false);
-}
-
-/* Generate IR for a real. */
-static LLVMValueRef llvm_flt(ast *a) {
-	return LLVMConstReal(llvm_type(a->t), a->v.v_flt);
+/* Generate IR for an array. */
+static LLVMValueRef llvm_arr(ast *a, syt *st) {
+	assert(A.k == AK_ARR);
+	
+	LLVMValueRef *va = calloc(CL, sizeof (LLVMValueRef));
+	
+	for (UINT i = 0; i < CL; i += 1) { va[i] = llvm_expr(C[i], st); }
+	
+	return LLVMConstArray(llvm_type(C[0]->t), va, CL);
 }
 
 /* Generate IR for a hash procedure. */
@@ -442,7 +445,8 @@ static inline void llvm_free(void) {
 static LLVMTypeRef llvm_type(type *t) {
 	switch (t->k) {
 	case TY_VOID: { return LLVMVoidType();  } break;
-	case TY_PTR:  { return LLVMPointerType(llvm_type(t->base), 0); } break;
+	case TY_PTR:  { return LLVMPointerType(llvm_type(t->base), 0);  } break;
+	case TY_ARR:  { return LLVMArrayType(llvm_type(t->base), t->l); } break;
 	case TY_BOOL: { return LLVMIntType(8);  } break;
 	case TY_B8:   { return LLVMIntType(8);  } break;
 	case TY_B16:  { return LLVMIntType(16); } break;
@@ -468,6 +472,14 @@ static LLVMTypeRef llvm_type(type *t) {
 static LLVMValueRef llvm_ival(type *t) {
 	switch (t->k) {
 	case TY_PTR:  { return LLVMConstNull(llvm_type(t->base)); } break;
+	case TY_ARR:  {
+		LLVMValueRef *va = calloc(t->l, sizeof (LLVMValueRef));
+		LLVMValueRef bv = llvm_ival(t->base);
+		
+		for (UINT i = 0; i < t->l; i += 1) { va[i] = bv; }
+		
+		return LLVMConstArray(llvm_type(t->base), va, t->l);
+	} break;
 	case TY_BOOL: { return LLVMConstInt(LLVMIntType(8),   0, false); } break;
 	case TY_B8:   { return LLVMConstInt(LLVMIntType(8),   0, false); } break;
 	case TY_B16:  { return LLVMConstInt(LLVMIntType(16),  0, false); } break;
diff --git a/src/parse.c b/src/parse.c
index 433a092..5d80fe5 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -10,12 +10,13 @@
 #include "type.h"
 #include "util/error.h"
 #include "util/util.h"
-#include "value.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+typedef struct { type *t; ast *a; } ta_pair;
+
 char *ast_ks[] = {
 	"AK_VOID", "AK_PROG", "AK_PROC", "AK_TYPE", "AK_CAST",
 
@@ -26,7 +27,7 @@ char *ast_ks[] = {
 
 	"AK_ASSIGN", "AK_AS_ADD", "AK_AS_SUB", "AK_AS_MUL", "AK_AS_DIV", "AK_AS_MOD",
 
-	"AK_ID_VAR", "AK_CALL", "AK_INT", "AK_FLT", "AK_BOOL",
+	"AK_ID_VAR", "AK_CALL", "AK_BOOL", "AK_INT", "AK_FLT", "AK_ARR",
 
 	"AK_HASH_SYSCALL"
 };
@@ -40,9 +41,10 @@ static ast *parse_stmt_if(lex *l, syt *st);
 static ast *parse_stmt_for(lex *l, syt *st);
 
 static ast *parse_expr(lex *l, syt *st, s32 o);
+static ast *parse_expr_compound(lex *l, syt *st);
 static ast *parse_expr_proc(lex *l, syt *st);
 
-static inline type *parse_type(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);
@@ -57,7 +59,9 @@ inline ast *ast_init(ast_k kind, UINT ln, UINT cl) {
 }
 
 inline void ast_free(ast **a) {
-	fprintf(stderr, "ast_free() NOT IMPLEMENTED\n"); /* TODO */
+	if ((*a)->s != NULL) { free((*a)->s); }
+	if ((*a)->c.a != NULL) { free((*a)->c.a); }
+	free(*a); a = NULL; /* TODO free LLVM pointers? */
 }
 
 /* Push a child AST node to an AST node. */
@@ -66,6 +70,7 @@ void ast_push(ast *a, ast *c) {
 	if (!ca) { error(1, SERR); } else { a->c.a = ca; ca = NULL; }
 
 	/* Store pointer from parent to child and vice versa */
+	assert(c->p == NULL);
 	c->p = a; a->c.a[a->c.al - 1] = c;
 }
 
@@ -83,7 +88,7 @@ void ast_displace(ast *a, ast *c) {
 		if (oa[i] == c) { continue; } a->c.a[j] = oa[i]; j += 1;
 	}
 
-	free(oa);
+	c->p = NULL; free(oa);
 }
 
 type *ast_type(ast *a, syt *st) {
@@ -165,7 +170,10 @@ static ast *parse_stmt_decl(lex *l, syt *st) {
 	assert(T.k == TK_ID); lex_next(l); assert(T.k == TK_COLON); lex_next(l);
 
 	/* Store the declaration's type if one is specified */
-	if (T.k == TK_ID || T.k == TK_OP_MUL || T.k == TK_LBRACK) { a->t = parse_type(l, st); }
+	if (T.k == TK_ID || T.k == TK_OP_MUL || T.k == TK_LBRACK) {
+		ta_pair ta = parse_type(l, st);
+		a->t = ta.t; /* TODO keep the AST component */
+	}
 
 	/* Assign a constant or variable value if one is specified */
 	if (T.k == TK_COLON || T.k == TK_ASSIGN) { lex_next(l); ast_push(a, parse_expr(l, st, 0)); }
@@ -253,12 +261,13 @@ static ast *parse_expr(lex *l, syt *st, s32 o) {
 
 		if (!(left->s = strdup(t.s))) { error(1, "%s", SERR); }
 	} break;
-	case TK_TRUE:   { left = ast_init(AK_BOOL, T.ln, T.cl); left->v = val_bool(true);  } goto boolean;
-	case TK_FALSE:  { left = ast_init(AK_BOOL, T.ln, T.cl); left->v = val_bool(false); } goto boolean;
+	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_NUM:    { left = parse_flt(l, st);       } break;
-	case TK_PROC:   { return parse_expr_proc(l, st); } break;
+	case TK_INT:    { left = parse_int(l, st);           } break;
+	case TK_FLT:    { left = parse_flt(l, st);           } break;
+	case TK_LBRACE: { return parse_expr_compound(l, st); } break;
+	case TK_PROC:   { return parse_expr_proc(l, st);     } break;
 	case TK_HASH: {
 		tok t = lex_next(l); bool needs_args = false;
 		left = ast_init(0, t.ln, t.cl);
@@ -308,6 +317,20 @@ static ast *parse_expr(lex *l, syt *st, s32 o) {
 	return left;
 }
 
+/* Parse a compound expression (arrays, structs, etc.). */
+static ast *parse_expr_compound(lex *l, syt *st) {
+	assert(T.k == TK_LBRACE);
+	
+	ast *a = ast_init(AK_ARR, T.ln, T.cl); lex_next(l);
+	
+	if (T.k != TK_RBRACE) for (;;) {
+		ast_push(a, parse_expr(l, st, 0));
+		if (T.k == TK_COMMA) { lex_next(l); } else { break; }
+	}
+	
+	lex_kind(l, TK_RBRACE); return a;
+}
+
 /* Parse a procedure expression. */
 static ast *parse_expr_proc(lex *l, syt *st) {
 	assert(T.k == TK_PROC);
@@ -319,15 +342,21 @@ static ast *parse_expr_proc(lex *l, syt *st) {
 	/* TODO */ lex_kind(l, TK_RPAREN);
 
 	/* Parse optional procedure return type */
-	if (T.k == TK_RARROW) { lex_next(l); a->t = parse_type(l, st); }
+	if (T.k == TK_RARROW) {
+		lex_next(l);
+		
+		ta_pair ta = parse_type(l, st);
+		a->t = ta.t; /* TODO handle AST component */
+	}
 	else { a->t = &TYPE(TY_VOID); }
 
 	ast_push(a, parse_stmt_compound(l, st)); return a;
 }
 
 /* Parse a type identifier. */
-static inline type *parse_type(lex *l, syt *st) {
-	type *r = NULL, *c; /* root and deepest child */
+static inline ta_pair parse_type(lex *l, syt *st) {
+	/* root, deepest child, and variable length */
+	type *r = NULL, *c; ast *a = NULL;
 
 	/* Parse optional pointer and array specifiers */
 	for (register type *t = NULL;;) switch (T.k) {
@@ -335,7 +364,13 @@ static inline type *parse_type(lex *l, syt *st) {
 	case TK_LBRACK: {
 		lex_next(l);
 
-		/* TODO parse integer expression */
+		/* TODO handle variable array length */
+		if (T.k != TK_RBRACK) {
+			assert(T.k == TK_INT);
+			a = parse_expr(l, st, 0);
+			t = type_arr(NULL, a->v_int);
+		}
+		else { t = type_arr(NULL, -1); }
 
 		lex_kind(l, TK_RBRACK);
 	} goto store;
@@ -348,7 +383,7 @@ static inline type *parse_type(lex *l, syt *st) {
 	if (s == NULL) { note(l->n, t.ln, t.cl, 0, "Use of undeclared identifier \"%s\"", t.s); }
 	else if (s->k != AK_TYPE) { note(l->n, t.ln, t.cl, 0, "Expected type identifier"); }
 
-	if (r == NULL) { return s->t; } else { c->base = s->t; return r; }
+	if (r == NULL) { (ta_pair){ s->t, a }; } else { c->base = s->t; return (ta_pair){ r, a }; }
 }
 
 /* Parse an integer. */
@@ -360,34 +395,33 @@ static ast *parse_int(lex *l, syt *st) {
 
 	if (!(a->s = strdup(t.s))) { error(1, "%s", SERR); }
 
-	a->v = val_u64(t.v_int);
+	a->v_int = t.v_int;
 
 	/* Determine the minimum integer type */
-	if (a->v.v_int <= U8_MAX) { a->t = &TYPE(TY_U8); }
-	else if (a->v.v_int <= U16_MAX) { a->t = &TYPE(TY_U16); }
-	else if (a->v.v_int <= U32_MAX) { a->t = &TYPE(TY_U32); }
-	else if (a->v.v_int <= U64_MAX) { a->t = &TYPE(TY_U64); }
-	else if (a->v.v_int <= U128_MAX) { a->t = &TYPE(TY_U128); }
+	if (a->v_int <= U8_MAX) { a->t = &TYPE(TY_U8); }
+	else if (a->v_int <= U16_MAX) { a->t = &TYPE(TY_U16); }
+	else if (a->v_int <= U32_MAX) { a->t = &TYPE(TY_U32); }
+	else if (a->v_int <= U64_MAX) { a->t = &TYPE(TY_U64); }
+	else if (a->v_int <= U128_MAX) { a->t = &TYPE(TY_U128); }
 
 	return a;
 }
 
-/* Parse a real. */
+/* Parse a floating-point number. */
 static ast *parse_flt(lex *l, syt *st) {
-	assert(T.k == TK_NUM);
+	assert(T.k == TK_FLT);
 
 	tok t = lex_next(l);
 	ast *a = ast_init(AK_FLT, t.ln, t.cl);
 
 	if (!(a->s = strdup(t.s))) { error(1, "%s", SERR); }
-	if (!(a->v = val_parse_flt(t.s)).k) {
-		note(l->n, t.ln, t.cl, 0, "%s: %s", t.s, SERR);
-	}
+	
+	a->v_flt = t.v_flt;
 
 	/* Determine the minimum float type */
-	if (a->v.v_flt <= F32_MAX) { a->t = &TYPE(TY_F32); }
-	else if (a->v.v_flt <= F64_MAX) { a->t = &TYPE(TY_F64); }
-	else if (a->v.v_flt <= F128_MAX) { a->t = &TYPE(TY_F128); }
+	if (a->v_flt <= F32_MAX) { a->t = &TYPE(TY_F32); }
+	else if (a->v_flt <= F64_MAX) { a->t = &TYPE(TY_F64); }
+	else if (a->v_flt <= F128_MAX) { a->t = &TYPE(TY_F128); }
 
 	return a;
 }
@@ -396,7 +430,7 @@ static ast *parse_flt(lex *l, syt *st) {
 	Expression operator precedence:
 	8 > expression group (parenthesis), function call
 	7 >
-	6 > positive (prefix +), negative (prefix -), address-of (prefix &), dereference (prefix *)
+	6 > positive (prefix +), negative (prefix -), bitwise not (prefix ~), address-of (prefix &), dereference (prefix *)
 	5 >
 	4 > multiplication (*), division (/), modulo (%)
 	3 > addition (+), subtraction (-)
@@ -418,7 +452,7 @@ static s32 tok_precedence(tok_k tk) {
 /* Get the precedence of an AST kind. */
 static s32 ast_precedence(ast_k ak) {
 	switch (ak) {
-	case AK_OP_POS: case AK_OP_NEG: case AK_OP_ADO: case AK_OP_DRF: { return 6; }
+	case AK_OP_POS: case AK_OP_NEG: case AK_BW_NOT: case AK_OP_ADO: case AK_OP_DRF: { return 6; }
 	case AK_OP_MUL: case AK_OP_DIV: case AK_OP_MOD: { return 4; }
 	case AK_OP_ADD: case AK_OP_SUB: { return 3; }
 	case AK_ASSIGN: case AK_AS_ADD: case AK_AS_SUB: case AK_AS_MUL: case AK_AS_DIV: case AK_AS_MOD: { return 1; }
@@ -434,20 +468,25 @@ void ast_print(ast *a, UINT indent) {
 	printf("%zu:%zu: %s \"%s\"", a->ln + 1, a->cl + 1, ast_ks[a->k], a->s);
 
 	/* Print type information if present */
-	for (type *t = a->t; t != NULL; t = t->base) { printf(" -> %s", t->s); }
+	if (a->t != NULL) { fputs(" -> ", stdout); }
+	for (type *t = a->t; t != NULL; t = t->base) switch (t->k) {
+	case TY_PTR: { printf("*"); } break;
+	case TY_ARR: { t->l != -1 ? printf("[%ld]", t->l) : printf("[]"); } break;
+	default:     { printf("%s", t->s); } break;
+	}
 
 	/* Indicate presence of various fields */
 	fputs(" [", stdout);
 	fputc(a->h != 0 ? 'h' : '-', stdout);
-	fputc(a->v.k != VK_NULL ? 'v' : '-', stdout);
+	// fputc(a->v.k != VK_NULL ? 'v' : '-', stdout);
 	fputc(a->st.a != NULL ? 's' : '-', stdout);
 	fputc(']', stdout);
 
 	/* Indicate if the AST node has a value */
-	switch (a->v.k) {
-	case VK_BOOL: { printf(a->v.v_bool ? " = true" : " = false"); } break;
-	case VK_INT:  { printf(" = %lu", a->v.v_int); } break;
-	case VK_FLT:  { printf(" = %lf", a->v.v_flt); } break;
+	switch (a->k) {
+	case AK_BOOL: { printf(a->v_bool ? " = true" : " = false"); } break;
+	case AK_INT:  { printf(" = %lu", a->v_int); } break;
+	case AK_FLT:  { printf(" = %lf", a->v_flt); } break;
 	}
 
 	/* Indicate if the AST node has no parent */
diff --git a/src/parse.h b/src/parse.h
index d0ad962..672346a 100644
--- a/src/parse.h
+++ b/src/parse.h
@@ -10,7 +10,6 @@
 #include "symbol.h"
 #include "type.h"
 #include "util/util.h"
-#include "value.h"
 
 #include <llvm-c/Types.h>
 
@@ -25,18 +24,20 @@ typedef enum {
 
 	AK_ASSIGN, AK_AS_ADD, AK_AS_SUB, AK_AS_MUL, AK_AS_DIV, AK_AS_MOD,
 
-	AK_ID_VAR, AK_CALL, AK_INT, AK_FLT, AK_BOOL,
+	AK_ID_VAR, AK_CALL, AK_BOOL, AK_INT, AK_FLT, AK_ARR,
 
 	AK_HASH_SYSCALL
 } ast_k;
 
 /*
-	k : Kind, ln : Line, cl : Column, h : Hash, s : String, t : Type, v: Value
+	k : Kind, ln : Line, cl : Column, h : Hash, s : String, t : Type
 	st : Symbol Table, p : Parent, c : Children
+	v_bool : Boolean Value, v_int : Integer Value, v_flt : Float Value
 */
 typedef struct ast_s {
-	ast_k k; UINT ln, cl; u64 h; char *s; type *t; val v; syt st;
+	ast_k k; UINT ln, cl; u64 h; char *s; type *t; syt st;
 	struct ast_s *p; struct { struct ast_s **a; UINT al; } c;
+	union { bool v_bool; u128 v_int; f128 v_flt; };
 
 	LLVMTypeRef llvm_t; LLVMValueRef llvm_v;
 } ast;
diff --git a/src/symbol.c b/src/symbol.c
index 09ff20d..43838b7 100644
--- a/src/symbol.c
+++ b/src/symbol.c
@@ -160,8 +160,16 @@ ast *syt_search_h(syt *st, u64 h, char *k) {
 /* Print a basic representation of a map to stdout. */
 void syt_print(syt *st) {
 	for (UINT i = 0; i < st->ac; i += 1) if (st->a[i].h != 0) {
-		if (ast_type(st->a[i].v, st)) { printf("%s -> %s\n", st->a[i].k, ast_type(st->a[i].v, st)->s); }
-		else { printf("%s -> (null)\n", st->a[i].k); }
+		fputs(st->a[i].k, stdout);
+		
+		if (st->a[i].v->t != NULL) { fputs(" -> ", stdout); }
+		for (type *t = st->a[i].v->t; t != NULL; t = t->base) switch (t->k) {
+		case TY_PTR: { printf("*"); } break;
+		case TY_ARR: { t->l != -1 ? printf("[%ld]", t->l) : printf("[]"); } break;
+		default:     { printf("%s", t->s); } break;
+		}
+		
+		fputc('\n', stdout);
 	}
 }
 
diff --git a/src/symbol.h b/src/symbol.h
index d7b049e..5bc42f0 100644
--- a/src/symbol.h
+++ b/src/symbol.h
@@ -8,7 +8,6 @@
 
 #include "type.h"
 #include "util/util.h"
-#include "value.h"
 
 typedef struct ast_s ast;
 
diff --git a/src/type.c b/src/type.c
index 15b1293..8247b38 100644
--- a/src/type.c
+++ b/src/type.c
@@ -11,9 +11,9 @@
 type types[] = {
 	{ TY_VOID, 0,       0, "void" },
 	{ TY_PTR,  TF_PTR, -1, "ptr"  },
-	{ TY_ARR,  TF_PTR, -1, "arr"  },
+	{ TY_ARR,  0,      -1, "arr"  },
 	{ TY_TYPE, 0,      -1, "type" },
-	{ TY_AUTO, 0,      -1, "auto" },
+	{ TY_ANY,  0,      -1, "any"  },
 
 	{ TY_BOOL, TF_BOOL, 1, "bool" },
 	{ TY_B8,   TF_BOOL, 1, "b8"   },
@@ -140,7 +140,11 @@ type *type_ptr(type *base, u64 n) {
 /* Initialise a new array type. */
 type *type_arr(type *base, u64 l) {
 	types_alloc(&types_a, 1);
-	return base; /* TODO */
+	
+	type *t = types_next(&types_a);
+	*t = TYPE(TY_ARR); t->l = l; t->base = base;
+	
+	return t;
 }
 
 /* Check if a type is a pointer. */
@@ -173,7 +177,7 @@ inline bool is_equal(type *t1, type *t2) {
 	/* Check if all base types match */
 	for (type *b1 = t1->base, *b2 = t2->base;; b1 = b1->base, b2 = b2->base) {
 		if (b1 == NULL && b2 == NULL) { break; }
-		if (b1 == NULL != b2 == NULL) { return false; }
+		if ((b1 == NULL) != (b2 == NULL)) { return false; }
 
 		if (b1->k != b2->k) { return false; }
 		if (b1->f != b2->f) { return false; }
diff --git a/src/type.h b/src/type.h
index 313ecde..31f8b6f 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_AUTO,
+	TY_VOID, TY_PTR, TY_ARR, TY_TYPE, TY_ANY,
 
 	TY_BOOL, TY_B8, TY_B16, TY_B32, TY_B64,
 
@@ -53,11 +53,7 @@ typedef enum {
 } type_f;
 
 /* k : Kind, f : Flags, l : Length, s : String */
-typedef struct type_s {
-	type_k k; type_f f; s64 l; char *s;
-	struct type_s *base;
-} type;
-
+typedef struct type_s { type_k k; type_f f; s64 l; char *s; struct type_s *base; } type;
 typedef struct { type *a; UINT al, ac; } type_a;
 
 extern type types[];
diff --git a/src/value.c b/src/value.c
deleted file mode 100644
index 4c46d83..0000000
--- a/src/value.c
+++ /dev/null
@@ -1,50 +0,0 @@
-// value.c
-// Value source file for G
-// Copyright (C) 2021, Jakob Wakeling
-// All rights reserved.
-
-#include "util/util.h"
-#include "value.h"
-
-#include <errno.h>
-#include <stdlib.h>
-
-const val val_null = { VK_NULL };
-
-val val_bool(bool v) { return (val){ VK_BOOL, .v_bool = v }; }
-val val_u64(u64 v) { return (val){ VK_INT, .v_int = v }; }
-val val_f128(f128 v) { return (val){ VK_FLT, .v_flt = v }; }
-
-/* Parse an integer string into a value. */
-val val_parse_int(char *s) {
-	val v = { VK_INT, .v_int = 0 }; u64 c; UINT b = 10;
-	
-	if (s[0] == '0') switch (s[1]) {
-	case 'b': { s += 2; b = 2;  } break; case 'o': { s += 2; b = 8;  } break;
-	case 'd': { s += 2; b = 10; } break; case 'z': { s += 2; b = 12; } break;
-	case 'x': { s += 2; b = 16; } break; default:  { s += 1; } break;
-	}
-	
-	for (; s[0]; ++s) {
-		if (s[0] >= '0' && s[0] <= '9') { c = *s - '0'; }
-		else if (s[0] >= 'A' && s[0] <= 'F') { c = *s - ('A' - 10); }
-		
-		if (c >= b) { errno = EDOM; return val_null; }
-		if (v.v_int > (U64_MAX - c) / b) { errno = ERANGE; return val_null; }
-		
-		v.v_int = v.v_int * b + c;
-	}
-	
-	return v;
-}
-
-/* Parse a real string into a value. */
-/* TODO remove reliance on strtold(). */
-val val_parse_flt(char *s) {
-	val v = { VK_FLT, .v_flt = 0 }; u64 c; char *endptr;
-	
-	v.v_flt = strtold(s, &endptr);
-	if (*endptr != '\0') { return val_null; }
-	
-	return v;
-}
diff --git a/src/value.h b/src/value.h
deleted file mode 100644
index 2c74007..0000000
--- a/src/value.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// value.h
-// Value header file for G
-// Copyright (C) 2021, Jakob Wakeling
-// All rights reserved.
-
-#ifndef G_VALUE_H_X2RKXBBA
-#define G_VALUE_H_X2RKXBBA
-
-#include "util/util.h"
-
-typedef enum { VK_NULL, VK_BOOL, VK_INT, VK_FLT } val_k;
-typedef struct { val_k k; union { bool v_bool; u64 v_int; f128 v_flt; }; } val;
-
-extern const val val_null;
-
-extern val val_bool(bool v);
-extern val val_u64(u64 v);
-extern val val_f128(f128 v);
-
-extern val val_parse_int(char *s);
-extern val val_parse_flt(char *s);
-
-#endif // G_VALUE_H_X2RKXBBA