From 9d600b66be6664ff02ee2d88290b96b63d0a46ef Mon Sep 17 00:00:00 2001 From: cozis Date: Wed, 3 Nov 2021 02:04:38 +0000 Subject: [PATCH] added equality and relational operators --- samples/if-else.noja | 6 ++ samples/loop.noja | 17 +++-- src/common/executable.c | 8 +++ src/common/executable.h | 6 ++ src/compiler/ASTi.h | 8 +++ src/compiler/compile.c | 12 ++++ src/compiler/parse.c | 61 +++++++++++++++- src/objects/o_int.c | 17 +++++ src/runtime/runtime.c | 156 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 285 insertions(+), 6 deletions(-) diff --git a/samples/if-else.noja b/samples/if-else.noja index 230ec61..976aeb5 100644 --- a/samples/if-else.noja +++ b/samples/if-else.noja @@ -7,5 +7,11 @@ if p: else a = 2; +if 'hello2' == 'hello': print('Yup!'); else print('Nop!'); +print('\n'); print(a); + +print(1 < 1 < 1); + + return a; \ No newline at end of file diff --git a/samples/loop.noja b/samples/loop.noja index d59247d..ba0f2ee 100644 --- a/samples/loop.noja +++ b/samples/loop.noja @@ -7,10 +7,19 @@ while p: p = false; } +# ------------------------------------- # +do + { + print('Hello 2!\n'); + } +while false; -do { - - print('Hello 2!\n'); +# ------------------------------------- # -} while true; \ No newline at end of file +i = 0; +while i < 10: + { + print('i = ', i+1, '\n'); + i = i + 1; + } diff --git a/src/common/executable.c b/src/common/executable.c index badc905..8897bbc 100644 --- a/src/common/executable.c +++ b/src/common/executable.c @@ -47,6 +47,14 @@ static const InstrInfo instr_table[] = { [OPCODE_SUB] = {"SUB", 0, NULL}, [OPCODE_MUL] = {"MUL", 0, NULL}, [OPCODE_DIV] = {"DIV", 0, NULL}, + + [OPCODE_EQL] = {"EQL", 0, NULL}, + [OPCODE_NQL] = {"NQL", 0, NULL}, + [OPCODE_LSS] = {"LSS", 0, NULL}, + [OPCODE_GRT] = {"GRT", 0, NULL}, + [OPCODE_LEQ] = {"LEQ", 0, NULL}, + [OPCODE_GEQ] = {"GEQ", 0, NULL}, + [OPCODE_ASS] = {"ASS", 1, (OperandType[]) {OPTP_STRING}}, [OPCODE_POP] = {"POP", 1, (OperandType[]) {OPTP_INT}}, [OPCODE_CALL] = {"CALL", 1, (OperandType[]) {OPTP_INT}}, diff --git a/src/common/executable.h b/src/common/executable.h index 711a7ee..41d7e99 100644 --- a/src/common/executable.h +++ b/src/common/executable.h @@ -28,6 +28,12 @@ typedef enum { OPCODE_SUB, OPCODE_MUL, OPCODE_DIV, + OPCODE_EQL, + OPCODE_NQL, + OPCODE_LSS, + OPCODE_GRT, + OPCODE_LEQ, + OPCODE_GEQ, OPCODE_ASS, OPCODE_POP, OPCODE_CALL, diff --git a/src/compiler/ASTi.h b/src/compiler/ASTi.h index a8f8fb7..46c75b2 100644 --- a/src/compiler/ASTi.h +++ b/src/compiler/ASTi.h @@ -22,6 +22,14 @@ typedef enum { EXPR_SUB, EXPR_MUL, EXPR_DIV, + + EXPR_EQL, + EXPR_NQL, + EXPR_LSS, + EXPR_LEQ, + EXPR_GRT, + EXPR_GEQ, + EXPR_ASS, EXPR_INT, EXPR_CALL, diff --git a/src/compiler/compile.c b/src/compiler/compile.c index bd3dbd1..82c5fc2 100644 --- a/src/compiler/compile.c +++ b/src/compiler/compile.c @@ -15,6 +15,12 @@ static Opcode exprkind_to_opcode(ExprKind kind) case EXPR_SUB: return OPCODE_SUB; case EXPR_MUL: return OPCODE_MUL; case EXPR_DIV: return OPCODE_DIV; + case EXPR_EQL: return OPCODE_EQL; + case EXPR_NQL: return OPCODE_NQL; + case EXPR_LSS: return OPCODE_LSS; + case EXPR_LEQ: return OPCODE_LEQ; + case EXPR_GRT: return OPCODE_GRT; + case EXPR_GEQ: return OPCODE_GEQ; default: UNREACHABLE; break; @@ -38,6 +44,12 @@ static _Bool emit_instr_for_node(ExeBuilder *exeb, Node *node, Error *error) case EXPR_SUB: case EXPR_MUL: case EXPR_DIV: + case EXPR_EQL: + case EXPR_NQL: + case EXPR_LSS: + case EXPR_LEQ: + case EXPR_GRT: + case EXPR_GEQ: { OperExprNode *oper = (OperExprNode*) expr; diff --git a/src/compiler/parse.c b/src/compiler/parse.c index d68dd79..0d60e3e 100644 --- a/src/compiler/parse.c +++ b/src/compiler/parse.c @@ -16,6 +16,10 @@ typedef enum { TSUB = '-', TMUL = '*', TDIV = '/', + + TLSS = '<', + TGRT = '>', + TASS = '=', TDONE = 256, @@ -33,6 +37,12 @@ typedef enum { TKWRETURN, TKWWHILE, TKWDO, + + TEQL, + TNQL, + TLEQ, + TGEQ, + } TokenKind; typedef struct Token Token; @@ -65,6 +75,9 @@ static inline _Bool isoper(char c) c == '-' || c == '*' || c == '/' || + c == '<' || + c == '>' || + c == '!' || c == '='; } @@ -239,6 +252,30 @@ AST *parse(Source *src, BPAlloc *alloc, Error *error) { tok->kind = TDIV; } + else if(matchop(str + tok->offset, tok->length, "==")) + { + tok->kind = TEQL; + } + else if(matchop(str + tok->offset, tok->length, "!=")) + { + tok->kind = TNQL; + } + else if(matchop(str + tok->offset, tok->length, "<")) + { + tok->kind = TLSS; + } + else if(matchop(str + tok->offset, tok->length, "<=")) + { + tok->kind = TLEQ; + } + else if(matchop(str + tok->offset, tok->length, ">")) + { + tok->kind = TGRT; + } + else if(matchop(str + tok->offset, tok->length, ">=")) + { + tok->kind = TGEQ; + } else if(matchop(str + tok->offset, tok->length, "=")) { tok->kind = TASS; @@ -1010,6 +1047,12 @@ static inline _Bool isbinop(Token *tok) tok->kind == '-' || tok->kind == '*' || tok->kind == '/' || + tok->kind == '<' || + tok->kind == '>' || + tok->kind == TLEQ || + tok->kind == TGEQ || + tok->kind == TEQL || + tok->kind == TNQL || tok->kind == '='; } @@ -1029,13 +1072,21 @@ static inline int precedenceof(Token *tok) case '=': return 0; + case '<': + case '>': + case TLEQ: + case TGEQ: + case TEQL: + case TNQL: + return 1; + case '+': case '-': - return 1; + return 2; case '*': case '/': - return 2; + return 3; default: return -100000000; @@ -1087,6 +1138,12 @@ static Node *parse_expression_2(Context *ctx, Node *left_expr, int min_prec) case '-': temp->base.kind = EXPR_SUB; break; case '*': temp->base.kind = EXPR_MUL; break; case '/': temp->base.kind = EXPR_DIV; break; + case '<': temp->base.kind = EXPR_LSS; break; + case '>': temp->base.kind = EXPR_GRT; break; + case TLEQ: temp->base.kind = EXPR_LEQ; break; + case TGEQ: temp->base.kind = EXPR_GEQ; break; + case TEQL: temp->base.kind = EXPR_EQL; break; + case TNQL: temp->base.kind = EXPR_NQL; break; case '=': temp->base.kind = EXPR_ASS; break; default: diff --git a/src/objects/o_int.c b/src/objects/o_int.c index 5f81ac4..f0d730a 100644 --- a/src/objects/o_int.c +++ b/src/objects/o_int.c @@ -4,6 +4,7 @@ static long long int to_int(Object *obj, Error *err); static void print(Object *obj, FILE *fp); +static _Bool op_eql(Object *self, Object *other); typedef struct { Object base; @@ -17,6 +18,7 @@ static const Type t_int = { .atomic = ATMTP_INT, .to_int = to_int, .print = print, + .op_eql = op_eql, }; static long long int to_int(Object *obj, Error *err) @@ -52,4 +54,19 @@ static void print(Object *obj, FILE *fp) assert(obj->type == &t_int); fprintf(fp, "%lld", ((IntObject*) obj)->val); +} + +static _Bool op_eql(Object *self, Object *other) +{ + assert(self != NULL); + assert(self->type == &t_int); + assert(other != NULL); + assert(other->type == &t_int); + + IntObject *i1, *i2; + + i1 = (IntObject*) self; + i2 = (IntObject*) other; + + return i1->val == i2->val; } \ No newline at end of file diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index fe21d1d..d11c182 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -349,6 +349,100 @@ static Object *do_math_op(Object *lop, Object *rop, Opcode opcode, Heap *heap, E return res; } +static Object *do_relational_op(Object *lop, Object *rop, Opcode opcode, Heap *heap, Error *error) +{ + assert(lop != NULL); + assert(rop != NULL); + + #define APPLY(x, y, z, id) \ + switch(opcode) \ + { \ + case OPCODE_LSS: (z) = (x) < (y); break; \ + case OPCODE_GRT: (z) = (x) > (y); break; \ + case OPCODE_LEQ: (z) = (x) <= (y); break; \ + case OPCODE_GEQ: (z) = (x) >= (y); break; \ + default: assert(0); break; \ + } + + _Bool res; + + if(Object_IsInt(lop)) + { + long long int raw_lop = Object_ToInt(lop, error); + + if(error->occurred) + return NULL; + + if(Object_IsInt(rop)) + { + // int + int + long long int raw_rop = Object_ToInt(rop, error); + + if(error->occurred) + return NULL; + + APPLY(raw_lop, raw_rop, res, id) + } + else if(Object_IsFloat(rop)) + { + // int + float + double raw_rop = Object_ToFloat(rop, error); + + if(error->occurred) + return NULL; + + APPLY((double) raw_lop, raw_rop, res, id) + } + else + { + Error_Report(error, 0, "Relational operation on a non-numeric object"); + return NULL; + } + } + else if(Object_IsFloat(lop)) + { + double raw_lop = Object_ToFloat(lop, error); + + if(error->occurred) + return NULL; + + if(Object_IsInt(rop)) + { + // float + int + long long int raw_rop = Object_ToInt(rop, error); + + if(error->occurred) + return NULL; + + APPLY(raw_lop, (double) raw_rop, res, id) + } + else if(Object_IsFloat(rop)) + { + // float + float + double raw_rop = Object_ToFloat(rop, error); + + if(error->occurred) + return NULL; + + APPLY(raw_lop, raw_rop, res, id) + } + else + { + Error_Report(error, 0, "Relational operation on a non-numeric object"); + return NULL; + } + } + else + { + Error_Report(error, 0, "Relational operation on a non-numeric object"); + return NULL; + } + + #undef APPLY + + return Object_FromBool(res, heap, error); +} + static _Bool step(Runtime *runtime, Error *error) { assert(runtime != NULL); @@ -407,6 +501,68 @@ static _Bool step(Runtime *runtime, Error *error) break; } + case OPCODE_EQL: + case OPCODE_NQL: + { + assert(opc == 0); + + Object *rop = Stack_Top(runtime->stack, 0); + Object *lop = Stack_Top(runtime->stack, -1); + + if(!Runtime_Pop(runtime, error, 2)) + return 0; + + // We managed to pop rop and lop, + // so we know they're not NULL. + assert(rop != NULL); + assert(lop != NULL); + + _Bool rawres = Object_Compare(lop, rop, error); + + if(error->occurred == 1) + return 0; + + if(opcode == OPCODE_NQL) + rawres = !rawres; + + Object *res = Object_FromBool(rawres, runtime->heap, error); + + if(res == NULL) + return 0; + + if(!Runtime_Push(runtime, error, res)) + return 0; + return 1; + } + + case OPCODE_LSS: + case OPCODE_GRT: + case OPCODE_LEQ: + case OPCODE_GEQ: + { + assert(opc == 0); + + Object *rop = Stack_Top(runtime->stack, 0); + Object *lop = Stack_Top(runtime->stack, -1); + + if(!Runtime_Pop(runtime, error, 2)) + return 0; + + // We managed to pop rop and lop, + // so we know they're not NULL. + assert(rop != NULL); + assert(lop != NULL); + + Object *res = do_relational_op(lop, rop, opcode, runtime->heap, error); + + if(res == NULL) + return 0; + + if(!Runtime_Push(runtime, error, res)) + return 0; + return 1; + } + case OPCODE_ASS: { assert(opc == 1);