From 4f6f3253c251e69aaacb88fcc0290aca5387a037 Mon Sep 17 00:00:00 2001 From: bog Date: Fri, 15 Dec 2023 22:31:01 +0100 Subject: [PATCH] :sparkles: num arithmetic operators. --- doc/roza.bnf | 7 ++- lib/commons.h | 1 + lib/compiler.c | 38 ++++++++++++- lib/lexer.c | 30 +++++++++- lib/lexer.h | 1 + lib/node.h | 14 +++-- lib/opcodes.h | 4 +- lib/parser.c | 109 ++++++++++++++++++++++++++++++++++++- lib/parser.h | 5 ++ lib/prepass.c | 9 ++- lib/vm.c | 51 ++++++++++++++++- meson.build | 7 +++ tests/acceptances/num.roza | 15 +++++ tests/units/lexer.c | 15 ++++- tests/units/parser.c | 8 +++ 15 files changed, 298 insertions(+), 16 deletions(-) create mode 100644 tests/acceptances/num.roza diff --git a/doc/roza.bnf b/doc/roza.bnf index e1c1706..a6d80e4 100644 --- a/doc/roza.bnf +++ b/doc/roza.bnf @@ -7,5 +7,10 @@ EQNE ::= | CMP | CMP eq CMP | CMP ne CMP -CMP ::= BUILTIN ((lt | le | ge | gt) BUILTIN)? +CMP ::= TERM ((lt | le | ge | gt) TERM)? +TERM ::= FACTOR ((add | sub) FACTOR)* +FACTOR ::= POWER ((mul | div | mod) POWER)* +POWER ::= UNARY (pow UNARY)? +UNARY ::= sub? GROUP +GROUP ::= BUILTIN | opar EXPR cpar BUILTIN ::= num | bool | str diff --git a/lib/commons.h b/lib/commons.h index 61f0193..5c07e8d 100644 --- a/lib/commons.h +++ b/lib/commons.h @@ -1,6 +1,7 @@ #ifndef RZ_COMMONS_H #define RZ_COMMONS_H +#include #include #include #include diff --git a/lib/compiler.c b/lib/compiler.c index bbc8164..fdd474a 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -80,7 +80,7 @@ void compiler_run(compiler_t* compiler, node_t* node) case NODE_LT: case NODE_LE: case NODE_GT: - case NODE_GE:{ + case NODE_GE: { assert(node->children.size == 2); for (size_t i=0; ichildren.size; i++) @@ -102,6 +102,42 @@ void compiler_run(compiler_t* compiler, node_t* node) } break; + case NODE_ADD: + case NODE_SUB: + case NODE_MUL: + case NODE_DIV: + case NODE_MODULO: + case NODE_POW: { + for (size_t i=0; ichildren.size; i++) + { + compiler_run(compiler, node_child(node, i)); + } + + Opcode op; + + if (node->type == NODE_SUB + && node->children.size == 1) + { + op = OP_USUB; + } + else + { + switch (node->type) + { + case NODE_ADD: op = OP_ADD; break; + case NODE_SUB: op = OP_SUB; break; + case NODE_MUL: op = OP_MUL; break; + case NODE_DIV: op = OP_DIV; break; + case NODE_MODULO: op = OP_MODULO; break; + case NODE_POW: op = OP_POW; break; + default: assert(0); + } + } + + mod_push_instr(compiler->mod, op, RZ_NO_PARAM); + + } break; + default: { fprintf(stderr, "Cannot compile unknown node '%s'", NodeTypeStr[node->type]); diff --git a/lib/lexer.c b/lib/lexer.c index 11079ee..8321bf4 100644 --- a/lib/lexer.c +++ b/lib/lexer.c @@ -38,7 +38,6 @@ node_t* lexer_try_new_next(lexer_t* lexer) // Comments // ======== - while (lexer->cursor < len && lexer->source[lexer->cursor] == '#') { @@ -53,6 +52,15 @@ node_t* lexer_try_new_next(lexer_t* lexer) // Text // ==== + RZ_TEXT("(", NODE_OPAR, 0); + RZ_TEXT(")", NODE_CPAR, 0); + + RZ_TEXT("+", NODE_ADD, 0); + RZ_TEXT("*", NODE_MUL, 0); + RZ_TEXT("/", NODE_DIV, 0); + RZ_TEXT("%", NODE_MODULO, 0); + RZ_TEXT("^", NODE_POW, 0); + RZ_TEXT("==", NODE_EQ, 0); RZ_TEXT("!=", NODE_NE, 0); RZ_TEXT("<=", NODE_LE, 0); @@ -111,6 +119,7 @@ node_t* lexer_try_new_next(lexer_t* lexer) } if (res_str.size > 0 + && res_str.data[res_str.size - 1] != '-' && (cursor >= len || !(isalnum(lexer->source[cursor]) || lexer->source[cursor] == '.'))) { @@ -127,6 +136,8 @@ node_t* lexer_try_new_next(lexer_t* lexer) str_free(&res_str); } + RZ_TEXT("-", NODE_SUB, 0); + if (lexer->cursor < len && lexer->err) { size_t const SZ = RZ_STR_LIMIT; @@ -186,6 +197,14 @@ void lexer_skip_spaces(lexer_t* lexer) } +void lexer_skip_next(lexer_t* lexer) +{ + assert(lexer); + node_t* node = lexer_try_new_next(lexer); + node_free(node); + free(node); +} + node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw, NodeType type, int has_value, int is_kw) @@ -321,6 +340,13 @@ int lexer_is_sep(lexer_t* lexer, size_t idx) return 1; } - return c == '=' + return + c == '=' + || c == '+' + || c == '-' + || c == '*' + || c == '/' + || c == '%' + || c == '^' || c == '!'; } diff --git a/lib/lexer.h b/lib/lexer.h index 4034b1b..1ece250 100644 --- a/lib/lexer.h +++ b/lib/lexer.h @@ -18,6 +18,7 @@ void lexer_free(lexer_t* lexer); node_t* lexer_try_new_next(lexer_t* lexer); NodeType lexer_peek(lexer_t* lexer, int lookahead); void lexer_skip_spaces(lexer_t* lexer); +void lexer_skip_next(lexer_t* lexer); node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw, NodeType type, int has_value, diff --git a/lib/node.h b/lib/node.h index 034b816..63ce10e 100644 --- a/lib/node.h +++ b/lib/node.h @@ -3,12 +3,14 @@ #include "commons.h" -#define NODE_TYPE(G) \ - G(NODE_MOD), \ - G(NODE_NUM), G(NODE_BOOL), G(NODE_STR), \ - G(NODE_ASSERT), \ - G(NODE_EQ), G(NODE_NE), \ - G(NODE_LT), G(NODE_LE), G(NODE_GT), G(NODE_GE) +#define NODE_TYPE(G) \ + G(NODE_MOD), \ + G(NODE_NUM), G(NODE_BOOL), G(NODE_STR), \ + G(NODE_ASSERT), \ + G(NODE_EQ), G(NODE_NE), \ + G(NODE_LT), G(NODE_LE), G(NODE_GT), G(NODE_GE), \ + G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), G(NODE_DIV), \ + G(NODE_MODULO), G(NODE_POW), G(NODE_OPAR), G(NODE_CPAR) RZ_ENUM_H(NodeType, NODE_TYPE); diff --git a/lib/opcodes.h b/lib/opcodes.h index 9c12f45..80ac6e9 100644 --- a/lib/opcodes.h +++ b/lib/opcodes.h @@ -7,7 +7,9 @@ G(OP_ASSERT), \ G(OP_PUSH), G(OP_POP), \ G(OP_EQ), G(OP_NE), \ - G(OP_LT), G(OP_LE), G(OP_GT), G(OP_GE) + G(OP_LT), G(OP_LE), G(OP_GT), G(OP_GE), \ + G(OP_ADD), G(OP_SUB), G(OP_MUL), G(OP_DIV), \ + G(OP_MODULO), G(OP_POW), G(OP_USUB) RZ_ENUM_H(Opcode, OPCODES); diff --git a/lib/parser.c b/lib/parser.c index 5995896..aae972e 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -107,7 +107,7 @@ node_t* parser_try_new_eqne(parser_t* parser) node_t* parser_try_new_cmp(parser_t* parser) { assert(parser); - node_t* lhs = parser_try_new_builtin(parser); + node_t* lhs = parser_try_new_term(parser); int next = lexer_peek(parser->lexer, 1); @@ -118,13 +118,118 @@ node_t* parser_try_new_cmp(parser_t* parser) { node_t* node = parser_try_new_consume(parser, next); node_add_new_child(node, lhs); - node_add_new_child(node, parser_try_new_builtin(parser)); + node_add_new_child(node, parser_try_new_term(parser)); lhs = node; } return lhs; } + +node_t* parser_try_new_term(parser_t* parser) +{ + assert(parser); + node_t* lhs = parser_try_new_factor(parser); + + while (1) + { + NodeType next = lexer_peek(parser->lexer, 1); + + if (next == NODE_ADD + || next == NODE_SUB) + { + node_t* node = parser_try_new_consume(parser, next); + node_add_new_child(node, lhs); + node_add_new_child(node, parser_try_new_factor(parser)); + + lhs = node; + } + else + { + break; + } + } + + return lhs; +} + +node_t* parser_try_new_factor(parser_t* parser) +{ + assert(parser); + node_t* lhs = parser_try_new_power(parser); + + while (1) + { + NodeType next = lexer_peek(parser->lexer, 1); + + if (next == NODE_MUL + || next == NODE_DIV + || next == NODE_MODULO) + { + node_t* node = parser_try_new_consume(parser, next); + node_add_new_child(node, lhs); + node_add_new_child(node, parser_try_new_power(parser)); + + lhs = node; + } + else + { + break; + } + } + + return lhs; +} + +node_t* parser_try_new_power(parser_t* parser) +{ + assert(parser); + node_t* lhs = parser_try_new_unary(parser); + + NodeType next = lexer_peek(parser->lexer, 1); + + if (next == NODE_POW) + { + node_t* node = parser_try_new_consume(parser, next); + node_add_new_child(node, lhs); + node_add_new_child(node, parser_try_new_unary(parser)); + lhs = node; + } + + return lhs; +} + +node_t* parser_try_new_unary(parser_t* parser) +{ + assert(parser); + NodeType next = lexer_peek(parser->lexer, 1); + + if (next == NODE_SUB) + { + node_t* node = parser_try_new_consume(parser, next); + node_add_new_child(node, parser_try_new_group(parser)); + return node; + } + + return parser_try_new_group(parser); +} + +node_t* parser_try_new_group(parser_t* parser) +{ + assert(parser); + NodeType next = lexer_peek(parser->lexer, 1); + + if (next == NODE_OPAR) + { + lexer_skip_next(parser->lexer); + node_t* node = parser_try_new_expr(parser); + lexer_skip_next(parser->lexer); + return node; + } + + return parser_try_new_builtin(parser); +} + node_t* parser_try_new_builtin(parser_t* parser) { assert(parser); diff --git a/lib/parser.h b/lib/parser.h index de2aef9..7cce2ad 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -19,6 +19,11 @@ node_t* parser_try_new_expr(parser_t* parser); node_t* parser_try_new_assert(parser_t* parser); node_t* parser_try_new_eqne(parser_t* parser); node_t* parser_try_new_cmp(parser_t* parser); +node_t* parser_try_new_term(parser_t* parser); +node_t* parser_try_new_factor(parser_t* parser); +node_t* parser_try_new_power(parser_t* parser); +node_t* parser_try_new_unary(parser_t* parser); +node_t* parser_try_new_group(parser_t* parser); node_t* parser_try_new_builtin(parser_t* parser); node_t* parser_try_new_consume(parser_t* parser, NodeType type); diff --git a/lib/prepass.c b/lib/prepass.c index 077f3b5..c96f76c 100644 --- a/lib/prepass.c +++ b/lib/prepass.c @@ -30,7 +30,14 @@ void prepass_run(prepass_t* prepass, node_t* node) case NODE_LT: case NODE_LE: case NODE_GT: - case NODE_GE:{ + case NODE_GE: + + case NODE_ADD: + case NODE_SUB: + case NODE_MUL: + case NODE_DIV: + case NODE_MODULO: + case NODE_POW:{ for (size_t i=0; ichildren.size; i++) { prepass_run(prepass, (node_t*) node->children.data[i]); diff --git a/lib/vm.c b/lib/vm.c index 1e47e72..14107e0 100644 --- a/lib/vm.c +++ b/lib/vm.c @@ -151,7 +151,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) vm->pc++; } break; - case OP_LT: + case OP_LT: case OP_LE: case OP_GT: case OP_GE: { @@ -181,6 +181,55 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) vm->pc++; } break; + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_MODULO: + case OP_POW: { + int r = vm_pop(vm); + int l = vm_pop(vm); + + value_t* lhs = mod->values.data[l]; + value_t* rhs = mod->values.data[r]; + + vm_ensure_type(vm, lhs, TYPE_NUM); + vm_ensure_type(vm, rhs, TYPE_NUM); + + double val = 0; + + switch (op) + { + case OP_ADD: val = lhs->value.num + rhs->value.num; break; + case OP_SUB: val = lhs->value.num - rhs->value.num; break; + case OP_MUL: val = lhs->value.num * rhs->value.num; break; + case OP_DIV: val = lhs->value.num / rhs->value.num; break; + case OP_MODULO: val = fmod(lhs->value.num, rhs->value.num); break; + case OP_POW: val = powf(lhs->value.num, rhs->value.num); break; + default: assert(0); break; + } + + value_t* res = tysy_new_num(vm->tysy, val, lhs->line); + vm_push_value(vm, mod, res); + + vm->pc++; + } break; + + case OP_USUB: { + int l = vm_pop(vm); + + value_t* lhs = mod->values.data[l]; + + vm_ensure_type(vm, lhs, TYPE_NUM); + + double val = -lhs->value.num; + + value_t* res = tysy_new_num(vm->tysy, val, lhs->line); + vm_push_value(vm, mod, res); + + vm->pc++; + } break; + default: { fprintf(stderr, "Cannot execute unknown opcode '%s'.\n", OpcodeStr[op]); diff --git a/meson.build b/meson.build index 758f755..e8e1049 100644 --- a/meson.build +++ b/meson.build @@ -4,6 +4,9 @@ project( version: '0.0.0' ) +cc = meson.get_compiler('c') +m_dep = cc.find_library('m', required: true) + conf = configuration_data() conf.set('version', meson.project_version()) @@ -39,6 +42,10 @@ roza_lib = static_library( # exec 'lib/vm.c', + ], + + dependencies: [ + m_dep ] ) diff --git a/tests/acceptances/num.roza b/tests/acceptances/num.roza new file mode 100644 index 0000000..8103fcf --- /dev/null +++ b/tests/acceptances/num.roza @@ -0,0 +1,15 @@ +# Num Arithmetic +# ============== +assert 5 + 2 == 7 +assert 5 - 2 == 3 +assert 2 - 5 == -3 +assert - 32 == -32 +assert 6 * 7 == 42 +assert 12 / 4 == 3 +assert 5 / 2 == 2.5 +assert 35 % 8 == 3 +assert 2^6 == 64 + +assert 1 + 2 * 3 == 7 +assert (1 + 2) * 3 == 9 +assert 5 == (3 + 2) \ No newline at end of file diff --git a/tests/units/lexer.c b/tests/units/lexer.c index d1d47a4..3e36ebc 100644 --- a/tests/units/lexer.c +++ b/tests/units/lexer.c @@ -77,7 +77,7 @@ Test(lexer, num) { "NUM[4.1]", "NUM[6]"); - test_lexer_ko("-3..14"); + test_lexer_ko("3..14"); test_lexer_ko("..2"); test_lexer_ko("2.."); } @@ -133,3 +133,16 @@ Test(lexer, cmp) { "GE", "NUM[5]"); } + +Test(lexer, num_arith) { + test_lexer("()+-*/^%", 8, + "OPAR", + "CPAR", + "ADD", + "SUB", + "MUL", + "DIV", + "POW", + "MODULO"); + +} diff --git a/tests/units/parser.c b/tests/units/parser.c index 1ea17a7..4eb0c04 100644 --- a/tests/units/parser.c +++ b/tests/units/parser.c @@ -83,3 +83,11 @@ Test(parser, cmp) { test_parser_ok("MOD(GT(NUM[3],NUM[3]))", " 3 > 3 "); test_parser_ok("MOD(GE(NUM[3],NUM[3]))", " 3 >= 3 "); } + +Test(parser, num_arith) { + test_parser_ok("MOD(ADD(NUM[1],MUL(NUM[2],NUM[3])))", " 1 + 2 * 3 "); + test_parser_ok("MOD(MUL(ADD(NUM[1],NUM[2]),NUM[3]))", " (1 + 2) * 3 "); + test_parser_ok("MOD(SUB(NUM[1],DIV(NUM[2],NUM[3])))", " 1 - 2 / 3 "); + test_parser_ok("MOD(DIV(SUB(NUM[1],NUM[2]),NUM[3]))", " (1 - 2) / 3 "); + test_parser_ok("MOD(POW(NUM[2],ADD(NUM[1],NUM[2])))", " 2^(1+2) "); +}