'equals' and 'not equals' operators.

main
bog 2023-12-15 19:30:20 +01:00
parent 90fe4c9f8e
commit 076fd79ff1
15 changed files with 220 additions and 20 deletions

View File

@ -1,6 +1,10 @@
MOD ::= EXPR*
EXPR ::=
| ASSERT
| BUILTIN
| EQNE
ASSERT ::= assert EXPR
EQNE ::=
| BUILTIN
| BUILTIN eq BUILTIN
| BUILTIN ne BUILTIN
BUILTIN ::= num | bool | str

View File

@ -64,6 +64,19 @@ void compiler_run(compiler_t* compiler, node_t* node)
mod_push_instr(compiler->mod, OP_ASSERT, RZ_NO_PARAM);
} break;
case NODE_NE:
case NODE_EQ: {
assert(node->children.size == 2);
for (size_t i=0; i<node->children.size; i++)
{
compiler_run(compiler, node_child(node, i));
}
mod_push_instr(compiler->mod,
node->type == NODE_EQ ? OP_EQ : OP_NE, RZ_NO_PARAM);
} break;
default: {
fprintf(stderr, "Cannot compile unknown node '%s'",
NodeTypeStr[node->type]);

View File

@ -1,10 +1,16 @@
#include "lexer.h"
#include "lib/commons.h"
#define RZ_KEYWORD(KW, NODE, VAL) \
{ \
node_t* kw = lexer_try_new_keyword(lexer, KW, NODE, VAL); \
if (kw) { lexer_skip_spaces(lexer); return kw; } \
#define RZ_KEYWORD(KW, NODE, VAL) \
{ \
node_t* kw = lexer_try_new_keyword(lexer, KW, NODE, VAL, 1); \
if (kw) { lexer_skip_spaces(lexer); return kw; } \
}
#define RZ_TEXT(KW, NODE, VAL) \
{ \
node_t* kw = lexer_try_new_keyword(lexer, KW, NODE, VAL, 0); \
if (kw) { lexer_skip_spaces(lexer); return kw; } \
}
void lexer_init(lexer_t* lexer, char const* source, err_t* err)
@ -30,6 +36,11 @@ node_t* lexer_try_new_next(lexer_t* lexer)
lexer_skip_spaces(lexer);
// Text
// ====
RZ_TEXT("==", NODE_EQ, 0);
RZ_TEXT("!=", NODE_NE, 0);
// Keywords
// ========
RZ_KEYWORD("true", NODE_BOOL, 1);
@ -156,7 +167,8 @@ void lexer_skip_spaces(lexer_t* lexer)
}
node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw,
NodeType type, int has_value)
NodeType type, int has_value,
int is_kw)
{
assert(lexer);
assert(kw);
@ -182,7 +194,7 @@ node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw,
int next_idx = lexer->cursor + len;
if (next_idx < strlen(lexer->source)
&& !isspace(lexer->source[next_idx]))
&& (is_kw && !lexer_is_sep(lexer, next_idx)))
{
return NULL;
}
@ -272,3 +284,23 @@ node_t* lexer_try_new_str(lexer_t* lexer)
return tok;
}
int lexer_is_sep(lexer_t* lexer, size_t idx)
{
assert(lexer);
if (idx >= strlen(lexer->source))
{
return 1;
}
char c = lexer->source[idx];
if (isspace(c))
{
return 1;
}
return c == '='
|| c == '!';
}

View File

@ -20,8 +20,11 @@ NodeType lexer_peek(lexer_t* lexer, int lookahead);
void lexer_skip_spaces(lexer_t* lexer);
node_t* lexer_try_new_keyword(lexer_t* lexer, char* kw,
NodeType type, int has_value);
NodeType type, int has_value,
int is_kw);
node_t* lexer_try_new_str(lexer_t* lexer);
int lexer_is_sep(lexer_t* lexer, size_t idx);
#endif

View File

