nitron

commit 48b2cd267ec43ddeaecb6c0a7e95c0deadfac924

Author: mesyeti <mesyeti@mesyeti.uk>

rename basic-boot to basic

  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 
  | 0 


diff --git a/basic/.gitignore b/basic/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..631d23f7093ec4d51a6bf2ededb3b33c0630dd30
--- /dev/null
+++ b/basic/.gitignore
@@ -0,0 +1 @@
+nitronb




diff --git a/basic/Makefile b/basic/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..785906776db0027224492a1270d26eb186a99c75
--- /dev/null
+++ b/basic/Makefile
@@ -0,0 +1,49 @@
+SOURCES := $(wildcard source/*.c) $(wildcard source/**/*.c)
+OBJECTS := $(patsubst source/%.c,bin/%.o,$(SOURCES))
+OUT     := nitronb
+LD      := $(CC)
+
+override CFLAGS += -std=c99 -Wall -Wextra -Wuninitialized -Wundef -pedantic
+override LDLIBS += -lm
+
+ifeq ($(BUILD),release)
+	override CFLAGS += -O3
+	#override CPPFLAGS += -NDEBUG
+else
+	override CFLAGS += -Og -g
+	ifeq ($(ASAN), y)
+		override CFLAGS += -fno-omit-frame-pointer -fsanitize=address
+		override LDFLAGS += -fsanitize=address
+	endif
+endif
+
+.SECONDEXPANSION:
+
+deps.filter := %.c %.h
+deps.option := -MM
+define deps
+$$(filter $$(deps.filter),,$$(shell $(CC) $(CFLAGS) $(CPPFLAGS) -E $(deps.option) $(1)))
+endef
+
+all: $(OUT)
+	@:
+
+run: $(OUT)
+	'$(dir $<)$(notdir $<)' $(RUNFLAGS)
+
+$(OUT): $(OBJECTS)
+	$(LD) $(LDFLAGS) $^ $(LDLIBS) -o $@
+
+bin/:
+	mkdir -p bin
+
+bin/%.o: source/%.c $(call deps,source/%.c) | bin/
+	$(CC) $(CFLAGS) $(CPPFLAGS) $< -c -o $@
+
+clean:
+	rm -r bin
+
+distclean: clean
+	rm $(OUT)
+
+.PHONY: all run clean distclean




diff --git a/basic/example.bas b/basic/example.bas
new file mode 100644
index 0000000000000000000000000000000000000000..2267bac07513ff4c398f934c27ab667969854ed0
--- /dev/null
+++ b/basic/example.bas
@@ -0,0 +1,10 @@
+'NITRON
+
+extern C func puts(ptr(char)) int
+
+puts "Hello, world!"
+
+dim ret as int
+ret = puts("Hello again, world!")
+
+quit ret




diff --git a/basic/source/common.h b/basic/source/common.h
new file mode 100644
index 0000000000000000000000000000000000000000..94f4fb2029fd67dc73686715ded950e26256c9a6
--- /dev/null
+++ b/basic/source/common.h
@@ -0,0 +1,9 @@
+#ifndef N_COMMON_H
+#define N_COMMON_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#endif




diff --git a/basic/source/error.c b/basic/source/error.c
new file mode 100644
index 0000000000000000000000000000000000000000..2149922c18c01d2bd86757208d040c3983d37e91
--- /dev/null
+++ b/basic/source/error.c
@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include "mem.h"
+#include "error.h"
+
+#define FMT(RET, FORMAT) do { \
+	int     n    = 0; \
+	size_t  size = 0; \
+	va_list ap; \
+\
+	va_start(ap, FORMAT); \
+	n = vsnprintf(RET, size, FORMAT, ap); \
+	va_end(ap); \
+\
+	assert(n >= 0); \
+\
+	size = n + 1; \
+	RET = (char*) SafeMalloc(size); \
+	if (RET == NULL) { \
+		return; \
+	} \
+\
+	va_start(ap, FORMAT); \
+	n = vsnprintf(RET, size, FORMAT, ap); \
+	va_end(ap); \
+\
+	assert(n >= 0); \
+} while (0);
+
+void PrintError(ErrorInfo err, const char* format, ...) {
+	char* ret = NULL;
+
+	FMT(ret, format);
+
+	#if 0
+		fprintf(stderr, "error: %s:%d: %s\n", err.file, (int) err.line, ret);
+	#else
+		#define RED   "\x1b[31m"
+		#define BLUE  "\x1b[34m"
+		#define RESET "\x1b[0m"
+		fprintf(
+			stderr, RED "error: " BLUE "%s:%d: " RESET "%s\n",
+			err.file, (int) err.line, ret
+		);
+	#endif
+
+	exit(1);
+}
+
+void PrintWarning(ErrorInfo err, const char* format, ...) {
+	char* ret = NULL;
+
+	FMT(ret, format);
+
+	#if 0
+		fprintf(stderr, "error: %s:%d: %s\n", err.file, (int) err.line, ret);
+	#else
+		#define YELLOW "\x1b[33m"
+		#define BLUE   "\x1b[34m"
+		#define RESET  "\x1b[0m"
+		fprintf(
+			stderr, YELLOW "warning: " BLUE "%s:%d: " RESET "%s\n",
+			err.file, (int) err.line, ret
+		);
+	#endif
+}




diff --git a/basic/source/error.h b/basic/source/error.h
new file mode 100644
index 0000000000000000000000000000000000000000..de5ff9f8e56c78843c3d6aefae933fcc325e3de4
--- /dev/null
+++ b/basic/source/error.h
@@ -0,0 +1,14 @@
+#ifndef N_ERROR_H
+#define N_ERROR_H
+
+#include "common.h"
+
+typedef struct {
+	const char* file;
+	size_t      line;
+} ErrorInfo;
+
+void PrintError(ErrorInfo err, const char* format, ...);
+void PrintWarning(ErrorInfo err, const char* format, ...);
+
+#endif




diff --git a/basic/source/lexer.c b/basic/source/lexer.c
new file mode 100644
index 0000000000000000000000000000000000000000..06ae344d28b518f6745713a23d6a05af98a2075c
--- /dev/null
+++ b/basic/source/lexer.c
@@ -0,0 +1,224 @@
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include "lexer.h"
+#include "string.h"
+
+Lexer Lexer_Init(FILE* file, const char* fileName) {
+	Lexer lexer;
+	lexer.file     = file;
+	lexer.success  = true;
+	lexer.fileName = fileName;
+	lexer.line     = 1;
+	return lexer;
+}
+
+void Lexer_Free(Lexer* lexer) {
+	fclose(lexer->file);
+}
+
+static bool IsSeparator(char ch) {
+	switch (ch) {
+		case ' ':
+		case '\n':
+		case '\t':
+		case '(':
+		case ')':
+		case ',':
+		case '+':
+		case '-':
+		case '*':
+		case '/':
+		case '%':
+		case '=':
+		case '<':
+		case '>':
+		case '\'': return true;
+		default:   return false;
+	}
+}
+
+static bool IsNumeric(const char* str) {
+	size_t len = strlen(str);
+
+	for (size_t i = 0; i < len; ++ i) {
+		if ((str[i] < '0') || (str[i] > '9')) return false;
+	}
+
+	return true;
+}
+
+#define GET_CHAR(VAR) \
+	do { \
+		if (fread(&VAR, 1, 1, lexer->file) != 1) { \
+			return LEXER_EOF; \
+		} \
+		if (VAR == '\n') ++ lexer->line; \
+	} while (0);
+
+#define GET_CHAR_ERROR(VAR) \
+	do { \
+		if (fread(&VAR, 1, 1, lexer->file) != 1) { \
+			ErrorInfo err = {lexer->fileName, lexer->line}; \
+			PrintError(err, "Unexpected EOF"); \
+			lexer->success = false; \
+			return LEXER_EOF; \
+		} \
+		if (VAR == '\n') ++ lexer->line; \
+	} while (0);
+
+#define TOKEN(TYPE, CONTENTS) ((Token) \
+	{(TYPE), (ErrorInfo) {lexer->fileName, lexer->line}, (CONTENTS)})
+
+int Lexer_Next(Lexer* lexer, Token* token) {
+	char ch;
+
+	GET_CHAR(ch)
+
+	switch (ch) {
+		case '"': {
+			String string = String_New();
+
+			while (true) {
+				GET_CHAR_ERROR(ch)
+				if (ch == '"') break;
+
+				String_AddChar(&string, ch);
+			}
+
+			*token = TOKEN(TOKEN_STRING, string.contents);
+			return LEXER_TOKEN;
+		}
+		case '(':  *token = TOKEN(TOKEN_LPAREN,   NULL); return LEXER_TOKEN;
+		case ')':  *token = TOKEN(TOKEN_RPAREN,   NULL); return LEXER_TOKEN;
+		case ',':  *token = TOKEN(TOKEN_COMMA,    NULL); return LEXER_TOKEN;
+		case '+':  *token = TOKEN(TOKEN_ADD,      NULL); return LEXER_TOKEN;
+		case '-':  *token = TOKEN(TOKEN_SUBTRACT, NULL); return LEXER_TOKEN;
+		case '*':  *token = TOKEN(TOKEN_MULTIPLY, NULL); return LEXER_TOKEN;
+		case '/':  *token = TOKEN(TOKEN_DIVIDE,   NULL); return LEXER_TOKEN;
+		case '%':  *token = TOKEN(TOKEN_MOD,      NULL); return LEXER_TOKEN;
+		case '=':  *token = TOKEN(TOKEN_EQUAL,    NULL); return LEXER_TOKEN;
+		case '<':  *token = TOKEN(TOKEN_LESS,     NULL); return LEXER_TOKEN;
+		case '>':  *token = TOKEN(TOKEN_GREATER,  NULL); return LEXER_TOKEN;
+		case '\n': *token = TOKEN(TOKEN_LINE,     NULL); return LEXER_TOKEN;
+		case '\'': {
+			while (true) {
+				GET_CHAR(ch);
+
+				if (ch == '\n') {
+					return LEXER_NONE;
+				}
+			}
+			return LEXER_EOF;
+		}
+		case ' ':
+		case '\t':  return LEXER_NONE;
+		default: {
+			// must be an identifier, keyword, or integer
+			String string = String_FromChar(ch);
+
+			while (true) {
+				GET_CHAR_ERROR(ch)
+				if (IsSeparator(ch)) {
+					fseek(lexer->file, -1, SEEK_CUR);
+					if (ch == '\n') -- lexer->line;
+					break;
+				}
+
+				String_AddChar(&string, ch);
+			}
+
+			if (IsNumeric(string.contents)) {
+				*token = TOKEN(TOKEN_INT, string.contents);
+				return LEXER_TOKEN;
+			}
+			if (!strcmp(string.contents, "extern")) {
+				*token = TOKEN(TOKEN_EXTERN, NULL);
+				String_Free(&string);
+				return LEXER_TOKEN;
+			}
+			if (!strcmp(string.contents, "func") || !strcmp(string.contents, "function")) {
+				*token = TOKEN(TOKEN_FUNC, NULL);
+				String_Free(&string);
+				return LEXER_TOKEN;
+			}
+			if (!strcmp(string.contents, "sub")) {
+				*token = TOKEN(TOKEN_SUB, NULL);
+				String_Free(&string);
+				return LEXER_TOKEN;
+			}
+			if (!strcmp(string.contents, "dim")) {
+				*token = TOKEN(TOKEN_DIM, NULL);
+				String_Free(&string);
+				return LEXER_TOKEN;
+			}
+			if (!strcmp(string.contents, "as")) {
+				*token = TOKEN(TOKEN_AS, NULL);
+				String_Free(&string);
+				return LEXER_TOKEN;
+			}
+			if (!strcmp(string.contents, "quit")) {
+				*token = TOKEN(TOKEN_QUIT, NULL);
+				String_Free(&string);
+				return LEXER_TOKEN;
+			}
+			if (!strcmp(string.contents, "ptr")) {
+				*token = TOKEN(TOKEN_PTR, NULL);
+				String_Free(&string);
+				return LEXER_TOKEN;
+			}
+			if (!strcmp(string.contents, "end")) {
+				*token = TOKEN(TOKEN_END, NULL);
+				String_Free(&string);
+				return LEXER_TOKEN;
+			}
+
+			*token = TOKEN(TOKEN_IDENTIFIER, string.contents);
+			return LEXER_TOKEN;
+		}
+	}
+}
+
+const char* Lexer_TypeVAsString(TokenType type) {
+	switch (type) {
+		case TOKEN_NULL:       return "null";
+		case TOKEN_EXTERN:     return "extern";
+		case TOKEN_FUNC:       return "func";
+		case TOKEN_SUB:        return "sub";
+		case TOKEN_DIM:        return "dim";
+		case TOKEN_AS:         return "as";
+		case TOKEN_QUIT:       return "quit";
+		case TOKEN_PTR:        return "ptr";
+		case TOKEN_END:        return "end";
+		case TOKEN_IDENTIFIER: return "identifier";
+		case TOKEN_STRING:     return "string";
+		case TOKEN_INT:        return "int";
+		case TOKEN_LINE:       return "line";
+		case TOKEN_LPAREN:     return "lparen";
+		case TOKEN_RPAREN:     return "rparen";
+		case TOKEN_COMMA:      return "comma";
+		case TOKEN_ADD:        return "add";
+		case TOKEN_SUBTRACT:   return "subtract";
+		case TOKEN_MULTIPLY:   return "multiply";
+		case TOKEN_DIVIDE:     return "divide";
+		case TOKEN_MOD:        return "mod";
+		case TOKEN_EQUAL:      return "equal";
+		case TOKEN_LESS:       return "less";
+		case TOKEN_GREATER:    return "greater";
+		default:               assert(0);
+	}
+}
+
+const char* Lexer_TypeAsString(Token* token) {
+	return Lexer_TypeVAsString(token->type);
+}
+
+void Lexer_PrintToken(Token* token) {
+	printf("%s: %s\n", Lexer_TypeAsString(token), token->contents);
+}
+
+void Lexer_FreeToken(Token* token) {
+	if (token->contents) {
+		free(token->contents);
+	}
+}




diff --git a/basic/source/lexer.h b/basic/source/lexer.h
new file mode 100644
index 0000000000000000000000000000000000000000..0f3e15fa515f4e38f3882d1480fa461bb3e6a706
--- /dev/null
+++ b/basic/source/lexer.h
@@ -0,0 +1,70 @@
+#ifndef N_LEXER_H
+#define N_LEXER_H
+
+#include <stdio.h>
+#include "error.h"
+#include "common.h"
+
+typedef enum {
+	TOKEN_NULL = 0,
+
+	// keywords
+	TOKEN_EXTERN,
+	TOKEN_FUNC,
+	TOKEN_SUB,
+	TOKEN_DIM,
+	TOKEN_AS,
+	TOKEN_QUIT,
+	TOKEN_PTR,
+	TOKEN_END,
+
+	// misc
+	TOKEN_IDENTIFIER,
+	TOKEN_STRING,
+	TOKEN_INT,
+	TOKEN_LINE,
+
+	// characters
+	TOKEN_LPAREN,
+	TOKEN_RPAREN,
+	TOKEN_COMMA,
+
+	// operators
+	TOKEN_ADD,
+	TOKEN_SUBTRACT,
+	TOKEN_MULTIPLY,
+	TOKEN_DIVIDE,
+	TOKEN_MOD,
+	TOKEN_EQUAL,
+	TOKEN_LESS,
+	TOKEN_GREATER
+} TokenType;
+
+enum {
+	LEXER_TOKEN,
+	LEXER_EOF,
+	LEXER_NONE
+};
+
+typedef struct {
+	TokenType type;
+	ErrorInfo err;
+	char*     contents;
+} Token;
+
+typedef struct {
+	FILE*       file;
+	bool        success;
+	const char* fileName;
+	size_t      line;
+} Lexer;
+
+Lexer       Lexer_Init(FILE* file, const char* fileName);
+void        Lexer_Free(Lexer* lexer);
+int         Lexer_Next(Lexer* lexer, Token* token);
+const char* Lexer_TypeVAsString(TokenType type);
+const char* Lexer_TypeAsString(Token* token);
+void        Lexer_PrintToken(Token* token);
+void        Lexer_FreeToken(Token* token);
+
+#endif




diff --git a/basic/source/main.c b/basic/source/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..a8e3fa95d0afaa3a9ac44b0c75f86f336a150b30
--- /dev/null
+++ b/basic/source/main.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <string.h>
+#include "lexer.h"
+#include "state.h"
+#include "parser.h"
+#include "common.h"
+#include "semanticAnalysis.h"
+
+int main(int argc, char** argv) {
+	const char* source = NULL;
+
+	bool printNodes  = false;
+	bool printTokens = false;
+
+	if (argc == 1) {
+		printf(
+			"Usage: %s SOURCE [FLAGS]\n", argv[0]
+		);
+	}
+
+	for (int i = 1; i < argc; ++ i) {
+		if (argv[i][0] != '-') {
+			if (source) {
+				fprintf(stderr, "Source file set multiple times");
+				return 1;
+			}
+
+			source = argv[i];
+		}
+		else if (strcmp(argv[i], "--dp") == 0) {
+			printNodes = true;
+		}
+		else if (strcmp(argv[i], "--dl") == 0) {
+			printTokens = true;
+		}
+		else {
+			fprintf(stderr, "Unknown flag: %s\n", argv[i]);
+			return 1;
+		}
+	}
+
+	if (!source) {
+		fprintf(stderr, "No source file provided\n");
+		return 1;
+	}
+
+	FILE* sourceFile = fopen(source, "r");
+
+	if (!sourceFile) {
+		fprintf(stderr, "Failed to open '%s'\n", source);
+		return 1;
+	}
+
+	Parser parser = Parser_Init(sourceFile, source, printTokens);
+
+	size_t len;
+	Node*  ret = Parser_Parse(&parser, &len);
+
+	if (printNodes) {
+		for (size_t i = 0; i < len; ++ i) {
+			Parser_PrintNode(&ret[i]);
+		}
+
+		return 0;
+	}
+
+	SemanticAnalysis_Init(8);
+
+	for (size_t i = 0; i < len; ++ i){
+		SemanticAnalysis_Analyse(&ret[i]);
+	}
+
+	State_DumpInfo();
+
+	return 0;
+}




diff --git a/basic/source/mem.c b/basic/source/mem.c
new file mode 100644
index 0000000000000000000000000000000000000000..bcef6460e47b9108ddcc2f316a9836fd77855b03
--- /dev/null
+++ b/basic/source/mem.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include "mem.h"
+#include "common.h"
+
+void* SafeMalloc(size_t size) {
+	if (size == 0) return NULL;
+
+	void* ret = malloc(size);
+
+	if (ret == NULL) {
+		fprintf(stderr, "Malloc returned NULL\n");
+		exit(1);
+	}
+
+	return ret;
+}
+
+void* SafeRealloc(void* ptr, size_t size) {
+	void* ret = realloc(ptr, size);
+
+	if ((ret == NULL) && (size > 0)) {
+		fprintf(stderr, "Malloc returned NULL\n");
+		exit(1);
+	}
+
+	return ret;
+}




diff --git a/basic/source/mem.h b/basic/source/mem.h
new file mode 100644
index 0000000000000000000000000000000000000000..1254b31036c2ac43f2f96bc0063f85ab6766d742
--- /dev/null
+++ b/basic/source/mem.h
@@ -0,0 +1,11 @@
+#ifndef N_MEM_H
+#define N_MEM_H
+
+#include "common.h"
+
+#define NEW(T) (T*) SafeMalloc(sizeof(T))
+
+void* SafeMalloc(size_t size);
+void* SafeRealloc(void* ptr, size_t size);
+
+#endif




diff --git a/basic/source/parser.c b/basic/source/parser.c
new file mode 100644
index 0000000000000000000000000000000000000000..2ae234724509cccb38c4a2cc5db7aacef610e5c1
--- /dev/null
+++ b/basic/source/parser.c
@@ -0,0 +1,664 @@
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include "mem.h"
+#include "util.h"
+#include "parser.h"
+
+Parser Parser_Init(FILE* file, const char* fileName, bool printTokens) {
+	Parser ret;
+	Lexer  lexer = Lexer_Init(file, fileName);
+
+	Token* tokens    = SafeMalloc(64 * sizeof(Token));
+	size_t tokensLen = 0;
+	size_t tokensCap = 64;
+
+	bool lexing = true;
+	while (lexing) {
+		Token token;
+
+		int status = Lexer_Next(&lexer, &token);
+
+		switch (status) {
+			case LEXER_TOKEN: {
+				tokens[tokensLen] = token;
+				++ tokensLen;
+
+				if (tokensLen >= tokensCap) {
+					tokensCap += 64;
+					tokens     = SafeRealloc(tokens, tokensCap * sizeof(Token));
+				}
+				break;
+			}
+			case LEXER_EOF:  lexing = false; break;
+			case LEXER_NONE: continue;
+		}
+	}
+
+	tokens       = SafeRealloc(tokens, tokensLen * sizeof(Token));
+	ret.tokens   = tokens;
+	ret.tokenNum = tokensLen;
+	ret.i        = 0;
+
+	if (printTokens) {
+		for (size_t i = 0; i < tokensLen; ++ i) {
+			Lexer_PrintToken(&tokens[i]);
+		}
+
+		exit(0);
+	}
+
+	return ret;
+}
+
+#define INFO(TYPE) ((NodeInfo) {TYPE, p->tokens[p->i].err})
+
+static Node ParseBinary(Parser* p);
+static Node ParseNode(Parser* p);
+
+static void Advance(Parser* p) {
+	++ p->i;
+
+	if (p->i >= p->tokenNum) {
+		PrintError(p->tokens[p->tokenNum - 1].err, "Unexpected EOF");
+	}
+}
+
+static void Expect(Parser* p, TokenType type) {
+	if (p->tokens[p->i].type != type) {
+		Token token2;
+		token2.type = type;
+
+		PrintError(
+			p->tokens[p->i].err, "Expected %s, got %s",
+			Lexer_TypeAsString(&token2), Lexer_TypeAsString(&p->tokens[p->i])
+		);
+	}
+}
+
+static Node ParseAtom(Parser* p) {
+	Token* token = &p->tokens[p->i];
+	Node   ret;
+
+	Advance(p);
+
+	switch (token->type) {
+		case TOKEN_IDENTIFIER: {
+			ret.ident = (IdentifierNode) {INFO(NODE_IDENTIFIER), NewString(token->contents)};
+			return ret;
+		}
+		case TOKEN_STRING: {
+			ret.string = (StringNode) {INFO(NODE_STRING), NewString(token->contents)};
+			return ret;
+		}
+		case TOKEN_INT: {
+			ret.integer = (IntNode) {INFO(NODE_INT), atoi(token->contents)};
+			return ret;
+		}
+		case TOKEN_LPAREN: {
+			ret = ParseBinary(p);
+			Expect(p, TOKEN_RPAREN);
+			Advance(p);
+			return ret;
+		}
+		default: {
+			PrintError(
+				token->err, "Failed to parse atom, got %s",
+				Lexer_TypeAsString(token)
+			);
+			exit(1);
+		}
+	}
+}
+
+static bool IsUnaryOperator(Token* tok) {
+	switch (tok->type) {
+		case TOKEN_ADD:
+		case TOKEN_SUBTRACT: return true;
+		default:             return false;
+	}
+}
+
+static Node ParseUnary(Parser* p) {
+	Token* tok = &p->tokens[p->i];
+
+	if (IsUnaryOperator(tok)) {
+		Advance(p);
+		Node  operand    = ParseUnary(p);
+		Node* operandPtr = SafeMalloc(sizeof(Node));
+		*operandPtr      = operand;
+
+		Node ret;
+		ret.unary = (UnaryNode) {
+			INFO(NODE_UNARY_OP), operandPtr, tok->type
+		};
+		return ret;
+	}
+	else {
+		return ParseAtom(p);
+	}
+}
+
+static Node ParseMulDivMod(Parser* p) {
+	Node left = ParseUnary(p);
+
+	while (
+		(p->tokens[p->i].type == TOKEN_MULTIPLY) ||
+		(p->tokens[p->i].type == TOKEN_DIVIDE) ||
+		(p->tokens[p->i].type == TOKEN_MOD)
+	) {
+		TokenType op = p->tokens[p->i].type;
+		Advance(p);
+		Node right = ParseUnary(p);
+
+		Node* leftPtr  = SafeMalloc(sizeof(Node));
+		Node* rightPtr = SafeMalloc(sizeof(Node));
+		*leftPtr       = left;
+		*rightPtr      = right;
+
+		left.bin = (BinaryNode) {
+			INFO(NODE_BINARY_OP), leftPtr, rightPtr, op
+		};
+	}
+
+	return left;
+}
+
+static Node ParseAddSub(Parser* p) {
+	Node left = ParseMulDivMod(p);
+
+	while (
+		(p->tokens[p->i].type == TOKEN_ADD) ||
+		(p->tokens[p->i].type == TOKEN_SUB)
+	) {
+		TokenType op = p->tokens[p->i].type;
+		Advance(p);
+		Node right = ParseMulDivMod(p);
+
+		Node* leftPtr  = SafeMalloc(sizeof(Node));
+		Node* rightPtr = SafeMalloc(sizeof(Node));
+		*leftPtr       = left;
+		*rightPtr      = right;
+
+		left.bin = (BinaryNode) {
+			INFO(NODE_BINARY_OP), leftPtr, rightPtr, op
+		};
+	}
+
+	return left;
+}
+
+static Node ParseComparison(Parser* p) {
+	Node left = ParseAddSub(p);
+
+	while (
+		(p->tokens[p->i].type == TOKEN_EQUAL) ||
+		(p->tokens[p->i].type == TOKEN_LESS) ||
+		(p->tokens[p->i].type == TOKEN_GREATER)
+	) {
+		TokenType op = p->tokens[p->i].type;
+		Advance(p);
+		Node right = ParseAddSub(p);
+
+		Node* leftPtr  = SafeMalloc(sizeof(Node));
+		Node* rightPtr = SafeMalloc(sizeof(Node));
+		*leftPtr       = left;
+		*rightPtr      = right;
+
+		left.bin = (BinaryNode) {
+			INFO(NODE_BINARY_OP), leftPtr, rightPtr, op
+		};
+	}
+
+	return left;
+}
+
+static Node ParseBinary(Parser* p) {
+	return ParseComparison(p);
+}
+
+static Node ParseType(Parser* p) {
+	Node ret;
+
+	switch (p->tokens[p->i].type) {
+		case TOKEN_PTR: {
+			Advance(p);
+			Expect(p, TOKEN_LPAREN);
+			Advance(p);
+
+			Node innerType = ParseType(p);
+			ret.ptr = (PtrNode) {INFO(NODE_PTR), SafeMalloc(sizeof(Node))};
+			*ret.ptr.inner = innerType;
+
+			Advance(p);
+			Expect(p, TOKEN_RPAREN);
+			return ret;
+		}
+		case TOKEN_IDENTIFIER: {
+			ret.ident = (IdentifierNode) {
+				INFO(NODE_IDENTIFIER), NewString(p->tokens[p->i].contents)
+			};
+			return ret;
+		}
+		default: {
+			PrintError(
+				p->tokens[p->i].err, "Unexpected %s token in type",
+				Lexer_TypeAsString(&p->tokens[p->i])
+			);
+			exit(1);
+		}
+	}
+}
+
+static FuncDec ParseFuncDec(Parser* p) {
+	FuncDecParam* params    = NULL;
+	size_t        paramsNum = 0;
+
+	bool sub = p->tokens[p->i].type == TOKEN_SUB;
+
+	Advance(p);
+	FuncDec ret;
+
+	Expect(p, TOKEN_IDENTIFIER);
+	ret.name = NewString(p->tokens[p->i].contents);
+
+	Advance(p);
+	Expect(p, TOKEN_LPAREN);
+	Advance(p);
+
+	while (p->tokens[p->i].type != TOKEN_RPAREN) {
+		FuncDecParam param;
+		Expect(p, TOKEN_IDENTIFIER);
+		param.name = NewString(p->tokens[p->i].contents);
+
+		Advance(p);
+		Expect(p, TOKEN_AS);
+
+		Advance(p);
+		Node  typeNode = ParseType(p);
+		Node* typePtr  = SafeMalloc(sizeof(Node));
+		*typePtr       = typeNode;
+		param.type     = typePtr;
+
+		++ paramsNum;
+		params = SafeRealloc(params, paramsNum * sizeof(FuncDecParam));
+		params[paramsNum - 1] = param;
+
+		Advance(p);
+		if (
+			(p->tokens[p->i].type != TOKEN_COMMA) &&
+			(p->tokens[p->i].type != TOKEN_RPAREN)
+		) {
+			PrintError(
+				p->tokens[p->i].err, "Unexpected %s token in parameter list",
+				Lexer_TypeAsString(&p->tokens[p->i])
+			);
+		}
+
+		if (p->tokens[p->i].type == TOKEN_COMMA) {
+			Advance(p);
+		}
+	}
+
+	ret.params    = params;
+	ret.paramsNum = paramsNum;
+
+	Advance(p);
+	if (sub) {
+		Expect(p, TOKEN_LINE);
+	}
+	else {
+		ret.ret  = SafeMalloc(sizeof(Node));
+		*ret.ret = ParseType(p);
+		Advance(p);
+		Expect(p, TOKEN_LINE);
+	}
+
+	return ret;
+}
+
+static Node ParseFuncDef(Parser* p) {
+	FuncDefNode ret;
+	ret.i   = INFO(NODE_FUNC_DEF);
+	ret.dec = ParseFuncDec(p);
+	Advance(p);
+
+	ret.body    = NULL;
+	ret.bodyLen = 0;
+
+	while (p->tokens[p->i].type != TOKEN_END) {
+		if (p->tokens[p->i].type == TOKEN_LINE) {
+			Advance(p);
+			continue;
+		}
+
+		Node node = ParseNode(p);
+
+		++ ret.bodyLen;
+		ret.body = SafeRealloc(ret.body, ret.bodyLen * sizeof(Node));
+		ret.body[ret.bodyLen - 1] = node;
+	}
+
+	Advance(p);
+	Expect(p, TOKEN_LINE);
+
+	Node node;
+	node.funcDef = ret;
+	return node;
+}
+
+static Node ParseDim(Parser* p) {
+	DimNode ret;
+	ret.i = INFO(NODE_DIM);
+
+	Advance(p);
+	Expect(p, TOKEN_IDENTIFIER);
+	ret.name = NewString(p->tokens[p->i].contents);
+
+	Advance(p);
+	Expect(p, TOKEN_AS);
+
+	Advance(p);
+	ret.varType  = SafeMalloc(sizeof(Node));
+	*ret.varType = ParseType(p);
+
+
+	Advance(p);
+	Expect(p, TOKEN_LINE);
+
+	Node node;
+	node.dim = ret;
+	return node;
+}
+
+static Node ParseFuncCall(Parser* p) {
+	FuncCallNode ret;
+	ret.i    = INFO(NODE_FUNC_CALL);
+	ret.func = NewString(p->tokens[p->i].contents);
+
+	Advance(p);
+	Node*  params    = NULL;
+	size_t paramsNum = 0;
+
+	while (p->tokens[p->i].type != TOKEN_LINE) {
+		Node param = ParseBinary(p);
+
+		++ paramsNum;
+		params                = SafeRealloc(params, paramsNum * sizeof(Node));
+		params[paramsNum - 1] = param;
+
+		if (
+			(p->tokens[p->i].type != TOKEN_LINE) &&
+			(p->tokens[p->i].type != TOKEN_COMMA)
+		) {
+			PrintError(
+				p->tokens[p->i].err, "Unexpected %s token in parameter list",
+				Lexer_TypeAsString(&p->tokens[p->i])
+			);
+		}
+		if (p->tokens[p->i].type == TOKEN_COMMA) {
+			Advance(p);
+		}
+	}
+
+	ret.params    = params;
+	ret.paramsNum = paramsNum;
+
+	Node node;
+	node.funcCall = ret;
+	return node;
+}
+
+static Node ParseAssign(Parser* p) {
+	AssignNode ret;
+	ret.i        = INFO(NODE_ASSIGN);
+	ret.variable = NewString(p->tokens[p->i].contents);
+
+	Advance(p);
+	Expect(p, TOKEN_EQUAL);
+	Advance(p);
+
+	ret.rValue  = SafeMalloc(sizeof(Node));
+	*ret.rValue = ParseBinary(p);
+
+	Expect(p, TOKEN_LINE);
+
+	Node node;
+	node.assign = ret;
+	return node;
+}
+
+static Node ParseNode(Parser* p) {
+	switch (p->tokens[p->i].type) {
+		case TOKEN_FUNC:
+		case TOKEN_SUB: return ParseFuncDef(p);
+		case TOKEN_DIM: return ParseDim(p);
+		case TOKEN_IDENTIFIER: {
+			if (p->i == p->tokenNum - 1) {
+				PrintError(p->tokens[p->i].err, "Unexpected EOF");
+			}
+
+			if (p->tokens[p->i + 1].type == TOKEN_EQUAL) {
+				return ParseAssign(p);
+			}
+
+			return ParseFuncCall(p);
+		}
+		default: {
+			PrintError(
+				p->tokens[p->i].err, "Unexpected %s token",
+				Lexer_TypeAsString(&p->tokens[p->i])
+			);
+			exit(1);
+		}
+	}
+}
+
+void Parser_PrintNode(Node* node) {
+	switch (node->i.type) {
+		case NODE_INT: printf("%d", node->integer.value); break;
+		case NODE_STRING: printf("%s", node->string.value); break;
+		case NODE_IDENTIFIER: printf("%s", node->ident.name); break;
+		case NODE_BINARY_OP: {
+			printf("(");
+			Parser_PrintNode(node->bin.left);
+
+			switch (node->bin.op) {
+				case TOKEN_ADD:      printf("+"); break;
+				case TOKEN_SUBTRACT: printf("-"); break;
+				case TOKEN_MULTIPLY: printf("*"); break;
+				case TOKEN_DIVIDE:   printf("/"); break;
+				case TOKEN_MOD:      printf("%%"); break;
+				case TOKEN_EQUAL:    printf("="); break;
+				case TOKEN_LESS:     printf("<"); break;
+				case TOKEN_GREATER:  printf(">"); break;
+				default:             assert(0);
+			}
+
+			Parser_PrintNode(node->bin.right);
+			printf(")");
+			break;
+		}
+		case NODE_UNARY_OP: {
+			printf("(");
+
+			switch (node->unary.op) {
+				case TOKEN_ADD:      printf("+"); break;
+				case TOKEN_SUBTRACT: printf("-"); break;
+				default:             assert(0);
+			}
+
+			Parser_PrintNode(node->unary.operand);
+			printf(")");
+			break;
+		}
+		case NODE_PTR: {
+			printf("ptr(");
+			Parser_PrintNode(node->ptr.inner);
+			break;
+		}
+		case NODE_FUNC_DEF: {
+			if (node->funcDef.dec.ret) {
+				printf("func ");
+			}
+			else {
+				printf("sub ");
+			}
+
+			printf("%s(", node->funcDef.dec.name);
+
+			for (size_t i = 0; i < node->funcDef.dec.paramsNum; ++ i) {
+				printf("%s as ", node->funcDef.dec.params[i].name);
+				Parser_PrintNode(node->funcDef.dec.params[i].type);
+
+				if (i < node->funcDef.dec.paramsNum - 1) {
+					printf(", ");
+				}
+			}
+
+			printf(")\n");
+
+			for (size_t i = 0; i < node->funcDef.bodyLen; ++ i) {
+				printf("\t");
+				Parser_PrintNode(&node->funcDef.body[i]);
+				puts("");
+			}
+			puts("end");
+			break;
+		}
+		case NODE_DIM: {
+			printf("dim %s as ", node->dim.name);
+			Parser_PrintNode(node->dim.varType);
+			puts("");
+			break;
+		}
+		case NODE_FUNC_CALL: {
+			printf("%s ", node->funcCall.func);
+
+			for (size_t i = 0; i < node->funcCall.paramsNum; ++ i) {
+				Parser_PrintNode(&node->funcCall.params[i]);
+
+				if (i < node->funcCall.paramsNum - 1) {
+					printf(", ");
+				}
+			}
+			puts("");
+			break;
+		}
+		case NODE_ASSIGN: {
+			printf("%s = ", node->assign.variable);
+			Parser_PrintNode(node->assign.rValue);
+			puts("");
+			break;
+		}
+		default: {
+			printf("unknown %d\n", node->i.type);
+			assert(0);
+		}
+	}
+}
+
+Node* Parser_Parse(Parser* p, size_t* lenOut) {
+	Node*  ret = NULL;
+	size_t len = 0;
+
+	for (p->i = 0; p->i < p->tokenNum; ++ p->i) {
+		if (p->tokens[p->i].type == TOKEN_LINE) continue;
+
+		Node node = ParseNode(p);
+
+		++ len;
+		ret          = SafeRealloc(ret, len * sizeof(Node));
+		ret[len - 1] = node;
+	}
+
+	for (size_t i = 0; i < p->tokenNum; ++ i) {
+		Lexer_FreeToken(&p->tokens[i]);
+	}
+	free(p->tokens);
+
+	*lenOut = len;
+	return ret;
+}
+
+void Parser_FreeNode(Node* node) {
+	switch (node->i.type) {
+		case NODE_STRING:     free(node->string.value); break;
+		case NODE_IDENTIFIER: free(node->ident.name);  break;
+		case NODE_BINARY_OP: {
+			Parser_FreeNode(node->bin.left);
+			Parser_FreeNode(node->bin.right);
+			free(node->bin.left);
+			free(node->bin.right);
+			break;
+		}
+		case NODE_UNARY_OP: {
+			Parser_FreeNode(node->unary.operand);
+			free(node->unary.operand);
+			break;
+		}
+		case NODE_PTR: {
+			Parser_FreeNode(node->ptr.inner);
+			free(node->ptr.inner);
+			break;
+		}
+		case NODE_FUNC_DEF: {
+			if (node->funcDef.dec.ret) {
+				Parser_FreeNode(node->funcDef.dec.ret);
+				free(node->funcDef.dec.ret);
+			}
+
+			for (size_t i = 0; i < node->funcDef.dec.paramsNum; ++ i) {
+				Parser_FreeNode(node->funcDef.dec.params[i].type);
+				free(node->funcDef.dec.params[i].type);
+			}
+
+			free(node->funcDef.dec.name);
+
+			for (size_t i = 0; i < node->funcDef.bodyLen; ++ i) {
+				Parser_FreeNode(&node->funcDef.body[i]);
+			}
+			free(node->funcDef.body);
+			break;
+		}
+		case NODE_DIM: {
+			free(node->dim.name);
+			Parser_FreeNode(node->dim.varType);
+			free(node->dim.varType);
+			break;
+		}
+		case NODE_FUNC_CALL: {
+			free(node->funcCall.func);
+
+			for (size_t i = 0; i < node->funcCall.paramsNum; ++ i) {
+				Parser_FreeNode(&node->funcCall.params[i]);
+			}
+			free(node->funcCall.params);
+			break;
+		}
+		case NODE_ASSIGN: {
+			free(node->assign.variable);
+			Parser_FreeNode(node->assign.rValue);
+			free(node->assign.rValue);
+			break;
+		}
+		default: return;
+	}
+}
+
+const char* Parser_TypeStr(NodeType type) {
+	switch (type) {
+		case NODE_NONE:       return "none";
+		case NODE_INT:        return "int";
+		case NODE_STRING:     return "string";
+		case NODE_IDENTIFIER: return "identifier";
+		case NODE_BINARY_OP:  return "binary operation";
+		case NODE_UNARY_OP:   return "unary operation";
+		case NODE_PTR:        return "ptr";
+		case NODE_FUNC_DEF:   return "function definition";
+		case NODE_DIM:        return "dim";
+		case NODE_FUNC_CALL:  return "function call";
+		case NODE_ASSIGN:     return "assignment";
+		default:              return "???";
+	}
+}




