G

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

AuthorJakob Wakeling <[email protected]>
Date2024-01-05 11:41:03
Commitce98c7aaef741ab5cda0560f446e77a34f718822
Parent37bab1883823d3f70471b53d3e0f545bca5644e4

Allow setting version via environment variable

Diffstat

M CMakeLists.txt | 4 ++--
M src/analyse.c | 15 +++++++++------
M src/init.c | 54 ++++++++++++++++++++++++++----------------------------
M src/llvm.c | 86 +++++++++++++++++++++++++++++++++++++++----------------------------------------
M src/llvm/attr.c | 2 +-
M src/log.c | 2 +-
M src/log.h | 2 +-
M src/main.c | 61 +++++++++++++++++++++++++++++++++++--------------------------
M src/parse.c | 92 ++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
M src/symbol.c | 28 +++++++++++++++-------------
M src/type.c | 16 ++++++++--------
M src/util/util.h | 7 +++----

12 files changed, 194 insertions, 175 deletions

diff --git a/CMakeLists.txt b/CMakeLists.txt
index fdec80c..6c5e537 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,12 +1,12 @@
 cmake_minimum_required(VERSION 3.21 FATAL_ERROR)
-project(G VERSION 0.2.0 LANGUAGES C)
+project(G LANGUAGES C)
 
 set(CMAKE_C_STANDARD 23)
 set(CMAKE_C_STANDARD_REQUIRED TRUE)
 set(CMAKE_C_EXTENSIONS FALSE)
 
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
-add_compile_definitions(PROJECT_VERSION="${PROJECT_VERSION}")
+add_compile_definitions(VERSION="$ENV{VERSION}")
 
 find_package(LLVM REQUIRED)
 