@ -1,4 +1,5 @@
#include "lexer.h"
#include "lib/commons.h"
#include "parser.h"
#include "loader.h"
#include "compiler.h"
@ -63,6 +64,15 @@ int loader_ldfile(loader_t* loader, char const* path, int debug)
node_t* node = parser_try_new_tree(&parser);
if (debug)
{
char msg[RZ_STR_LIMIT];
node_str(node, msg, RZ_STR_LIMIT);
printf("\n======== AST ========\n");
printf("%s\n", msg);
}
if (node)
{
mod_t mod;
@ -102,7 +112,7 @@ int loader_ldfile(loader_t* loader, char const* path, int debug)
// execute
{
vm_t vm;
vm_init(&vm, &err);
vm_init(&vm, &tysy, &err);
int status = vm_exec_mod(&vm, &mod);

View File

@ -6,7 +6,8 @@
#define NODE_TYPE(G) \
G(NODE_MOD), \
G(NODE_NUM), G(NODE_BOOL), G(NODE_STR), \
G(NODE_ASSERT)
G(NODE_ASSERT), \
G(NODE_EQ), G(NODE_NE)
RZ_ENUM_H(NodeType, NODE_TYPE);

View File

@ -5,7 +5,8 @@
#define OPCODES(G) \
G(OP_ASSERT), \
G(OP_PUSH), G(OP_POP)
G(OP_PUSH), G(OP_POP), \
G(OP_EQ), G(OP_NE)
RZ_ENUM_H(Opcode, OPCODES);

View File

@ -61,7 +61,7 @@ node_t* parser_try_new_expr(parser_t* parser)
return parser_try_new_assert(parser);
}
return parser_try_new_builtin(parser);
return parser_try_new_eqne(parser);
}
node_t* parser_try_new_assert(parser_t* parser)
@ -77,6 +77,33 @@ node_t* parser_try_new_assert(parser_t* parser)
return node;
}
node_t* parser_try_new_eqne(parser_t* parser)
{
assert(parser);
node_t* lhs = parser_try_new_builtin(parser);
while (1)
{
NodeType next_type = lexer_peek(parser->lexer, 1);
if (next_type == NODE_EQ || next_type == NODE_NE)
{
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));
lhs = node;
}
else
{
break;
}
}
return lhs;
}
node_t* parser_try_new_builtin(parser_t* parser)
{
assert(parser);

View File

@ -17,6 +17,7 @@ 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_eqne(parser_t* parser);
node_t* parser_try_new_builtin(parser_t* parser);
node_t* parser_try_new_consume(parser_t* parser, NodeType type);

View File

@ -24,7 +24,9 @@ void prepass_run(prepass_t* prepass, node_t* node)
case NODE_NUM:
case NODE_STR:
case NODE_BOOL:
case NODE_ASSERT:{
case NODE_ASSERT:
case NODE_EQ:
case NODE_NE: {
for (size_t i=0; i<node->children.size; i++)
{
prepass_run(prepass, (node_t*) node->children.data[i]);

View File

@ -1,8 +1,9 @@
#include "vm.h"
#include "lib/commons.h"
#include "lib/mod.h"
#include "lib/type.h"
void vm_init(vm_t* vm, err_t* err)
void vm_init(vm_t* vm, tysy_t* tysy, err_t* err)
{
assert(vm);
@ -10,6 +11,7 @@ void vm_init(vm_t* vm, err_t* err)
vm->bp = 0;
vm->sp = 0;
vm->tysy = tysy;
vm->err = err;
}
@ -18,6 +20,24 @@ void vm_free(vm_t* vm)
assert(vm);
}
void vm_push_value(vm_t* vm, mod_t* mod, value_t* value)
{
assert(vm);
assert(value);
param_t param = mod_push_new_value(mod, value);
vm_push(vm, param);
}
void vm_push(vm_t* vm, param_t param)
{
assert(vm);
assert(vm->sp + 1 < RZ_STACK_LIMIT);
vm->stack[vm->sp] = param;
vm->sp++;
}
param_t vm_pop(vm_t* vm)
{
assert(vm);
@ -111,6 +131,54 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param)
} break;
case OP_NE:
case OP_EQ: {
int l = vm_pop(vm);
int r = vm_pop(vm);
assert(l != -1);
assert(r != -1);
value_t* rhs = mod->values.data[l];
value_t* lhs = mod->values.data[r];
int same_type = lhs->type->kind == rhs->type->kind;
int type = lhs->type->kind;
int eq = 0;
if (same_type)
{
if (type == TYPE_NUM)
{
eq = lhs->value.num == rhs->value.num;
}
else if (type == TYPE_BOOL)
{
eq = lhs->value.bool == rhs->value.bool;
}
else if (type == TYPE_STR)
{
eq = strcmp(lhs->value.str, rhs->value.str) == 0;
}
else
{
fprintf(stderr, "Cannot test equality on unknown type '%s'.\n",
TypeKindStr[type]);
abort();
}
}
value_t* res = tysy_new_bool(vm->tysy,
(op == OP_EQ
? (same_type && eq)
: (!same_type || !eq)),
rhs->line);
vm_push_value(vm, mod, res);
vm->pc++;
} break;
default: {
fprintf(stderr, "Cannot execute unknown opcode '%s'.\n",
OpcodeStr[op]);

View File

@ -5,8 +5,10 @@
#include "opcodes.h"
#include "mod.h"
#include "err.h"
#include "tysy.h"
typedef struct {
tysy_t* tysy;
param_t stack[RZ_STACK_LIMIT];
size_t pc;
size_t bp;
@ -15,9 +17,11 @@ typedef struct {
err_t* err;
} vm_t;
void vm_init(vm_t* vm, err_t* err);
void vm_init(vm_t* vm, tysy_t* tysy, err_t* err);
void vm_free(vm_t* vm);
void vm_push_value(vm_t* vm, mod_t* mod, value_t* value);
void vm_push(vm_t* vm, param_t param);
param_t vm_pop(vm_t* vm);
size_t vm_stack_str(vm_t* vm, char* buffer, size_t size);

View File

@ -0,0 +1,13 @@
assert 5 == 5
assert 3 != 5
assert true == true
assert false == false
assert false != true
assert "hello" == "hello"
assert "world" != "hello"
assert 7 != "hello"
assert false != 23
assert "bim" != true

View File

@ -8,13 +8,13 @@ static void test_lexer_ok(char const* oracle, char const* source)
lexer_init(&lex, source, NULL);
node_t* tok = lexer_try_new_next(&lex);
cr_assert(tok != NULL);
cr_assert(tok != NULL, "error -> %s", source);
size_t const SZ = 512;
char str[SZ];
node_str(tok, str, SZ);
cr_assert_str_eq(str, oracle);
cr_assert_str_eq(str, oracle, "error -> %s", source);
node_free(tok);
lexer_free(&lex);
@ -30,7 +30,7 @@ static void test_lexer_ko(char const* source)
node_t* tok = lexer_try_new_next(&lex);
cr_assert(tok == NULL);
cr_assert(tok == NULL, "error -> %s", source);
cr_assert(err.size > 0);
err_free(&err);
@ -48,7 +48,7 @@ static void test_lexer(char const* source, size_t n, ...)
for (size_t i=0; i<n; i++)
{
node_t* tok = lexer_try_new_next(&lexer);
cr_assert(NULL != tok);
cr_assert(NULL != tok, "error -> %s", source);
size_t const SZ = 256;
char tok_str[SZ];
@ -109,3 +109,19 @@ Test(lexer, str) {
Test(lexer, assert) {
test_lexer("assert", 1, "ASSERT");
}
Test(lexer, cmp) {
test_lexer("== !=", 2,
"EQ",
"NE");
test_lexer("false==true", 3,
"BOOL[false]",
"EQ",
"BOOL[true]");
test_lexer("false!=true", 3,
"BOOL[false]",
"NE",
"BOOL[true]");
}

View File

@ -15,7 +15,7 @@ static void test_parser_ok(char const* oracle, char const* source)
node_t* node = parser_try_new_tree(&parser);
cr_assert(NULL != node);
cr_assert(NULL != node, "error -> %s", source);
size_t const SZ = 256;
char msg[SZ];
@ -71,3 +71,8 @@ Test(parser, assert) {
test_parser_ok("MOD(ASSERT(BOOL[false]))", " assert false ");
test_parser_ok("MOD(ASSERT(BOOL[true]))", " assert true ");
}
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 ");
}