From caf6b3b47c85e26addc95cf0a611329ac031d725 Mon Sep 17 00:00:00 2001 From: bog Date: Sat, 16 Dec 2023 19:12:20 +0100 Subject: [PATCH] :sparkles: bool arithmetic operators. --- doc/roza.bnf | 6 ++- lib/compiler.c | 73 ++++++++++++++++++++++++++++++++++++ lib/lexer.c | 8 +++- lib/loader.c | 24 ++++++------ lib/mod.c | 4 +- lib/mod.h | 2 +- lib/node.h | 3 +- lib/opcodes.h | 5 ++- lib/parser.c | 74 +++++++++++++++++++++++++++++++++++-- lib/parser.h | 2 + lib/prepass.c | 7 +++- lib/vm.c | 64 ++++++++++++++++++++++++++++++++ tests/acceptances/bool.roza | 16 ++++++++ tests/acceptances/num.roza | 4 +- tests/units/lexer.c | 8 ++++ tests/units/parser.c | 8 ++++ 16 files changed, 283 insertions(+), 25 deletions(-) create mode 100644 tests/acceptances/bool.roza diff --git a/doc/roza.bnf b/doc/roza.bnf index a6d80e4..5212b8e 100644 --- a/doc/roza.bnf +++ b/doc/roza.bnf @@ -1,8 +1,10 @@ MOD ::= EXPR* EXPR ::= | ASSERT -| EQNE +| OR ASSERT ::= assert EXPR +OR ::= AND (or AND)* +AND ::= EQNE (and EQNE)* EQNE ::= | CMP | CMP eq CMP @@ -11,6 +13,6 @@ 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 +UNARY ::= sub? GROUP | not? GROUP GROUP ::= BUILTIN | opar EXPR cpar BUILTIN ::= num | bool | str diff --git a/lib/compiler.c b/lib/compiler.c index fdd474a..bc209f3 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -1,5 +1,6 @@ #include "compiler.h" #include "lib/commons.h" +#include "lib/mod.h" void compiler_init(compiler_t* compiler, mod_t* mod, tysy_t* tysy, err_t* err) { @@ -138,6 +139,78 @@ void compiler_run(compiler_t* compiler, node_t* node) } break; + case NODE_AND: { + size_t const SZ = 512; + size_t to_false[SZ]; + size_t sz = 0; + + for (size_t i=0; ichildren.size; i++) + { + compiler_run(compiler, node_child(node, i)); + size_t addr = mod_push_instr(compiler->mod, OP_BRF, RZ_NO_PARAM); + to_false[sz++] = addr; + } + + // True case + value_t* t = tysy_new_bool(compiler->tysy, 1, node->line); + param_t t_param = mod_push_new_value(compiler->mod, t); + mod_push_instr(compiler->mod, OP_PUSH, t_param); + size_t to_end = mod_push_instr(compiler->mod, OP_BR, RZ_NO_PARAM); + + // False case + size_t program_sz = compiler->mod->program.size; + for (size_t i=0; imod->program.params[to_false[i]] = program_sz; + } + + value_t* f = tysy_new_bool(compiler->tysy, 0, node->line); + param_t f_param = mod_push_new_value(compiler->mod, f); + mod_push_instr(compiler->mod, OP_PUSH, f_param); + + compiler->mod->program.params[to_end] = compiler->mod->program.size; + + } break; + + case NODE_OR: { + size_t const SZ = 512; + size_t to_true[SZ]; + size_t sz = 0; + + for (size_t i=0; ichildren.size; i++) + { + compiler_run(compiler, node_child(node, i)); + size_t addr = mod_push_instr(compiler->mod, OP_BRT, RZ_NO_PARAM); + to_true[sz++] = addr; + } + + // False case + value_t* f = tysy_new_bool(compiler->tysy, 0, node->line); + param_t f_param = mod_push_new_value(compiler->mod, f); + mod_push_instr(compiler->mod, OP_PUSH, f_param); + size_t to_end = mod_push_instr(compiler->mod, OP_BR, RZ_NO_PARAM); + + // True case + size_t program_sz = compiler->mod->program.size; + for (size_t i=0; imod->program.params[to_true[i]] = program_sz; + } + + value_t* t = tysy_new_bool(compiler->tysy, 1, node->line); + param_t t_param = mod_push_new_value(compiler->mod, t); + mod_push_instr(compiler->mod, OP_PUSH, t_param); + + compiler->mod->program.params[to_end] = compiler->mod->program.size; + + } break; + + case NODE_NOT: { + assert(node->children.size > 0); + compiler_run(compiler, (node_t*) node->children.data[0]); + mod_push_instr(compiler->mod, OP_NOT, 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 8321bf4..65799f9 100644 --- a/lib/lexer.c +++ b/lib/lexer.c @@ -70,6 +70,9 @@ node_t* lexer_try_new_next(lexer_t* lexer) // Keywords // ======== + RZ_KEYWORD("and", NODE_AND, 0); + RZ_KEYWORD("or", NODE_OR, 0); + RZ_KEYWORD("not", NODE_NOT, 0); RZ_KEYWORD("true", NODE_BOOL, 1); RZ_KEYWORD("false", NODE_BOOL, 1); RZ_KEYWORD("assert", NODE_ASSERT, 0); @@ -348,5 +351,8 @@ int lexer_is_sep(lexer_t* lexer, size_t idx) || c == '/' || c == '%' || c == '^' - || c == '!'; + || c == '!' + || c == '(' + || c == ')' + ; } diff --git a/lib/loader.c b/lib/loader.c index 120edaf..05741f2 100644 --- a/lib/loader.c +++ b/lib/loader.c @@ -63,6 +63,7 @@ int loader_ldfile(loader_t* loader, char const* path, int debug) parser_init(&parser, &lex, &err); node_t* node = parser_try_new_tree(&parser); + assert(node); if (debug) { @@ -109,6 +110,13 @@ int loader_ldfile(loader_t* loader, char const* path, int debug) abort(); } + if (debug) + { + char msg[RZ_STR_LIMIT]; + mod_str(&mod, msg, RZ_STR_LIMIT); + printf("%s", msg); + } + // execute { vm_t vm; @@ -131,18 +139,10 @@ int loader_ldfile(loader_t* loader, char const* path, int debug) if (debug) { - { - char msg[RZ_STR_LIMIT]; - mod_str(&mod, msg, RZ_STR_LIMIT); - printf("%s", msg); - } - - { - char msg[RZ_STR_LIMIT]; - vm_stack_str(&vm, msg, RZ_STR_LIMIT); - printf("\n======== STACK ========\n"); - printf("%s\n", msg); - } + char msg[RZ_STR_LIMIT]; + vm_stack_str(&vm, msg, RZ_STR_LIMIT); + printf("\n======== STACK ========\n"); + printf("%s\n", msg); } vm_free(&vm); diff --git a/lib/mod.c b/lib/mod.c index f1132aa..139d036 100644 --- a/lib/mod.c +++ b/lib/mod.c @@ -35,7 +35,7 @@ void mod_free(mod_t* mod) mod->values.cap = 0; } -void mod_push_instr(mod_t* mod, Opcode op, param_t param) +size_t mod_push_instr(mod_t* mod, Opcode op, param_t param) { assert(mod); @@ -58,6 +58,8 @@ void mod_push_instr(mod_t* mod, Opcode op, param_t param) mod->program.ops[mod->program.size] = op; mod->program.params[mod->program.size] = param; mod->program.size++; + + return mod->program.size - 1; } size_t mod_str(mod_t* mod, char* buffer, size_t size) diff --git a/lib/mod.h b/lib/mod.h index 1af1ab7..2b43ea3 100644 --- a/lib/mod.h +++ b/lib/mod.h @@ -23,7 +23,7 @@ typedef struct { void mod_init(mod_t* mod); void mod_free(mod_t* mod); -void mod_push_instr(mod_t* mod, Opcode op, param_t param); +size_t mod_push_instr(mod_t* mod, Opcode op, param_t param); size_t mod_str(mod_t* mod, char* buffer, size_t size); size_t mod_push_new_value(mod_t* mod, value_t* value); diff --git a/lib/node.h b/lib/node.h index 63ce10e..6b0efc8 100644 --- a/lib/node.h +++ b/lib/node.h @@ -10,7 +10,8 @@ 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) + G(NODE_MODULO), G(NODE_POW), G(NODE_OPAR), G(NODE_CPAR), \ + G(NODE_AND), G(NODE_OR), G(NODE_NOT) RZ_ENUM_H(NodeType, NODE_TYPE); diff --git a/lib/opcodes.h b/lib/opcodes.h index 80ac6e9..a83c2e2 100644 --- a/lib/opcodes.h +++ b/lib/opcodes.h @@ -9,7 +9,10 @@ G(OP_EQ), G(OP_NE), \ 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) + G(OP_MODULO), G(OP_POW), G(OP_USUB), \ + G(OP_AND), G(OP_OR), G(OP_NOT), \ + G(OP_BRF), G(OP_BRT), G(OP_BR) + RZ_ENUM_H(Opcode, OPCODES); diff --git a/lib/parser.c b/lib/parser.c index aae972e..62bfce2 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -61,7 +61,7 @@ node_t* parser_try_new_expr(parser_t* parser) return parser_try_new_assert(parser); } - return parser_try_new_eqne(parser); + return parser_try_new_or(parser); } node_t* parser_try_new_assert(parser_t* parser) @@ -77,10 +77,52 @@ node_t* parser_try_new_assert(parser_t* parser) return node; } +node_t* parser_try_new_or(parser_t* parser) +{ + assert(parser); + node_t* lhs = parser_try_new_and(parser); + + while (1) + { + int next = lexer_peek(parser->lexer, 1); + if (next != NODE_OR) { break; } + + node_t* node = parser_try_new_consume(parser, next); + node_add_new_child(node, lhs); + node_add_new_child(node, parser_try_new_and(parser)); + lhs = node; + } + + return lhs; +} + +node_t* parser_try_new_and(parser_t* parser) +{ + assert(parser); + node_t* lhs = parser_try_new_eqne(parser); + + while (1) + { + int next = lexer_peek(parser->lexer, 1); + if (next != NODE_AND) { break; } + + node_t* node = parser_try_new_consume(parser, next); + assert(node); + node_add_new_child(node, lhs); + node_t* eqne = parser_try_new_eqne(parser); + assert(eqne); + node_add_new_child(node, eqne); + lhs = node; + } + + return lhs; +} + node_t* parser_try_new_eqne(parser_t* parser) { assert(parser); node_t* lhs = parser_try_new_cmp(parser); + assert(lhs); while (1) { @@ -108,6 +150,7 @@ node_t* parser_try_new_cmp(parser_t* parser) { assert(parser); node_t* lhs = parser_try_new_term(parser); + assert(lhs); int next = lexer_peek(parser->lexer, 1); @@ -130,6 +173,7 @@ node_t* parser_try_new_term(parser_t* parser) { assert(parser); node_t* lhs = parser_try_new_factor(parser); + assert(lhs); while (1) { @@ -157,6 +201,7 @@ node_t* parser_try_new_factor(parser_t* parser) { assert(parser); node_t* lhs = parser_try_new_power(parser); + assert(lhs); while (1) { @@ -185,6 +230,7 @@ node_t* parser_try_new_power(parser_t* parser) { assert(parser); node_t* lhs = parser_try_new_unary(parser); + assert(lhs); NodeType next = lexer_peek(parser->lexer, 1); @@ -207,11 +253,27 @@ node_t* parser_try_new_unary(parser_t* parser) if (next == NODE_SUB) { node_t* node = parser_try_new_consume(parser, next); + assert(node); + node_add_new_child(node, parser_try_new_group(parser)); + assert(node); + return node; } - return parser_try_new_group(parser); + if (next == NODE_NOT) + { + node_t* node = parser_try_new_consume(parser, next); + assert(node); + node_add_new_child(node, parser_try_new_unary(parser)); + + return node; + } + + node_t* group = parser_try_new_group(parser); + assert(group); + + return group; } node_t* parser_try_new_group(parser_t* parser) @@ -223,11 +285,17 @@ node_t* parser_try_new_group(parser_t* parser) { lexer_skip_next(parser->lexer); node_t* node = parser_try_new_expr(parser); + assert(node); + assert(lexer_peek(parser->lexer, 1) == NODE_CPAR); lexer_skip_next(parser->lexer); return node; } - return parser_try_new_builtin(parser); + node_t* builtin = parser_try_new_builtin(parser); + + assert(builtin); + + return builtin; } node_t* parser_try_new_builtin(parser_t* parser) diff --git a/lib/parser.h b/lib/parser.h index 7cce2ad..407b577 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -17,6 +17,8 @@ node_t* parser_try_new_tree(parser_t* parser); node_t* parser_try_new_mod(parser_t* parser); node_t* parser_try_new_expr(parser_t* parser); node_t* parser_try_new_assert(parser_t* parser); +node_t* parser_try_new_or(parser_t* parser); +node_t* parser_try_new_and(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); diff --git a/lib/prepass.c b/lib/prepass.c index c96f76c..bcbb9a2 100644 --- a/lib/prepass.c +++ b/lib/prepass.c @@ -37,7 +37,12 @@ void prepass_run(prepass_t* prepass, node_t* node) case NODE_MUL: case NODE_DIV: case NODE_MODULO: - case NODE_POW:{ + case NODE_POW: + + case NODE_OR: + case NODE_AND: + case NODE_NOT: + { 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 14107e0..5c6ecae 100644 --- a/lib/vm.c +++ b/lib/vm.c @@ -215,6 +215,32 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) vm->pc++; } break; + case OP_AND: + case OP_OR: { + 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_BOOL); + vm_ensure_type(vm, rhs, TYPE_BOOL); + + int val = 0; + + switch (op) + { + case OP_AND: val = lhs->value.bool && rhs->value.bool; break; + case OP_OR: val = lhs->value.bool || rhs->value.bool; break; + default: assert(0); break; + } + + value_t* res = tysy_new_bool(vm->tysy, val, lhs->line); + vm_push_value(vm, mod, res); + + vm->pc++; + } break; + case OP_USUB: { int l = vm_pop(vm); @@ -230,6 +256,44 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) vm->pc++; } break; + case OP_NOT: { + int l = vm_pop(vm); + value_t* lhs = mod->values.data[l]; + vm_ensure_type(vm, lhs, TYPE_BOOL); + + int val = !lhs->value.bool; + + value_t* res = tysy_new_bool(vm->tysy, val, lhs->line); + vm_push_value(vm, mod, res); + + vm->pc++; + } break; + + case OP_BRT: + case OP_BRF: { + param_t p = vm_pop(vm); + value_t* value = mod->values.data[p]; + vm_ensure_type(vm, value, TYPE_BOOL); + + if (op == OP_BRT && value->value.bool) + { + vm->pc = param; + } + else if (op == OP_BRF && !value->value.bool) + { + vm->pc = param; + } + else + { + vm->pc++; + } + + } break; + + case OP_BR: { + vm->pc = param; + } break; + default: { fprintf(stderr, "Cannot execute unknown opcode '%s'.\n", OpcodeStr[op]); diff --git a/tests/acceptances/bool.roza b/tests/acceptances/bool.roza new file mode 100644 index 0000000..abc1358 --- /dev/null +++ b/tests/acceptances/bool.roza @@ -0,0 +1,16 @@ +# BOOL ARITHMETIC +# =============== +assert not true == false +assert not false == true +assert false == not true +assert not not true == true +assert not not not false == true +assert (true and true) == true +assert (true and false) == false +assert (false and true) == false +assert (false and false) == false + +assert (true or true) == true +assert (true or false) == true +assert (false or true) == true +assert (false or false) == false diff --git a/tests/acceptances/num.roza b/tests/acceptances/num.roza index 8103fcf..a7d9eb7 100644 --- a/tests/acceptances/num.roza +++ b/tests/acceptances/num.roza @@ -1,4 +1,4 @@ -# Num Arithmetic +# NUM ARITHMETIC # ============== assert 5 + 2 == 7 assert 5 - 2 == 3 @@ -9,7 +9,7 @@ assert 12 / 4 == 3 assert 5 / 2 == 2.5 assert 35 % 8 == 3 assert 2^6 == 64 - +assert -2^3 == -8 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 3e36ebc..e4f8f13 100644 --- a/tests/units/lexer.c +++ b/tests/units/lexer.c @@ -146,3 +146,11 @@ Test(lexer, num_arith) { "MODULO"); } + +Test(lexer, bool_arith) { + test_lexer("and or not", 3, + "AND", + "OR", + "NOT"); + +} diff --git a/tests/units/parser.c b/tests/units/parser.c index 4eb0c04..753e332 100644 --- a/tests/units/parser.c +++ b/tests/units/parser.c @@ -91,3 +91,11 @@ Test(parser, num_arith) { 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) "); } + +Test(parser, bool_arith) { + test_parser_ok("MOD(OR(AND(BOOL[true],BOOL[false]),NOT(BOOL[true])))", + " true and false or not true "); + + test_parser_ok("MOD(ASSERT(AND(BOOL[true],BOOL[false])))", + " assert (true and false) "); +}