diff --git a/doc/roza.bnf b/doc/roza.bnf index f918b08..e1c1706 100644 --- a/doc/roza.bnf +++ b/doc/roza.bnf @@ -4,7 +4,8 @@ EXPR ::= | EQNE ASSERT ::= assert EXPR EQNE ::= -| BUILTIN -| BUILTIN eq BUILTIN -| BUILTIN ne BUILTIN +| CMP +| CMP eq CMP +| CMP ne CMP +CMP ::= BUILTIN ((lt | le | ge | gt) BUILTIN)? BUILTIN ::= num | bool | str diff --git a/lib/compiler.c b/lib/compiler.c index a8b0455..bbc8164 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -77,6 +77,31 @@ void compiler_run(compiler_t* compiler, node_t* node) node->type == NODE_EQ ? OP_EQ : OP_NE, RZ_NO_PARAM); } break; + case NODE_LT: + case NODE_LE: + case NODE_GT: + case NODE_GE:{ + assert(node->children.size == 2); + + for (size_t i=0; ichildren.size; i++) + { + compiler_run(compiler, node_child(node, i)); + } + + Opcode op; + switch (node->type) + { + case NODE_LT: op = OP_LT; break; + case NODE_LE: op = OP_LE; break; + case NODE_GT: op = OP_GT; break; + case NODE_GE: op = OP_GE; 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 d0bd370..b523fcf 100644 --- a/lib/lexer.c +++ b/lib/lexer.c @@ -40,6 +40,10 @@ node_t* lexer_try_new_next(lexer_t* lexer) // ==== RZ_TEXT("==", NODE_EQ, 0); RZ_TEXT("!=", NODE_NE, 0); + RZ_TEXT("<=", NODE_LE, 0); + RZ_TEXT(">=", NODE_GE, 0); + RZ_TEXT("<", NODE_LT, 0); + RZ_TEXT(">", NODE_GT, 0); // Keywords // ======== @@ -92,7 +96,8 @@ node_t* lexer_try_new_next(lexer_t* lexer) } if (res_str.size > 0 - && (cursor >= len || isspace(lexer->source[cursor]))) + && (cursor >= len || !(isalnum(lexer->source[cursor]) + || lexer->source[cursor] == '.'))) { node_t* tok = malloc(sizeof(node_t)); node_init(tok, NODE_NUM, res_str.data, lexer->line); diff --git a/lib/node.h b/lib/node.h index 0f0c81a..034b816 100644 --- a/lib/node.h +++ b/lib/node.h @@ -3,11 +3,12 @@ #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) +#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) RZ_ENUM_H(NodeType, NODE_TYPE); diff --git a/lib/opcodes.h b/lib/opcodes.h index 88d93ec..9c12f45 100644 --- a/lib/opcodes.h +++ b/lib/opcodes.h @@ -6,7 +6,8 @@ #define OPCODES(G) \ G(OP_ASSERT), \ G(OP_PUSH), G(OP_POP), \ - G(OP_EQ), G(OP_NE) + G(OP_EQ), G(OP_NE), \ + G(OP_LT), G(OP_LE), G(OP_GT), G(OP_GE) RZ_ENUM_H(Opcode, OPCODES); diff --git a/lib/parser.c b/lib/parser.c index 684de5a..5995896 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -80,7 +80,7 @@ node_t* parser_try_new_assert(parser_t* parser) node_t* parser_try_new_eqne(parser_t* parser) { assert(parser); - node_t* lhs = parser_try_new_builtin(parser); + node_t* lhs = parser_try_new_cmp(parser); while (1) { @@ -91,7 +91,7 @@ node_t* parser_try_new_eqne(parser_t* parser) node_t* node = lexer_try_new_next(parser->lexer); node_add_new_child(node, lhs); - node_add_new_child(node, parser_try_new_builtin(parser)); + node_add_new_child(node, parser_try_new_cmp(parser)); lhs = node; } @@ -104,6 +104,27 @@ node_t* parser_try_new_eqne(parser_t* parser) return lhs; } +node_t* parser_try_new_cmp(parser_t* parser) +{ + assert(parser); + node_t* lhs = parser_try_new_builtin(parser); + + int next = lexer_peek(parser->lexer, 1); + + if (next == NODE_LT + || next == NODE_GT + || next == NODE_LE + || next == NODE_GE) + { + 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)); + lhs = node; + } + + return lhs; +} + node_t* parser_try_new_builtin(parser_t* parser) { assert(parser); diff --git a/lib/parser.h b/lib/parser.h index 52ad5ba..de2aef9 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -18,6 +18,7 @@ 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_eqne(parser_t* parser); +node_t* parser_try_new_cmp(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 2ee845b..077f3b5 100644 --- a/lib/prepass.c +++ b/lib/prepass.c @@ -26,7 +26,11 @@ void prepass_run(prepass_t* prepass, node_t* node) case NODE_BOOL: case NODE_ASSERT: case NODE_EQ: - case NODE_NE: { + case NODE_NE: + case NODE_LT: + case NODE_LE: + case NODE_GT: + case NODE_GE:{ 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 39d1583..1e47e72 100644 --- a/lib/vm.c +++ b/lib/vm.c @@ -2,6 +2,7 @@ #include "lib/commons.h" #include "lib/mod.h" #include "lib/type.h" +#include "lib/tysy.h" void vm_init(vm_t* vm, tysy_t* tysy, err_t* err) { @@ -109,12 +110,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) if (value->type->kind != TYPE_BOOL) { - char msg[RZ_STR_LIMIT]; - snprintf(msg, RZ_STR_LIMIT, - "type mismatch: BOOL expected, got %s.", - TypeKindStr[value->type->kind] + strlen("TYPE_")); - - err_fatal(vm->err, msg, value->line); + vm_ensure_type(vm, value, TYPE_BOOL); vm->pc++; break; @@ -155,6 +151,36 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) vm->pc++; } break; + case OP_LT: + case OP_LE: + case OP_GT: + case OP_GE: { + 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); + + int val = 0; + + switch (op) + { + case OP_LT: val = lhs->value.num < rhs->value.num; break; + case OP_LE: val = lhs->value.num <= rhs->value.num; break; + case OP_GT: val = lhs->value.num > rhs->value.num; break; + case OP_GE: val = lhs->value.num >= rhs->value.num; break; + default: break; + } + + value_t* res = tysy_new_bool(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]); @@ -164,3 +190,22 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) return 0; } + +void vm_ensure_type(vm_t* vm, value_t* value, TypeKind want) +{ + assert(vm); + assert(value); + TypeKind got = value->type->kind; + int line = value->line; + + if (got != want) + { + char msg[RZ_STR_LIMIT]; + snprintf(msg, RZ_STR_LIMIT, + "type mismatch: %s expected, got %s.", + TypeKindStr[want] + strlen("TYPE_"), + TypeKindStr[got] + strlen("TYPE_")); + + err_fatal(vm->err, msg, line); + } +} diff --git a/lib/vm.h b/lib/vm.h index af410ff..9745e9a 100644 --- a/lib/vm.h +++ b/lib/vm.h @@ -28,5 +28,6 @@ size_t vm_stack_str(vm_t* vm, char* buffer, size_t size); int vm_exec_mod(vm_t* vm, mod_t* mod); int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param); +void vm_ensure_type(vm_t* vm, value_t* value, TypeKind want); #endif diff --git a/tests/acceptances/cmp.roza b/tests/acceptances/cmp.roza index edb7507..a4d2770 100644 --- a/tests/acceptances/cmp.roza +++ b/tests/acceptances/cmp.roza @@ -11,3 +11,17 @@ assert "world" != "hello" assert 7 != "hello" assert false != 23 assert "bim" != true + +assert 3 < 5 == true +assert 5 < 5 == false +assert 7 < 5 == false +assert 3 <= 5 == true +assert 5 <= 5 == true +assert 7 <= 5 == false + +assert 3 > 5 == false +assert 5 > 5 == false +assert 7 > 5 == true +assert 3 >= 5 == false +assert 5 >= 5 == true +assert 7 >= 5 == true diff --git a/tests/units/lexer.c b/tests/units/lexer.c index 183f875..d1d47a4 100644 --- a/tests/units/lexer.c +++ b/tests/units/lexer.c @@ -124,4 +124,12 @@ Test(lexer, cmp) { "BOOL[false]", "NE", "BOOL[true]"); + + test_lexer("1< <= > >=5", 6, + "NUM[1]", + "LT", + "LE", + "GT", + "GE", + "NUM[5]"); } diff --git a/tests/units/parser.c b/tests/units/parser.c index 25c7f3b..1ea17a7 100644 --- a/tests/units/parser.c +++ b/tests/units/parser.c @@ -76,3 +76,10 @@ Test(parser, eqne) { test_parser_ok("MOD(EQ(NUM[3],NUM[3]))", " 3 == 3 "); test_parser_ok("MOD(NE(NUM[3],NUM[3]))", " 3 != 3 "); } + +Test(parser, cmp) { + test_parser_ok("MOD(LT(NUM[3],NUM[3]))", " 3 < 3 "); + test_parser_ok("MOD(LE(NUM[3],NUM[3]))", " 3 <= 3 "); + test_parser_ok("MOD(GT(NUM[3],NUM[3]))", " 3 > 3 "); + test_parser_ok("MOD(GE(NUM[3],NUM[3]))", " 3 >= 3 "); +}