diff --git a/src/analyse.c b/src/analyse.c
index bb4c50d..d4789e3 100644
--- a/src/analyse.c
+++ b/src/analyse.c
@@ -54,7 +54,7 @@ static inline void analyse_stmt_comp(ast *a, syt *st) {
 /* Analyse a declaration statement. */
 static void analyse_stmt_decl(ast *a, syt *st) {
 	assert(A.c.al == 0 || A.c.al == 1);
-	if (A.c.al == 0) { assert(A.t != NULL); return; }
+	if (A.c.al == 0) { assert(A.t != null); return; }
 
 	analyse_expr(C[0], st);
 	type *value_type = ast_type(C[0], st);
@@ -65,7 +65,7 @@ static void analyse_stmt_decl(ast *a, syt *st) {
 	}
 	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; }
+		if (A.t == null) { A.t = C[0]->t; }
 
 		/* If the type has been specified, check that the value is compatible */
 		else {
@@ -74,7 +74,7 @@ static void analyse_stmt_decl(ast *a, syt *st) {
 	}
 	else if (is_int(value_type)) {
 		/* If a type has not been specified, set the type based on the value */
-		if (A.t == NULL) {
+		if (A.t == null) {
 			A.t = C[0]->t;
 
 			/* If the value type is smaller than 32 bit, upgrade it */
@@ -101,14 +101,14 @@ static void analyse_stmt_decl(ast *a, syt *st) {
 	}
 	else if (is_flt(value_type)) {
 		/* If a type has not been specified, set the type based on the value */
-		if (A.t == NULL) { A.t = A.c.a[0]->t; }
+		if (A.t == null) { A.t = A.c.a[0]->t; }
 
 		/* If the type has been specified, check that the value is compatible */
 		else {
 			/* TODO */
 		}
 	}
-	else if (value_type->k == TY_STRING) { if (A.t == NULL) { A.t = C[0]->t; } return; }
+	else if (value_type->k == TY_STRING) { if (A.t == null) { A.t = C[0]->t; } return; }
 
 	else { note("TODO", A.ln, A.cl, -1, "Unhandled value kind %s", ast_ks[C[0]->k]); }
 }
@@ -156,7 +156,7 @@ 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); }
+		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;
@@ -165,12 +165,12 @@ static void analyse_expr(ast *a, syt *st) {
 		assert(CL >= 1);
 		assert(C[0]->k == AK_ID);
 		assert(C[0]->h != 0);
-		assert(C[0]->s != NULL);
+		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); }
+		if (sym == null) { note("TODO", A.ln, A.cl, 0, "Use of undeclared identifier \"%s\"", C[0]->s); }
 
 		else if (sym->k == AK_TYPE) {
 			A.k = AK_CAST; A.t = sym->t;
diff --git a/src/init.c b/src/init.c
index 359848f..a555a61 100644
--- a/src/init.c
+++ b/src/init.c
@@ -9,51 +9,51 @@
 #include <string.h>
 
 static ast kwds[] = {
-	{ AK_TYPE, 0, 0, 0, "ptr", &TYPE(TY_PTR), { 0 }, NULL },
+	{ AK_TYPE, 0, 0, 0, "ptr", &TYPE(TY_PTR), { 0 }, null },
 
 	/* Boolean Types */
-	{ AK_TYPE, 0, 0, 0, "bool", &TYPE(TY_BOOL), { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "b8",   &TYPE(TY_B8),   { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "b16",  &TYPE(TY_B16),  { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "b32",  &TYPE(TY_B32),  { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "b64",  &TYPE(TY_B64),  { 0 }, NULL },
+	{ AK_TYPE, 0, 0, 0, "bool", &TYPE(TY_BOOL), { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "b8",   &TYPE(TY_B8),   { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "b16",  &TYPE(TY_B16),  { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "b32",  &TYPE(TY_B32),  { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "b64",  &TYPE(TY_B64),  { 0 }, null },
 
 	/* Integer Types */
-	{ AK_TYPE, 0, 0, 0, "uint", &TYPE(TY_UINT), { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "u8",   &TYPE(TY_U8),   { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "u16",  &TYPE(TY_U16),  { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "u32",  &TYPE(TY_U32),  { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "u64",  &TYPE(TY_U64),  { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "u128", &TYPE(TY_U128), { 0 }, NULL },
+	{ AK_TYPE, 0, 0, 0, "uint", &TYPE(TY_UINT), { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "u8",   &TYPE(TY_U8),   { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "u16",  &TYPE(TY_U16),  { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "u32",  &TYPE(TY_U32),  { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "u64",  &TYPE(TY_U64),  { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "u128", &TYPE(TY_U128), { 0 }, null },
 
-	{ AK_TYPE, 0, 0, 0, "sint", &TYPE(TY_SINT), { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "s8",   &TYPE(TY_S8),   { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "s16",  &TYPE(TY_S16),  { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "s32",  &TYPE(TY_S32),  { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "s64",  &TYPE(TY_S64),  { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "s128", &TYPE(TY_S128), { 0 }, NULL },
+	{ AK_TYPE, 0, 0, 0, "sint", &TYPE(TY_SINT), { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "s8",   &TYPE(TY_S8),   { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "s16",  &TYPE(TY_S16),  { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "s32",  &TYPE(TY_S32),  { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "s64",  &TYPE(TY_S64),  { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "s128", &TYPE(TY_S128), { 0 }, null },
 
 	/* Floating Point Types */
-	{ AK_TYPE, 0, 0, 0, "f16",  &TYPE(TY_F32),  { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "f32",  &TYPE(TY_F32),  { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "f64",  &TYPE(TY_F64),  { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "f128", &TYPE(TY_F128), { 0 }, NULL },
+	{ AK_TYPE, 0, 0, 0, "f16",  &TYPE(TY_F32),  { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "f32",  &TYPE(TY_F32),  { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "f64",  &TYPE(TY_F64),  { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "f128", &TYPE(TY_F128), { 0 }, null },
 
 	/* Character and String Types */
-	{ AK_TYPE, 0, 0, 0, "char",   &TYPE(TY_CHAR),   { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "rune",   &TYPE(TY_RUNE),   { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "string", &TYPE(TY_STRING), { 0 }, NULL },
+	{ AK_TYPE, 0, 0, 0, "char",   &TYPE(TY_CHAR),   { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "rune",   &TYPE(TY_RUNE),   { 0 }, null },
+	{ AK_TYPE, 0, 0, 0, "string", &TYPE(TY_STRING), { 0 }, null },
 
 	/* Builtins */
-	{ AK_PROC, 0, 0, 0, "cstring", &(type){ TY_PTR, TF_PTR, -1, "", &TYPE(TY_U8) }, { 0 }, NULL },
-	{ AK_PROC, 0, 0, 0, "len", &TYPE(TY_UINT), { 0 }, NULL },
+	{ AK_PROC, 0, 0, 0, "cstring", &(type){ TY_PTR, TF_PTR, -1, "", &TYPE(TY_U8) }, { 0 }, null },
+	{ AK_PROC, 0, 0, 0, "len", &TYPE(TY_UINT), { 0 }, null },
 
-	{ AK_VOID, 0, 0, 0, NULL, NULL, { 0 }, NULL }
+	{ AK_VOID, 0, 0, 0, null, null, { 0 }, null }
 };
 
 void initialise(void) {
 	/* Populate the keyword symbol table */
-	for (uptr i = 0; kwds[i].s != NULL; i += 1) {
+	for (uptr i = 0; kwds[i].s != null; i += 1) {
 		kwds[i].h = syt_hash(kwds[i].s, strlen(kwds[i].s));
 		syt_insert_h(&kwt, kwds[i].h, kwds[i].s, &kwds[i]);
 	}
diff --git a/src/llvm.c b/src/llvm.c
index 6f9f1c8..e9245d3 100644
--- a/src/llvm.c
+++ b/src/llvm.c
@@ -22,10 +22,10 @@
 #include <stdlib.h>
 #include <string.h>
 
-static char *file_name = NULL, *module_name = NULL;
-static LLVMContextRef llvm_context = NULL;
-static LLVMModuleRef  llvm_module  = NULL;
-static LLVMBuilderRef llvm_builder = NULL;
+static char *file_name = null, *module_name = null;
+static LLVMContextRef llvm_context = null;
+static LLVMModuleRef  llvm_module  = null;
+static LLVMBuilderRef llvm_builder = null;
 
 static LLVMValueRef llvm_stmt(ast *a, syt *st);
 static LLVMValueRef llvm_stmt_compound(ast *a, syt *st);
@@ -59,10 +59,10 @@ void llvm(ast *a, char *file, llvm_action action) {
 	/* Generate IR for all child nodes */
 	for (uptr i = 0; i < a->c.al; i += 1) { llvm_stmt_decl(a->c.a[i], &a->st); }
 
-	char *err = NULL; bool failed = false;
+	char *err = null; bool failed = false;
 
 	failed = LLVMVerifyModule(llvm_module, LLVMPrintMessageAction, &err);
-	LLVMDisposeMessage(err); err = NULL;
+	LLVMDisposeMessage(err); err = null;
 
 	switch (action) {
 	case llvm_obj: case llvm_asm: {
@@ -78,12 +78,12 @@ void llvm(ast *a, char *file, llvm_action action) {
 		LLVMInitializeAllAsmParsers();
 		LLVMInitializeAllAsmPrinters();
 
-		LLVMTargetRef target = NULL;
+		LLVMTargetRef target = null;
 
 		if (LLVMGetTargetFromTriple(triple, &target, &err)) {
 			panic("LLVMGetTargetFromTriple: %s", err);
 		}
-		if (err != NULL) { panic("%s", err); } LLVMDisposeMessage(err); err = NULL;
+		if (err != null) { panic("%s", err); } LLVMDisposeMessage(err); err = null;
 
 		LLVMTargetMachineRef machine = LLVMCreateTargetMachine(
 			target, triple, "generic", "", LLVMCodeGenLevelNone,
@@ -95,7 +95,7 @@ void llvm(ast *a, char *file, llvm_action action) {
 		if (LLVMTargetMachineEmitToFile(machine, llvm_module, outfile, outform, &err)) {
 			panic("LLVMTargetMachineEmitToFile: %s", err);
 		}
-		if (err != NULL) { panic("%s", err); } LLVMDisposeMessage(err); err = NULL;
+		if (err != null) { panic("%s", err); } LLVMDisposeMessage(err); err = null;
 
 		LLVMDisposeTargetMachine(machine);
 	} break;
@@ -103,7 +103,7 @@ void llvm(ast *a, char *file, llvm_action action) {
 		if (LLVMPrintModuleToFile(llvm_module, "llvm.ll", &err)) {
 			panic("LLVMPrintModuleToFile: %s", err);
 		}
-		if (err != NULL) { panic("%s", err); } LLVMDisposeMessage(err); err = NULL;
+		if (err != null) { panic("%s", err); } LLVMDisposeMessage(err); err = null;
 	} break;
 	case llvm_bc: {
 		if (LLVMWriteBitcodeToFile(llvm_module, "llvm.bc")) {
@@ -132,7 +132,7 @@ static LLVMValueRef llvm_stmt(ast *a, syt *st) {
 /* Generate IR for a compound statement. */
 static LLVMValueRef llvm_stmt_compound(ast *a, syt *st) {
 	for (uptr i = 0; i < A.c.al; i += 1) { llvm_stmt(C[i], &A.st); }
-	return NULL;
+	return null;
 }
 
 /* Generate IR for a declaration statement. */
@@ -170,12 +170,12 @@ static LLVMValueRef llvm_stmt_decl(ast *a, syt *st) {
 		}
 	}
 
-	return NULL;
+	return null;
 }
 
 /* Generate IR for a return statement. */
 static LLVMValueRef llvm_stmt_return(ast *a, syt *st) {
-	return LLVMBuildRet(llvm_builder, a->c.al > 0 ? llvm_expr(C[0], st, true) : NULL);
+	return LLVMBuildRet(llvm_builder, a->c.al > 0 ? llvm_expr(C[0], st, true) : null);
 }
 
 /* Generate IR for an if statement. */
@@ -188,7 +188,7 @@ static LLVMValueRef llvm_stmt_if(ast *a, syt *st) {
 
 	ast *p = find_proc(a); assert(p->k == AK_DECL);
 
-	LLVMBasicBlockRef b1 = LLVMAppendBasicBlock(p->llvm_v, ""), b2 = NULL;
+	LLVMBasicBlockRef b1 = LLVMAppendBasicBlock(p->llvm_v, ""), b2 = null;
 	if (CL == 4) { b2 = LLVMAppendBasicBlock(p->llvm_v, ""); }
 	LLVMBasicBlockRef b3 = LLVMAppendBasicBlock(p->llvm_v, "");
 
@@ -204,7 +204,7 @@ static LLVMValueRef llvm_stmt_if(ast *a, syt *st) {
 
 	LLVMPositionBuilderAtEnd(llvm_builder, b3);
 
-	return NULL;
+	return null;
 }
 
 /* Generate IR for a for statement. */
@@ -232,7 +232,7 @@ static LLVMValueRef llvm_stmt_for(ast *a, syt *st) {
 
 	LLVMPositionBuilderAtEnd(llvm_builder, b3);
 
-	return NULL;
+	return null;
 }
 
 #define BuildICmp(b, op, lhs, rhs, st) LLVMBuildICmp(b, op, llvm_expr(lhs, st, true), llvm_expr(rhs, st, true), "")
@@ -250,7 +250,7 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 	case AK_CAST: { return llvm_expr_cast(a, st); } break;
 	case AK_ID: {
 		ast *sym = syt_search(st, a->s);
-		if (sym == NULL) { note(file_name, A.ln, A.cl, -1, "Undefined variable \"%s\"", A.s); }
+		if (sym == null) { note(file_name, A.ln, A.cl, -1, "Undefined variable \"%s\"", A.s); }
 		if (!sym->llvm_v) { note(file_name, A.ln, A.cl, -1, "Variable \"%s\" follows (llvm:llvm_expr)", A.s); }
 
 		if (!load || sym->t->k == TY_ARR) { return sym->llvm_v; }
@@ -258,7 +258,7 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 	} break;
 	case AK_CALL: {
 		ast *sym = syt_search_h(st, C[0]->h, C[0]->s);
-		if (sym == NULL) { note(file_name, A.ln, A.cl, -1, "Undefined procedure \"%s\" (llvm:llvm_expr)", C[0]->s); }
+		if (sym == null) { note(file_name, A.ln, A.cl, -1, "Undefined procedure \"%s\" (llvm:llvm_expr)", C[0]->s); }
 		if (!sym->llvm_v) { note(file_name, A.ln, A.cl, -1, "Procedure \"%s\" follows (llvm:llvm_expr)", C[0]->s); }
 
 		LLVMValueRef args[CL - 1]; for (uptr i = 1; i < CL; i += 1) { args[i - 1] = llvm_expr(C[i], st, true); }
@@ -279,7 +279,7 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 	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)"); }
+		if (t == null) { note(file_name, A.ln, A.cl, -1, "Subtree is missing a type (llvm:llvm_expr)"); }
 
 		if (is_int(t)) { return LLVMBuildNeg(llvm_builder, llvm_expr(C[0], st, true), "neg"); }
 		else if (is_flt(t)) { return LLVMBuildFNeg(llvm_builder, llvm_expr(C[0], st, true), "neg"); }
@@ -288,8 +288,8 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 	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)"); }
+		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)"); }
 
 		if (!load) { return llvm_expr(C[0], st, true); }
 		else { return LLVMBuildLoad2(llvm_builder, llvm_type(t->base), llvm_expr(C[0], st, true), ""); }
@@ -367,7 +367,7 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 
 	case AK_BW_NOT: {
 		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)"); }
+		if (t == null) { note(file_name, A.ln, A.cl, -1, "Subtree is missing a type (llvm:llvm_expr)"); }
 
 		if (is_int(t)) {
 			return LLVMBuildXor(llvm_builder, llvm_expr(C[0], st, true), LLVMConstInt(llvm_type(t), -1, false), "");
@@ -385,25 +385,25 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 		return LLVMBuildStore(llvm_builder, llvm_expr(C[1], st, true), llvm_expr(C[0], st, false));
 	} break;
 	case AK_AS_ADD: {
-		register type *t = ast_type(C[0], st); register LLVMValueRef v = NULL;
+		register type *t = ast_type(C[0], st); register LLVMValueRef v = null;
 		if      (is_int(t)) { v = LLVMBuildAdd(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 		else if (is_flt(t)) { v = LLVMBuildFAdd(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 		return LLVMBuildStore(llvm_builder, v, llvm_expr(C[0], st, false));
 	} break;
 	case AK_AS_SUB: {
-		register type *t = ast_type(C[0], st); register LLVMValueRef v = NULL;
+		register type *t = ast_type(C[0], st); register LLVMValueRef v = null;
 		if      (is_int(t)) { v = LLVMBuildSub(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 		else if (is_flt(t)) { v = LLVMBuildFSub(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 		return LLVMBuildStore(llvm_builder, v, llvm_expr(C[0], st, false));
 	} break;
 	case AK_AS_MUL: {
-		register type *t = ast_type(C[0], st); register LLVMValueRef v = NULL;
+		register type *t = ast_type(C[0], st); register LLVMValueRef v = null;
 		if      (is_int(t)) { v = LLVMBuildMul(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 		else if (is_flt(t)) { v = LLVMBuildFMul(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 		return LLVMBuildStore(llvm_builder, v, llvm_expr(C[0], st, false));
 	} break;
 	case AK_AS_DIV: {
-		register type *t = ast_type(C[0], st); register LLVMValueRef v = NULL;
+		register type *t = ast_type(C[0], st); register LLVMValueRef v = null;
 		if (is_int(t)) {
 			if (is_sign(t)) { v = LLVMBuildSDiv(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 			else            { v = LLVMBuildUDiv(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
@@ -412,7 +412,7 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 		return LLVMBuildStore(llvm_builder, v, llvm_expr(C[0], st, false));
 	} break;
 	case AK_AS_MOD: {
-		register type *t = ast_type(C[0], st); register LLVMValueRef v = NULL;
+		register type *t = ast_type(C[0], st); register LLVMValueRef v = null;
 		if (is_int(t)) {
 			if (is_sign(t)) { v = LLVMBuildSRem(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
 			else            { v = LLVMBuildURem(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); }
@@ -424,7 +424,7 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 	default: { panic("Unhandled AST kind \"%s\" (llvm:llvm_expr)", ast_ks[a->k]); } break;
 	}
 
-	return NULL;
+	return null;
 }
 
 /* Generate IR for a procedure. */
@@ -434,7 +434,7 @@ 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(A.t != null);
 	assert(CL == 1 || CL == 2);
 
 	ast *c = CL == 1 ? C[0] : C[1];
@@ -471,7 +471,7 @@ static LLVMValueRef llvm_expr_cast(ast *a, syt *st) {
 
 	panic("Unhandled cast \"%s\" -> \"%s\" (llvm:llvm_expr_cast)", expr_type->s, A.t->s);
 
-	return NULL;
+	return null;
 }
 
 /* Generate IR for a builtin. */
@@ -479,7 +479,7 @@ static LLVMValueRef llvm_expr_builtin(ast *a, syt *st) {
 	assert(A.k == AK_BUILTIN);
 
 	ast *sym = syt_search_h(st, C[0]->h, C[0]->s);
-	if (sym == NULL) { note(file_name, A.ln, A.cl, -1, "Undefined builtin \"%s\" (llvm:llvm_expr_builtin)", C[0]->s); }
+	if (sym == null) { note(file_name, A.ln, A.cl, -1, "Undefined builtin \"%s\" (llvm:llvm_expr_builtin)", C[0]->s); }
 
 	if (strncmp(sym->s, "cstring", 7) == 0) {
 		assert(CL == 2);
@@ -498,7 +498,7 @@ static LLVMValueRef llvm_expr_builtin(ast *a, syt *st) {
 		return LLVMBuildLoad2(llvm_builder, LLVMIntType(64), v, "");
 	}
 
-	return NULL;
+	return null;
 }
 
 /* Generate IR for a hash procedure. */
@@ -512,7 +512,7 @@ static LLVMValueRef llvm_expr_hash(ast *a, syt *st) {
 	}
 
 	LLVMTypeRef func_type = LLVMFunctionType(llvm_type(&TYPE(TY_SINT)), argt, CL, false);
-	LLVMValueRef inline_asm = NULL;
+	LLVMValueRef inline_asm = null;
 
 	/* TODO check architecture */
 	/* x86-64 */ {
@@ -592,13 +592,13 @@ static LLVMTypeRef llvm_type(type *t) {
 	case TY_F64:  { return LLVMDoubleType(); } break;
 	case TY_F128: { return LLVMFP128Type();  } break;
 	case TY_STRING: {
-		static LLVMTypeRef str_t = NULL; if (str_t == NULL) {
+		static LLVMTypeRef str_t = null; if (str_t == null) {
 			str_t = LLVMStructCreateNamed(llvm_context, "string");
 			LLVMTypeRef ta[2] = { LLVMPointerType(LLVMIntType(8), 0), LLVMIntType(64) };
 			LLVMStructSetBody(str_t, ta, 2, false);
 		} return str_t;
 	} break;
-	default: { return NULL; } break;
+	default: { return null; } break;
 	}
 }
 
@@ -629,7 +629,7 @@ static LLVMValueRef llvm_ival(type *t) {
 	case TY_F64:  { return LLVMConstReal(LLVMDoubleType(), 0.0); } break;
 	case TY_F128: { return LLVMConstReal(LLVMFP128Type(),  0.0); } break;
 	case TY_STRING: {
-		static LLVMValueRef vs = NULL; if (vs == NULL) {
+		static LLVMValueRef vs = null; if (vs == null) {
 			LLVMValueRef va[2] = {
 				LLVMBuildGlobalString(llvm_builder, "", ""),
 				LLVMConstInt(LLVMIntType(64), 0, false)
@@ -640,6 +640,6 @@ static LLVMValueRef llvm_ival(type *t) {
 			LLVMSetGlobalConstant(vs, true);
 		} return vs;
 	}
-	default: { return NULL; } break;
+	default: { return null; } break;
 	}
 }
diff --git a/src/llvm/attr.c b/src/llvm/attr.c
index 4b775e9..2e9a72d 100644
--- a/src/llvm/attr.c
+++ b/src/llvm/attr.c
@@ -19,7 +19,7 @@ static char *llvm_func_attr_s[] = {
 /* Add an attribute to a function, returns -1 if the attribute does not exist. */
 int LLVMAddFuncAttr(LLVMContextRef C, LLVMValueRef F, llvm_func_attr attr, unsigned index) {
 	u32 ak = LLVMGetEnumAttributeKindForName(llvm_func_attr_s[attr], strlen(llvm_func_attr_s[attr]));
-	if (ak == 0) { dprint(NULL, 0, 0, "Attribute \"%s\" does not exist", llvm_func_attr_s[attr]); return -1; }
+	if (ak == 0) { dprint(null, 0, 0, "Attribute \"%s\" does not exist", llvm_func_attr_s[attr]); return -1; }
 
 	LLVMAttributeRef ar = LLVMCreateEnumAttribute(C, ak, 0);
 	LLVMAddAttributeAtIndex(F, index, ar); return 0;
diff --git a/src/log.c b/src/log.c
index 2ed9c85..67232e5 100644
--- a/src/log.c
+++ b/src/log.c
@@ -35,6 +35,6 @@ bool has_error(void) { return log_count > 0; }
 
 /* Print a panic message and exit. */
 [[noreturn]] void __panic(const char *file, uptr ln, const char *format, ...) {
-	fflush(stderr); fprintf(stderr, file != NULL ? "panic: %s:%lu: " : "panic: ", file, ln);
+	fflush(stderr); fprintf(stderr, file != null ? "panic: %s:%lu: " : "panic: ", file, ln);
 	va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fputc('\n', stderr); exit(-1);
 }
diff --git a/src/log.h b/src/log.h
index f71e9dd..764db93 100644
--- a/src/log.h
+++ b/src/log.h
@@ -16,7 +16,7 @@ extern bool has_error(void);
 
 #ifdef NDEBUG
 #define dprint(file, ln, cl, format, ...) ((void)0)
-#define panic(format, ...) (__panic(NULL, 0, format __VA_OPT__(,) __VA_ARGS__))
+#define panic(format, ...) (__panic(null, 0, format __VA_OPT__(,) __VA_ARGS__))
 #else
 #define dprint(file, ln, cl, format, ...) (note(file, ln, cl, 9, format __VA_OPT__(,) __VA_ARGS__))
 #define panic(format, ...) (__panic(__FILE__, __LINE__, format __VA_OPT__(,) __VA_ARGS__))
diff --git a/src/main.c b/src/main.c
index b4e32dc..2de3fd6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -18,9 +18,12 @@ static struct {
 	bool bflag, Bflag, cflag, Eflag, pflag, Pflag, qflag, Sflag;
 } args = {};
 
-static char *output = NULL;
+static char *output = null;
 static int verbosity = 0;
 
+static const char *const help;
+static const char *const version;
+
 static void compile(const char *file, char *src, uptr len);
 static void compile_file(const char *file);
 
@@ -28,16 +31,13 @@ static void opt_f(const char *arg);
 static void opt_O(const char *arg);
 static void opt_W(const char *arg);
 
-static void hlp(void);
-static void ver(void);
-
 int main(int ac, char *av[]) { A0 = av[0];
 	struct opt opt = OPTGET_INIT;
 	opt.str = "bBcEf:O:pPqSvW:";
 	opt.lops = (struct lop[]){
 		{ "help",    ARG_NUL, 256 },
 		{ "version", ARG_NUL, 257 },
-		{ NULL, 0, 0 }
+		{ null, 0, 0 }
 	};
 
 	for (int o; (o = optget(&opt, av, 1)) != -1;) switch (o) {
@@ -54,8 +54,8 @@ int main(int ac, char *av[]) { A0 = av[0];
 	case 'S': { args.Sflag = true; } break; /* Output assembly files */
 	case 'v': { verbosity += 1;    } break; /* Increase verbosity */
 	case 'W': { opt_W(opt.arg);    } break; /* Configure warnings and errors */
-	case 256: { hlp(); } return 0;
-	case 257: { ver(); } return 0;
+	case 256: { fputs(help, stdout);    } return 0;
+	case 257: { fputs(version, stdout); } return 0;
 	default: {} return 1;
 	}
 
@@ -114,34 +114,32 @@ static void opt_W(const char *arg) {
 	else if (strcmp(arg, "3") == 0) { log_level = 3; }
 	else if (strcmp(arg, "4") == 0) { log_level = 4; }
 	else if (strcmp(arg, "error") == 0) { log_waerr = true; }
-	else { note(NULL, 0, 0, 1, "-W%s: unknown warning option", arg); }
+	else { note(null, 0, 0, 1, "-W%s: unknown warning option", arg); }
 }
 
-/* Print help information. */
-static void hlp(void) {
-	puts("G - G Programming Language\n");
-	puts("Usage: g file...\n");
-	puts("Options:");
-	puts("  -b,       Output LLVM IR files");
-	puts("  -B,       Output LLVM bitcode files");
-	puts("  -c,       Output object files");
-	puts("  -E,       Output lexer tokens");
-	// puts("  -f,       Configure formatting");
-	// puts("  -o file,  Specify output file");
-	// puts("  -O,       Configure optimisation");
-	puts("  -p,       Output parser AST");
-	puts("  -P,       Output analyser AST");
-	puts("  -q,       Silence certain outputs (for benchmarking)");
-	puts("  -S,       Output assembly files");
-	// puts("  -v,       Increase verbosity ");
-	// puts("  -W,       Configure warnings and errors");
-	puts("  --help    Display help information");
-	puts("  --version Display version information");
-}
+static const char *const help =
+	"G - G Programming Language\n"
+	"Usage: g file...\n"
+	"Options:\n"
+	"  -b,       Output LLVM IR files\n"
+	"  -B,       Output LLVM bitcode files\n"
+	"  -c,       Output object files\n"
+	"  -E,       Output lexer tokens\n"
+	// "  -f,       Configure formatting\n"
+	// "  -o file,  Specify output file\n"
+	// "  -O,       Configure optimisation\n"
+	"  -p,       Output parser AST\n"
+	"  -P,       Output analyser AST\n"
+	"  -q,       Silence certain outputs (for benchmarking)\n"
+	"  -S,       Output assembly files\n"
+	// "  -v,       Increase verbosity \n"
+	// "  -W,       Configure warnings and errors\n"
+	"  --help    Display help information\n"
+	"  --version Display version information\n"
+;
 
-/* Print version information. */
-static void ver(void) {
-	puts("G, version " PROJECT_VERSION);
-	puts("Copyright (C) 2021, Jakob Wakeling");
-	puts("All rights reserved.");
-}
+static const char *const version =
+	"G, version " VERSION "\n"
+	"Copyright (C) 2021, Jakob Wakeling\n"
+	"All rights reserved.\n"
+;
diff --git a/src/parse.c b/src/parse.c
index 394552f..3e71095 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -63,26 +63,26 @@ static inline char *strdup_or_fail(const char *s);
 
 /* Initialise an AST node. */
 inline ast *ast_init(ast_k kind, uptr ln, uptr cl) {
-	ast *a = calloc(1, sizeof (*a)); if (a == NULL) { panic(SERR); }
+	ast *a = calloc(1, sizeof (*a)); if (a == null) { panic(SERR); }
 	a->k = kind; a->ln = ln; a->cl = cl; return a;
 }
 
 inline void ast_free(ast **a) {
-	if (a == NULL || *a == NULL) { return; }
-	if ((*a)->s != NULL) { free((*a)->s); }
-	if ((*a)->c.a != NULL) { free((*a)->c.a); }
-	free(*a); a = NULL;
+	if (a == null || *a == null) { return; }
+	if ((*a)->s != null) { free((*a)->s); }
+	if ((*a)->c.a != null) { free((*a)->c.a); }
+	free(*a); a = null;
 }
 
 /* Push a child AST node to an AST node. */
 void ast_push(ast *a, ast *c) {
-	assert(a != NULL); assert(c != NULL);
+	assert(a != null); assert(c != null);
 
 	ast **ca = realloc(a->c.a, (a->c.al += 1) * sizeof (ast *));
-	if (!ca) { panic(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);
+	assert(c->p == null);
 	c->p = a; a->c.a[a->c.al - 1] = c;
 }
 
@@ -94,24 +94,24 @@ void ast_displace(ast *a, ast *c) {
 	if (!found) { return; }
 
 	ast **ca = calloc((a->c.al -= 1), sizeof (ast *));
-	if (!ca) { panic(SERR); } else { a->c.a = ca; ca = NULL; }
+	if (!ca) { panic(SERR); } else { a->c.a = ca; ca = null; }
 
 	for (uptr 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;
 	}
 
-	c->p = NULL; free(oa);
+	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);
+	assert(a != null); assert(c != null); assert(w != null);
 	ast **oa = a->c.a; uptr ol = a->c.al, ifound; bool found = false;
 
 	for (uptr 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);
+	w->p = a; a->c.a[ifound] = w; c->p = null; ast_push(w, c);
 }
 
 /* Get the type of an AST node. */
@@ -122,7 +122,7 @@ type *ast_type(ast *a, syt *st) {
 	/* Search the symbol table for a type first */
 	if (a->s) {
 		ast *sym = syt_search(st, a->s);
-		if (sym != NULL) {
+		if (sym != null) {
 			if (sym->c.al != 0 && sym->c.a[0]->k == AK_PROC) { return sym->c.a[0]->t; }
 			else { return sym->t; }
 		}
@@ -135,8 +135,8 @@ type *ast_type(ast *a, syt *st) {
 		else { return rt; }
 	}
 
-	/* If no type is found, return NULL */
-	return NULL;
+	/* If no type is found, return null */
+	return null;
 }
 
 /* Push an AST to an AST array. */
@@ -147,7 +147,7 @@ void ast_a_push(ast_a *aa, ast *a) {
 
 /* Pop an AST from an AST array. */
 ast *ast_a_pop(ast_a *aa) {
-	return (aa->al ? aa->a[aa->al -= 1] : NULL);
+	return (aa->al ? aa->a[aa->al -= 1] : null);
 }
 
 #define T (l->t) /* lex_peek equivalent */
@@ -160,8 +160,8 @@ ast *parse(lex *l) {
 
 	/* Parse and append all child nodes */
 	for (ast *c; T.k != TK_EOF;) {
-		if ((c = parse_stmt_decl(l, &a->st, true)) != NULL) { ast_push(a, c); }
-		else { note(l->n, T.ln, T.cl, -1, "NULL AST (parse:parse_stmt_decl)"); }
+		if ((c = parse_stmt_decl(l, &a->st, true)) != null) { ast_push(a, c); }
+		else { note(l->n, T.ln, T.cl, -1, "null AST (parse:parse_stmt_decl)"); }
 	}
 
 	return a;
@@ -222,7 +222,7 @@ 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 && CL(a) == 0) { note(l->n, T.ln, T.cl, 0, "A declaration without a type is invalid"); }
+	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 */
@@ -311,7 +311,7 @@ static ast *parse_stmt_for(lex *l, syt *st) {
 
 /* Parse an expression. */
 static ast *parse_expr(lex *l, syt *st, s32 o) {
-	ast *left = NULL; switch (T.k) {
+	ast *left = null; switch (T.k) {
 	case TK_ID: {
 		left = ast_init(AK_ID, T.ln, T.cl); left->h = T.h;
 		left->s = strdup_or_fail(T.s); lex_next(l);
@@ -348,7 +348,7 @@ static ast *parse_expr(lex *l, syt *st, s32 o) {
 	}
 
 	/* Parse an infix expression if one is present */
-	for (ast *a = NULL; tok_precedence(T.k) > o; left = a) switch (T.k) {
+	for (ast *a = null; tok_precedence(T.k) > o; left = a) switch (T.k) {
 	case TK_LPAREN: {
 		if (left->k == AK_HASH) { a = left; }
 		else { a = ast_init(AK_CALL, left->ln, left->cl); ast_push(a, left); }
@@ -446,11 +446,11 @@ static ast *parse_expr_proc(lex *l, syt *st) {
 /* Parse a type identifier. */
 static inline ta_pair parse_type(lex *l, syt *st) {
 	/* root, deepest child, and variable length */
-	type *r = NULL, *c; ast *a = NULL;
+	type *r = null, *c; ast *a = null;
 
 	/* Parse optional pointer and array specifiers */
-	for (register type *t = NULL;;) switch (T.k) {
-	case TK_MUL: { lex_next(l); t = type_ptr(NULL, 1); } goto store;
+	for (register type *t = null;;) switch (T.k) {
+	case TK_MUL: { lex_next(l); t = type_ptr(null, 1); } goto store;
 	case TK_LBRACK: {
 		lex_next(l);
 
@@ -458,22 +458,22 @@ static inline ta_pair parse_type(lex *l, syt *st) {
 		if (T.k != TK_RBRACK) {
 			assert(T.k == TK_INT);
 			a = parse_expr(l, st, 0);
-			t = type_arr(NULL, a->v_int);
+			t = type_arr(null, a->v_int);
 		}
-		else { t = type_arr(NULL, -1); }
+		else { t = type_arr(null, -1); }
 
 		lex_kind(l, TK_RBRACK);
 	} goto store;
-	store: { if (r == NULL) { r = c = t; } else { c = (c->base = t); }} break;
+	store: { if (r == null) { r = c = t; } else { c = (c->base = t); }} break;
 	default: { goto escape; }
 	} escape:;
 
 	/* Parse the base type identifier */
 	tok t = lex_kind(l, TK_ID); ast *s = syt_search_h(st, t.h, t.s);
-	if (s == NULL) { note(l->n, t.ln, t.cl, 0, "Use of undeclared identifier \"%s\"", t.s); }
+	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 (ta_pair){ s->t, a }; } else { c->base = s->t; return (ta_pair){ r, a }; }
+	if (r == null) { return (ta_pair){ s->t, a }; } else { c->base = s->t; return (ta_pair){ r, a }; }
 }
 
 /* Parse an integer. AK_INT is terminal. */
@@ -558,7 +558,7 @@ static s32 ast_precedence(ast_k ak) {
 /* Duplicate a string or fail. */
 static inline char *strdup_or_fail(const char *s) {
 	register char *r = strdup(s);
-	if (r == NULL) { panic("%s", SERR); }
+	if (r == null) { panic("%s", SERR); }
 	return r;
 }
 
@@ -570,8 +570,8 @@ void ast_print(ast *a, uptr indent) {
 	printf("%zu:%zu: %s \"%s\"", a->ln + 1, a->cl + 1, ast_ks[a->k], a->s);
 
 	/* Print type information if present */
-	if (a->t != NULL) { fputs(" -> ", stdout); }
-	for (type *t = a->t; t != NULL; t = t->base) switch (t->k) {
+	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;
@@ -580,8 +580,7 @@ void ast_print(ast *a, uptr indent) {
 	/* Indicate presence of various fields */
 	fputs(" [", stdout);
 	fputc(a->h != 0 ? 'h' : '-', stdout);
-	// fputc(a->v.k != VK_NULL ? 'v' : '-', stdout);
-	fputc(a->st.a != NULL ? 's' : '-', stdout);
+	fputc(a->st.a != null ? 's' : '-', stdout);
 	fputc(']', stdout);
 
 	/* Indicate if the AST node has a value */
@@ -593,17 +592,17 @@ void ast_print(ast *a, uptr indent) {
 	}
 
 	/* Indicate if the AST node has no parent */
-	if (a->p == NULL) { printf(" NO PARENT"); }
+	if (a->p == null) { printf(" NO PARENT"); }
 
 	fputc('\n', stdout);
 
 	/* Print AST children */
 	for (uptr 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\"%s ---\n", ast_ks[a->k], a->s, a->st.pt == NULL ? " NO PARENT" : "");
+	if (a->st.a != null) {
+		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\"%s ---\n", ast_ks[a->k], a->s, a->st.pt == NULL ? " NO PARENT" : "");
+		printf("--- SYT for %s \"%s\"%s ---\n", ast_ks[a->k], a->s, a->st.pt == null ? " NO PARENT" : "");
 	}
 
 	return;
diff --git a/src/symbol.c b/src/symbol.c
index a1ecde9..21629c1 100644
--- a/src/symbol.c
+++ b/src/symbol.c
@@ -12,7 +12,7 @@
 #define INITIAL_CAPACITY 64
 #define LOAD_FACTOR 0.90
 
-syt kwt = (syt){ NULL, 0, 0, NULL };
+syt kwt = (syt){ null, 0, 0, null };
 
 static void syt_resize(syt *st);
 
@@ -20,18 +20,18 @@ static void syt_resize(syt *st);
 syt *syt_aloc(void) { return calloc(1, sizeof (syt)); }
 
 /* Initialise a symbol table. */
-// void syt_init(syt *st) { *st = (syt){ NULL, 0, 0, NULL }; }
+// void syt_init(syt *st) { *st = (syt){ null, 0, 0, null }; }
 
 /* Uninitialise a symbol table. */
 void syt_free(syt *st) {
-	if (st == NULL || st->a == NULL) { return; }
+	if (st == null || st->a == null) { return; }
 
 	for (uptr i = 0; i < st->ac; i += 1) {
 		if (st->a[i].h == 0) { continue; }
 		free(st->a[i].k); /*free(s->a[i].v)*/
 	}
 
-	free(st->a); *st = (syt){ NULL, 0, 0 };
+	free(st->a); *st = (syt){ null, 0, 0 };
 }
 
 /* Compute the hash of some data. Will not return 0. */
@@ -91,14 +91,14 @@ void syt_insert_h(syt *st, u64 h, char *k, ast *v) {
 
 /* Lookup a key-value pair from a map using a precalculated hash. */
 ast *syt_lookup_h(syt *st, u64 h, char *k) {
-	if (st->a == NULL) { return NULL; }
+	if (st->a == null) { return null; }
 
 	uptr i = h % st->ac;
 
 	for (uptr dist = 0;; i = (i + 1) % st->ac, dist += 1) {
-		if (st->a[i].h == 0) { return NULL; }
+		if (st->a[i].h == 0) { return null; }
 
-		if (dist > DIB(i)) { return NULL; /* ? */ }
+		if (dist > DIB(i)) { return null; /* ? */ }
 		if ((st->a[i].h == h) && (strcmp(st->a[i].k, k) == 0)) {
 			return st->a[i].v;
 		}
@@ -107,7 +107,7 @@ ast *syt_lookup_h(syt *st, u64 h, char *k) {
 
 /* Remove a key-value pair from a map using a precalculated hash. */
 void syt_remove_h(syt *st, u64 h, char *k) {
-	if (st->a == NULL) { return; }
+	if (st->a == null) { return; }
 
 	uptr i = h % st->ac;
 
@@ -118,7 +118,7 @@ void syt_remove_h(syt *st, u64 h, char *k) {
 		if ((st->a[i].h == h) && (strcmp(st->a[i].k, k) == 0)) {
 			/* If the element to be removed is found, then deallocate it */
 			free(st->a[i].k);
-			st->a[i] = (typeof (*st->a)){ 0, NULL, NULL }; st->al -= 1;
+			st->a[i] = (typeof (*st->a)){ 0, null, null }; st->al -= 1;
 
 			/*  */
 			for (uptr j = (i + 1) % st->ac;; i = j, j = (j + 1) % st->ac) {
@@ -137,14 +137,14 @@ void syt_remove_h(syt *st, u64 h, char *k) {
 /* Search for a symbol in the symbol tree using a precaclculated hash. */
 ast *syt_search_h(syt *st, u64 h, char *k) {
 	ast *sm = syt_lookup_h(&kwt, h, k);
-	if (sm != NULL) { return sm; }
+	if (sm != null) { return sm; }
 
-	for (; st != NULL; st = st->pt) {
+	for (; st != null; st = st->pt) {
 		sm = syt_lookup_h(st, h, k);
-		if (sm != NULL) { return sm; }
+		if (sm != null) { return sm; }
 	}
 
-	return NULL;
+	return null;
 }
 
 /* Print a basic representation of a map to stdout. */
@@ -160,8 +160,8 @@ void syt_print(syt *st) {
 			fputc(')', 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) {
+		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;
diff --git a/src/type.c b/src/type.c
index 7a55077..40c37b7 100644
--- a/src/type.c
+++ b/src/type.c
@@ -99,13 +99,13 @@ type types[] = {
 	{ TY_STRING, TF_STRING,        16, "string" },
 };
 
-static type_a types_a = { NULL, 0, 0 };
+static type_a types_a = { null, 0, 0 };
 
 /* Allocate a types array with a minimum number of spaces. */
 static inline void types_alloc(type_a *ta, u64 min) {
-	if (ta->a == NULL) {
+	if (ta->a == null) {
 		ta->ac = 128; ta->a = malloc(ta->ac * sizeof (type));
-		if (ta->a == NULL) { error(1, "types: %s", SERR); }
+		if (ta->a == null) { error(1, "types: %s", SERR); }
 	}
 
 	register u64 mul = 1, sp = ta->ac - ta->al;
@@ -113,7 +113,7 @@ static inline void types_alloc(type_a *ta, u64 min) {
 
 	if (mul > 1) {
 		ta->ac *= mul; ta->a = realloc(ta->a, ta->ac * sizeof (type));
-		if (ta->a == NULL) { error(1, "types: %s", SERR); }
+		if (ta->a == null) { error(1, "types: %s", SERR); }
 	}
 }
 
@@ -165,7 +165,7 @@ inline bool is_sign(type *t) { return (t->f & TF_SIGN); }
 
 /* Check if two types are equal. */
 inline bool is_equal(type *t1, type *t2) {
-	if (t1 == NULL || t2 == NULL) { return false; }
+	if (t1 == null || t2 == null) { return false; }
 	if (t1 == t2) { return true; }
 
 	if (t1->k != t2->k) { return false; }
@@ -174,8 +174,8 @@ 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) { break; }
+		if ((b1 == null) != (b2 == null)) { return false; }
 
 		if (b1->k != b2->k) { return false; }
 		if (b1->f != b2->f) { return false; }
@@ -187,7 +187,7 @@ inline bool is_equal(type *t1, type *t2) {
 
 /* Check if two types are compatible. */
 inline bool is_com(type *t1, type *t2) {
-	if (t1 == NULL || t2 == NULL) { return false; }
+	if (t1 == null || t2 == null) { return false; }
 	if (is_equal(t1, t2)) { return true; }
 
 	if (is_int(t1) && is_int(t2)) { return true; }
diff --git a/src/util/util.h b/src/util/util.h
index bf29f59..31b9bec 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -6,10 +6,11 @@
 
 #include <assert.h>
 #include <float.h>
-#include <stddef.h>
 #include <stdint.h>
 
-/* Type Definitions */
+#define null nullptr
+typedef typeof(null) null_t;
+
 typedef uint8_t   u8;
 typedef uint16_t  u16;
 typedef uint32_t  u32;
@@ -26,7 +27,6 @@ typedef float       f32;
 typedef double      f64;
 typedef long double f128;
 
-/* Type Limits */
 #define U8_MIN   UINT8_MIN
 #define U8_MAX   UINT8_MAX
 #define U16_MIN  UINT16_MIN
@@ -56,7 +56,6 @@ typedef long double f128;
 #define F128_MIN LDBL_MIN
 #define F128_MAX LDBL_MAX
 
-/* Miscellaneous */
 #define BIT(x) (1 << (x))
 
 #endif // G_UTIL_UTIL_H_KP8NS9DC