G

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

AuthorJakob Wakeling <[email protected]>
Date2023-06-22 03:15:33
Commit09e2c5c89f7c6842d18b03f11e2be6f0d859a54b
Parente91c41c67b91e52b496da535c5e020965e7c19b5

Implement the bitwise not unary operator

Diffstat

D doc/ast.md | 6 ------
R examples/hello_rudimentary.g -> examples/hello.g | 0
M examples/main.g | 7 ++++---
M src/analyse.c | 21 +++++++++++----------
M src/lex.c | 4 ++--
M src/llvm.c | 57 ++++++++++++++++++++++++++++++++++++++++-----------------
M src/parse.c | 6 ++++--
M src/parse.h | 4 ++--

8 files changed, 63 insertions, 42 deletions

diff --git a/doc/ast.md b/doc/ast.md
deleted file mode 100644
index 3e65c48..0000000
--- a/doc/ast.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# AST Structures
-
-## AK_DECL
-
-If an AST node of kind AK_DECL has a child, it shall be an expression representing the
-initialisation value.
diff --git a/examples/hello_rudimentary.g b/examples/hello.g
similarity index 100%
rename from examples/hello_rudimentary.g
rename to examples/hello.g
diff --git a/examples/main.g b/examples/main.g
index ffc53d8..5cfdb4f 100644
--- a/examples/main.g
+++ b/examples/main.g
@@ -1,4 +1,5 @@
-main :: proc() -> u64 {
-	var := true;
-	return u64(var);
+main :: proc() -> u8 {
+	var : u8 = u8(0b11111101);
+	inv : u8 = ~var;
+	return inv;
 }
diff --git a/src/analyse.c b/src/analyse.c
index 56683a6..6d5ba1d 100644
--- a/src/analyse.c
+++ b/src/analyse.c
@@ -45,13 +45,13 @@ static void analyse_stmt(ast *a, syt *st) {
 
 /* Analyse a compound statement. */
 static inline void analyse_stmt_comp(ast *a, syt *st) {
-	for (UINT i = 0; i < A.c.al; i += 1) { analyse_stmt(A.c.a[i], st); }
+	for (UINT i = 0; i < A.c.al; i += 1) { analyse_stmt(A.c.a[i], &A.st); }
 }
 
 /* Analyse a declaration statement. */
 static void analyse_stmt_decl(ast *a, syt *st) {
 	assert(A.c.al == 0 || A.c.al == 1);
-	if (A.c.al == 0) { assert(a->t != NULL); return; }
+	if (A.c.al == 0) { assert(A.t != NULL); return; }
 
 	analyse_expr(C[0], st);
 	type *value_type = ast_type(C[0], st);
@@ -59,7 +59,7 @@ static void analyse_stmt_decl(ast *a, syt *st) {
 	if (C[0]->k == AK_PROC) { return; /* TODO */ }
 	else if (is_bool(value_type)) {
 		/* If a type has not been specified, set the type based on the value */
-		if (a->t == NULL) { a->t = C[0]->t; }
+		if (A.t == NULL) { A.t = C[0]->t; }
 
 		/* If the type has been specified, check that the value is compatible */
 		else {
@@ -68,19 +68,19 @@ static void analyse_stmt_decl(ast *a, syt *st) {
 	}
 	else if (is_int(value_type)) {
 		/* If a type has not been specified, set the type based on the value */
-		if (a->t == NULL) {
-			a->t = C[0]->t;
+		if (A.t == NULL) {
+			A.t = C[0]->t;
 
 			/* If the value type is smaller than 32 bit, upgrade it */
-			if (a->t->l < 4) {
-				if (is_sign(a->t)) { a->t = &TYPE(TY_S32); }
-				else { a->t = &TYPE(TY_U32); }
+			if (A.t->l < 4) {
+				if (is_sign(A.t)) { A.t = &TYPE(TY_S32); }
+				else { A.t = &TYPE(TY_U32); }
 
 				/* Insert a type cast node between parent and child */
 				/* FIXME this behaviour is incorrect, should only implicitely cast for literals, not all expressions */
 				ast *cast = ast_init(); cast->k = AK_CAST;
 				cast->ln = C[0]->ln; cast->cl = C[0]->cl;
-				cast->t = a->t;
+				cast->t = A.t;
 
 				ast *child = C[0];
 
@@ -97,7 +97,7 @@ static void analyse_stmt_decl(ast *a, syt *st) {
 	}
 	else if (is_flt(value_type)) {
 		/* If a type has not been specified, set the type based on the value */
-		if (a->t == NULL) { a->t = a->c.a[0]->t; }
+		if (A.t == NULL) { A.t = A.c.a[0]->t; }
 
 		/* If the type has been specified, check that the value is compatible */
 		else {
diff --git a/src/lex.c b/src/lex.c
index a558be8..aac8c2c 100644
--- a/src/lex.c
+++ b/src/lex.c
@@ -75,7 +75,7 @@ tok lex_peek(lex *l) { return T; }
 
 /* Lex the next token, and return the current one. */
 tok lex_next(lex *l) {
-	redo:; if (T.k == TK_EOF) { return T; }
+	reset:; if (T.k == TK_EOF) { return T; }
 	tok t = T; T = (tok){ 0 };
 
 	/* Skip null characters and whitespace */
@@ -139,7 +139,7 @@ tok lex_next(lex *l) {
 		for (P += 1; is_alpha(C) || is_digit_dec(C) || C == '_'; P += 1);
 		sl = P - s; CL += sl;
 
-		if (sl <= 1) { note(l->n, T.ln, T.cl, 0, "A hash must be followed by an identifier"); goto redo; }
+		if (sl <= 1) { note(l->n, T.ln, T.cl, 0, "A hash must be followed by an identifier"); goto reset; }
 
 		T.k = TK_HASH; T.h = syt_hash(s, sl); if (!(T.s = strndup(s, sl))) { error(1, SERR); }
 	}
diff --git a/src/llvm.c b/src/llvm.c
index 416afa6..14bed4d 100644
--- a/src/llvm.c
+++ b/src/llvm.c
@@ -236,7 +236,7 @@ static LLVMValueRef llvm_stmt_for(ast *a, syt *st) {
 
 /* Generate IR for an expression. */
 static LLVMValueRef llvm_expr(ast *a, syt *st) {
-	switch (A.k) {
+	reset:; switch (A.k) {
 	case AK_PROC: { return llvm_expr_proc(a, st); } break;
 	case AK_CAST: { return llvm_expr_cast(a, st); } break;
 	case AK_BOOL: { return llvm_bool(a); } break;
@@ -256,28 +256,44 @@ static LLVMValueRef llvm_expr(ast *a, syt *st) {
 
 		return LLVMBuildCall2(llvm_builder, v->llvm_t, v->llvm_v, NULL, 0, "");
 	} break;
+	case AK_OP_POS: { a = C[0]; goto reset; /* no-op */ }
+	case AK_OP_NEG: {
+		type *t = ast_type(C[0], st);
+		if (t == NULL) { note(file_name, A.ln, A.cl, -1, "Subtree is missing a type (llvm:llvm_expr)"); }
+		
+		if (is_int(t)) { return LLVMBuildNeg(llvm_builder, llvm_expr(C[0], st), "neg"); }
+		else if (is_flt(t)) { return LLVMBuildFNeg(llvm_builder, llvm_expr(C[0], st), "neg"); }
+		else { note(file_name, A.ln, A.cl, -1, "Expression cannot be made negative (llvm:llvm_expr)"); }
+	} break;
+	case AK_BW_NOT: {
+		type *t = ast_type(C[0], st);
+		if (t == NULL) { note(file_name, A.ln, A.cl, -1, "Subtree is missing a type (llvm:llvm_expr)"); }
+		
+		if (is_int(t)) {
+			return LLVMBuildXor(llvm_builder, llvm_expr(C[0], st), LLVMConstInt(llvm_type(t), -1, false), "");
+		}
+		else if (is_flt(t)) {
+			/* TODO should floating point numbers be invertable? */
+			LLVMValueRef bc = LLVMBuildBitCast(llvm_builder, llvm_expr(C[0], st), LLVMIntType(t->l * 8), "");
+			LLVMValueRef vr = LLVMBuildXor(llvm_builder, bc, LLVMConstInt(LLVMIntType(t->l * 8), -1, false), "");
+			return LLVMBuildBitCast(llvm_builder, vr, llvm_type(t), "");
+		}
+		else { note(file_name, A.ln, A.cl, -1, "Expression cannot be inverted (llvm:llvm_expr)"); }
+	} break;
 	case AK_OP_ADD: {
-		return LLVMBuildAdd(llvm_builder, llvm_expr(A.c.a[0], st), llvm_expr(A.c.a[1], st), "add");
+		return LLVMBuildAdd(llvm_builder, llvm_expr(C[0], st), llvm_expr(C[1], st), "add");
 	} break;
 	case AK_OP_SUB: {
-		return LLVMBuildSub(llvm_builder, llvm_expr(A.c.a[0], st), llvm_expr(A.c.a[1], st), "sub");
+		return LLVMBuildSub(llvm_builder, llvm_expr(C[0], st), llvm_expr(C[1], st), "sub");
 	} break;
 	case AK_OP_MUL: {
-		return LLVMBuildMul(llvm_builder, llvm_expr(A.c.a[0], st), llvm_expr(A.c.a[1], st), "mul");
+		return LLVMBuildMul(llvm_builder, llvm_expr(C[0], st), llvm_expr(C[1], st), "mul");
 	} break;
 	case AK_OP_DIV: {
-		return LLVMBuildSDiv(llvm_builder, llvm_expr(A.c.a[0], st), llvm_expr(A.c.a[1], st), "div");
+		return LLVMBuildSDiv(llvm_builder, llvm_expr(C[0], st), llvm_expr(C[1], st), "div");
 	} break;
 	case AK_OP_MOD: {
-		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(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(file_name, A.ln, A.cl, -1, "Expression cannot be made negative (LLVM Failsafe)"); }
+		return LLVMBuildSRem(llvm_builder, llvm_expr(C[0], st), llvm_expr(C[1], st), "mod");
 	} break;
 	case AK_OP_ADO: {
 		ast *v = syt_search(st, C[0]->s);
diff --git a/src/parse.c b/src/parse.c
index c800c0d..039c5ab 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -19,9 +19,9 @@
 char *ast_ks[] = {
 	"AK_VOID", "AK_PROG", "AK_PROC", "AK_TYPE", "AK_CAST",
 
-	"AK_STMT", "AK_COMP", "AK_DECL", "AK_RETURN", "AK_IF", "AK_FOR",
+	"AK_COMP", "AK_DECL", "AK_RETURN", "AK_IF", "AK_FOR",
 
-	"AK_OP_POS", "AK_OP_NEG", "AK_OP_ADO", "AK_OP_DRF",
+	"AK_OP_POS", "AK_OP_NEG", "AK_BW_NOT", "AK_OP_ADO", "AK_OP_DRF",
 	"AK_OP_ADD", "AK_OP_SUB", "AK_OP_MUL", "AK_OP_DIV", "AK_OP_MOD",
 
 	"AK_ASSIGN", "AK_AS_ADD", "AK_AS_SUB", "AK_AS_MUL", "AK_AS_DIV", "AK_AS_MOD",
@@ -300,6 +300,7 @@ static ast *parse_expr(lex *l, syt *st, s32 o) {
 	case TK_LPAREN: { lex_next(l); left = parse_expr(l, st, 0); lex_kind(l, TK_RPAREN); } break;
 	case TK_OP_ADD: { left = ast_kind(AK_OP_POS); } goto prefix;
 	case TK_OP_SUB: { left = ast_kind(AK_OP_NEG); } goto prefix;
+	case TK_BW_NOT: { left = ast_kind(AK_BW_NOT); } goto prefix;
 	case TK_BW_AND: { left = ast_kind(AK_OP_ADO); } goto prefix;
 	case TK_OP_MUL: { left = ast_kind(AK_OP_DRF); } goto prefix;
 	prefix: { lex_next(l); ast_push(left, parse_expr(l, st, ast_precedence(left->k))); } break;
diff --git a/src/parse.h b/src/parse.h
index 25b2675..96f30af 100644
--- a/src/parse.h
+++ b/src/parse.h
@@ -18,9 +18,9 @@
 typedef enum {
 	AK_VOID, AK_PROG, AK_PROC, AK_TYPE, AK_CAST,
 
-	AK_STMT, AK_COMP, AK_DECL, AK_RETURN, AK_IF, AK_FOR,
+	AK_COMP, AK_DECL, AK_RETURN, AK_IF, AK_FOR,
 
-	AK_OP_POS, AK_OP_NEG, AK_OP_ADO, AK_OP_DRF,
+	AK_OP_POS, AK_OP_NEG, AK_BW_NOT, AK_OP_ADO, AK_OP_DRF,
 	AK_OP_ADD, AK_OP_SUB, AK_OP_MUL, AK_OP_DIV, AK_OP_MOD,
 
 	AK_ASSIGN, AK_AS_ADD, AK_AS_SUB, AK_AS_MUL, AK_AS_DIV, AK_AS_MOD,