From 22cf99faa99027208f9704d5c068fdfa033eb039 Mon Sep 17 00:00:00 2001 From: bog Date: Mon, 1 Apr 2024 23:33:43 +0200 Subject: [PATCH] :sparkles: comparisons operations. --- doc/grammar.bnf | 4 +- features/cmp.sk | 17 ++++++ lib/include/node.h | 4 +- lib/include/parser.h | 2 + lib/include/prog.h | 3 +- lib/include/state.h | 4 ++ lib/include/token.h | 4 +- lib/src/compiler.c | 33 +++++++++++ lib/src/exec.c | 15 +++++ lib/src/lexer.c | 11 +++- lib/src/parser.c | 132 ++++++++++++++++++++++++++++++++++++++++++- lib/src/state.c | 41 ++++++++++++++ tests/lexer.h | 14 +++++ tests/parser.h | 17 ++++++ 14 files changed, 294 insertions(+), 7 deletions(-) create mode 100644 features/cmp.sk diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 97d2fee..644be89 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -4,7 +4,9 @@ EXPR ::= | ASSERT ASSERT ::= assert EXPR eq EXPR OR ::= AND (or AND)* -AND ::= TERM (and TERM)* +AND ::= EQ (and EQ)* +EQ ::= CMP ((equal|not_equal) CMP)? +CMP ::= TERM ((lt|gt|le|ge) TERM)? TERM ::= FACTOR ((add|sub) FACTOR)* FACTOR ::= USUB ((mul|div|mod) USUB)* USUB ::= sub* NOT diff --git a/features/cmp.sk b/features/cmp.sk new file mode 100644 index 0000000..fea4fd3 --- /dev/null +++ b/features/cmp.sk @@ -0,0 +1,17 @@ +assert 4 < 5 eq true +assert 5 < 5 eq false +assert 6 < 5 eq false +assert 4 <= 5 eq true +assert 5 <= 5 eq true +assert 6 <= 5 eq false + +assert 3 > 1 eq true +assert 3 > 3 eq false +assert 3.0 > 7.9 eq false +assert 3 >= 1 eq true +assert 3 >= 3 eq true +assert 3 >= 7 eq false + +assert 5 == 5 eq true +assert 5 <> 4 eq true +assert "bim" <> "bim" eq false diff --git a/lib/include/node.h b/lib/include/node.h index 26b8904..081aa75 100644 --- a/lib/include/node.h +++ b/lib/include/node.h @@ -10,7 +10,9 @@ G(NODE_ADD), G(NODE_SUB), G(NODE_MUL),\ G(NODE_DIV), G(NODE_POW), G(NODE_MOD),\ G(NODE_USUB), G(NODE_ASSERT_EQ), \ G(NODE_BOOL), G(NODE_AND), G(NODE_OR), \ -G(NODE_NOT), G(NODE_FLOAT), G(NODE_STRING) +G(NODE_NOT), G(NODE_FLOAT), G(NODE_STRING), \ +G(NODE_LT), G(NODE_LE), G(NODE_GT), G(NODE_GE), \ +G(NODE_EQUAL), G(NODE_NOT_EQUAL) SK_ENUM_H(NodeKind, NODE_KIND); diff --git a/lib/include/parser.h b/lib/include/parser.h index 7d09a7d..31614de 100644 --- a/lib/include/parser.h +++ b/lib/include/parser.h @@ -22,6 +22,8 @@ struct node* parser_try_expr(struct parser* self); struct node* parser_try_assert(struct parser* self); struct node* parser_try_or(struct parser* self); struct node* parser_try_and(struct parser* self); +struct node* parser_try_eq(struct parser* self); +struct node* parser_try_cmp(struct parser* self); struct node* parser_try_term(struct parser* self); struct node* parser_try_factor(struct parser* self); struct node* parser_try_usub(struct parser* self); diff --git a/lib/include/prog.h b/lib/include/prog.h index d230327..7fb64f9 100644 --- a/lib/include/prog.h +++ b/lib/include/prog.h @@ -11,7 +11,8 @@ G(OP_ADD), G(OP_SUB), G(OP_MUL), \ G(OP_DIV), G(OP_MOD), G(OP_POW), \ G(OP_USUB), G(OP_ASSERT_EQ), \ G(OP_NOT), G(OP_AND), G(OP_OR), \ -G(OP_BR), G(OP_BRF) +G(OP_BR), G(OP_BRF), G(OP_LT), G(OP_GT), \ +G(OP_EQUAL) SK_ENUM_H(Opcode, OPCODE); diff --git a/lib/include/state.h b/lib/include/state.h index 6edaf48..12ad993 100644 --- a/lib/include/state.h +++ b/lib/include/state.h @@ -71,4 +71,8 @@ SK state_and(struct state* self); SK state_or(struct state* self); SK state_not(struct state* self); +SK state_lt(struct state* self); +SK state_gt(struct state* self); +SK state_eq(struct state* self); + #endif diff --git a/lib/include/token.h b/lib/include/token.h index fe762c5..ee0d311 100644 --- a/lib/include/token.h +++ b/lib/include/token.h @@ -11,7 +11,9 @@ G(TOKEN_MUL), G(TOKEN_DIV), G(TOKEN_MOD), \ G(TOKEN_POW), G(TOKEN_OPAR), G(TOKEN_CPAR), \ G(TOKEN_ASSERT), G(TOKEN_ASSERT_EQ), \ G(TOKEN_BOOL), G(TOKEN_AND), G(TOKEN_OR), \ -G(TOKEN_NOT), G(TOKEN_FLOAT), G(TOKEN_STRING) +G(TOKEN_NOT), G(TOKEN_FLOAT), G(TOKEN_STRING), \ +G(TOKEN_LT), G(TOKEN_LE), G(TOKEN_GT), G(TOKEN_GE), \ +G(TOKEN_EQUAL), G(TOKEN_NOT_EQUAL) SK_ENUM_H(TokenKind, TOKEN_KIND); diff --git a/lib/src/compiler.c b/lib/src/compiler.c index 932f18b..7617d17 100644 --- a/lib/src/compiler.c +++ b/lib/src/compiler.c @@ -76,6 +76,39 @@ void compiler_compile(struct compiler* self, val); } break; + case NODE_LT: { + compiler_compile_children(self, node, prog); + prog_add_instr(prog, OP_LT, SK_NO_PARAM); + } break; + + case NODE_LE: { + compiler_compile_children(self, node, prog); + prog_add_instr(prog, OP_GT, SK_NO_PARAM); + prog_add_instr(prog, OP_NOT, SK_NO_PARAM); + } break; + + case NODE_GT: { + compiler_compile_children(self, node, prog); + prog_add_instr(prog, OP_GT, SK_NO_PARAM); + } break; + + case NODE_GE: { + compiler_compile_children(self, node, prog); + prog_add_instr(prog, OP_LT, SK_NO_PARAM); + prog_add_instr(prog, OP_NOT, SK_NO_PARAM); + } break; + + case NODE_EQUAL: { + compiler_compile_children(self, node, prog); + prog_add_instr(prog, OP_EQUAL, SK_NO_PARAM); + } break; + + case NODE_NOT_EQUAL: { + compiler_compile_children(self, node, prog); + prog_add_instr(prog, OP_EQUAL, SK_NO_PARAM); + prog_add_instr(prog, OP_NOT, SK_NO_PARAM); + } break; + case NODE_ADD: { compiler_compile_children(self, node, prog); prog_add_instr(prog, OP_ADD, SK_NO_PARAM); diff --git a/lib/src/exec.c b/lib/src/exec.c index 8060ef0..34dbe13 100644 --- a/lib/src/exec.c +++ b/lib/src/exec.c @@ -112,6 +112,21 @@ void exec_execute(struct exec* self, self->pc++; } break; + case OP_LT: { + state_lt(state); + self->pc++; + } break; + + case OP_GT: { + state_gt(state); + self->pc++; + } break; + + case OP_EQUAL: { + state_eq(state); + self->pc++; + } break; + case OP_SUB: { state_sub(state); self->pc++; diff --git a/lib/src/lexer.c b/lib/src/lexer.c index b02c327..d624228 100644 --- a/lib/src/lexer.c +++ b/lib/src/lexer.c @@ -86,7 +86,10 @@ bool lexer_is_sep(struct lexer* self, size_t index) || c == '*' || c == '/' || c == '%' - || c == '^'; + || c == '^' + || c == '<' + || c == '>' + || c == '='; } bool lexer_next_is(struct lexer* self, TokenKind kind) @@ -147,6 +150,10 @@ struct token* lexer_try_new_next(struct lexer* self) { return tok; } + SK_SCAN_TEXT("<=", TOKEN_LE); + SK_SCAN_TEXT(">=", TOKEN_GE); + SK_SCAN_TEXT("==", TOKEN_EQUAL); + SK_SCAN_TEXT("<>", TOKEN_NOT_EQUAL); SK_SCAN_TEXT("+", TOKEN_ADD); SK_SCAN_TEXT("-", TOKEN_SUB); SK_SCAN_TEXT("*", TOKEN_MUL); @@ -155,6 +162,8 @@ struct token* lexer_try_new_next(struct lexer* self) SK_SCAN_TEXT("%", TOKEN_MOD); SK_SCAN_TEXT("(", TOKEN_OPAR); SK_SCAN_TEXT(")", TOKEN_CPAR); + SK_SCAN_TEXT(">", TOKEN_GT); + SK_SCAN_TEXT("<", TOKEN_LT); SK_SCAN_KEYWORD("assert", TOKEN_ASSERT, NULL); SK_SCAN_KEYWORD("eq", TOKEN_ASSERT_EQ, NULL); SK_SCAN_KEYWORD("true", TOKEN_BOOL, "true"); diff --git a/lib/src/parser.c b/lib/src/parser.c index 9736933..6a06c2b 100644 --- a/lib/src/parser.c +++ b/lib/src/parser.c @@ -176,7 +176,7 @@ struct node* parser_try_or(struct parser* self) struct node* parser_try_and(struct parser* self) { assert(self); - struct node* lhs = SK_TRY(parser_try_term); + struct node* lhs = SK_TRY(parser_try_eq); if (!lhs) { return NULL; } while (lexer_next_is(&self->lexer, TOKEN_AND)) @@ -187,7 +187,7 @@ struct node* parser_try_and(struct parser* self) )); node_push_new_child(node, lhs); - struct node* rhs = SK_TRY(parser_try_term); + struct node* rhs = SK_TRY(parser_try_eq); if (!rhs) { @@ -203,6 +203,134 @@ struct node* parser_try_and(struct parser* self) return lhs; } +struct node* parser_try_eq(struct parser* self) +{ + assert(self); + struct node* lhs = SK_TRY(parser_try_cmp); + if (!lhs) { return NULL; } + + if (lexer_next_is(&self->lexer, TOKEN_EQUAL)) + { + struct token* tok = lexer_try_new_next(&self->lexer); + struct node* rhs = SK_TRY(parser_try_cmp); + if (!rhs) + { + node_free(lhs); free(lhs); + return NULL; + } + + struct node* node = malloc(sizeof(struct node)); + + node_init(node, NODE_EQUAL, tok); + + node_push_new_child(node, lhs); + node_push_new_child(node, rhs); + lhs = node; + } + else if (lexer_next_is(&self->lexer, TOKEN_NOT_EQUAL)) + { + struct token* tok = lexer_try_new_next(&self->lexer); + struct node* rhs = SK_TRY(parser_try_cmp); + if (!rhs) + { + node_free(lhs); free(lhs); + return NULL; + } + + struct node* node = malloc(sizeof(struct node)); + + node_init(node, NODE_NOT_EQUAL, tok); + + node_push_new_child(node, lhs); + node_push_new_child(node, rhs); + lhs = node; + } + + return lhs; +} + +struct node* parser_try_cmp(struct parser* self) +{ + assert(self); + struct node* lhs = SK_TRY(parser_try_term); + if (!lhs) { return NULL; } + + if (lexer_next_is(&self->lexer, TOKEN_LT)) + { + struct token* tok = lexer_try_new_next(&self->lexer); + struct node* rhs = SK_TRY(parser_try_term); + if (!rhs) + { + node_free(lhs); free(lhs); + return NULL; + } + + struct node* node = malloc(sizeof(struct node)); + + node_init(node, NODE_LT, tok); + + node_push_new_child(node, lhs); + node_push_new_child(node, rhs); + lhs = node; + } + else if (lexer_next_is(&self->lexer, TOKEN_LE)) + { + struct token* tok = lexer_try_new_next(&self->lexer); + struct node* rhs = SK_TRY(parser_try_term); + if (!rhs) + { + node_free(lhs); free(lhs); + return NULL; + } + + struct node* node = malloc(sizeof(struct node)); + + node_init(node, NODE_LE, tok); + + node_push_new_child(node, lhs); + node_push_new_child(node, rhs); + lhs = node; + } + else if (lexer_next_is(&self->lexer, TOKEN_GT)) + { + struct token* tok = lexer_try_new_next(&self->lexer); + struct node* rhs = SK_TRY(parser_try_term); + if (!rhs) + { + node_free(lhs); free(lhs); + return NULL; + } + + struct node* node = malloc(sizeof(struct node)); + + node_init(node, NODE_GT, tok); + + node_push_new_child(node, lhs); + node_push_new_child(node, rhs); + lhs = node; + } + else if (lexer_next_is(&self->lexer, TOKEN_GE)) + { + struct token* tok = lexer_try_new_next(&self->lexer); + struct node* rhs = SK_TRY(parser_try_term); + if (!rhs) + { + node_free(lhs); free(lhs); + return NULL; + } + + struct node* node = malloc(sizeof(struct node)); + + node_init(node, NODE_GE, tok); + + node_push_new_child(node, lhs); + node_push_new_child(node, rhs); + lhs = node; + } + + return lhs; +} + struct node* parser_try_term(struct parser* self) { assert(self); diff --git a/lib/src/state.c b/lib/src/state.c index 4dd2646..5ec9e27 100644 --- a/lib/src/state.c +++ b/lib/src/state.c @@ -517,3 +517,44 @@ SK state_not(struct state* self) left->line ); } + +SK state_lt(struct state* self) +{ + assert(self); + SK rhs = state_pop(self); + SK lhs = state_pop(self); + + return state_push_bool( + self, + state_as_real(self, lhs) < state_as_real(self, rhs), + state_line(self, lhs) + ); +} + +SK state_gt(struct state* self) +{ + assert(self); + SK rhs = state_pop(self); + SK lhs = state_pop(self); + + return state_push_bool( + self, + state_as_real(self, lhs) > state_as_real(self, rhs), + state_line(self, lhs) + ); +} + +SK state_eq(struct state* self) +{ + assert(self); + SK rhs = state_pop(self); + SK lhs = state_pop(self); + struct value* left = state_try_get_value(self, lhs); + struct value* right = state_try_get_value(self, rhs); + + return state_push_bool( + self, + value_equals(left, right), + state_line(self, lhs) + ); +} diff --git a/tests/lexer.h b/tests/lexer.h index 38dec7c..f29fd15 100644 --- a/tests/lexer.h +++ b/tests/lexer.h @@ -96,6 +96,19 @@ static void test_lexer_string() "STRING[\n\r\t\e]" ); } + +static void test_lexer_cmp() +{ + test_lexer("< <= > >= == <>", 6, + "LT", + "LE", + "GT", + "GE", + "EQUAL", + "NOT_EQUAL" + ); +} + void register_lexer() { CU_pSuite suite = CU_add_suite("Lexer", 0, 0); @@ -104,6 +117,7 @@ void register_lexer() CU_add_test(suite, "Booleans", test_lexer_bool); CU_add_test(suite, "Floats", test_lexer_float); CU_add_test(suite, "Strings", test_lexer_string); + CU_add_test(suite, "Comparisons", test_lexer_cmp); } #endif diff --git a/tests/parser.h b/tests/parser.h index 89f18fc..93a0c06 100644 --- a/tests/parser.h +++ b/tests/parser.h @@ -71,6 +71,22 @@ static void test_parser_string() "\"ok pizza NOW\""); } +static void test_parser_cmp() +{ + test_parser("ROOT(LT(INT[5],INT[3]))", + "5<3"); + test_parser("ROOT(LE(INT[5],INT[3]))", + "5<=3"); + test_parser("ROOT(GT(INT[5],INT[3]))", + "5>3"); + test_parser("ROOT(GE(INT[5],INT[3]))", + "5>=3"); + test_parser("ROOT(EQUAL(INT[5],INT[3]))", + "5==3"); + test_parser("ROOT(NOT_EQUAL(INT[5],INT[3]))", + "5<>3"); +} + void register_parser() { CU_pSuite suite = CU_add_suite("Parser", 0, 0); @@ -78,6 +94,7 @@ void register_parser() CU_add_test(suite, "Assertions", test_parser_assert); CU_add_test(suite, "Booleans", test_parser_bool); CU_add_test(suite, "Strings", test_parser_string); + CU_add_test(suite, "Comparisons", test_parser_cmp); } #endif