diff --git a/basic/source/parser.h b/basic/source/parser.h
new file mode 100644
index 0000000000000000000000000000000000000000..9e643df70c4de70613cbcd791b17f86efca21126
--- /dev/null
+++ b/basic/source/parser.h
@@ -0,0 +1,127 @@
+#ifndef N_PARSER_H
+#define N_PARSER_H
+
+#include "lexer.h"
+#include "error.h"
+#include "common.h"
+
+typedef enum {
+	NODE_NONE,
+	NODE_INT,
+	NODE_STRING,
+	NODE_IDENTIFIER,
+	NODE_BINARY_OP,
+	NODE_UNARY_OP,
+	NODE_PTR,
+	NODE_FUNC_DEF,
+	NODE_DIM,
+	NODE_FUNC_CALL,
+	NODE_ASSIGN
+} NodeType;
+
+typedef struct {
+	NodeType  type;
+	ErrorInfo err;
+} NodeInfo;
+
+typedef union Node Node;
+
+typedef struct {
+	NodeInfo i;
+	int      value;
+} IntNode;
+
+typedef struct {
+	NodeInfo i;
+	char*    value;
+} StringNode;
+
+typedef struct {
+	NodeInfo i;
+	char*    name;
+} IdentifierNode;
+
+typedef struct {
+	NodeInfo  i;
+	Node*     left;
+	Node*     right;
+	TokenType op;
+} BinaryNode;
+
+typedef struct {
+	NodeInfo  i;
+	Node*     operand;
+	TokenType op;
+} UnaryNode;
+
+typedef struct {
+	NodeInfo i;
+	Node*    inner;
+} PtrNode;
+
+typedef struct {
+	Node*  type;
+	char*  name;
+} FuncDecParam;
+
+typedef struct {
+	FuncDecParam* params;
+	size_t        paramsNum;
+	Node*         ret;
+	char*         name;
+} FuncDec;
+
+typedef struct {
+	NodeInfo i;
+	FuncDec  dec;
+	Node*    body;
+	size_t   bodyLen;
+} FuncDefNode;
+
+typedef struct {
+	NodeInfo i;
+	char*    name;
+	Node*    varType;
+} DimNode;
+
+typedef struct {
+	NodeInfo i;
+	char*    func;
+	Node*    params;
+	size_t   paramsNum;
+} FuncCallNode;
+
+typedef struct {
+	NodeInfo i;
+	char*    variable;
+	Node*    rValue;
+} AssignNode;
+
+union Node {
+	NodeInfo       i;
+	IntNode        integer;
+	StringNode     string;
+	IdentifierNode ident;
+	BinaryNode     bin;
+	UnaryNode      unary;
+	PtrNode        ptr;
+	FuncDefNode    funcDef;
+	DimNode        dim;
+	FuncCallNode   funcCall;
+	AssignNode     assign;
+};
+
+typedef struct {
+	Token* tokens;
+	size_t tokenNum;
+	size_t i;
+} Parser;
+
+Parser Parser_Init(FILE* file, const char* fileName, bool printTokens);
+void   Parser_PrintNode(Node* node);
+Node*  Parser_Parse(Parser* p, size_t* lenOut);
+void   Parser_FreeNode(Node* node);
+
+const char* Parser_TypeStr(NodeType type);
+
+#endif




diff --git a/basic/source/semanticAnalysis.c b/basic/source/semanticAnalysis.c
new file mode 100644
index 0000000000000000000000000000000000000000..c275153bb71bcc85fc06e2e5a9372f795796eb04
--- /dev/null
+++ b/basic/source/semanticAnalysis.c
@@ -0,0 +1,382 @@
+#include <assert.h>
+#include <string.h>
+#include "mem.h"
+#include "util.h"
+#include "error.h"
+#include "state.h"
+#include "parser.h"
+#include "semanticAnalysis.h"
+
+void SemanticAnalysis_Init(size_t wordSize) {
+	State_Init();
+
+	// add primitive types
+	State_MakePrimitive("u8",    1,        PRIM_UINT);
+	State_MakePrimitive("i8",    1,        PRIM_INT);
+	State_MakePrimitive("u16",   2,        PRIM_UINT);
+	State_MakePrimitive("i16",   2,        PRIM_INT);
+	State_MakePrimitive("u32",   4,        PRIM_UINT);
+	State_MakePrimitive("i32",   4,        PRIM_INT);
+	State_MakePrimitive("u64",   8,        PRIM_UINT);
+	State_MakePrimitive("i64",   8,        PRIM_INT);
+	State_MakePrimitive("uint",  wordSize, PRIM_UINT);
+	State_MakePrimitive("int",   wordSize, PRIM_INT);
+	State_MakePrimitive("char",  1,        PRIM_UINT);
+	State_MakePrimitive("f32",   4,        PRIM_FLOAT);
+	State_MakePrimitive("f64",   8,        PRIM_FLOAT);
+	State_MakePrimitive("bool",  1,        PRIM_UINT);
+	State_MakePrimitive("__lit", 0,        PRIM_INT);
+
+	Type type;
+	type.name = NewString("unit");
+	type.type = TYPE_UNIT;
+	State_AddType(type);
+}
+
+void Expect(Node* node, NodeType type) {
+	if (node->i.type != type) {
+		PrintError(node->i.err,
+			"Expected %s, got %s", Parser_TypeStr(type),
+			Parser_TypeStr(node->i.type)
+		);
+	}
+}
+
+void Unexpected(Node* node) {
+	PrintError(node->i.err, "Unexpected %s", Parser_TypeStr(node->i.type));
+}
+
+static UsedType NodeAsUsedType(Node* node) {
+	UsedType ret;
+	ret.ptr   = 0;
+	ret.array = false; // no arrays yet
+
+	while (true) {
+		switch (node->i.type) {
+			case NODE_PTR: {
+				node = node->ptr.inner;
+				++ ret.ptr;
+				continue;
+			}
+			case NODE_IDENTIFIER: {
+				Type* type = State_GetType(node->ident.name);
+
+				if (!type) {
+					PrintError(node->i.err, "Type '%s' does not exist", node->ident.name);
+				}
+
+				ret.typeIdx = type - state.types;
+				return ret;
+			}
+			default: {
+				Unexpected(node);
+				exit(1);
+			}
+		}
+	}
+}
+
+static Type* GetTypeFromUsed(UsedType type) {
+	return &state.types[type.typeIdx];
+}
+
+#define GET_PRIM(NAME, PTR) \
+	((UsedType) {(PTR), State_GetType(NAME) - state.types, 0})
+
+static UsedType EvalType(Node* node);
+
+static UsedType EvalBinOp(Node* node) {
+	UsedType left  = EvalType(node->bin.left);
+	UsedType right = EvalType(node->bin.right);
+
+	Type* leftType  = GetTypeFromUsed(left);
+	Type* rightType = GetTypeFromUsed(right);
+
+	if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) {
+		PrintError(node->i.err, "You can only use primitive types in binary expressions");
+	}
+
+	if (left.ptr) {
+		if (right.ptr) {
+			switch (node->bin.op) {
+				case TOKEN_EQUAL:
+				case TOKEN_LESS:
+				case TOKEN_GREATER: return GET_PRIM("bool", 0);
+				case TOKEN_SUB:     return GET_PRIM("uint", 0);
+				default: {
+					PrintError(
+						node->i.err, "Operation '%s' not allowed here",
+						Lexer_TypeVAsString(node->bin.op)
+					);
+				}
+			}
+		}
+		else {
+			switch (node->bin.op) {
+				case TOKEN_ADD:
+				case TOKEN_SUB: {
+					if (rightType->primType == PRIM_FLOAT) {
+						PrintError(node->i.err, "Cannot add float to pointer");
+					}
+					return left;
+				}
+				default: {
+					PrintError(
+						node->i.err, "Operation '%s' not allowed here",
+						Lexer_TypeVAsString(node->bin.op)
+					);
+				}
+			}
+		}
+	}
+	else if (right.ptr) {
+		switch (node->bin.op) {
+			case TOKEN_ADD:
+			case TOKEN_SUB: {
+				if (leftType->primType == PRIM_FLOAT) {
+					PrintError(node->i.err, "Cannot add float to pointer");
+				}
+				return right;
+			}
+			default: {
+				PrintError(
+					node->i.err, "Operation '%s' not allowed here",
+					Lexer_TypeVAsString(node->bin.op)
+				);
+			}
+		}
+	}
+	else if (!right.ptr) {
+		switch (node->bin.op) {
+			case TOKEN_EQUAL: return GET_PRIM("bool", 0);
+			case TOKEN_LESS:
+			case TOKEN_GREATER: {
+				if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) {
+					PrintError(
+						node->i.err, "Operation '%s' only works on primitives",
+						Lexer_TypeVAsString(node->bin.op)
+					);
+				}
+				return GET_PRIM("bool", 0);
+			}
+			case TOKEN_ADD:
+			case TOKEN_SUBTRACT:
+			case TOKEN_MULTIPLY:
+			case TOKEN_DIVIDE:
+			case TOKEN_MOD: {
+				if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) {
+					PrintError(
+						node->i.err, "Operation '%s' only works on primitives",
+						Lexer_TypeVAsString(node->bin.op)
+					);
+				}
+
+				if (leftType->primType == rightType->primType) {
+					if (leftType->size > rightType->size) {
+						return left;
+					}
+					else {
+						return right;
+					}
+				}
+				else {
+					PrintError(
+						node->i.err, "Incompatible types in '%s' operation",
+						Lexer_TypeVAsString(node->bin.op)
+					);
+				}
+			}
+			default: break;
+		}
+
+		PrintError(
+			node->i.err, "Operation '%s' not allowed here",
+			Lexer_TypeVAsString(node->bin.op)
+		);
+	}
+
+	exit(1);
+}
+
+static UsedType EvalType(Node* node) {
+	static bool inFunc = false;
+
+	switch (node->i.type) {
+		case NODE_INT:    return GET_PRIM("__lit", 0);
+		case NODE_STRING: return GET_PRIM("char", 1);
+		case NODE_IDENTIFIER: {
+			Var* var = State_GetVar(node->ident.name);
+
+			if (!var) {
+				PrintError(
+					node->i.err, "Variable '%s' doesn't exist", node->ident.name
+				);
+			}
+
+			return var->type;
+		}
+		case NODE_BINARY_OP: return EvalBinOp(node);
+		case NODE_UNARY_OP: {
+			UsedType operand = EvalType(node->unary.operand);
+			Type*    type    = GetTypeFromUsed(operand);
+
+			if (type->type != TYPE_PRIM) {
+				PrintError(node->i.err,
+					"Only primitive types can be used with unary operators"
+				);
+			}
+
+			if ((type->primType == PRIM_UINT) && (node->unary.op == TOKEN_SUBTRACT)) {
+				PrintWarning(node->i.err, "Using negative sign on unsigned value");
+			}
+
+			return operand;
+		}
+		case NODE_PTR: {
+			UsedType type = EvalType(node->ptr.inner);
+			++ type.ptr;
+			return type;
+		}
+		case NODE_FUNC_DEF: {
+			if (inFunc) {
+				PrintError(node->i.err, "Nested function definitions are not allowed");
+			}
+
+			Function func;
+			func.sub = node->funcDef.dec.ret != NULL;
+
+			if (!func.sub) {
+				func.ret = NodeAsUsedType(node->funcDef.dec.ret);
+			}
+
+			func.paramsLen = node->funcDef.dec.paramsNum;
+			func.params    = SafeMalloc(func.paramsLen * sizeof(FuncParam));
+
+			for (size_t i = 0; i < func.paramsLen; ++ i) {
+				func.params[i].type = NodeAsUsedType(
+					node->funcDef.dec.params[i].type
+				);
+				func.params[i].name = node->funcDef.dec.params[i].name;
+			}
+
+			func.name = NewString(node->funcDef.dec.name);
+			State_AddFunc(func);
+
+			// now analyse the body
+			State_AddScope();
+			inFunc = true;
+
+			// add parameters to this scope
+			for (size_t i = 0; i < func.paramsLen; ++ i) {
+				Var var;
+				var.type = func.params[i].type;
+				var.name = NewString(func.params[i].name);
+				State_AddVar(var);
+			}
+
+			for (size_t i = 0; i < node->funcDef.bodyLen; ++ i) {
+				EvalType(&node->funcDef.body[i]);
+			}
+			inFunc = false;
+			State_FreeScope();
+			return GET_PRIM("unit", 0);
+		}
+		case NODE_FUNC_CALL: {
+			Function* func = State_GetFunc(node->funcCall.func);
+			if (!func) {
+				PrintError(node->i.err, "Function %s does not exist", node->funcCall.func);
+			}
+
+			if (func->paramsLen != node->funcCall.paramsNum) {
+				PrintError(
+					node->i.err, "Expected %d parameters, got %d",
+					func->paramsLen, node->funcCall.paramsNum
+				);
+			}
+
+			for (size_t i = 0; i < func->paramsLen; ++ i) {
+				UsedType type      = EvalType(&node->funcCall.params[i]);
+				UsedType param     = func->params[i].type;
+				Type*    paramType = GetTypeFromUsed(param);
+				Type*    callType  = GetTypeFromUsed(type);
+
+				bool paramLit = strcmp(callType->name, "__lit") == 0;
+
+				if (!State_UsedTypeEq(type, param)) {
+					if (
+						(callType->type == TYPE_PRIM) && (paramType->type == TYPE_PRIM) &&
+						(callType->primType == paramType->primType) &&
+						(paramType->size >= callType->size)
+					) {
+						continue;
+					}
+
+					if (paramLit && (paramType->type == TYPE_PRIM)) continue;
+
+					PrintError(node->i.err,
+						"Expected %s for parameter %s, got %s",
+						paramType->name, func->params[i].name, callType->name
+					);
+				}
+			}
+
+			if (func->sub) {
+				return GET_PRIM("unit", 0);
+			}
+			else {
+				return func->ret;
+			}
+		}
+		case NODE_DIM: {
+			Var var;
+			var.type = NodeAsUsedType(node->dim.varType);
+			var.name = NewString(node->dim.name);
+			State_AddVar(var);
+			return GET_PRIM("unit", 0);
+		}
+		case NODE_ASSIGN: {
+			Var* var = State_GetVar(node->assign.variable);
+
+			if (!var) {
+				PrintError(
+					node->i.err, "Variable '%s' doesn't exist",
+					node->assign.variable
+				);
+			}
+
+			UsedType left  = var->type;
+			UsedType right = EvalType(node->assign.rValue);
+
+			Type* leftType  = GetTypeFromUsed(left);
+			Type* rightType = GetTypeFromUsed(right);
+
+			bool compatible = true;
+
+			if ((left.ptr != right.ptr) || (leftType->type != rightType->type)) {
+				compatible = false;
+			}
+			else if (leftType->type == TYPE_PRIM) {
+				if (leftType->primType != rightType->primType) compatible = false;
+				else if (leftType->size < rightType->size)     compatible = false;
+			}
+
+			if (!compatible) {
+				PrintError(
+					node->i.err, "Type '%s' cannot be assigned to '%s' variable",
+					GetTypeFromUsed(left)->name, GetTypeFromUsed(right)->name
+				);
+			}
+
+			return GET_PRIM("unit", 0);
+		}
+		default: {
+			Unexpected(node);
+		}
+	}
+
+	assert(0);
+}
+
+void SemanticAnalysis_Analyse(Node* node) {
+	EvalType(node);
+}




diff --git a/basic/source/semanticAnalysis.h b/basic/source/semanticAnalysis.h
new file mode 100644
index 0000000000000000000000000000000000000000..341ccb8ac4a96ebcd1d94b58b4aad9fa914a881e
--- /dev/null
+++ b/basic/source/semanticAnalysis.h
@@ -0,0 +1,9 @@
+#ifndef N_SEMANTIC_ANALYSIS_H
+#define N_SEMANTIC_ANALYSIS_H
+
+#include "common.h"
+
+void SemanticAnalysis_Init(size_t wordSize);
+void SemanticAnalysis_Analyse(Node* node);
+
+#endif




diff --git a/basic/source/state.c b/basic/source/state.c
new file mode 100644
index 0000000000000000000000000000000000000000..c2d901ca81d47638cf32a652631f8562859bda74
--- /dev/null
+++ b/basic/source/state.c
@@ -0,0 +1,127 @@
+#include <stdio.h>
+#include <string.h>
+#include "mem.h"
+#include "util.h"
+#include "state.h"
+
+State state;
+
+void State_Init(void) {
+	state.types        = NULL;
+	state.typeNum      = 0;
+	state.scopes[0]    = NULL;
+	state.scopeSize[0] = 0;
+	state.scopeNum     = 1;
+	state.funcs        = NULL;
+	state.funcNum      = 0;
+}
+
+void State_AddType(Type type) {
+	++ state.typeNum;
+	state.types = SafeRealloc(state.types, state.typeNum * sizeof(Type));
+	state.types[state.typeNum - 1] = type;
+}
+
+void State_AddVar(Var var) {
+	size_t scope = state.scopeNum - 1;
+
+	++ state.scopeSize[scope];
+	state.scopes[scope] = SafeRealloc(
+		state.scopes[scope], state.scopeSize[scope] * sizeof(Var)
+	);
+	state.scopes[scope][state.scopeSize[scope] - 1] = var;
+}
+
+bool State_AddScope(void) {
+	if (state.scopeNum >= 16) return false;
+
+	++ state.scopeNum;
+	state.scopeSize[state.scopeNum - 1] = 0;
+	state.scopes[state.scopeNum - 1]    = NULL;
+	return true;
+}
+
+void State_AddFunc(Function func) {
+	++ state.funcNum;
+	state.funcs = SafeRealloc(state.funcs, state.funcNum * sizeof(Function));
+	state.funcs[state.funcNum - 1] = func;
+}
+
+void State_FreeScope(void) {
+	Var*   scope    = state.scopes[state.scopeNum - 1];
+	size_t scopeNum = state.scopeSize[state.scopeNum - 1];
+
+	for (size_t i = 0; i < scopeNum; ++ i) {
+		free(scope[i].name);
+	}
+
+	-- state.scopeNum;
+}
+
+Var* State_GetVar(const char* name) {
+	for (size_t i = state.scopeNum; i --> 0;) {
+		for (size_t j = 0; j < state.scopeSize[i]; ++ j) {
+			if (strcmp(state.scopes[i][j].name, name) == 0) {
+				return &state.scopes[i][j];
+			}
+		}
+	}
+
+	return NULL;
+}
+
+Type* State_GetType(const char* name) {
+	for (size_t i = 0; i < state.typeNum; ++ i) {
+		if (strcmp(state.types[i].name, name) == 0) {
+			return &state.types[i];
+		}
+	}
+
+	return NULL;
+}
+
+Function* State_GetFunc(const char* name) {
+	for (size_t i = 0; i < state.funcNum; ++ i) {
+		if (strcmp(state.funcs[i].name, name) == 0) {
+			return &state.funcs[i];
+		}
+	}
+
+	return NULL;
+}
+
+void State_MakePrimitive(const char* name, size_t size, int primType) {
+	Type type;
+	type.type     = TYPE_PRIM;
+	type.name     = NewString(name);
+	type.size     = size;
+	type.primType = primType;
+	State_AddType(type);
+}
+
+bool State_UsedTypeEq(UsedType a, UsedType b) {
+	return (a.ptr == b.ptr) && (a.typeIdx == b.typeIdx) && (a.array == b.array);
+}
+
+void State_DumpInfo(void) {
+	puts("TYPES");
+	puts("=====");
+	for (size_t i = 0; i < state.typeNum; ++ i) {
+		printf("- %s\n", state.types[i].name);
+	}
+	puts("\n");
+
+	puts("GLOBALS");
+	puts("=======");
+	for (size_t i = 0; i < state.scopeSize[0]; ++ i) {
+		printf("- %s\n", state.scopes[0][i].name);
+	}
+	puts("\n");
+
+	puts("FUNCTIONS");
+	puts("=========");
+	for (size_t i = 0; i < state.funcNum; ++ i) {
+		printf("- %s\n", state.funcs[i].name);
+	}
+	puts("\n");
+}




diff --git a/basic/source/state.h b/basic/source/state.h
new file mode 100644
index 0000000000000000000000000000000000000000..bff6a6faeea769784107c85cb53b3b8e1e1da066
--- /dev/null
+++ b/basic/source/state.h
@@ -0,0 +1,80 @@
+#ifndef N_STATE_H
+#define N_STATE_H
+
+#include "common.h"
+
+enum {
+	TYPE_PRIM, // is this "primitive", or Primula the manul?
+	TYPE_STRUCT,
+	TYPE_UNIT
+};
+
+enum {
+	PRIM_UINT,
+	PRIM_INT,
+	PRIM_FLOAT
+};
+
+typedef struct {
+	size_t ptr;
+	size_t typeIdx;
+	size_t array; // 0 if not array, contains length if not array
+} UsedType;
+
+typedef struct {
+	int    type;
+	char*  name;
+	size_t size;
+
+	// only for "struct" types
+	UsedType* structure;
+	size_t    structLen;
+
+	// only for "primitive" types
+	int primType;
+} Type;
+
+typedef struct {
+	UsedType type;
+	char*    name;
+} Var;
+
+typedef struct {
+	UsedType type;
+	char*    name;
+} FuncParam;
+
+typedef struct {
+	bool       sub;
+	UsedType   ret;
+	FuncParam* params;
+	size_t     paramsLen;
+	char*      name;
+} Function;
+
+typedef struct {
+	Type*     types;
+	size_t    typeNum;
+	Var*      scopes[16];
+	size_t    scopeSize[16];
+	size_t    scopeNum;
+	Function* funcs;
+	size_t    funcNum;
+} State;
+
+extern State state;
+
+void      State_Init(void);
+void      State_AddType(Type type);
+void      State_AddVar(Var var);
+bool      State_AddScope(void);
+void      State_AddFunc(Function func);
+void      State_FreeScope(void);
+Var*      State_GetVar(const char* name);
+Type*     State_GetType(const char* name);
+Function* State_GetFunc(const char* name);
+void      State_MakePrimitive(const char* name, size_t size, int primType);
+bool      State_UsedTypeEq(UsedType a, UsedType b);
+void      State_DumpInfo(void);
+
+#endif




diff --git a/basic/source/string.c b/basic/source/string.c
new file mode 100644
index 0000000000000000000000000000000000000000..7e42275935d2fa0e7044dae2263a0c206aadf2de
--- /dev/null
+++ b/basic/source/string.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <string.h>
+#include "mem.h"
+#include "string.h"
+
+String String_New(void) {
+	char* contents = SafeMalloc(1);
+	*contents      = 0;
+
+	return (String) {
+		contents, 1
+	};
+}
+
+String String_FromChar(char ch) {
+	String ret = String_New();
+	String_AddChar(&ret, ch);
+	return ret;
+}
+
+void String_Free(String* string) {
+	free(string->contents);
+}
+
+void String_Add(String* string, char* value) {
+	size_t newLen = strlen(string->contents) + strlen(value) + 1;
+	size_t newCap = string->capacity;
+
+	while (newCap < newLen) {
+		newCap *= 2;
+	}
+
+	if (newCap != string->capacity) {
+		string->capacity = newCap;
+		string->contents = SafeRealloc(string->contents, newCap);
+	}
+
+	strcat(string->contents, value);
+}
+
+void String_AddChar(String* string, char ch) {
+	char str[] = {ch, 0};
+	String_Add(string, str);
+}
+
+size_t String_Len(String* string) {
+	return strlen(string->contents);
+}




diff --git a/basic/source/string.h b/basic/source/string.h
new file mode 100644
index 0000000000000000000000000000000000000000..98e30f87cba52e57745b1493d7be6621416d8936
--- /dev/null
+++ b/basic/source/string.h
@@ -0,0 +1,18 @@
+#ifndef N_STRING_H
+#define N_STRING_H
+
+#include "common.h"
+
+typedef struct {
+	char*  contents;
+	size_t capacity;
+} String;
+
+String String_New(void);
+String String_FromChar(char ch);
+void   String_Free(String* string);
+void   String_Add(String* string, char* value);
+void   String_AddChar(String* string, char ch);
+size_t String_Len(String* string);
+
+#endif




diff --git a/basic/source/util.c b/basic/source/util.c
new file mode 100644
index 0000000000000000000000000000000000000000..3524bb0b4c307b7a93a3b07c1c2c13fe438d9956
--- /dev/null
+++ b/basic/source/util.c
@@ -0,0 +1,16 @@
+#include <string.h>
+#include "mem.h"
+#include "common.h"
+
+char* NewString(const char* src) {
+	char* ret = SafeMalloc(strlen(src) + 1);
+	strcpy(ret, src);
+	return ret;
+}
+
+char* ConcatString(const char* first, const char* second) {
+	char* ret = SafeMalloc(strlen(first) + strlen(second) + 1);
+	strcpy(ret, first);
+	strcat(ret, second);
+	return ret;
+}




diff --git a/basic/source/util.h b/basic/source/util.h
new file mode 100644
index 0000000000000000000000000000000000000000..aa57880a037cf533331c0e4cdedfcef4953b4465
--- /dev/null
+++ b/basic/source/util.h
@@ -0,0 +1,22 @@
+#ifndef N_UTIL_H
+#define N_UTIL_H
+
+#include <stdio.h>
+
+#define FUNCTION_POINTER(TYPE, NAME, ...) TYPE (*NAME)(__VA_ARGS__)
+#define MIN(A, B) (((A) < (B))? (A) : (B))
+#define MAX(A, B) (((A) > (B))? (A) : (B))
+
+#define SWAP(TYPE, A, B) \
+	do { \
+		TYPE* macro##a   = &(A); \
+		TYPE* macro##b   = &(B); \
+		TYPE  macro##tmp = *macro##a; \
+		*macro##a = *macro##b; \
+		*macro##b = macro##tmp; \
+	} while (0);
+
+char* NewString(const char* src);
+char* ConcatString(const char* first, const char* second);
+
+#endif




diff --git a/basic/test.bas b/basic/test.bas
new file mode 100644
index 0000000000000000000000000000000000000000..af13a8b74c4cbcb49dd5ac303c39c0578ebf48fb
--- /dev/null
+++ b/basic/test.bas
@@ -0,0 +1,20 @@
+'NITRON
+
+dim foo as int
+
+func manul(foo as int, bar as int) int
+	dim bar as u32
+end
+
+manul 1, (2 * 3) + -5 * 4
+
+foo = 3 + 4
+
+dim a as int
+dim b as f32
+dim c as int
+
+manul a + c, 5
+
+dim myPtr as ptr(int)
+myPtr = myPtr * 5




