Author | Jakob Wakeling <[email protected]> |
Date | 2023-07-04 11:29:05 |
Commit | 0f9bb9dd1ca7d9724da7e4b2ce0624dc2b2e1eb0 |
Parent | 421414826683053aae33f88755c0fbea14a06513 |
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,