From f1b82208cedc5e558ad2f685dca454a89d02e586 Mon Sep 17 00:00:00 2001 From: bog Date: Tue, 19 Mar 2024 16:25:02 +0100 Subject: [PATCH] :sparkles: comparison operators. --- doc/grammar.bnf | 4 ++- lib/bytecode.h | 3 ++- lib/ccm.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/ccm.h | 8 ++++++ lib/compiler.c | 39 ++++++++++++++++++++++++++++ lib/exec.c | 15 +++++++++++ lib/lexer.c | 6 +++++ lib/node.h | 5 ++-- lib/parser.c | 62 ++++++++++++++++++++++++++++++++++++++++++-- lib/parser.h | 2 ++ tests/cmp.ccm | 23 +++++++++++++++++ tests/str.ccm | 29 +++++++++++++++++++++ 12 files changed, 259 insertions(+), 6 deletions(-) create mode 100644 tests/cmp.ccm create mode 100644 tests/str.ccm diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 5024392..c08e7bf 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -5,7 +5,9 @@ EXPR ::= ASSERT ::= (assert_eq|assert_ne) tuple IN ::= OR (in OR)? OR ::= AND (or AND)* -AND ::= TERM (and TERM)* +AND ::= EQNE (and EQNE)* +EQNE ::= CMP ((eq|ne) CMP)? +CMP ::= TERM ((lt|le|gt|ge) TERM)? TERM ::= FACTOR ((add|sub) FACTOR)* FACTOR ::= USUB ((mul|div|mod) USUB)* USUB ::= sub* NOT diff --git a/lib/bytecode.h b/lib/bytecode.h index 23ae1f1..77f7a0d 100644 --- a/lib/bytecode.h +++ b/lib/bytecode.h @@ -8,7 +8,8 @@ G(OP_PUSH), G(OP_POP), \ G(OP_ADD), G(OP_SUB), G(OP_USUB), G(OP_MUL), \ G(OP_DIV), G(OP_POW), G(OP_MOD), G(OP_MK_TUPLE), \ G(OP_ASSERT_EQ), G(OP_ASSERT_NE), G(OP_BRF), G(OP_BR), \ -G(OP_NOT), G(OP_IN), G(OP_INDEX) +G(OP_NOT), G(OP_IN), G(OP_INDEX), G(OP_EQ), G(OP_LT), \ +G(OP_GT) CCM_ENUM_H(Opcode, OPCODES); diff --git a/lib/ccm.c b/lib/ccm.c index 692272c..11c20ff 100644 --- a/lib/ccm.c +++ b/lib/ccm.c @@ -84,6 +84,13 @@ CCM ccm_from_value(ccm_t* self, value_t* value) } } +value_t* ccm_to_value(ccm_t* self, CCM value) +{ + assert(self); + assert(value < self->values.size); + return (value_t*) self->values.data[value]; +} + int ccm_is_num(ccm_t* self, CCM value) { value_t const* val = self->values.data[value]; @@ -386,3 +393,65 @@ void ccm_not(ccm_t* self) ccm_push(self, ccm_to_boolean(self, !lhs, line)); } + +void ccm_eq(ccm_t* self) +{ + CCM ccm_rhs = ccm_pop(self); + CCM ccm_lhs = ccm_pop(self); + + value_t* lhs = ccm_to_value(self, ccm_lhs); + value_t* rhs = ccm_to_value(self, ccm_rhs); + int line = ((value_t*) self->values.data[ccm_lhs])->line; + + int eq = value_equals(lhs, rhs); + ccm_push(self, ccm_to_boolean(self, eq, line)); +} + +void ccm_ne(ccm_t* self) +{ + CCM ccm_rhs = ccm_pop(self); + CCM ccm_lhs = ccm_pop(self); + + value_t* lhs = ccm_to_value(self, ccm_lhs); + value_t* rhs = ccm_to_value(self, ccm_rhs); + int line = ((value_t*) self->values.data[ccm_lhs])->line; + + int eq = value_equals(lhs, rhs); + ccm_push(self, ccm_to_boolean(self, !eq, line)); +} + +void ccm_lt(ccm_t* self) +{ + CCM ccm_rhs = ccm_pop(self); + CCM ccm_lhs = ccm_pop(self); + + double lhs = ccm_from_num(self, ccm_lhs); + double rhs = ccm_from_num(self, ccm_rhs); + int line = ((value_t*) self->values.data[ccm_lhs])->line; + + ccm_push(self, ccm_to_boolean(self, lhs < rhs, line)); +} + +void ccm_le(ccm_t* self) +{ + ccm_gt(self); + ccm_not(self); +} + +void ccm_gt(ccm_t* self) +{ + CCM ccm_rhs = ccm_pop(self); + CCM ccm_lhs = ccm_pop(self); + + double lhs = ccm_from_num(self, ccm_lhs); + double rhs = ccm_from_num(self, ccm_rhs); + int line = ((value_t*) self->values.data[ccm_lhs])->line; + + ccm_push(self, ccm_to_boolean(self, lhs > rhs, line)); +} + +void ccm_ge(ccm_t* self) +{ + ccm_lt(self); + ccm_not(self); +} diff --git a/lib/ccm.h b/lib/ccm.h index 7744c06..a665ee6 100644 --- a/lib/ccm.h +++ b/lib/ccm.h @@ -20,6 +20,7 @@ void ccm_free(ccm_t* self); size_t ccm_str(ccm_t* self, char* buffer, size_t size); CCM ccm_from_value(ccm_t* self, value_t* value); +value_t* ccm_to_value(ccm_t* self, CCM value); int ccm_is_num(ccm_t* self, CCM value); double ccm_from_num(ccm_t* self, CCM value); @@ -51,4 +52,11 @@ void ccm_mod(ccm_t* self); void ccm_pow(ccm_t* self); void ccm_not(ccm_t* self); +void ccm_eq(ccm_t* self); +void ccm_ne(ccm_t* self); +void ccm_lt(ccm_t* self); +void ccm_le(ccm_t* self); +void ccm_gt(ccm_t* self); +void ccm_ge(ccm_t* self); + #endif diff --git a/lib/compiler.c b/lib/compiler.c index 7537469..48fad60 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -30,6 +30,45 @@ void compiler_compile(compiler_t* self, switch (node->kind) { + case NODE_LT: { + compiler_compile(self, node->children.data[0], prog); + compiler_compile(self, node->children.data[1], prog); + prog_add_instr(prog, OP_LT, CCM_NO_PARAM); + } break; + + case NODE_LE: { + compiler_compile(self, node->children.data[0], prog); + compiler_compile(self, node->children.data[1], prog); + prog_add_instr(prog, OP_GT, CCM_NO_PARAM); + prog_add_instr(prog, OP_NOT, CCM_NO_PARAM); + } break; + + case NODE_GT: { + compiler_compile(self, node->children.data[0], prog); + compiler_compile(self, node->children.data[1], prog); + prog_add_instr(prog, OP_GT, CCM_NO_PARAM); + } break; + + case NODE_GE: { + compiler_compile(self, node->children.data[0], prog); + compiler_compile(self, node->children.data[1], prog); + prog_add_instr(prog, OP_LT, CCM_NO_PARAM); + prog_add_instr(prog, OP_NOT, CCM_NO_PARAM); + } break; + + case NODE_EQ: { + compiler_compile(self, node->children.data[0], prog); + compiler_compile(self, node->children.data[1], prog); + prog_add_instr(prog, OP_EQ, CCM_NO_PARAM); + } break; + + case NODE_NE: { + compiler_compile(self, node->children.data[0], prog); + compiler_compile(self, node->children.data[1], prog); + prog_add_instr(prog, OP_EQ, CCM_NO_PARAM); + prog_add_instr(prog, OP_NOT, CCM_NO_PARAM); + } break; + case NODE_INDEX: { for (size_t i=0; ichildren.size; i++) { diff --git a/lib/exec.c b/lib/exec.c index 4ebc617..83d9961 100644 --- a/lib/exec.c +++ b/lib/exec.c @@ -44,6 +44,21 @@ void exec_instr(exec_t* self, switch (op) { + case OP_EQ: { + ccm_eq(ccm); + self->pc++; + } break; + + case OP_LT: { + ccm_lt(ccm); + self->pc++; + } break; + + case OP_GT: { + ccm_gt(ccm); + self->pc++; + } break; + case OP_INDEX: { CCM ccm_target = ccm_pop(ccm); diff --git a/lib/lexer.c b/lib/lexer.c index dca1c3c..d730d3e 100644 --- a/lib/lexer.c +++ b/lib/lexer.c @@ -15,6 +15,12 @@ void lexer_init(lexer_t* self) str_init(&self->separators); vec_init(&self->texts); + lexer_add_text(self, "<>", NODE_NE); + lexer_add_text(self, "==", NODE_EQ); + lexer_add_text(self, "<=", NODE_LE); + lexer_add_text(self, ">=", NODE_GE); + lexer_add_text(self, "<", NODE_LT); + lexer_add_text(self, ">", NODE_GT); lexer_add_text(self, ",", NODE_COMMA); lexer_add_text(self, "(", NODE_OPAR); lexer_add_text(self, ")", NODE_CPAR); diff --git a/lib/node.h b/lib/node.h index dbba48c..5896388 100644 --- a/lib/node.h +++ b/lib/node.h @@ -12,7 +12,8 @@ G(NODE_DIV), G(NODE_MOD), G(NODE_COMMA), G(NODE_TUPLE), \ G(NODE_ASSERT_EQ), G(NODE_ASSERT_NE), G(NODE_BOOL), \ G(NODE_AND), G(NODE_OR), G(NODE_NOT), G(NODE_IN), \ G(NODE_OSQUARE), G(NODE_CSQUARE), G(NODE_INDEX), \ -G(NODE_STR) +G(NODE_STR), G(NODE_LT), G(NODE_LE), G(NODE_GT), \ +G(NODE_GE), G(NODE_EQ), G(NODE_NE) CCM_ENUM_H(NodeKind, NODE_KIND); @@ -23,7 +24,7 @@ typedef struct { int line; } node_t; -void node_init(node_t* self, NodeKind kind, +void node_init(node_t* self, NodeKind kind, char const* value, int line); void node_free(node_t* self); diff --git a/lib/parser.c b/lib/parser.c index 0903fe1..808fbf0 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -229,7 +229,7 @@ node_t* parser_try_new_or(parser_t* self) node_t* parser_try_new_and(parser_t* self) { assert(self); - node_t* lhs = CCM_TRY(parser_try_new_term); + node_t* lhs = CCM_TRY(parser_try_new_eqne); if (!lhs) { return NULL; } while (lexer_peek_kind(self->lexer, NODE_AND, 0)) @@ -239,7 +239,7 @@ node_t* parser_try_new_and(parser_t* self) node_init(node, NODE_AND, "", lhs->line); node_push_new_child(node, lhs); - node_t* rhs = CCM_TRY(parser_try_new_term); + node_t* rhs = CCM_TRY(parser_try_new_eqne); if (!rhs) { @@ -256,6 +256,64 @@ node_t* parser_try_new_and(parser_t* self) return lhs; } +node_t* parser_try_new_eqne(parser_t* self) +{ + assert(self); + node_t* lhs = CCM_TRY(parser_try_new_cmp); + if (!lhs) { return NULL; } + + if (lexer_peek_kind(self->lexer, NODE_EQ, 0) + || lexer_peek_kind(self->lexer, NODE_NE, 0)) + { + node_t* node = lexer_try_new_next(self->lexer); + + node_t* rhs = CCM_TRY(parser_try_new_cmp); + + if (!rhs) + { + node_free(node); free(node); + node_free(lhs); free(lhs); + return NULL; + } + + node_push_new_child(node, lhs); + node_push_new_child(node, rhs); + lhs = node; + } + + return lhs; +} + +node_t* parser_try_new_cmp(parser_t* self) +{ + assert(self); + node_t* lhs = CCM_TRY(parser_try_new_term); + if (!lhs) { return NULL; } + + if (lexer_peek_kind(self->lexer, NODE_LT, 0) + || lexer_peek_kind(self->lexer, NODE_LE, 0) + || lexer_peek_kind(self->lexer, NODE_GT, 0) + || lexer_peek_kind(self->lexer, NODE_GE, 0)) + { + node_t* node = lexer_try_new_next(self->lexer); + + node_t* rhs = CCM_TRY(parser_try_new_term); + + if (!rhs) + { + node_free(node); free(node); + node_free(lhs); free(lhs); + return NULL; + } + + node_push_new_child(node, lhs); + node_push_new_child(node, rhs); + lhs = node; + } + + return lhs; +} + node_t* parser_try_new_term(parser_t* self) { assert(self); diff --git a/lib/parser.h b/lib/parser.h index d75b796..f7407d9 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -29,6 +29,8 @@ node_t* parser_try_new_assert(parser_t* self); node_t* parser_try_new_in(parser_t* self); node_t* parser_try_new_or(parser_t* self); node_t* parser_try_new_and(parser_t* self); +node_t* parser_try_new_eqne(parser_t* self); +node_t* parser_try_new_cmp(parser_t* self); node_t* parser_try_new_term(parser_t* self); node_t* parser_try_new_factor(parser_t* self); node_t* parser_try_new_usub(parser_t* self); diff --git a/tests/cmp.ccm b/tests/cmp.ccm new file mode 100644 index 0000000..24af0de --- /dev/null +++ b/tests/cmp.ccm @@ -0,0 +1,23 @@ +assert_eq (true, 3 == 3) +assert_eq (false, "3" == 3) +assert_eq (false, 5 == 3) + +assert_eq (false, 3 <> 3) +assert_eq (true, "3" <> 3) +assert_eq (true, 5 <> 3) + +assert_eq (true, 5 < 7) +assert_eq (false, 5 < 5) +assert_eq (false, 9 < 4) + +assert_eq (true, 5 <= 7) +assert_eq (true, 5 <= 5) +assert_eq (false, 9 <= 4) + +assert_eq (true, 12 > 1) +assert_eq (false, -2 > -2) +assert_eq (false, 0 > 8) + +assert_eq (true, 12 >= 1) +assert_eq (true, -2 >= -2) +assert_eq (false, 0 >= 8) diff --git a/tests/str.ccm b/tests/str.ccm new file mode 100644 index 0000000..6f5fc09 --- /dev/null +++ b/tests/str.ccm @@ -0,0 +1,29 @@ +assert_eq ("hello", "hello") +assert_ne ("hello", "world") + +# INDEX +# ===== +assert_eq ("hello"[0], "h") +assert_eq ("hello"[1], "e") +assert_eq ("hello"[2], "l") +assert_eq ("hello"[3], "l") +assert_eq ("hello"[4], "o") + +assert_eq ("hello"[-5], "h") +assert_eq ("hello"[-4], "e") +assert_eq ("hello"[-3], "l") +assert_eq ("hello"[-2], "l") +assert_eq ("hello"[-1], "o") + +# BINOPS +# ====== +assert_eq ( + "hello world", "hello " + + "world" +) + +assert_eq ("www", "w" * 3) +assert_eq ("wwww", 4 * "w") +assert_eq ("abab", ("a" + "b") * 2) +assert_eq ("abab", 2 * ("a" + "b")) +