diff --git a/basic-boot/.gitignore b/basic-boot/.gitignore
deleted file mode 100644
index 631d23f7093ec4d51a6bf2ededb3b33c0630dd30..0000000000000000000000000000000000000000
--- a/basic-boot/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-nitronb




diff --git a/basic-boot/Makefile b/basic-boot/Makefile
deleted file mode 100644
index 785906776db0027224492a1270d26eb186a99c75..0000000000000000000000000000000000000000
--- a/basic-boot/Makefile
+++ /dev/null
@@ -1,49 +0,0 @@
-SOURCES := $(wildcard source/*.c) $(wildcard source/**/*.c)
-OBJECTS := $(patsubst source/%.c,bin/%.o,$(SOURCES))
-OUT     := nitronb
-LD      := $(CC)
-
-override CFLAGS += -std=c99 -Wall -Wextra -Wuninitialized -Wundef -pedantic
-override LDLIBS += -lm
-
-ifeq ($(BUILD),release)
-	override CFLAGS += -O3
-	#override CPPFLAGS += -NDEBUG
-else
-	override CFLAGS += -Og -g
-	ifeq ($(ASAN), y)
-		override CFLAGS += -fno-omit-frame-pointer -fsanitize=address
-		override LDFLAGS += -fsanitize=address
-	endif
-endif
-
-.SECONDEXPANSION:
-
-deps.filter := %.c %.h
-deps.option := -MM
-define deps
-$$(filter $$(deps.filter),,$$(shell $(CC) $(CFLAGS) $(CPPFLAGS) -E $(deps.option) $(1)))
-endef
-
-all: $(OUT)
-	@:
-
-run: $(OUT)
-	'$(dir $<)$(notdir $<)' $(RUNFLAGS)
-
-$(OUT): $(OBJECTS)
-	$(LD) $(LDFLAGS) $^ $(LDLIBS) -o $@
-
-bin/:
-	mkdir -p bin
-
-bin/%.o: source/%.c $(call deps,source/%.c) | bin/
-	$(CC) $(CFLAGS) $(CPPFLAGS) $< -c -o $@
-
-clean:
-	rm -r bin
-
-distclean: clean
-	rm $(OUT)
-
-.PHONY: all run clean distclean




diff --git a/basic-boot/example.bas b/basic-boot/example.bas
deleted file mode 100644
index 2267bac07513ff4c398f934c27ab667969854ed0..0000000000000000000000000000000000000000
--- a/basic-boot/example.bas
+++ /dev/null
@@ -1,10 +0,0 @@
-'NITRON
-
-extern C func puts(ptr(char)) int
-
-puts "Hello, world!"
-
-dim ret as int
-ret = puts("Hello again, world!")
-
-quit ret




diff --git a/basic-boot/source/common.h b/basic-boot/source/common.h
deleted file mode 100644
index 94f4fb2029fd67dc73686715ded950e26256c9a6..0000000000000000000000000000000000000000
--- a/basic-boot/source/common.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef N_COMMON_H
-#define N_COMMON_H
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdbool.h>
-
-#endif




diff --git a/basic-boot/source/error.c b/basic-boot/source/error.c
deleted file mode 100644
index 2149922c18c01d2bd86757208d040c3983d37e91..0000000000000000000000000000000000000000
--- a/basic-boot/source/error.c
+++ /dev/null
@@ -1,67 +0,0 @@
-#include <stdio.h>
-#include <stdarg.h>
-#include <assert.h>
-#include "mem.h"
-#include "error.h"
-
-#define FMT(RET, FORMAT) do { \
-	int     n    = 0; \
-	size_t  size = 0; \
-	va_list ap; \
-\
-	va_start(ap, FORMAT); \
-	n = vsnprintf(RET, size, FORMAT, ap); \
-	va_end(ap); \
-\
-	assert(n >= 0); \
-\
-	size = n + 1; \
-	RET = (char*) SafeMalloc(size); \
-	if (RET == NULL) { \
-		return; \
-	} \
-\
-	va_start(ap, FORMAT); \
-	n = vsnprintf(RET, size, FORMAT, ap); \
-	va_end(ap); \
-\
-	assert(n >= 0); \
-} while (0);
-
-void PrintError(ErrorInfo err, const char* format, ...) {
-	char* ret = NULL;
-
-	FMT(ret, format);
-
-	#if 0
-		fprintf(stderr, "error: %s:%d: %s\n", err.file, (int) err.line, ret);
-	#else
-		#define RED   "\x1b[31m"
-		#define BLUE  "\x1b[34m"
-		#define RESET "\x1b[0m"
-		fprintf(
-			stderr, RED "error: " BLUE "%s:%d: " RESET "%s\n",
-			err.file, (int) err.line, ret
-		);
-	#endif
-
-	exit(1);
-}
-
-void PrintWarning(ErrorInfo err, const char* format, ...) {
-	char* ret = NULL;
-
-	FMT(ret, format);
-
-	#if 0
-		fprintf(stderr, "error: %s:%d: %s\n", err.file, (int) err.line, ret);
-	#else
-		#define YELLOW "\x1b[33m"
-		#define BLUE   "\x1b[34m"
-		#define RESET  "\x1b[0m"
-		fprintf(
-			stderr, YELLOW "warning: " BLUE "%s:%d: " RESET "%s\n",
-			err.file, (int) err.line, ret
-		);
-	#endif
-}




diff --git a/basic-boot/source/error.h b/basic-boot/source/error.h
deleted file mode 100644
index de5ff9f8e56c78843c3d6aefae933fcc325e3de4..0000000000000000000000000000000000000000
--- a/basic-boot/source/error.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef N_ERROR_H
-#define N_ERROR_H
-
-#include "common.h"
-
-typedef struct {
-	const char* file;
-	size_t      line;
-} ErrorInfo;
-
-void PrintError(ErrorInfo err, const char* format, ...);
-void PrintWarning(ErrorInfo err, const char* format, ...);
-
-#endif




diff --git a/basic-boot/source/lexer.c b/basic-boot/source/lexer.c
deleted file mode 100644
index 06ae344d28b518f6745713a23d6a05af98a2075c..0000000000000000000000000000000000000000
--- a/basic-boot/source/lexer.c
+++ /dev/null
@@ -1,224 +0,0 @@
-#include <stdio.h>
-#include <assert.h>
-#include <string.h>
-#include "lexer.h"
-#include "string.h"
-
-Lexer Lexer_Init(FILE* file, const char* fileName) {
-	Lexer lexer;
-	lexer.file     = file;
-	lexer.success  = true;
-	lexer.fileName = fileName;
-	lexer.line     = 1;
-	return lexer;
-}
-
-void Lexer_Free(Lexer* lexer) {
-	fclose(lexer->file);
-}
-
-static bool IsSeparator(char ch) {
-	switch (ch) {
-		case ' ':
-		case '\n':
-		case '\t':
-		case '(':
-		case ')':
-		case ',':
-		case '+':
-		case '-':
-		case '*':
-		case '/':
-		case '%':
-		case '=':
-		case '<':
-		case '>':
-		case '\'': return true;
-		default:   return false;
-	}
-}
-
-static bool IsNumeric(const char* str) {
-	size_t len = strlen(str);
-
-	for (size_t i = 0; i < len; ++ i) {
-		if ((str[i] < '0') || (str[i] > '9')) return false;
-	}
-
-	return true;
-}
-
-#define GET_CHAR(VAR) \
-	do { \
-		if (fread(&VAR, 1, 1, lexer->file) != 1) { \
-			return LEXER_EOF; \
-		} \
-		if (VAR == '\n') ++ lexer->line; \
-	} while (0);
-
-#define GET_CHAR_ERROR(VAR) \
-	do { \
-		if (fread(&VAR, 1, 1, lexer->file) != 1) { \
-			ErrorInfo err = {lexer->fileName, lexer->line}; \
-			PrintError(err, "Unexpected EOF"); \
-			lexer->success = false; \
-			return LEXER_EOF; \
-		} \
-		if (VAR == '\n') ++ lexer->line; \
-	} while (0);
-
-#define TOKEN(TYPE, CONTENTS) ((Token) \
-	{(TYPE), (ErrorInfo) {lexer->fileName, lexer->line}, (CONTENTS)})
-
-int Lexer_Next(Lexer* lexer, Token* token) {
-	char ch;
-
-	GET_CHAR(ch)
-
-	switch (ch) {
-		case '"': {
-			String string = String_New();
-
-			while (true) {
-				GET_CHAR_ERROR(ch)
-				if (ch == '"') break;
-
-				String_AddChar(&string, ch);
-			}
-
-			*token = TOKEN(TOKEN_STRING, string.contents);
-			return LEXER_TOKEN;
-		}
-		case '(':  *token = TOKEN(TOKEN_LPAREN,   NULL); return LEXER_TOKEN;
-		case ')':  *token = TOKEN(TOKEN_RPAREN,   NULL); return LEXER_TOKEN;
-		case ',':  *token = TOKEN(TOKEN_COMMA,    NULL); return LEXER_TOKEN;
-		case '+':  *token = TOKEN(TOKEN_ADD,      NULL); return LEXER_TOKEN;
-		case '-':  *token = TOKEN(TOKEN_SUBTRACT, NULL); return LEXER_TOKEN;
-		case '*':  *token = TOKEN(TOKEN_MULTIPLY, NULL); return LEXER_TOKEN;
-		case '/':  *token = TOKEN(TOKEN_DIVIDE,   NULL); return LEXER_TOKEN;
-		case '%':  *token = TOKEN(TOKEN_MOD,      NULL); return LEXER_TOKEN;
-		case '=':  *token = TOKEN(TOKEN_EQUAL,    NULL); return LEXER_TOKEN;
-		case '<':  *token = TOKEN(TOKEN_LESS,     NULL); return LEXER_TOKEN;
-		case '>':  *token = TOKEN(TOKEN_GREATER,  NULL); return LEXER_TOKEN;
-		case '\n': *token = TOKEN(TOKEN_LINE,     NULL); return LEXER_TOKEN;
-		case '\'': {
-			while (true) {
-				GET_CHAR(ch);
-
-				if (ch == '\n') {
-					return LEXER_NONE;
-				}
-			}
-			return LEXER_EOF;
-		}
-		case ' ':
-		case '\t':  return LEXER_NONE;
-		default: {
-			// must be an identifier, keyword, or integer
-			String string = String_FromChar(ch);
-
-			while (true) {
-				GET_CHAR_ERROR(ch)
-				if (IsSeparator(ch)) {
-					fseek(lexer->file, -1, SEEK_CUR);
-					if (ch == '\n') -- lexer->line;
-					break;
-				}
-
-				String_AddChar(&string, ch);
-			}
-
-			if (IsNumeric(string.contents)) {
-				*token = TOKEN(TOKEN_INT, string.contents);
-				return LEXER_TOKEN;
-			}
-			if (!strcmp(string.contents, "extern")) {
-				*token = TOKEN(TOKEN_EXTERN, NULL);
-				String_Free(&string);
-				return LEXER_TOKEN;
-			}
-			if (!strcmp(string.contents, "func") || !strcmp(string.contents, "function")) {
-				*token = TOKEN(TOKEN_FUNC, NULL);
-				String_Free(&string);
-				return LEXER_TOKEN;
-			}
-			if (!strcmp(string.contents, "sub")) {
-				*token = TOKEN(TOKEN_SUB, NULL);
-				String_Free(&string);
-				return LEXER_TOKEN;
-			}
-			if (!strcmp(string.contents, "dim")) {
-				*token = TOKEN(TOKEN_DIM, NULL);
-				String_Free(&string);
-				return LEXER_TOKEN;
-			}
-			if (!strcmp(string.contents, "as")) {
-				*token = TOKEN(TOKEN_AS, NULL);
-				String_Free(&string);
-				return LEXER_TOKEN;
-			}
-			if (!strcmp(string.contents, "quit")) {
-				*token = TOKEN(TOKEN_QUIT, NULL);
-				String_Free(&string);
-				return LEXER_TOKEN;
-			}
-			if (!strcmp(string.contents, "ptr")) {
-				*token = TOKEN(TOKEN_PTR, NULL);
-				String_Free(&string);
-				return LEXER_TOKEN;
-			}
-			if (!strcmp(string.contents, "end")) {
-				*token = TOKEN(TOKEN_END, NULL);
-				String_Free(&string);
-				return LEXER_TOKEN;
-			}
-
-			*token = TOKEN(TOKEN_IDENTIFIER, string.contents);
-			return LEXER_TOKEN;
-		}
-	}
-}
-
-const char* Lexer_TypeVAsString(TokenType type) {
-	switch (type) {
-		case TOKEN_NULL:       return "null";
-		case TOKEN_EXTERN:     return "extern";
-		case TOKEN_FUNC:       return "func";
-		case TOKEN_SUB:        return "sub";
-		case TOKEN_DIM:        return "dim";
-		case TOKEN_AS:         return "as";
-		case TOKEN_QUIT:       return "quit";
-		case TOKEN_PTR:        return "ptr";
-		case TOKEN_END:        return "end";
-		case TOKEN_IDENTIFIER: return "identifier";
-		case TOKEN_STRING:     return "string";
-		case TOKEN_INT:        return "int";
-		case TOKEN_LINE:       return "line";
-		case TOKEN_LPAREN:     return "lparen";
-		case TOKEN_RPAREN:     return "rparen";
-		case TOKEN_COMMA:      return "comma";
-		case TOKEN_ADD:        return "add";
-		case TOKEN_SUBTRACT:   return "subtract";
-		case TOKEN_MULTIPLY:   return "multiply";
-		case TOKEN_DIVIDE:     return "divide";
-		case TOKEN_MOD:        return "mod";
-		case TOKEN_EQUAL:      return "equal";
-		case TOKEN_LESS:       return "less";
-		case TOKEN_GREATER:    return "greater";
-		default:               assert(0);
-	}
-}
-
-const char* Lexer_TypeAsString(Token* token) {
-	return Lexer_TypeVAsString(token->type);
-}
-
-void Lexer_PrintToken(Token* token) {
-	printf("%s: %s\n", Lexer_TypeAsString(token), token->contents);
-}
-
-void Lexer_FreeToken(Token* token) {
-	if (token->contents) {
-		free(token->contents);
-	}
-}




diff --git a/basic-boot/source/lexer.h b/basic-boot/source/lexer.h
deleted file mode 100644
index 0f3e15fa515f4e38f3882d1480fa461bb3e6a706..0000000000000000000000000000000000000000
--- a/basic-boot/source/lexer.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef N_LEXER_H
-#define N_LEXER_H
-
-#include <stdio.h>
-#include "error.h"
-#include "common.h"
-
-typedef enum {
-	TOKEN_NULL = 0,
-
-	// keywords
-	TOKEN_EXTERN,
-	TOKEN_FUNC,
-	TOKEN_SUB,
-	TOKEN_DIM,
-	TOKEN_AS,
-	TOKEN_QUIT,
-	TOKEN_PTR,
-	TOKEN_END,
-
-	// misc
-	TOKEN_IDENTIFIER,
-	TOKEN_STRING,
-	TOKEN_INT,
-	TOKEN_LINE,
-
-	// characters
-	TOKEN_LPAREN,
-	TOKEN_RPAREN,
-	TOKEN_COMMA,
-
-	// operators
-	TOKEN_ADD,
-	TOKEN_SUBTRACT,
-	TOKEN_MULTIPLY,
-	TOKEN_DIVIDE,
-	TOKEN_MOD,
-	TOKEN_EQUAL,
-	TOKEN_LESS,
-	TOKEN_GREATER
-} TokenType;
-
-enum {
-	LEXER_TOKEN,
-	LEXER_EOF,
-	LEXER_NONE
-};
-
-typedef struct {
-	TokenType type;
-	ErrorInfo err;
-	char*     contents;
-} Token;
-
-typedef struct {
-	FILE*       file;
-	bool        success;
-	const char* fileName;
-	size_t      line;
-} Lexer;
-
-Lexer       Lexer_Init(FILE* file, const char* fileName);
-void        Lexer_Free(Lexer* lexer);
-int         Lexer_Next(Lexer* lexer, Token* token);
-const char* Lexer_TypeVAsString(TokenType type);
-const char* Lexer_TypeAsString(Token* token);
-void        Lexer_PrintToken(Token* token);
-void        Lexer_FreeToken(Token* token);
-
-#endif




diff --git a/basic-boot/source/main.c b/basic-boot/source/main.c
deleted file mode 100644
index a8e3fa95d0afaa3a9ac44b0c75f86f336a150b30..0000000000000000000000000000000000000000
--- a/basic-boot/source/main.c
+++ /dev/null
@@ -1,76 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include "lexer.h"
-#include "state.h"
-#include "parser.h"
-#include "common.h"
-#include "semanticAnalysis.h"
-
-int main(int argc, char** argv) {
-	const char* source = NULL;
-
-	bool printNodes  = false;
-	bool printTokens = false;
-
-	if (argc == 1) {
-		printf(
-			"Usage: %s SOURCE [FLAGS]\n", argv[0]
-		);
-	}
-
-	for (int i = 1; i < argc; ++ i) {
-		if (argv[i][0] != '-') {
-			if (source) {
-				fprintf(stderr, "Source file set multiple times");
-				return 1;
-			}
-
-			source = argv[i];
-		}
-		else if (strcmp(argv[i], "--dp") == 0) {
-			printNodes = true;
-		}
-		else if (strcmp(argv[i], "--dl") == 0) {
-			printTokens = true;
-		}
-		else {
-			fprintf(stderr, "Unknown flag: %s\n", argv[i]);
-			return 1;
-		}
-	}
-
-	if (!source) {
-		fprintf(stderr, "No source file provided\n");
-		return 1;
-	}
-
-	FILE* sourceFile = fopen(source, "r");
-
-	if (!sourceFile) {
-		fprintf(stderr, "Failed to open '%s'\n", source);
-		return 1;
-	}
-
-	Parser parser = Parser_Init(sourceFile, source, printTokens);
-
-	size_t len;
-	Node*  ret = Parser_Parse(&parser, &len);
-
-	if (printNodes) {
-		for (size_t i = 0; i < len; ++ i) {
-			Parser_PrintNode(&ret[i]);
-		}
-
-		return 0;
-	}
-
-	SemanticAnalysis_Init(8);
-
-	for (size_t i = 0; i < len; ++ i){
-		SemanticAnalysis_Analyse(&ret[i]);
-	}
-
-	State_DumpInfo();
-
-	return 0;
-}




diff --git a/basic-boot/source/mem.c b/basic-boot/source/mem.c
deleted file mode 100644
index bcef6460e47b9108ddcc2f316a9836fd77855b03..0000000000000000000000000000000000000000
--- a/basic-boot/source/mem.c
+++ /dev/null
@@ -1,27 +0,0 @@
-#include <stdio.h>
-#include "mem.h"
-#include "common.h"
-
-void* SafeMalloc(size_t size) {
-	if (size == 0) return NULL;
-
-	void* ret = malloc(size);
-
-	if (ret == NULL) {
-		fprintf(stderr, "Malloc returned NULL\n");
-		exit(1);
-	}
-
-	return ret;
-}
-
-void* SafeRealloc(void* ptr, size_t size) {
-	void* ret = realloc(ptr, size);
-
-	if ((ret == NULL) && (size > 0)) {
-		fprintf(stderr, "Malloc returned NULL\n");
-		exit(1);
-	}
-
-	return ret;
-}




diff --git a/basic-boot/source/mem.h b/basic-boot/source/mem.h
deleted file mode 100644
index 1254b31036c2ac43f2f96bc0063f85ab6766d742..0000000000000000000000000000000000000000
--- a/basic-boot/source/mem.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef N_MEM_H
-#define N_MEM_H
-
-#include "common.h"
-
-#define NEW(T) (T*) SafeMalloc(sizeof(T))
-
-void* SafeMalloc(size_t size);
-void* SafeRealloc(void* ptr, size_t size);
-
-#endif




diff --git a/basic-boot/source/parser.c b/basic-boot/source/parser.c
deleted file mode 100644
index 2ae234724509cccb38c4a2cc5db7aacef610e5c1..0000000000000000000000000000000000000000
--- a/basic-boot/source/parser.c
+++ /dev/null
@@ -1,664 +0,0 @@
-#include <stdio.h>
-#include <assert.h>
-#include <string.h>
-#include "mem.h"
-#include "util.h"
-#include "parser.h"
-
-Parser Parser_Init(FILE* file, const char* fileName, bool printTokens) {
-	Parser ret;
-	Lexer  lexer = Lexer_Init(file, fileName);
-
-	Token* tokens    = SafeMalloc(64 * sizeof(Token));
-	size_t tokensLen = 0;
-	size_t tokensCap = 64;
-
-	bool lexing = true;
-	while (lexing) {
-		Token token;
-
-		int status = Lexer_Next(&lexer, &token);
-
-		switch (status) {
-			case LEXER_TOKEN: {
-				tokens[tokensLen] = token;
-				++ tokensLen;
-
-				if (tokensLen >= tokensCap) {
-					tokensCap += 64;
-					tokens     = SafeRealloc(tokens, tokensCap * sizeof(Token));
-				}
-				break;
-			}
-			case LEXER_EOF:  lexing = false; break;
-			case LEXER_NONE: continue;
-		}
-	}
-
-	tokens       = SafeRealloc(tokens, tokensLen * sizeof(Token));
-	ret.tokens   = tokens;
-	ret.tokenNum = tokensLen;
-	ret.i        = 0;
-
-	if (printTokens) {
-		for (size_t i = 0; i < tokensLen; ++ i) {
-			Lexer_PrintToken(&tokens[i]);
-		}
-
-		exit(0);
-	}
-
-	return ret;
-}
-
-#define INFO(TYPE) ((NodeInfo) {TYPE, p->tokens[p->i].err})
-
-static Node ParseBinary(Parser* p);
-static Node ParseNode(Parser* p);
-
-static void Advance(Parser* p) {
-	++ p->i;
-
-	if (p->i >= p->tokenNum) {
-		PrintError(p->tokens[p->tokenNum - 1].err, "Unexpected EOF");
-	}
-}
-
-static void Expect(Parser* p, TokenType type) {
-	if (p->tokens[p->i].type != type) {
-		Token token2;
-		token2.type = type;
-
-		PrintError(
-			p->tokens[p->i].err, "Expected %s, got %s",
-			Lexer_TypeAsString(&token2), Lexer_TypeAsString(&p->tokens[p->i])
-		);
-	}
-}
-
-static Node ParseAtom(Parser* p) {
-	Token* token = &p->tokens[p->i];
-	Node   ret;
-
-	Advance(p);
-
-	switch (token->type) {
-		case TOKEN_IDENTIFIER: {
-			ret.ident = (IdentifierNode) {INFO(NODE_IDENTIFIER), NewString(token->contents)};
-			return ret;
-		}
-		case TOKEN_STRING: {
-			ret.string = (StringNode) {INFO(NODE_STRING), NewString(token->contents)};
-			return ret;
-		}
-		case TOKEN_INT: {
-			ret.integer = (IntNode) {INFO(NODE_INT), atoi(token->contents)};
-			return ret;
-		}
-		case TOKEN_LPAREN: {
-			ret = ParseBinary(p);
-			Expect(p, TOKEN_RPAREN);
-			Advance(p);
-			return ret;
-		}
-		default: {
-			PrintError(
-				token->err, "Failed to parse atom, got %s",
-				Lexer_TypeAsString(token)
-			);
-			exit(1);
-		}
-	}
-}
-
-static bool IsUnaryOperator(Token* tok) {
-	switch (tok->type) {
-		case TOKEN_ADD:
-		case TOKEN_SUBTRACT: return true;
-		default:             return false;
-	}
-}
-
-static Node ParseUnary(Parser* p) {
-	Token* tok = &p->tokens[p->i];
-
-	if (IsUnaryOperator(tok)) {
-		Advance(p);
-		Node  operand    = ParseUnary(p);
-		Node* operandPtr = SafeMalloc(sizeof(Node));
-		*operandPtr      = operand;
-
-		Node ret;
-		ret.unary = (UnaryNode) {
-			INFO(NODE_UNARY_OP), operandPtr, tok->type
-		};
-		return ret;
-	}
-	else {
-		return ParseAtom(p);
-	}
-}
-
-static Node ParseMulDivMod(Parser* p) {
-	Node left = ParseUnary(p);
-
-	while (
-		(p->tokens[p->i].type == TOKEN_MULTIPLY) ||
-		(p->tokens[p->i].type == TOKEN_DIVIDE) ||
-		(p->tokens[p->i].type == TOKEN_MOD)
-	) {
-		TokenType op = p->tokens[p->i].type;
-		Advance(p);
-		Node right = ParseUnary(p);
-
-		Node* leftPtr  = SafeMalloc(sizeof(Node));
-		Node* rightPtr = SafeMalloc(sizeof(Node));
-		*leftPtr       = left;
-		*rightPtr      = right;
-
-		left.bin = (BinaryNode) {
-			INFO(NODE_BINARY_OP), leftPtr, rightPtr, op
-		};
-	}
-
-	return left;
-}
-
-static Node ParseAddSub(Parser* p) {
-	Node left = ParseMulDivMod(p);
-
-	while (
-		(p->tokens[p->i].type == TOKEN_ADD) ||
-		(p->tokens[p->i].type == TOKEN_SUB)
-	) {
-		TokenType op = p->tokens[p->i].type;
-		Advance(p);
-		Node right = ParseMulDivMod(p);
-
-		Node* leftPtr  = SafeMalloc(sizeof(Node));
-		Node* rightPtr = SafeMalloc(sizeof(Node));
-		*leftPtr       = left;
-		*rightPtr      = right;
-
-		left.bin = (BinaryNode) {
-			INFO(NODE_BINARY_OP), leftPtr, rightPtr, op
-		};
-	}
-
-	return left;
-}
-
-static Node ParseComparison(Parser* p) {
-	Node left = ParseAddSub(p);
-
-	while (
-		(p->tokens[p->i].type == TOKEN_EQUAL) ||
-		(p->tokens[p->i].type == TOKEN_LESS) ||
-		(p->tokens[p->i].type == TOKEN_GREATER)
-	) {
-		TokenType op = p->tokens[p->i].type;
-		Advance(p);
-		Node right = ParseAddSub(p);
-
-		Node* leftPtr  = SafeMalloc(sizeof(Node));
-		Node* rightPtr = SafeMalloc(sizeof(Node));
-		*leftPtr       = left;
-		*rightPtr      = right;
-
-		left.bin = (BinaryNode) {
-			INFO(NODE_BINARY_OP), leftPtr, rightPtr, op
-		};
-	}
-
-	return left;
-}
-
-static Node ParseBinary(Parser* p) {
-	return ParseComparison(p);
-}
-
-static Node ParseType(Parser* p) {
-	Node ret;
-
-	switch (p->tokens[p->i].type) {
-		case TOKEN_PTR: {
-			Advance(p);
-			Expect(p, TOKEN_LPAREN);
-			Advance(p);
-
-			Node innerType = ParseType(p);
-			ret.ptr = (PtrNode) {INFO(NODE_PTR), SafeMalloc(sizeof(Node))};
-			*ret.ptr.inner = innerType;
-
-			Advance(p);
-			Expect(p, TOKEN_RPAREN);
-			return ret;
-		}
-		case TOKEN_IDENTIFIER: {
-			ret.ident = (IdentifierNode) {
-				INFO(NODE_IDENTIFIER), NewString(p->tokens[p->i].contents)
-			};
-			return ret;
-		}
-		default: {
-			PrintError(
-				p->tokens[p->i].err, "Unexpected %s token in type",
-				Lexer_TypeAsString(&p->tokens[p->i])
-			);
-			exit(1);
-		}
-	}
-}
-
-static FuncDec ParseFuncDec(Parser* p) {
-	FuncDecParam* params    = NULL;
-	size_t        paramsNum = 0;
-
-	bool sub = p->tokens[p->i].type == TOKEN_SUB;
-
-	Advance(p);
-	FuncDec ret;
-
-	Expect(p, TOKEN_IDENTIFIER);
-	ret.name = NewString(p->tokens[p->i].contents);
-
-	Advance(p);
-	Expect(p, TOKEN_LPAREN);
-	Advance(p);
-
-	while (p->tokens[p->i].type != TOKEN_RPAREN) {
-		FuncDecParam param;
-		Expect(p, TOKEN_IDENTIFIER);
-		param.name = NewString(p->tokens[p->i].contents);
-
-		Advance(p);
-		Expect(p, TOKEN_AS);
-
-		Advance(p);
-		Node  typeNode = ParseType(p);
-		Node* typePtr  = SafeMalloc(sizeof(Node));
-		*typePtr       = typeNode;
-		param.type     = typePtr;
-
-		++ paramsNum;
-		params = SafeRealloc(params, paramsNum * sizeof(FuncDecParam));
-		params[paramsNum - 1] = param;
-
-		Advance(p);
-		if (
-			(p->tokens[p->i].type != TOKEN_COMMA) &&
-			(p->tokens[p->i].type != TOKEN_RPAREN)
-		) {
-			PrintError(
-				p->tokens[p->i].err, "Unexpected %s token in parameter list",
-				Lexer_TypeAsString(&p->tokens[p->i])
-			);
-		}
-
-		if (p->tokens[p->i].type == TOKEN_COMMA) {
-			Advance(p);
-		}
-	}
-
-	ret.params    = params;
-	ret.paramsNum = paramsNum;
-
-	Advance(p);
-	if (sub) {
-		Expect(p, TOKEN_LINE);
-	}
-	else {
-		ret.ret  = SafeMalloc(sizeof(Node));
-		*ret.ret = ParseType(p);
-		Advance(p);
-		Expect(p, TOKEN_LINE);
-	}
-
-	return ret;
-}
-
-static Node ParseFuncDef(Parser* p) {
-	FuncDefNode ret;
-	ret.i   = INFO(NODE_FUNC_DEF);
-	ret.dec = ParseFuncDec(p);
-	Advance(p);
-
-	ret.body    = NULL;
-	ret.bodyLen = 0;
-
-	while (p->tokens[p->i].type != TOKEN_END) {
-		if (p->tokens[p->i].type == TOKEN_LINE) {
-			Advance(p);
-			continue;
-		}
-
-		Node node = ParseNode(p);
-
-		++ ret.bodyLen;
-		ret.body = SafeRealloc(ret.body, ret.bodyLen * sizeof(Node));
-		ret.body[ret.bodyLen - 1] = node;
-	}
-
-	Advance(p);
-	Expect(p, TOKEN_LINE);
-
-	Node node;
-	node.funcDef = ret;
-	return node;
-}
-
-static Node ParseDim(Parser* p) {
-	DimNode ret;
-	ret.i = INFO(NODE_DIM);
-
-	Advance(p);
-	Expect(p, TOKEN_IDENTIFIER);
-	ret.name = NewString(p->tokens[p->i].contents);
-
-	Advance(p);
-	Expect(p, TOKEN_AS);
-
-	Advance(p);
-	ret.varType  = SafeMalloc(sizeof(Node));
-	*ret.varType = ParseType(p);
-
-
-	Advance(p);
-	Expect(p, TOKEN_LINE);
-
-	Node node;
-	node.dim = ret;
-	return node;
-}
-
-static Node ParseFuncCall(Parser* p) {
-	FuncCallNode ret;
-	ret.i    = INFO(NODE_FUNC_CALL);
-	ret.func = NewString(p->tokens[p->i].contents);
-
-	Advance(p);
-	Node*  params    = NULL;
-	size_t paramsNum = 0;
-
-	while (p->tokens[p->i].type != TOKEN_LINE) {
-		Node param = ParseBinary(p);
-
-		++ paramsNum;
-		params                = SafeRealloc(params, paramsNum * sizeof(Node));
-		params[paramsNum - 1] = param;
-
-		if (
-			(p->tokens[p->i].type != TOKEN_LINE) &&
-			(p->tokens[p->i].type != TOKEN_COMMA)
-		) {
-			PrintError(
-				p->tokens[p->i].err, "Unexpected %s token in parameter list",
-				Lexer_TypeAsString(&p->tokens[p->i])
-			);
-		}
-		if (p->tokens[p->i].type == TOKEN_COMMA) {
-			Advance(p);
-		}
-	}
-
-	ret.params    = params;
-	ret.paramsNum = paramsNum;
-
-	Node node;
-	node.funcCall = ret;
-	return node;
-}
-
-static Node ParseAssign(Parser* p) {
-	AssignNode ret;
-	ret.i        = INFO(NODE_ASSIGN);
-	ret.variable = NewString(p->tokens[p->i].contents);
-
-	Advance(p);
-	Expect(p, TOKEN_EQUAL);
-	Advance(p);
-
-	ret.rValue  = SafeMalloc(sizeof(Node));
-	*ret.rValue = ParseBinary(p);
-
-	Expect(p, TOKEN_LINE);
-
-	Node node;
-	node.assign = ret;
-	return node;
-}
-
-static Node ParseNode(Parser* p) {
-	switch (p->tokens[p->i].type) {
-		case TOKEN_FUNC:
-		case TOKEN_SUB: return ParseFuncDef(p);
-		case TOKEN_DIM: return ParseDim(p);
-		case TOKEN_IDENTIFIER: {
-			if (p->i == p->tokenNum - 1) {
-				PrintError(p->tokens[p->i].err, "Unexpected EOF");
-			}
-
-			if (p->tokens[p->i + 1].type == TOKEN_EQUAL) {
-				return ParseAssign(p);
-			}
-
-			return ParseFuncCall(p);
-		}
-		default: {
-			PrintError(
-				p->tokens[p->i].err, "Unexpected %s token",
-				Lexer_TypeAsString(&p->tokens[p->i])
-			);
-			exit(1);
-		}
-	}
-}
-
-void Parser_PrintNode(Node* node) {
-	switch (node->i.type) {
-		case NODE_INT: printf("%d", node->integer.value); break;
-		case NODE_STRING: printf("%s", node->string.value); break;
-		case NODE_IDENTIFIER: printf("%s", node->ident.name); break;
-		case NODE_BINARY_OP: {
-			printf("(");
-			Parser_PrintNode(node->bin.left);
-
-			switch (node->bin.op) {
-				case TOKEN_ADD:      printf("+"); break;
-				case TOKEN_SUBTRACT: printf("-"); break;
-				case TOKEN_MULTIPLY: printf("*"); break;
-				case TOKEN_DIVIDE:   printf("/"); break;
-				case TOKEN_MOD:      printf("%%"); break;
-				case TOKEN_EQUAL:    printf("="); break;
-				case TOKEN_LESS:     printf("<"); break;
-				case TOKEN_GREATER:  printf(">"); break;
-				default:             assert(0);
-			}
-
-			Parser_PrintNode(node->bin.right);
-			printf(")");
-			break;
-		}
-		case NODE_UNARY_OP: {
-			printf("(");
-
-			switch (node->unary.op) {
-				case TOKEN_ADD:      printf("+"); break;
-				case TOKEN_SUBTRACT: printf("-"); break;
-				default:             assert(0);
-			}
-
-			Parser_PrintNode(node->unary.operand);
-			printf(")");
-			break;
-		}
-		case NODE_PTR: {
-			printf("ptr(");
-			Parser_PrintNode(node->ptr.inner);
-			break;
-		}
-		case NODE_FUNC_DEF: {
-			if (node->funcDef.dec.ret) {
-				printf("func ");
-			}
-			else {
-				printf("sub ");
-			}
-
-			printf("%s(", node->funcDef.dec.name);
-
-			for (size_t i = 0; i < node->funcDef.dec.paramsNum; ++ i) {
-				printf("%s as ", node->funcDef.dec.params[i].name);
-				Parser_PrintNode(node->funcDef.dec.params[i].type);
-
-				if (i < node->funcDef.dec.paramsNum - 1) {
-					printf(", ");
-				}
-			}
-
-			printf(")\n");
-
-			for (size_t i = 0; i < node->funcDef.bodyLen; ++ i) {
-				printf("\t");
-				Parser_PrintNode(&node->funcDef.body[i]);
-				puts("");
-			}
-			puts("end");
-			break;
-		}
-		case NODE_DIM: {
-			printf("dim %s as ", node->dim.name);
-			Parser_PrintNode(node->dim.varType);
-			puts("");
-			break;
-		}
-		case NODE_FUNC_CALL: {
-			printf("%s ", node->funcCall.func);
-
-			for (size_t i = 0; i < node->funcCall.paramsNum; ++ i) {
-				Parser_PrintNode(&node->funcCall.params[i]);
-
-				if (i < node->funcCall.paramsNum - 1) {
-					printf(", ");
-				}
-			}
-			puts("");
-			break;
-		}
-		case NODE_ASSIGN: {
-			printf("%s = ", node->assign.variable);
-			Parser_PrintNode(node->assign.rValue);
-			puts("");
-			break;
-		}
-		default: {
-			printf("unknown %d\n", node->i.type);
-			assert(0);
-		}
-	}
-}
-
-Node* Parser_Parse(Parser* p, size_t* lenOut) {
-	Node*  ret = NULL;
-	size_t len = 0;
-
-	for (p->i = 0; p->i < p->tokenNum; ++ p->i) {
-		if (p->tokens[p->i].type == TOKEN_LINE) continue;
-
-		Node node = ParseNode(p);
-
-		++ len;
-		ret          = SafeRealloc(ret, len * sizeof(Node));
-		ret[len - 1] = node;
-	}
-
-	for (size_t i = 0; i < p->tokenNum; ++ i) {
-		Lexer_FreeToken(&p->tokens[i]);
-	}
-	free(p->tokens);
-
-	*lenOut = len;
-	return ret;
-}
-
-void Parser_FreeNode(Node* node) {
-	switch (node->i.type) {
-		case NODE_STRING:     free(node->string.value); break;
-		case NODE_IDENTIFIER: free(node->ident.name);  break;
-		case NODE_BINARY_OP: {
-			Parser_FreeNode(node->bin.left);
-			Parser_FreeNode(node->bin.right);
-			free(node->bin.left);
-			free(node->bin.right);
-			break;
-		}
-		case NODE_UNARY_OP: {
-			Parser_FreeNode(node->unary.operand);
-			free(node->unary.operand);
-			break;
-		}
-		case NODE_PTR: {
-			Parser_FreeNode(node->ptr.inner);
-			free(node->ptr.inner);
-			break;
-		}
-		case NODE_FUNC_DEF: {
-			if (node->funcDef.dec.ret) {
-				Parser_FreeNode(node->funcDef.dec.ret);
-				free(node->funcDef.dec.ret);
-			}
-
-			for (size_t i = 0; i < node->funcDef.dec.paramsNum; ++ i) {
-				Parser_FreeNode(node->funcDef.dec.params[i].type);
-				free(node->funcDef.dec.params[i].type);
-			}
-
-			free(node->funcDef.dec.name);
-
-			for (size_t i = 0; i < node->funcDef.bodyLen; ++ i) {
-				Parser_FreeNode(&node->funcDef.body[i]);
-			}
-			free(node->funcDef.body);
-			break;
-		}
-		case NODE_DIM: {
-			free(node->dim.name);
-			Parser_FreeNode(node->dim.varType);
-			free(node->dim.varType);
-			break;
-		}
-		case NODE_FUNC_CALL: {
-			free(node->funcCall.func);
-
-			for (size_t i = 0; i < node->funcCall.paramsNum; ++ i) {
-				Parser_FreeNode(&node->funcCall.params[i]);
-			}
-			free(node->funcCall.params);
-			break;
-		}
-		case NODE_ASSIGN: {
-			free(node->assign.variable);
-			Parser_FreeNode(node->assign.rValue);
-			free(node->assign.rValue);
-			break;
-		}
-		default: return;
-	}
-}
-
-const char* Parser_TypeStr(NodeType type) {
-	switch (type) {
-		case NODE_NONE:       return "none";
-		case NODE_INT:        return "int";
-		case NODE_STRING:     return "string";
-		case NODE_IDENTIFIER: return "identifier";
-		case NODE_BINARY_OP:  return "binary operation";
-		case NODE_UNARY_OP:   return "unary operation";
-		case NODE_PTR:        return "ptr";
-		case NODE_FUNC_DEF:   return "function definition";
-		case NODE_DIM:        return "dim";
-		case NODE_FUNC_CALL:  return "function call";
-		case NODE_ASSIGN:     return "assignment";
-		default:              return "???";
-	}
-}




diff --git a/basic-boot/source/parser.h b/basic-boot/source/parser.h
deleted file mode 100644
index 9e643df70c4de70613cbcd791b17f86efca21126..0000000000000000000000000000000000000000
--- a/basic-boot/source/parser.h
+++ /dev/null
@@ -1,127 +0,0 @@
-#ifndef N_PARSER_H
-#define N_PARSER_H
-
-#include "lexer.h"
-#include "error.h"
-#include "common.h"
-
-typedef enum {
-	NODE_NONE,
-	NODE_INT,
-	NODE_STRING,
-	NODE_IDENTIFIER,
-	NODE_BINARY_OP,
-	NODE_UNARY_OP,
-	NODE_PTR,
-	NODE_FUNC_DEF,
-	NODE_DIM,
-	NODE_FUNC_CALL,
-	NODE_ASSIGN
-} NodeType;
-
-typedef struct {
-	NodeType  type;
-	ErrorInfo err;
-} NodeInfo;
-
-typedef union Node Node;
-
-typedef struct {
-	NodeInfo i;
-	int      value;
-} IntNode;
-
-typedef struct {
-	NodeInfo i;
-	char*    value;
-} StringNode;
-
-typedef struct {
-	NodeInfo i;
-	char*    name;
-} IdentifierNode;
-
-typedef struct {
-	NodeInfo  i;
-	Node*     left;
-	Node*     right;
-	TokenType op;
-} BinaryNode;
-
-typedef struct {
-	NodeInfo  i;
-	Node*     operand;
-	TokenType op;
-} UnaryNode;
-
-typedef struct {
-	NodeInfo i;
-	Node*    inner;
-} PtrNode;
-
-typedef struct {
-	Node*  type;
-	char*  name;
-} FuncDecParam;
-
-typedef struct {
-	FuncDecParam* params;
-	size_t        paramsNum;
-	Node*         ret;
-	char*         name;
-} FuncDec;
-
-typedef struct {
-	NodeInfo i;
-	FuncDec  dec;
-	Node*    body;
-	size_t   bodyLen;
-} FuncDefNode;
-
-typedef struct {
-	NodeInfo i;
-	char*    name;
-	Node*    varType;
-} DimNode;
-
-typedef struct {
-	NodeInfo i;
-	char*    func;
-	Node*    params;
-	size_t   paramsNum;
-} FuncCallNode;
-
-typedef struct {
-	NodeInfo i;
-	char*    variable;
-	Node*    rValue;
-} AssignNode;
-
-union Node {
-	NodeInfo       i;
-	IntNode        integer;
-	StringNode     string;
-	IdentifierNode ident;
-	BinaryNode     bin;
-	UnaryNode      unary;
-	PtrNode        ptr;
-	FuncDefNode    funcDef;
-	DimNode        dim;
-	FuncCallNode   funcCall;
-	AssignNode     assign;
-};
-
-typedef struct {
-	Token* tokens;
-	size_t tokenNum;
-	size_t i;
-} Parser;
-
-Parser Parser_Init(FILE* file, const char* fileName, bool printTokens);
-void   Parser_PrintNode(Node* node);
-Node*  Parser_Parse(Parser* p, size_t* lenOut);
-void   Parser_FreeNode(Node* node);
-
-const char* Parser_TypeStr(NodeType type);
-
-#endif




diff --git a/basic-boot/source/semanticAnalysis.c b/basic-boot/source/semanticAnalysis.c
deleted file mode 100644
index c275153bb71bcc85fc06e2e5a9372f795796eb04..0000000000000000000000000000000000000000
--- a/basic-boot/source/semanticAnalysis.c
+++ /dev/null
@@ -1,382 +0,0 @@
-#include <assert.h>
-#include <string.h>
-#include "mem.h"
-#include "util.h"
-#include "error.h"
-#include "state.h"
-#include "parser.h"
-#include "semanticAnalysis.h"
-
-void SemanticAnalysis_Init(size_t wordSize) {
-	State_Init();
-
-	// add primitive types
-	State_MakePrimitive("u8",    1,        PRIM_UINT);
-	State_MakePrimitive("i8",    1,        PRIM_INT);
-	State_MakePrimitive("u16",   2,        PRIM_UINT);
-	State_MakePrimitive("i16",   2,        PRIM_INT);
-	State_MakePrimitive("u32",   4,        PRIM_UINT);
-	State_MakePrimitive("i32",   4,        PRIM_INT);
-	State_MakePrimitive("u64",   8,        PRIM_UINT);
-	State_MakePrimitive("i64",   8,        PRIM_INT);
-	State_MakePrimitive("uint",  wordSize, PRIM_UINT);
-	State_MakePrimitive("int",   wordSize, PRIM_INT);
-	State_MakePrimitive("char",  1,        PRIM_UINT);
-	State_MakePrimitive("f32",   4,        PRIM_FLOAT);
-	State_MakePrimitive("f64",   8,        PRIM_FLOAT);
-	State_MakePrimitive("bool",  1,        PRIM_UINT);
-	State_MakePrimitive("__lit", 0,        PRIM_INT);
-
-	Type type;
-	type.name = NewString("unit");
-	type.type = TYPE_UNIT;
-	State_AddType(type);
-}
-
-void Expect(Node* node, NodeType type) {
-	if (node->i.type != type) {
-		PrintError(node->i.err,
-			"Expected %s, got %s", Parser_TypeStr(type),
-			Parser_TypeStr(node->i.type)
-		);
-	}
-}
-
-void Unexpected(Node* node) {
-	PrintError(node->i.err, "Unexpected %s", Parser_TypeStr(node->i.type));
-}
-
-static UsedType NodeAsUsedType(Node* node) {
-	UsedType ret;
-	ret.ptr   = 0;
-	ret.array = false; // no arrays yet
-
-	while (true) {
-		switch (node->i.type) {
-			case NODE_PTR: {
-				node = node->ptr.inner;
-				++ ret.ptr;
-				continue;
-			}
-			case NODE_IDENTIFIER: {
-				Type* type = State_GetType(node->ident.name);
-
-				if (!type) {
-					PrintError(node->i.err, "Type '%s' does not exist", node->ident.name);
-				}
-
-				ret.typeIdx = type - state.types;
-				return ret;
-			}
-			default: {
-				Unexpected(node);
-				exit(1);
-			}
-		}
-	}
-}
-
-static Type* GetTypeFromUsed(UsedType type) {
-	return &state.types[type.typeIdx];
-}
-
-#define GET_PRIM(NAME, PTR) \
-	((UsedType) {(PTR), State_GetType(NAME) - state.types, 0})
-
-static UsedType EvalType(Node* node);
-
-static UsedType EvalBinOp(Node* node) {
-	UsedType left  = EvalType(node->bin.left);
-	UsedType right = EvalType(node->bin.right);
-
-	Type* leftType  = GetTypeFromUsed(left);
-	Type* rightType = GetTypeFromUsed(right);
-
-	if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) {
-		PrintError(node->i.err, "You can only use primitive types in binary expressions");
-	}
-
-	if (left.ptr) {
-		if (right.ptr) {
-			switch (node->bin.op) {
-				case TOKEN_EQUAL:
-				case TOKEN_LESS:
-				case TOKEN_GREATER: return GET_PRIM("bool", 0);
-				case TOKEN_SUB:     return GET_PRIM("uint", 0);
-				default: {
-					PrintError(
-						node->i.err, "Operation '%s' not allowed here",
-						Lexer_TypeVAsString(node->bin.op)
-					);
-				}
-			}
-		}
-		else {
-			switch (node->bin.op) {
-				case TOKEN_ADD:
-				case TOKEN_SUB: {
-					if (rightType->primType == PRIM_FLOAT) {
-						PrintError(node->i.err, "Cannot add float to pointer");
-					}
-					return left;
-				}
-				default: {
-					PrintError(
-						node->i.err, "Operation '%s' not allowed here",
-						Lexer_TypeVAsString(node->bin.op)
-					);
-				}
-			}
-		}
-	}
-	else if (right.ptr) {
-		switch (node->bin.op) {
-			case TOKEN_ADD:
-			case TOKEN_SUB: {
-				if (leftType->primType == PRIM_FLOAT) {
-					PrintError(node->i.err, "Cannot add float to pointer");
-				}
-				return right;
-			}
-			default: {
-				PrintError(
-					node->i.err, "Operation '%s' not allowed here",
-					Lexer_TypeVAsString(node->bin.op)
-				);
-			}
-		}
-	}
-	else if (!right.ptr) {
-		switch (node->bin.op) {
-			case TOKEN_EQUAL: return GET_PRIM("bool", 0);
-			case TOKEN_LESS:
-			case TOKEN_GREATER: {
-				if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) {
-					PrintError(
-						node->i.err, "Operation '%s' only works on primitives",
-						Lexer_TypeVAsString(node->bin.op)
-					);
-				}
-				return GET_PRIM("bool", 0);
-			}
-			case TOKEN_ADD:
-			case TOKEN_SUBTRACT:
-			case TOKEN_MULTIPLY:
-			case TOKEN_DIVIDE:
-			case TOKEN_MOD: {
-				if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) {
-					PrintError(
-						node->i.err, "Operation '%s' only works on primitives",
-						Lexer_TypeVAsString(node->bin.op)
-					);
-				}
-
-				if (leftType->primType == rightType->primType) {
-					if (leftType->size > rightType->size) {
-						return left;
-					}
-					else {
-						return right;
-					}
-				}
-				else {
-					PrintError(
-						node->i.err, "Incompatible types in '%s' operation",
-						Lexer_TypeVAsString(node->bin.op)
-					);
-				}
-			}
-			default: break;
-		}
-
-		PrintError(
-			node->i.err, "Operation '%s' not allowed here",
-			Lexer_TypeVAsString(node->bin.op)
-		);
-	}
-
-	exit(1);
-}
-
-static UsedType EvalType(Node* node) {
-	static bool inFunc = false;
-
-	switch (node->i.type) {
-		case NODE_INT:    return GET_PRIM("__lit", 0);
-		case NODE_STRING: return GET_PRIM("char", 1);
-		case NODE_IDENTIFIER: {
-			Var* var = State_GetVar(node->ident.name);
-
-			if (!var) {
-				PrintError(
-					node->i.err, "Variable '%s' doesn't exist", node->ident.name
-				);
-			}
-
-			return var->type;
-		}
-		case NODE_BINARY_OP: return EvalBinOp(node);
-		case NODE_UNARY_OP: {
-			UsedType operand = EvalType(node->unary.operand);
-			Type*    type    = GetTypeFromUsed(operand);
-
-			if (type->type != TYPE_PRIM) {
-				PrintError(node->i.err,
-					"Only primitive types can be used with unary operators"
-				);
-			}
-
-			if ((type->primType == PRIM_UINT) && (node->unary.op == TOKEN_SUBTRACT)) {
-				PrintWarning(node->i.err, "Using negative sign on unsigned value");
-			}
-
-			return operand;
-		}
-		case NODE_PTR: {
-			UsedType type = EvalType(node->ptr.inner);
-			++ type.ptr;
-			return type;
-		}
-		case NODE_FUNC_DEF: {
-			if (inFunc) {
-				PrintError(node->i.err, "Nested function definitions are not allowed");
-			}
-
-			Function func;
-			func.sub = node->funcDef.dec.ret != NULL;
-
-			if (!func.sub) {
-				func.ret = NodeAsUsedType(node->funcDef.dec.ret);
-			}
-
-			func.paramsLen = node->funcDef.dec.paramsNum;
-			func.params    = SafeMalloc(func.paramsLen * sizeof(FuncParam));
-
-			for (size_t i = 0; i < func.paramsLen; ++ i) {
-				func.params[i].type = NodeAsUsedType(
-					node->funcDef.dec.params[i].type
-				);
-				func.params[i].name = node->funcDef.dec.params[i].name;
-			}
-
-			func.name = NewString(node->funcDef.dec.name);
-			State_AddFunc(func);
-
-			// now analyse the body
-			State_AddScope();
-			inFunc = true;
-
-			// add parameters to this scope
-			for (size_t i = 0; i < func.paramsLen; ++ i) {
-				Var var;
-				var.type = func.params[i].type;
-				var.name = NewString(func.params[i].name);
-				State_AddVar(var);
-			}
-
-			for (size_t i = 0; i < node->funcDef.bodyLen; ++ i) {
-				EvalType(&node->funcDef.body[i]);
-			}
-			inFunc = false;
-			State_FreeScope();
-			return GET_PRIM("unit", 0);
-		}
-		case NODE_FUNC_CALL: {
-			Function* func = State_GetFunc(node->funcCall.func);
-			if (!func) {
-				PrintError(node->i.err, "Function %s does not exist", node->funcCall.func);
-			}
-
-			if (func->paramsLen != node->funcCall.paramsNum) {
-				PrintError(
-					node->i.err, "Expected %d parameters, got %d",
-					func->paramsLen, node->funcCall.paramsNum
-				);
-			}
-
-			for (size_t i = 0; i < func->paramsLen; ++ i) {
-				UsedType type      = EvalType(&node->funcCall.params[i]);
-				UsedType param     = func->params[i].type;
-				Type*    paramType = GetTypeFromUsed(param);
-				Type*    callType  = GetTypeFromUsed(type);
-
-				bool paramLit = strcmp(callType->name, "__lit") == 0;
-
-				if (!State_UsedTypeEq(type, param)) {
-					if (
-						(callType->type == TYPE_PRIM) && (paramType->type == TYPE_PRIM) &&
-						(callType->primType == paramType->primType) &&
-						(paramType->size >= callType->size)
-					) {
-						continue;
-					}
-
-					if (paramLit && (paramType->type == TYPE_PRIM)) continue;
-
-					PrintError(node->i.err,
-						"Expected %s for parameter %s, got %s",
-						paramType->name, func->params[i].name, callType->name
-					);
-				}
-			}
-
-			if (func->sub) {
-				return GET_PRIM("unit", 0);
-			}
-			else {
-				return func->ret;
-			}
-		}
-		case NODE_DIM: {
-			Var var;
-			var.type = NodeAsUsedType(node->dim.varType);
-			var.name = NewString(node->dim.name);
-			State_AddVar(var);
-			return GET_PRIM("unit", 0);
-		}
-		case NODE_ASSIGN: {
-			Var* var = State_GetVar(node->assign.variable);
-
-			if (!var) {
-				PrintError(
-					node->i.err, "Variable '%s' doesn't exist",
-					node->assign.variable
-				);
-			}
-
-			UsedType left  = var->type;
-			UsedType right = EvalType(node->assign.rValue);
-
-			Type* leftType  = GetTypeFromUsed(left);
-			Type* rightType = GetTypeFromUsed(right);
-
-			bool compatible = true;
-
-			if ((left.ptr != right.ptr) || (leftType->type != rightType->type)) {
-				compatible = false;
-			}
-			else if (leftType->type == TYPE_PRIM) {
-				if (leftType->primType != rightType->primType) compatible = false;
-				else if (leftType->size < rightType->size)     compatible = false;
-			}
-
-			if (!compatible) {
-				PrintError(
-					node->i.err, "Type '%s' cannot be assigned to '%s' variable",
-					GetTypeFromUsed(left)->name, GetTypeFromUsed(right)->name
-				);
-			}
-
-			return GET_PRIM("unit", 0);
-		}
-		default: {
-			Unexpected(node);
-		}
-	}
-
-	assert(0);
-}
-
-void SemanticAnalysis_Analyse(Node* node) {
-	EvalType(node);
-}




