✨ comparison operators.
parent
bd84f3bb3d
commit
975a089dbd
|
@ -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
|
||||
|
|
|
@ -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; i<node->children.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]);
|
||||
|
|
|
@ -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);
|
||||
|
|
11
lib/node.h
11
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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
25
lib/parser.c
25
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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; i<node->children.size; i++)
|
||||
{
|
||||
prepass_run(prepass, (node_t*) node->children.data[i]);
|
||||
|
|
57
lib/vm.c
57
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);
|
||||
}
|
||||
}
|
||||
|
|
1
lib/vm.h
1
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]");
|
||||
}
|
||||
|
|
|
@ -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 ");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue