G

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

AuthorJakob Wakeling <[email protected]>
Date2023-07-04 11:29:05
Commit0f9bb9dd1ca7d9724da7e4b2ce0624dc2b2e1eb0
Parent421414826683053aae33f88755c0fbea14a06513

Implement comparison and logical expressions

Diffstat

M src/init.c | 2 +-
M src/lex.c | 22 ++++++++++++----------
M src/lex.h | 3 ++-
M src/llvm.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
M src/parse.c | 44 ++++++++++++++++++++++++++++++++++----------
M src/parse.h | 7 +++++++

6 files changed, 134 insertions, 41 deletions

diff --git a/src/init.c b/src/init.c
index 1b82d1d..542a9b0 100644
--- a/src/init.c
+++ b/src/init.c
@@ -14,11 +14,11 @@ static ast kwds[] = {
 	{ AK_TYPE, 0, 0, 0, "ptr",  &TYPE(TY_PTR),  { 0 }, NULL },
 
 	/* Boolean Types */
+	{ AK_TYPE, 0, 0, 0, "bool", &TYPE(TY_BOOL), { 0 }, NULL },
 	{ AK_TYPE, 0, 0, 0, "b8",   &TYPE(TY_B8),   { 0 }, NULL },
 	{ AK_TYPE, 0, 0, 0, "b16",  &TYPE(TY_B16),  { 0 }, NULL },
 	{ AK_TYPE, 0, 0, 0, "b32",  &TYPE(TY_B32),  { 0 }, NULL },
 	{ AK_TYPE, 0, 0, 0, "b64",  &TYPE(TY_B64),  { 0 }, NULL },
-	{ AK_TYPE, 0, 0, 0, "bool", &TYPE(TY_B8),   { 0 }, NULL },
 
 	/* Integer Types */
 	{ AK_TYPE, 0, 0, 0, "uint", &TYPE(TY_UINT), { 0 }, NULL },
diff --git a/src/lex.c b/src/lex.c
index 8584ec8..cc63f7f 100644
--- a/src/lex.c
+++ b/src/lex.c
@@ -24,7 +24,8 @@ char *tok_ks[] = {
 	"TK_COLON",  "TK_SCOLON", "TK_COMMA",  "TK_PERIOD", "TK_RARROW", "TK_QMARK", "TK_HASH",
 
 	"TK_OP_ADD", "TK_OP_SUB", "TK_OP_MUL", "TK_OP_DIV", "TK_OP_MOD",
-	"TK_OP_EQ",  "TK_OP_NEQ", "TK_OP_GT",  "TK_OP_LT",  "TK_OP_GTE", "TK_OP_LTE",
+	"TK_EQ", "TK_NE", "TK_LT", "TK_LE", "TK_GT", "TK_GE",
+	
 	"TK_LO_NOT", "TK_LO_AND", "TK_LO_OR",
 	"TK_BW_NOT", "TK_BW_AND", "TK_BW_OR",  "TK_BW_XOR", "TK_BW_SHL", "TK_BW_SHR",
 
@@ -113,8 +114,8 @@ tok lex_next(lex *l) {
 		sl = P - s; CL += sl; T.h = syt_hash(s, sl);
 
 		if      (strncmp(s, "null",   4) == 0) { T.k = TK_NULL;   }
-		else if (strncmp(s, "true",   5) == 0) { T.k = TK_TRUE;   }
-		else if (strncmp(s, "false",  6) == 0) { T.k = TK_FALSE;  }
+		else if (strncmp(s, "true",   4) == 0) { T.k = TK_TRUE;   }
+		else if (strncmp(s, "false",  5) == 0) { T.k = TK_FALSE;  }
 		else if (strncmp(s, "return", 6) == 0) { T.k = TK_RETURN; }
 		else if (strncmp(s, "if",     2) == 0) { T.k = TK_IF;     }
 		else if (strncmp(s, "else",   4) == 0) { T.k = TK_ELSE;   }
@@ -179,19 +180,19 @@ tok lex_next(lex *l) {
 		} break;
 		case '=': switch (D) {
 			default:  { T.k = TK_ASSIGN; P += 1; CL += 1; } break;
-			case '=': { T.k = TK_OP_EQ;  P += 2; CL += 2; } break;
+			case '=': { T.k = TK_EQ;  P += 2; CL += 2; } break;
 		} break;
 		case '<': switch (D) {
-			default:  { T.k = TK_OP_LT;  P += 1; CL += 1; } break;
-			case '=': { T.k = TK_OP_LTE; P += 2; CL += 2; } break;
+			default:  { T.k = TK_LT;  P += 1; CL += 1; } break;
+			case '=': { T.k = TK_LE; P += 2; CL += 2; } break;
 		} break;
 		case '>': switch (D) {
-			default:  { T.k = TK_OP_GT;  P += 1; CL += 1; } break;
-			case '=': { T.k = TK_OP_GTE; P += 2; CL += 2; } break;
+			default:  { T.k = TK_GT;  P += 1; CL += 1; } break;
+			case '=': { T.k = TK_GE; P += 2; CL += 2; } break;
 		} break;
 		case '!': switch (D) {
 			default:  { T.k = TK_LO_NOT; P += 1; CL += 1; } break;
-			case '=': { T.k = TK_OP_NEQ; P += 2; CL += 2; } break;
+			case '=': { T.k = TK_NE; P += 2; CL += 2; } break;
 		} break;
 		case '&': switch (D) {
 			default:  { T.k = TK_BW_AND; P += 1; CL += 1; } break;
diff --git a/src/lex.h b/src/lex.h
index f48d162..b6adccf 100644
--- a/src/lex.h
+++ b/src/lex.h
@@ -18,7 +18,8 @@ typedef enum {
 	TK_COLON,  TK_SCOLON, TK_COMMA,  TK_PERIOD, TK_RARROW, TK_QMARK, TK_HASH,
 
 	TK_OP_ADD, TK_OP_SUB, TK_OP_MUL, TK_OP_DIV, TK_OP_MOD,
-	TK_OP_EQ,  TK_OP_NEQ, TK_OP_GT,  TK_OP_LT,  TK_OP_GTE, TK_OP_LTE,
+	TK_EQ, TK_NE, TK_LT, TK_LE, TK_GT, TK_GE,
+	
 	TK_LO_NOT, TK_LO_AND, TK_LO_OR,
 	TK_BW_NOT, TK_BW_AND, TK_BW_OR,  TK_BW_XOR, TK_BW_SHL, TK_BW_SHR,
 
diff --git a/src/llvm.c b/src/llvm.c
index 41a2b30..652ac76 100644
--- a/src/llvm.c
+++ b/src/llvm.c
@@ -184,6 +184,9 @@ static LLVMValueRef llvm_stmt_for(ast *a, syt *st) {
 	return NULL; /* TODO */
 }
 
+#define BuildICmp(b, op, lhs, rhs, st) LLVMBuildICmp(b, op, llvm_expr(lhs, st, true), llvm_expr(rhs, st, true), "")
+#define BuildFCmp(b, op, lhs, rhs, st) LLVMBuildFCmp(b, op, llvm_expr(lhs, st, true), llvm_expr(rhs, st, true), "")
+
 /* Generate IR for an expression. */
 static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 	reset:; switch (A.k) {
@@ -229,6 +232,7 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 		if (!load) { return vr; }
 		else { return LLVMBuildLoad2(llvm_builder, llvm_type(sym->t->base), vr, ""); }
 	}
+	
 	case AK_OP_POS: { a = C[0]; goto reset; /* no-op */ } break;
 	case AK_OP_NEG: {
 		type *t = ast_type(C[0], st);
@@ -238,21 +242,20 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 		else if (is_flt(t)) { return LLVMBuildFNeg(llvm_builder, llvm_expr(C[0], st, true), "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)"); }
+	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)"); }
 
-		if (is_int(t)) {
-			return LLVMBuildXor(llvm_builder, llvm_expr(C[0], st, true), 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, true), 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)"); }
+		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, true), "");
+	}
+	
 	case AK_OP_ADD: {
 		return LLVMBuildAdd(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), "add");
 	} break;
@@ -268,19 +271,64 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) {
 	case AK_OP_MOD: {
 		return LLVMBuildSRem(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), "mod");
 	} 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 (?) */
+	
+	case AK_EQ:  {
+		if      (is_int(ast_type(C[0], st))) { return BuildICmp(llvm_builder, LLVMIntEQ,   C[0], C[1], st); }
+		else if (is_flt(ast_type(C[0], st))) { return BuildFCmp(llvm_builder, LLVMRealOEQ, C[0], C[1], st); }
 	} 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)"); }
+	case AK_NE: {
+		if      (is_int(ast_type(C[0], st))) { return BuildICmp(llvm_builder, LLVMIntNE,   C[0], C[1], st); }
+		else if (is_flt(ast_type(C[0], st))) { return BuildFCmp(llvm_builder, LLVMRealONE, C[0], C[1], st); }
+	} break;
+	case AK_LT:  {
+		if (is_int(ast_type(C[0], st))) {
+			if (is_sign(ast_type(C[0], st))) { return BuildICmp(llvm_builder, LLVMIntSLT,  C[0], C[1], st); }
+			else                             { return BuildICmp(llvm_builder, LLVMIntULT,  C[0], C[1], st); }
+		}
+		else if (is_flt(ast_type(C[0], st))) { return BuildFCmp(llvm_builder, LLVMRealOLT, C[0], C[1], st); }
+	} break;
+	case AK_LE: {
+		if (is_int(ast_type(C[0], st))) {
+			if (is_sign(ast_type(C[0], st))) { return BuildICmp(llvm_builder, LLVMIntSLE,  C[0], C[1], st); }
+			else                             { return BuildICmp(llvm_builder, LLVMIntULE,  C[0], C[1], st); }
+		}
+		else if (is_flt(ast_type(C[0], st))) { return BuildFCmp(llvm_builder, LLVMRealOLE, C[0], C[1], st); }
+	} break;
+	case AK_GT:  {
+		if (is_int(ast_type(C[0], st))) {
+			if (is_sign(ast_type(C[0], st))) { return BuildICmp(llvm_builder, LLVMIntSGT,  C[0], C[1], st); }
+			else                             { return BuildICmp(llvm_builder, LLVMIntUGT,  C[0], C[1], st); }
+		}
+		else if (is_flt(ast_type(C[0], st))) { return BuildFCmp(llvm_builder, LLVMRealOGT, C[0], C[1], st); }
+	} break;
+	case AK_GE: {
+		if (is_int(ast_type(C[0], st))) {
+			if (is_sign(ast_type(C[0], st))) { return BuildICmp(llvm_builder, LLVMIntSGE,  C[0], C[1], st); }
+			else                             { return BuildICmp(llvm_builder, LLVMIntUGE,  C[0], C[1], st); }
+		}
+		else if (is_flt(ast_type(C[0], st))) { return BuildFCmp(llvm_builder, LLVMRealOGE, C[0], C[1], st); }
+	} break;
+	
+	case AK_LO_NOT: { return LLVMBuildNot(llvm_builder, llvm_expr(C[0], st, true), ""); } break;
+	case AK_LO_AND: { return LLVMBuildAnd(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), ""); } break;
+	case AK_LO_OR:  { return LLVMBuildOr(llvm_builder, llvm_expr(C[0], st, true), llvm_expr(C[1], st, true), "");  } 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)"); }
 
-		return LLVMBuildLoad2(llvm_builder, llvm_type(C[0]->t->base), llvm_expr(C[0], st, true), "");
-	}
+		if (is_int(t)) {
+			return LLVMBuildXor(llvm_builder, llvm_expr(C[0], st, true), 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, true), 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_ASSIGN: {
 		return LLVMBuildStore(llvm_builder, llvm_expr(C[1], st, true), llvm_expr(C[0], st, false));
 	} break;
diff --git a/src/parse.c b/src/parse.c
index 2b1fed7..1628314 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -22,9 +22,14 @@ char *ast_ks[] = {
 
 	"AK_COMP", "AK_DECL", "AK_RETURN", "AK_IF", "AK_FOR",
 
-	"AK_OP_POS", "AK_OP_NEG", "AK_BW_NOT", "AK_OP_ADO", "AK_OP_DRF",
+	"AK_OP_POS", "AK_OP_NEG", "AK_OP_ADO", "AK_OP_DRF",
 	"AK_OP_ADD", "AK_OP_SUB", "AK_OP_MUL", "AK_OP_DIV", "AK_OP_MOD",
 
+	"AK_EQ", "AK_NE", "AK_LT", "AK_LE", "AK_GT", "AK_GE",
+	
+	"AK_LO_NOT", "AK_LO_AND", "AK_LO_OR",
+	"AK_BW_NOT", "AK_BW_AND", "AK_BW_OR",  "AK_BW_XOR", "AK_BW_SHL", "AK_BW_SHR",
+	
 	"AK_ASSIGN", "AK_AS_ADD", "AK_AS_SUB", "AK_AS_MUL", "AK_AS_DIV", "AK_AS_MOD",
 
 	"AK_ID", "AK_CALL", "AK_BOOL", "AK_INT", "AK_FLT", "AK_ARR", "AK_SUBS",
@@ -259,6 +264,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_init(AK_OP_POS, T.ln, T.cl); } goto prefix;
 	case TK_OP_SUB: { left = ast_init(AK_OP_NEG, T.ln, T.cl); } goto prefix;
+	case TK_LO_NOT: { left = ast_init(AK_LO_NOT, T.ln, T.cl); } goto prefix;
 	case TK_BW_NOT: { left = ast_init(AK_BW_NOT, T.ln, T.cl); } goto prefix;
 	case TK_BW_AND: { left = ast_init(AK_OP_ADO, T.ln, T.cl); } goto prefix;
 	case TK_OP_MUL: { left = ast_init(AK_OP_DRF, T.ln, T.cl); } goto prefix;
@@ -300,6 +306,14 @@ static ast *parse_expr(lex *l, syt *st, s32 o) {
 	case TK_OP_MUL: { a = ast_init(AK_OP_MUL, T.ln, T.cl); } goto infix;
 	case TK_OP_DIV: { a = ast_init(AK_OP_DIV, T.ln, T.cl); } goto infix;
 	case TK_OP_MOD: { a = ast_init(AK_OP_MOD, T.ln, T.cl); } goto infix;
+	case TK_EQ: { a = ast_init(AK_EQ, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_NE: { a = ast_init(AK_NE, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_LT: { a = ast_init(AK_LT, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_LE: { a = ast_init(AK_LE, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_GT: { a = ast_init(AK_GT, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_GE: { a = ast_init(AK_GE, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_LO_AND: { a = ast_init(AK_LO_AND, T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
+	case TK_LO_OR:  { a = ast_init(AK_LO_OR,  T.ln, T.cl); a->t = &TYPE(TY_BOOL); } goto infix;
 	infix: { lex_next(l); ast_push(a, left); ast_push(a, parse_expr(l, st, ast_precedence(a->k))); } break;
 	default: { /* Ignored */ } break;
 	}
@@ -430,12 +444,13 @@ static ast *parse_flt(lex *l, syt *st) {
 /*
 	Expression operator precedence:
 	8 > expression group (parenthesis), procedure call, array subscripting
-	7 > 
-	6 > positive (prefix +), negative (prefix -), bitwise not (prefix ~), address-of (prefix &), dereference (prefix *)
-	5 > 
-	4 > multiplication (*), division (/), modulo (%)
-	3 > addition (+), subtraction (-)
-	2 > 
+	7 > positive (prefix +), negative (prefix -), logical not (prefix !), bitwise not (prefix ~),
+		address-of (prefix &), dereference (prefix *)
+	6 > multiplication (*), division (/), modulo (%)
+	5 > addition (+), subtraction (-)
+	4 > comparison (==, !=, <, >, <=, >=)
+	3 > logical and (&&)
+	2 > logical or (||)
 	1 > assignment (=, +=, -=, *=, /=, %=)
 */
 
@@ -443,8 +458,11 @@ static ast *parse_flt(lex *l, syt *st) {
 static s32 tok_precedence(tok_k tk) {
 	switch (tk) {
 	case TK_LPAREN: case TK_LBRACK: { return 8; }
-	case TK_OP_MUL: case TK_OP_DIV: case TK_OP_MOD: { return 4; }
-	case TK_OP_ADD: case TK_OP_SUB: { return 3; }
+	case TK_OP_MUL: case TK_OP_DIV: case TK_OP_MOD: { return 6; }
+	case TK_OP_ADD: case TK_OP_SUB: { return 5; }
+	case TK_EQ: case TK_NE: case TK_LT: case TK_LE: case TK_GT: case TK_GE: { return 4; }
+	case TK_LO_AND: { return 3; }
+	case TK_LO_OR: { return 2; }
 	case TK_ASSIGN: case TK_AS_ADD: case TK_AS_SUB: case TK_AS_MUL: case TK_AS_DIV: case TK_AS_MOD: { return 1; }
 	default: { return 0; }
 	}
@@ -454,9 +472,12 @@ static s32 tok_precedence(tok_k tk) {
 static s32 ast_precedence(ast_k ak) {
 	switch (ak) {
 	case AK_CALL: case AK_SUBS: { return 8; }
-	case AK_OP_POS: case AK_OP_NEG: case AK_BW_NOT: case AK_OP_ADO: case AK_OP_DRF: { return 6; }
-	case AK_OP_MUL: case AK_OP_DIV: case AK_OP_MOD: { return 4; }
-	case AK_OP_ADD: case AK_OP_SUB: { return 3; }
+	case AK_OP_POS: case AK_OP_NEG: case AK_LO_NOT: case AK_BW_NOT: case AK_OP_ADO: case AK_OP_DRF: { return 7; }
+	case AK_OP_MUL: case AK_OP_DIV: case AK_OP_MOD: { return 6; }
+	case AK_OP_ADD: case AK_OP_SUB: { return 5; }
+	case AK_EQ: case AK_NE: case AK_LT: case AK_LE: case AK_GT: case AK_GE: { return 4; }
+	case AK_LO_AND: { return 3; }
+	case AK_LO_OR: { return 2; }
 	case AK_ASSIGN: case AK_AS_ADD: case AK_AS_SUB: case AK_AS_MUL: case AK_AS_DIV: case AK_AS_MOD: { return 1; }
 	default: { return 0; }
 	}
diff --git a/src/parse.h b/src/parse.h
index 50dd366..98beb68 100644
--- a/src/parse.h
+++ b/src/parse.h
@@ -19,9 +19,14 @@ typedef enum {
 
 	AK_COMP, AK_DECL, AK_RETURN, AK_IF, AK_FOR,
 
-	AK_OP_POS, AK_OP_NEG, AK_BW_NOT, AK_OP_ADO, AK_OP_DRF,
+	AK_OP_POS, AK_OP_NEG, AK_OP_ADO, AK_OP_DRF,
 	AK_OP_ADD, AK_OP_SUB, AK_OP_MUL, AK_OP_DIV, AK_OP_MOD,
 
+	AK_EQ, AK_NE, AK_LT, AK_LE, AK_GT, AK_GE,
+	
+	AK_LO_NOT, AK_LO_AND, AK_LO_OR,
+	AK_BW_NOT, AK_BW_AND, AK_BW_OR,  AK_BW_XOR, AK_BW_SHL, AK_BW_SHR,
+	
 	AK_ASSIGN, AK_AS_ADD, AK_AS_SUB, AK_AS_MUL, AK_AS_DIV, AK_AS_MOD,
 
 	AK_ID, AK_CALL, AK_BOOL, AK_INT, AK_FLT, AK_ARR, AK_SUBS,