G

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

AuthorJakob Wakeling <[email protected]>
Date2023-06-20 12:35:52
Commit861312df2e6595ce69883e19124fd938e76939d4
Parentf782ab77f3815498c6a66fa186799d604ac01196

Implement address of and dereference code gen

Diffstat

M examples/main.g | 7 ++++---
M src/analyse.c | 14 ++++++--------
M src/llvm.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
M src/llvm.h | 5 +++--
M src/log.c | 3 +++
M src/log.h | 1 +
M src/main.c | 8 +++++---
M src/parse.c | 43 +++++++++++++++++--------------------------
M src/parse.h | 2 ++
M src/type.c | 4 ++++
M src/type.h | 4 +++-

11 files changed, 101 insertions, 67 deletions

diff --git a/examples/main.g b/examples/main.g
index bb04a56..ca0c6c4 100644
--- a/examples/main.g
+++ b/examples/main.g
@@ -1,4 +1,5 @@
-main :: proc() -> u32 {
-	var : *u32 = &u32(4);
-	return *var;
+pepo :: proc() -> u64 {
+	var1 : f32 = f32(1.0);
+	var2 : *f32 = &var1;
+	return u64(var2);
 }
diff --git a/src/analyse.c b/src/analyse.c
index 5ee38bc..e77bd8e 100644
--- a/src/analyse.c
+++ b/src/analyse.c
@@ -16,7 +16,6 @@
 static void analyse_stmt(ast *a, syt *st);
 static void analyse_stmt_comp(ast *a, syt *st);
 static void analyse_stmt_decl(ast *a, syt *st);
-static void analyse_stmt_expr(ast *a, syt *st);
 static void analyse_stmt_return(ast *a, syt *st);
 
 static void analyse_expr(ast *a, syt *st);
@@ -39,7 +38,7 @@ static void analyse_stmt(ast *a, syt *st) {
 	case AK_COMP:   { analyse_stmt_comp(a, st);   } break;
 	case AK_DECL:   { analyse_stmt_decl(a, st);   } break;
 	case AK_RETURN: { analyse_stmt_return(a, st); } break;
-	default:        { analyse_stmt_expr(a, st);   } break;
+	default:        { analyse_expr(a, st);        } break;
 	}
 }
 
@@ -102,19 +101,18 @@ static void analyse_stmt_decl(ast *a, syt *st) {
 	else { note("TODO", a->ln, a->cl, -1, "unhandled value kind %s", ast_ks[a->c.a[0]->k]); }
 }
 
-/* Analyse an expression statement. */
-static void analyse_stmt_expr(ast *a, syt *st) {
-	analyse_expr(a, st);
-}
-
 /* Analyse a return statement. */
 static void analyse_stmt_return(ast *a, syt *st) {
 	/* TODO Check if the return type matches or is compatible with the given value */
+	analyse_expr(C[0], st);
 }
 
 /* Analyse an expression. */
 static void analyse_expr(ast *a, syt *st) {
 	if (A.k == AK_PROC) { analyse_expr_proc(a, st); }
+	if (A.k == AK_OP_DRF) { A.t = ast_type(C[0], st)->base; }
+	
+	for (UINT i = 0; i < A.c.al; i += 1) { analyse_expr(C[i], st); }
 }
 
 /* Analyse a procedure expression. */
diff --git a/src/llvm.c b/src/llvm.c
index b3cb5fb..2b315a5 100644
--- a/src/llvm.c
+++ b/src/llvm.c
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+static char *file_name = NULL, *module_name = NULL;
 static LLVMContextRef llvm_context = NULL;
 static LLVMModuleRef  llvm_module  = NULL;
 static LLVMBuilderRef llvm_builder = NULL;
@@ -44,7 +45,7 @@ static LLVMValueRef llvm_flt(ast *a);
 
 static LLVMValueRef llvm_hash(ast *a, syt *st);
 
-static inline void llvm_init(void);
+static inline void llvm_init(char *file);
 static inline void llvm_free(void);
 static LLVMTypeRef llvm_type(type *t);
 static LLVMValueRef llvm_ival(type *t);
@@ -53,8 +54,8 @@ static LLVMValueRef llvm_ival(type *t);
 #define C (a->c.a) /* AST child shorthand "C[i]" */
 
 /* Generate IR from an AST with LLVM. */
-void llvm(ast *a) {
-	llvm_init();
+void llvm(ast *a, char *file) {
+	llvm_init(file);
 
 	/* Generate IR for all child nodes */
 	for (UINT i = 0; i < a->c.al; i += 1) { llvm_stmt_decl(a->c.a[i], &a->st); }
@@ -85,8 +86,8 @@ void llvm(ast *a) {
 	LLVMDisposeTargetMachine(machine); llvm_free();
 }
 
-void llvm_bitcode(ast *a) {
-	llvm_init();
+void llvm_bitcode(ast *a, char *file) {
+	llvm_init(file);
 
 	/* Generate IR for all child nodes */
 	for (UINT i = 0; i < a->c.al; i += 1) { llvm_stmt_decl(a->c.a[i], &a->st); }
@@ -100,8 +101,8 @@ void llvm_bitcode(ast *a) {
 	LLVMDisposeMessage(err); llvm_free();
 }
 
-void llvm_ir(ast *a) {
-	llvm_init();
+void llvm_ir(ast *a, char *file) {
+	llvm_init(file);
 
 	/* Generate IR for all child nodes */
 	for (UINT i = 0; i < a->c.al; i += 1) { llvm_stmt_decl(a->c.a[i], &a->st); }
@@ -126,7 +127,7 @@ static LLVMValueRef llvm_stmt(ast *a, syt *st) {
 	case AK_FOR:    { return llvm_stmt_for(a, st);      } break;
 	case AK_HASH_SYSCALL:
 		{ return llvm_hash(a, st); } break;
-	default:        { error(2, "llvm_stmt: Unhandled AST kind %s", ast_ks[a->k]); } break;
+	default: { note(file_name, a->ln, a->cl, -1, "Unhandled AST kind %s (llvm:llvm_stmt)", ast_ks[a->k]); } break;
 	}
 }
 
@@ -155,12 +156,12 @@ static LLVMValueRef llvm_stmt_decl(ast *a, syt *st) {
 	}
 	else {
 		if (a->p->k == AK_PROG) { /* Global */
-			LLVMValueRef v = LLVMAddGlobal(llvm_module, llvm_type(A.t), a->s);
+			LLVMValueRef v = LLVMAddGlobal(llvm_module, llvm_type(A.t), "");
 			LLVMSetInitializer(v, a->c.al ? llvm_expr(C[0], st) : llvm_ival(a->t));
 			a->llvm_v = v;
 		}
 		else { /* Local */
-			LLVMValueRef v = LLVMBuildAlloca(llvm_builder, llvm_type(A.t), a->s);
+			LLVMValueRef v = LLVMBuildAlloca(llvm_builder, llvm_type(A.t), "");
 			LLVMBuildStore(llvm_builder, a->c.al ? llvm_expr(C[0], st) : llvm_ival(a->t), v);
 			a->llvm_v = v;
 		}
@@ -181,7 +182,7 @@ static LLVMValueRef llvm_stmt_assn(ast *a, syt *st) {
 	);
 
 	ast *v = syt_search(st, a->s);
-	if (v == NULL) { error(2, "llvm_expr: Undefined variable %s", a->s); }
+	if (v == NULL) { note(file_name, a->ln, a->cl, 0, "Undefined variable %s (LLVM Failsafe)", a->s); }
 
 	switch (a->k) {
 	case AK_ASSIGN: {
@@ -244,9 +245,9 @@ static LLVMValueRef llvm_expr(ast *a, syt *st) {
 	case AK_FLT: { return llvm_flt(a); } break;
 	case AK_ID_VAR: {
 		ast *v = syt_search(st, a->s);
-		if (v == NULL) { error(2, "llvm_expr: Undefined variable %s", a->s); }
+		if (v == NULL) { note(file_name, A.ln, A.cl, 0, "Undefined variable %s", A.s); }
 
-		return LLVMBuildLoad2(llvm_builder, llvm_type(v->t), v->llvm_v, v->s);
+		return LLVMBuildLoad2(llvm_builder, llvm_type(v->t), v->llvm_v, "");
 	} break;
 	case AK_CALL: {
 		ast *v = syt_search(st, a->s);
@@ -254,7 +255,7 @@ static LLVMValueRef llvm_expr(ast *a, syt *st) {
 
 		if (!v->llvm_v) { error(2, "llvm_expr: Procedure follows"); }
 
-		return LLVMBuildCall2(llvm_builder, v->llvm_t, v->llvm_v, NULL, 0, v->s);
+		return LLVMBuildCall2(llvm_builder, v->llvm_t, v->llvm_v, NULL, 0, "");
 	} break;
 	case AK_OP_ADD: {
 		return LLVMBuildAdd(llvm_builder, llvm_expr(A.c.a[0], st), llvm_expr(A.c.a[1], st), "add");
@@ -272,14 +273,27 @@ static LLVMValueRef llvm_expr(ast *a, syt *st) {
 		return LLVMBuildSRem(llvm_builder, llvm_expr(A.c.a[0], st), llvm_expr(A.c.a[1], st), "mod");
 	} break;
 	case AK_OP_NEG: {
-		type *t = ast_type(A.c.a[0], st);
-		if (t == NULL) { note("TODO", A.ln, A.cl, -1, "Subtree is missing a type"); }
+		type *t = ast_type(C[0], st);
+		if (t == NULL) { note(file_name, A.ln, A.cl, -1, "Subtree is missing a type (llvm:llvm_expr)"); }
 
 		if (is_int(t)) { return LLVMBuildNeg(llvm_builder, llvm_expr(A.c.a[0], st), "neg"); }
 		else if (is_flt(t)) { return LLVMBuildFNeg(llvm_builder, llvm_expr(A.c.a[0], st), "neg"); }
-		else { note("TODO", A.ln, A.cl, -1, "Expression cannot be made negative (LLVM Failsafe)"); }
+		else { note(file_name, A.ln, A.cl, -1, "Expression cannot be made negative (LLVM Failsafe)"); }
+	} break;
+	case AK_OP_ADO: {
+		ast *v = syt_search(st, C[0]->s);
+		if (v == NULL) { note(file_name, A.ln, A.cl, -1, "Undefined variable (llvm:llvm_expr)"); }
+		if (v->llvm_v == NULL) { note(file_name, A.ln, A.cl, -1, "Variable follows (llvm:llvm_expr)"); }
+		
+		return v->llvm_v; /* TODO handle non-variables (?) */
+	} break;
+	case AK_OP_DRF: {
+		if (C[0]->t == NULL) { note(file_name, A.ln, A.cl, -1, "Child is missing a type (llvm:llvm_expr)"); }
+		if (C[0]->t->base == NULL) { note(file_name, A.ln, A.cl, -1, "Cannot be dereferenced (llvm:llvm_expr)"); }
+		
+		return LLVMBuildLoad2(llvm_builder, llvm_type(C[0]->t->base), llvm_expr(C[0], st), "");
 	}
-	default: { error(2, "llvm_expr unknown kind %s", ast_ks[a->k]); } break;
+	default: { note(file_name, A.ln, A.cl, -1, "Unhandled AST kind %s (llvm:llvm_expr)", ast_ks[a->k]); } break;
 	}
 }
 
@@ -294,7 +308,12 @@ static LLVMValueRef llvm_expr_cast(ast *a, syt *st) {
 
 	type *expr_type = ast_type(C[0], st);
 
-	if (is_int(expr_type)) {
+	if (is_ptr(expr_type)) {
+		if (is_int(a->t)) {
+			return LLVMBuildPtrToInt(llvm_builder, llvm_expr(C[0], st), llvm_type(a->t), "");
+		}
+	}
+	else if (is_int(expr_type)) {
 		if (is_int(a->t)) {
 			return LLVMBuildIntCast2(llvm_builder, llvm_expr(C[0], st), llvm_type(a->t), is_sign(a->t), "cast");
 		}
@@ -321,7 +340,7 @@ static LLVMValueRef llvm_expr_cast(ast *a, syt *st) {
 		}
 	}
 
-	note("TODO", a->ln, a->cl, -1, "llvm_expr_cast unhandled type %s or %s", expr_type->s, a->t->s);
+	note(file_name, A.ln, A.cl, -1, "unhandled type %s or %s (llvm:llvm_expr_cast)", expr_type->s, a->t->s);
 }
 
 /* Generate IR for an integer. */
@@ -377,9 +396,10 @@ static LLVMValueRef llvm_hash(ast *a, syt *st) {
 }
 
 /* Initialise LLVM. */
-static inline void llvm_init(void) {
+static inline void llvm_init(char *file) {
+	file_name = file;
 	llvm_context = LLVMGetGlobalContext();
-	llvm_module  = LLVMModuleCreateWithName("G");
+	llvm_module  = LLVMModuleCreateWithName(file_name);
 	llvm_builder = LLVMCreateBuilder();
 }
 
@@ -393,7 +413,8 @@ static inline void llvm_free(void) {
 /* Return the appropriate LLVMTypeRef for a G type. */
 static LLVMTypeRef llvm_type(type *t) {
 	switch (t->k) {
-	case TY_ZERO: { return LLVMVoidType();  } break;
+	case TY_VOID: { return LLVMVoidType();  } break;
+	case TY_PTR:  { return LLVMPointerType(llvm_type(t->base), 0); } break;
 	case TY_B8:   { return LLVMIntType(8);  } break;
 	case TY_B16:  { return LLVMIntType(16); } break;
 	case TY_B32:  { return LLVMIntType(32); } break;
@@ -417,6 +438,7 @@ static LLVMTypeRef llvm_type(type *t) {
 /* Return the default value for a G type. */
 static LLVMValueRef llvm_ival(type *t) {
 	switch (t->k) {
+	case TY_PTR:  { return LLVMConstNull(llvm_type(t->base)); } break;
 	case TY_B8:   { return LLVMConstInt(LLVMIntType(8),   0, false); } break;
 	case TY_B16:  { return LLVMConstInt(LLVMIntType(16),  0, false); } break;
 	case TY_B32:  { return LLVMConstInt(LLVMIntType(32),  0, false); } break;
diff --git a/src/llvm.h b/src/llvm.h
index c35ad06..3349152 100644
--- a/src/llvm.h
+++ b/src/llvm.h
@@ -8,8 +8,8 @@
 
 #include "parse.h"
 
-extern void llvm(ast *a);
-extern void llvm_bitcode(ast *a);
-extern void llvm_ir(ast *a);
+extern void llvm(ast *a, char *file);
+extern void llvm_bitcode(ast *a, char *file);
+extern void llvm_ir(ast *a, char *file);
 
 #endif // G_LLVM_H_CZUMSHFW
diff --git a/src/log.c b/src/log.c
index b32eb2e..8166eff 100644
--- a/src/log.c
+++ b/src/log.c
@@ -29,3 +29,6 @@ void note(const char *file, UINT ln, UINT cl, sint level, const char *format, ..
 
 	if (level == 0 && (log_count += 1) == log_limit) { fprintf(stderr, "fatal: error limit reached"); exit(1); }
 }
+
+/* Check if there has been an error. */
+bool has_error(void) { return log_count > 0; }
diff --git a/src/log.h b/src/log.h
index e469f35..bfb69d5 100644
--- a/src/log.h
+++ b/src/log.h
@@ -12,5 +12,6 @@ extern bool log_waerr;
 extern sint log_level, log_limit;
 
 extern void note(const char *file, UINT ln, UINT cl, sint level, const char *format, ...);
+extern bool has_error(void);
 
 #endif // G_LOG_H_1RPM5P9E
diff --git a/src/main.c b/src/main.c
index 4e412fd..21552d4 100644
--- a/src/main.c
+++ b/src/main.c
@@ -77,12 +77,14 @@ static void compile(const char * file, char *src, UINT len) {
 	ast *a = parse(&l);
 	if (pflag) { if (!qflag) { ast_print(a, 0); } goto end; }
 
+	if (has_error()) { exit(1); }
+	
 	analyse(a);
 	if (Pflag) { if (!qflag) { ast_print(a, 0); } goto end; }
 
-	if (bflag) { llvm_ir(a); }
-	else if (Bflag) { llvm_bitcode(a); }
-	else { llvm(a); }
+	if (bflag) { llvm_ir(a, strdup(file)); }
+	else if (Bflag) { llvm_bitcode(a, strdup(file)); }
+	else { llvm(a, strdup(file)); }
 
 	end:; return;
 }
diff --git a/src/parse.c b/src/parse.c
index 3274d20..ae3c65d 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -56,6 +56,10 @@ inline ast *ast_init(void) {
 	if (!a) { error(1, SERR); } return a;
 }
 
+inline void ast_free(ast **a) {
+	fprintf(stderr, "ast_free() NOT IMPLEMENTED\n"); /* TODO */
+}
+
 /* Initialise an AST node of a specific kind. */
 inline ast *ast_kind(ast_k kind) {
 	ast *a = ast_init(); a->k = kind; return a;
@@ -123,8 +127,8 @@ ast *parse(lex *l) {
 
 	/* Parse and append all child nodes */
 	for (ast *c; T.k != TK_EOF;) {
-		if ((c = parse_stmt_decl(l, &a->st, NULL))) { ast_push(a, c); }
-		else { error(1, "NULL AST (parse:parse_stmt_decl)"); }
+		if ((c = parse_stmt_decl(l, &a->st, NULL)) != NULL) { ast_push(a, c); }
+		else { note(l->n, T.ln, T.cl, -1, "NULL AST (parse:parse_stmt_decl)"); }
 	}
 
 	return a;
@@ -176,24 +180,18 @@ static ast *parse_stmt_decl(lex *l, syt *st, ast *a) {
 	if (T.k == TK_ID) {
 		ast *s = syt_search_h(st, T.h, T.s);
 
-		if (s == NULL) { error( /* ERROR */
-			1, "%s:%zu:%zu: error: use of undeclared identifier \"%s\"",
-			l->n, T.ln + 1, T.cl + 1, T.s
-		); }
-		if (s->k != AK_TYPE) { error( /* ERROR */
-			1, "%s:%zu:%zu: error: expected type identifier",
-			l->n, T.ln + 1, T.cl + 1
-		); }
+		if (s == NULL) { note(l->n, T.ln, T.cl, 0, "Use of undeclared identifier \"%s\"", T.s); }
+		if (s->k != AK_TYPE) { note(l->n, T.ln, T.cl, 0, "Expected type identifier"); }
 
 		a->t = type_ptrc(s->t, pn); lex_next(l);
 		if (T.k == TK_SCOLON) { lex_next(l); goto end; }
 	}
-	else if (pn != 0) { note(l->n, T.ln, T.cl, 1, "Expected a type identifier for pointer type"); }
-	else if (T.k == TK_SCOLON) { note(l->n, T.ln, T.cl, 1, "A declaration without a type is invalid"); }
+	else if (pn != 0) { note(l->n, T.ln, T.cl, 0, "Expected a type identifier for pointer type"); }
+	else if (T.k == TK_SCOLON) { note(l->n, T.ln, T.cl, 0, "A declaration without a type is invalid"); }
 
 	/* Assign a constant or variable value */
 	if (T.k == TK_COLON || T.k == TK_ASSIGN) { lex_next(l); ast_push(a, parse_expr(l, st, 0)); }
-	else { error(1, "%s:%zu:%zu: error: expected ':' or '='", l->n, T.ln + 1, T.cl + 1); }
+	else { note(l->n, T.ln, T.cl, 0, "expected ':' or '='"); }
 
 	/* Parse a semicolon if one is required */
 	if (a->c.a[0]->k != AK_PROC) { lex_kind(l, TK_SCOLON); }
@@ -212,7 +210,7 @@ static ast *parse_stmt_assn(lex *l, syt *st, ast *a) {
 	case TK_AS_DIV: { a->k = AK_AS_DIV; } goto expr;
 	case TK_AS_MOD: { a->k = AK_AS_MOD; } goto expr;
 	expr: { lex_next(l); ast_push(a, parse_expr(l, st, 0)); } break;
-	default: { error(1, "%s:%zu:%zu: error: expected assignment operator", l->n, T.ln + 1, T.cl + 1); } break;
+	default: { note(l->n, T.ln, T.cl, 0, "Expected assignment operator"); } break;
 	}
 
 	lex_kind(l, TK_SCOLON); return a;
@@ -348,18 +346,12 @@ static ast *parse_expr_proc(lex *l, syt *st) {
 		lex_next(l); tok t = lex_kind(l, TK_ID);
 		ast *s = syt_search_h(st, t.h, t.s);
 
-		if (s == NULL) { error( /* ERROR */
-			1, "%s:%zu:%zu: error: use of undefined identifier \"%s\"",
-			l->n, T.ln + 1, T.cl + 1, t.s
-		); }
-		if (s->k != AK_TYPE) { error( /* ERROR */
-			1, "%s:%zu:%zu: error: expected type identifier",
-			l->n, T.ln + 1, T.cl + 1
-		); }
+		if (s == NULL) { note(l->n, T.ln, T.cl, 0, "use of undefined identifier \"%s\"", t.s); }
+		if (s->k != AK_TYPE) { note(l->n, T.ln, T.cl, 0, "expected type identifier"); }
 
 		a->t = s->t;
 	}
-	else { a->t = &TYPE(TY_ZERO); }
+	else { a->t = &TYPE(TY_VOID); }
 
 	ast_push(a, parse_stmt_compound(l, st)); return a;
 }
@@ -447,7 +439,7 @@ void ast_print(ast *a, UINT i) {
 
 	switch (a->k) {
 	case AK_DECL: { if (a->t == NULL) { break; }}
-	case AK_PROC: case AK_CAST: case AK_INT: case AK_FLT: {
+	case AK_PROC: case AK_CAST: case AK_INT: case AK_FLT: case AK_OP_DRF: {
 		printf(" -> %s", a->t != NULL ? a->t->s : "untyped");
 		if (a->t != NULL) for (type *t = a->t->base; t != NULL; t = t->base) {
 			printf(" -> %s", t->s);
diff --git a/src/parse.h b/src/parse.h
index 203db0c..f5f6697 100644
--- a/src/parse.h
+++ b/src/parse.h
@@ -46,7 +46,9 @@ typedef struct { ast **a; UINT al; } ast_a;
 extern char *ast_ks[];
 
 extern ast *ast_init(void);
+extern void ast_free(ast **a);
 extern ast *ast_kind(ast_k kind);
+
 extern void ast_push(ast *a, ast *c);
 extern void ast_displace(ast *a, ast *c);
 extern type *ast_type(ast *a, syt *st);
diff --git a/src/type.c b/src/type.c
index f5b7a4d..5f53617 100644
--- a/src/type.c
+++ b/src/type.c
@@ -9,7 +9,7 @@
 #include <stdlib.h>
 
 type types[] = {
-	{ TY_ZERO, 0,       0, "void" },
+	{ TY_VOID, 0,       0, "void" },
 	{ TY_TYPE, 0,      -1, "type" },
 	{ TY_PTR,  TF_PTR, -1, "ptr"  },
 	{ TY_AUTO, 0,      -1, "auto" },
@@ -136,6 +136,9 @@ type *type_ptrc(type *base, u64 n) {
 	return r;
 }
 
+/* Check if a type is a pointer. */
+inline bool is_ptr(type *t) { return (t->f & TF_PTR); }
+
 /* Check if a type is numerical. */
 inline bool is_num(type *t) { return (t->f & TF_NUM); }
 
diff --git a/src/type.h b/src/type.h
index fc07919..afd19ce 100644
--- a/src/type.h
+++ b/src/type.h
@@ -11,7 +11,7 @@
 #define TYPE(a) (types[a])
 
 typedef enum {
-	TY_ZERO, TY_TYPE, TY_PTR, TY_AUTO,
+	TY_VOID, TY_TYPE, TY_PTR, TY_AUTO,
 
 	TY_BOOL, TY_B8, TY_B16, TY_B32, TY_B64,
 
@@ -65,6 +65,7 @@ extern type_a types_a;
 
 extern type *type_ptrc(type *base, u64 n);
 
+extern bool is_ptr(type *t);
 extern bool is_num(type *t);
 extern bool is_int(type *t);
 extern bool is_flt(type *t);