diff --git a/basic-boot/source/semanticAnalysis.h b/basic-boot/source/semanticAnalysis.h
deleted file mode 100644
index 341ccb8ac4a96ebcd1d94b58b4aad9fa914a881e..0000000000000000000000000000000000000000
--- a/basic-boot/source/semanticAnalysis.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef N_SEMANTIC_ANALYSIS_H
-#define N_SEMANTIC_ANALYSIS_H
-
-#include "common.h"
-
-void SemanticAnalysis_Init(size_t wordSize);
-void SemanticAnalysis_Analyse(Node* node);
-
-#endif




diff --git a/basic-boot/source/state.c b/basic-boot/source/state.c
deleted file mode 100644
index c2d901ca81d47638cf32a652631f8562859bda74..0000000000000000000000000000000000000000
--- a/basic-boot/source/state.c
+++ /dev/null
@@ -1,127 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include "mem.h"
-#include "util.h"
-#include "state.h"
-
-State state;
-
-void State_Init(void) {
-	state.types        = NULL;
-	state.typeNum      = 0;
-	state.scopes[0]    = NULL;
-	state.scopeSize[0] = 0;
-	state.scopeNum     = 1;
-	state.funcs        = NULL;
-	state.funcNum      = 0;
-}
-
-void State_AddType(Type type) {
-	++ state.typeNum;
-	state.types = SafeRealloc(state.types, state.typeNum * sizeof(Type));
-	state.types[state.typeNum - 1] = type;
-}
-
-void State_AddVar(Var var) {
-	size_t scope = state.scopeNum - 1;
-
-	++ state.scopeSize[scope];
-	state.scopes[scope] = SafeRealloc(
-		state.scopes[scope], state.scopeSize[scope] * sizeof(Var)
-	);
-	state.scopes[scope][state.scopeSize[scope] - 1] = var;
-}
-
-bool State_AddScope(void) {
-	if (state.scopeNum >= 16) return false;
-
-	++ state.scopeNum;
-	state.scopeSize[state.scopeNum - 1] = 0;
-	state.scopes[state.scopeNum - 1]    = NULL;
-	return true;
-}
-
-void State_AddFunc(Function func) {
-	++ state.funcNum;
-	state.funcs = SafeRealloc(state.funcs, state.funcNum * sizeof(Function));
-	state.funcs[state.funcNum - 1] = func;
-}
-
-void State_FreeScope(void) {
-	Var*   scope    = state.scopes[state.scopeNum - 1];
-	size_t scopeNum = state.scopeSize[state.scopeNum - 1];
-
-	for (size_t i = 0; i < scopeNum; ++ i) {
-		free(scope[i].name);
-	}
-
-	-- state.scopeNum;
-}
-
-Var* State_GetVar(const char* name) {
-	for (size_t i = state.scopeNum; i --> 0;) {
-		for (size_t j = 0; j < state.scopeSize[i]; ++ j) {
-			if (strcmp(state.scopes[i][j].name, name) == 0) {
-				return &state.scopes[i][j];
-			}
-		}
-	}
-
-	return NULL;
-}
-
-Type* State_GetType(const char* name) {
-	for (size_t i = 0; i < state.typeNum; ++ i) {
-		if (strcmp(state.types[i].name, name) == 0) {
-			return &state.types[i];
-		}
-	}
-
-	return NULL;
-}
-
-Function* State_GetFunc(const char* name) {
-	for (size_t i = 0; i < state.funcNum; ++ i) {
-		if (strcmp(state.funcs[i].name, name) == 0) {
-			return &state.funcs[i];
-		}
-	}
-
-	return NULL;
-}
-
-void State_MakePrimitive(const char* name, size_t size, int primType) {
-	Type type;
-	type.type     = TYPE_PRIM;
-	type.name     = NewString(name);
-	type.size     = size;
-	type.primType = primType;
-	State_AddType(type);
-}
-
-bool State_UsedTypeEq(UsedType a, UsedType b) {
-	return (a.ptr == b.ptr) && (a.typeIdx == b.typeIdx) && (a.array == b.array);
-}
-
-void State_DumpInfo(void) {
-	puts("TYPES");
-	puts("=====");
-	for (size_t i = 0; i < state.typeNum; ++ i) {
-		printf("- %s\n", state.types[i].name);
-	}
-	puts("\n");
-
-	puts("GLOBALS");
-	puts("=======");
-	for (size_t i = 0; i < state.scopeSize[0]; ++ i) {
-		printf("- %s\n", state.scopes[0][i].name);
-	}
-	puts("\n");
-
-	puts("FUNCTIONS");
-	puts("=========");
-	for (size_t i = 0; i < state.funcNum; ++ i) {
-		printf("- %s\n", state.funcs[i].name);
-	}
-	puts("\n");
-}




diff --git a/basic-boot/source/state.h b/basic-boot/source/state.h
deleted file mode 100644
index bff6a6faeea769784107c85cb53b3b8e1e1da066..0000000000000000000000000000000000000000
--- a/basic-boot/source/state.h
+++ /dev/null
@@ -1,80 +0,0 @@
-#ifndef N_STATE_H
-#define N_STATE_H
-
-#include "common.h"
-
-enum {
-	TYPE_PRIM, // is this "primitive", or Primula the manul?
-	TYPE_STRUCT,
-	TYPE_UNIT
-};
-
-enum {
-	PRIM_UINT,
-	PRIM_INT,
-	PRIM_FLOAT
-};
-
-typedef struct {
-	size_t ptr;
-	size_t typeIdx;
-	size_t array; // 0 if not array, contains length if not array
-} UsedType;
-
-typedef struct {
-	int    type;
-	char*  name;
-	size_t size;
-
-	// only for "struct" types
-	UsedType* structure;
-	size_t    structLen;
-
-	// only for "primitive" types
-	int primType;
-} Type;
-
-typedef struct {
-	UsedType type;
-	char*    name;
-} Var;
-
-typedef struct {
-	UsedType type;
-	char*    name;
-} FuncParam;
-
-typedef struct {
-	bool       sub;
-	UsedType   ret;
-	FuncParam* params;
-	size_t     paramsLen;
-	char*      name;
-} Function;
-
-typedef struct {
-	Type*     types;
-	size_t    typeNum;
-	Var*      scopes[16];
-	size_t    scopeSize[16];
-	size_t    scopeNum;
-	Function* funcs;
-	size_t    funcNum;
-} State;
-
-extern State state;
-
-void      State_Init(void);
-void      State_AddType(Type type);
-void      State_AddVar(Var var);
-bool      State_AddScope(void);
-void      State_AddFunc(Function func);
-void      State_FreeScope(void);
-Var*      State_GetVar(const char* name);
-Type*     State_GetType(const char* name);
-Function* State_GetFunc(const char* name);
-void      State_MakePrimitive(const char* name, size_t size, int primType);
-bool      State_UsedTypeEq(UsedType a, UsedType b);
-void      State_DumpInfo(void);
-
-#endif




diff --git a/basic-boot/source/string.c b/basic-boot/source/string.c
deleted file mode 100644
index 7e42275935d2fa0e7044dae2263a0c206aadf2de..0000000000000000000000000000000000000000
--- a/basic-boot/source/string.c
+++ /dev/null
@@ -1,48 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include "mem.h"
-#include "string.h"
-
-String String_New(void) {
-	char* contents = SafeMalloc(1);
-	*contents      = 0;
-
-	return (String) {
-		contents, 1
-	};
-}
-
-String String_FromChar(char ch) {
-	String ret = String_New();
-	String_AddChar(&ret, ch);
-	return ret;
-}
-
-void String_Free(String* string) {
-	free(string->contents);
-}
-
-void String_Add(String* string, char* value) {
-	size_t newLen = strlen(string->contents) + strlen(value) + 1;
-	size_t newCap = string->capacity;
-
-	while (newCap < newLen) {
-		newCap *= 2;
-	}
-
-	if (newCap != string->capacity) {
-		string->capacity = newCap;
-		string->contents = SafeRealloc(string->contents, newCap);
-	}
-
-	strcat(string->contents, value);
-}
-
-void String_AddChar(String* string, char ch) {
-	char str[] = {ch, 0};
-	String_Add(string, str);
-}
-
-size_t String_Len(String* string) {
-	return strlen(string->contents);
-}




diff --git a/basic-boot/source/string.h b/basic-boot/source/string.h
deleted file mode 100644
index 98e30f87cba52e57745b1493d7be6621416d8936..0000000000000000000000000000000000000000
--- a/basic-boot/source/string.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef N_STRING_H
-#define N_STRING_H
-
-#include "common.h"
-
-typedef struct {
-	char*  contents;
-	size_t capacity;
-} String;
-
-String String_New(void);
-String String_FromChar(char ch);
-void   String_Free(String* string);
-void   String_Add(String* string, char* value);
-void   String_AddChar(String* string, char ch);
-size_t String_Len(String* string);
-
-#endif




diff --git a/basic-boot/source/util.c b/basic-boot/source/util.c
deleted file mode 100644
index 3524bb0b4c307b7a93a3b07c1c2c13fe438d9956..0000000000000000000000000000000000000000
--- a/basic-boot/source/util.c
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <string.h>
-#include "mem.h"
-#include "common.h"
-
-char* NewString(const char* src) {
-	char* ret = SafeMalloc(strlen(src) + 1);
-	strcpy(ret, src);
-	return ret;
-}
-
-char* ConcatString(const char* first, const char* second) {
-	char* ret = SafeMalloc(strlen(first) + strlen(second) + 1);
-	strcpy(ret, first);
-	strcat(ret, second);
-	return ret;
-}




diff --git a/basic-boot/source/util.h b/basic-boot/source/util.h
deleted file mode 100644
index aa57880a037cf533331c0e4cdedfcef4953b4465..0000000000000000000000000000000000000000
--- a/basic-boot/source/util.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef N_UTIL_H
-#define N_UTIL_H
-
-#include <stdio.h>
-
-#define FUNCTION_POINTER(TYPE, NAME, ...) TYPE (*NAME)(__VA_ARGS__)
-#define MIN(A, B) (((A) < (B))? (A) : (B))
-#define MAX(A, B) (((A) > (B))? (A) : (B))
-
-#define SWAP(TYPE, A, B) \
-	do { \
-		TYPE* macro##a   = &(A); \
-		TYPE* macro##b   = &(B); \
-		TYPE  macro##tmp = *macro##a; \
-		*macro##a = *macro##b; \
-		*macro##b = macro##tmp; \
-	} while (0);
-
-char* NewString(const char* src);
-char* ConcatString(const char* first, const char* second);
-
-#endif




diff --git a/basic-boot/test.bas b/basic-boot/test.bas
deleted file mode 100644
index af13a8b74c4cbcb49dd5ac303c39c0578ebf48fb..0000000000000000000000000000000000000000
--- a/basic-boot/test.bas
+++ /dev/null
@@ -1,20 +0,0 @@
-'NITRON
-
-dim foo as int
-
-func manul(foo as int, bar as int) int
-	dim bar as u32
-end
-
-manul 1, (2 * 3) + -5 * 4
-
-foo = 3 + 4
-
-dim a as int
-dim b as f32
-dim c as int
-
-manul a + c, 5
-
-dim myPtr as ptr(int)
-myPtr = myPtr * 5