Compare commits
3 Commits
8a930db339
...
bd84f3bb3d
Author | SHA1 | Date |
---|---|---|
bog | bd84f3bb3d | |
bog | 076fd79ff1 | |
bog | 90fe4c9f8e |
|
@ -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
|
||||
|
|
|
@ -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]);
|
||||
|
|
44
lib/lexer.c
44
lib/lexer.c
|
@ -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 == '!';
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
12
lib/loader.c
12
lib/loader.c
|
@ -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);
|
||||
|
||||
|
|
10
lib/mod.c
10
lib/mod.c
|
@ -98,16 +98,6 @@ size_t mod_push_new_value(mod_t* mod, value_t* value)
|
|||
assert(mod);
|
||||
assert(value);
|
||||
|
||||
for (size_t i=0; i<mod->values.size; i++)
|
||||
{
|
||||
if (value_eq(value, mod->values.data[i]))
|
||||
{
|
||||
value_free(value);
|
||||
free(value);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (mod->values.size == 0)
|
||||
{
|
||||
mod->values.cap = 2;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
29
lib/parser.c
29
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_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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
|
|
46
lib/vm.c
46
lib/vm.c
|
@ -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,30 @@ 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 eq = value_eq(lhs, rhs);
|
||||
|
||||
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]);
|
||||
|
|
6
lib/vm.h
6
lib/vm.h
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -14,8 +14,6 @@ do
|
|||
TRACE="$(roza $file 2>&1)"
|
||||
RES=$?
|
||||
|
||||
# echo -en "\e[33m$file\e[0m ... "
|
||||
|
||||
if [ $RES -ne 0 ]
|
||||
then
|
||||
echo
|
||||
|
@ -32,14 +30,7 @@ do
|
|||
TOTAL=$(($TOTAL + 1))
|
||||
done
|
||||
|
||||
echo
|
||||
|
||||
if [ $PASSED -eq $TOTAL ]
|
||||
then
|
||||
echo -e "\e[32m$H All tests passed $H\e[0m"
|
||||
else
|
||||
echo -e "\e[31m$H Some tests failed $H\e[0m"
|
||||
fi
|
||||
echo -e "\e[33m$H SUMMARY $H\e[0m"
|
||||
|
||||
echo "PASSED: $PASSED"
|
||||
for ok in $PASSED_LST
|
||||
|
@ -57,3 +48,10 @@ done
|
|||
|
||||
echo
|
||||
echo "TOTAL: $TOTAL"
|
||||
|
||||
if [ $PASSED -eq $TOTAL ]
|
||||
then
|
||||
echo -e "\e[32m$H All tests passed $H\e[0m"
|
||||
else
|
||||
echo -e "\e[31m$H Some tests failed $H\e[0m"
|
||||
fi
|
||||
|
|
|
@ -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]");
|
||||
}
|
||||
|
|
|
@ -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 ");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue