✨ assert expression ✅ acceptance tests.
parent
77fda3a432
commit
e8b10498cf
|
@ -11,10 +11,11 @@ int main(int argc, char** argv)
|
||||||
module_init(&module);
|
module_init(&module);
|
||||||
|
|
||||||
module_load_source(&module, argv[1]);
|
module_load_source(&module, argv[1]);
|
||||||
module_compile(&module);
|
int status = module_compile(&module);
|
||||||
|
|
||||||
module_free(&module);
|
module_free(&module);
|
||||||
errors_free();
|
errors_free();
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
ROOT ::= EXPR*
|
ROOT ::= EXPR*
|
||||||
EXPR ::= TERM
|
EXPR ::=
|
||||||
|
| TERM
|
||||||
|
| ASSERT
|
||||||
|
ASSERT ::= assert EXPR eq EXPR
|
||||||
TERM ::= FACTOR ((add|sub) FACTOR)*
|
TERM ::= FACTOR ((add|sub) FACTOR)*
|
||||||
FACTOR ::= USUB ((mul|div|mod) USUB)*
|
FACTOR ::= USUB ((mul|div|mod) USUB)*
|
||||||
USUB ::= sub* POW
|
USUB ::= sub* POW
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
assert 32 eq 32
|
||||||
|
|
||||||
|
assert 1 + 1 eq 2
|
||||||
|
assert 6 * 7 eq 42
|
||||||
|
assert 1 + 2 * 3 eq 7
|
||||||
|
assert (1 + 2) * 3 eq 9
|
||||||
|
assert -2^3 eq -8
|
||||||
|
assert 32 % 7 eq 4
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
TOTAL=0
|
||||||
|
FAILED=0
|
||||||
|
|
||||||
|
for file in $(find . -name "*.sk")
|
||||||
|
do
|
||||||
|
OUTPUT=$(skopy $file 2>&1)
|
||||||
|
RET=$?
|
||||||
|
|
||||||
|
echo -en "\e[35m$file\e[0m ... "
|
||||||
|
|
||||||
|
if [ $RET -eq 0 ]
|
||||||
|
then
|
||||||
|
echo -e "\e[32mpassed\e[0m"
|
||||||
|
else
|
||||||
|
echo -e "\e[31mfailed\e[0m"
|
||||||
|
echo "$OUTPUT"
|
||||||
|
FAILED=$(($FAILED + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
TOTAL=$(($TOTAL + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $FAILED -eq 0 ]
|
||||||
|
then
|
||||||
|
echo -e "\e[32m=== All tests passed ===\e[0m"
|
||||||
|
else
|
||||||
|
echo -e "\e[31m=== Some tests failed ===\e[0m"
|
||||||
|
fi
|
|
@ -21,6 +21,7 @@ void lexer_init(struct lexer* self, char const* source);
|
||||||
void lexer_free(struct lexer* self);
|
void lexer_free(struct lexer* self);
|
||||||
|
|
||||||
void lexer_skip_spaces(struct lexer* self);
|
void lexer_skip_spaces(struct lexer* self);
|
||||||
|
bool lexer_is_sep(struct lexer* self, size_t index);
|
||||||
|
|
||||||
bool lexer_next_is(struct lexer* self, TokenKind kind);
|
bool lexer_next_is(struct lexer* self, TokenKind kind);
|
||||||
|
|
||||||
|
@ -30,5 +31,8 @@ struct token* lexer_try_scan_int(struct lexer* self);
|
||||||
struct token* lexer_try_scan_text(struct lexer* self,
|
struct token* lexer_try_scan_text(struct lexer* self,
|
||||||
char const* text,
|
char const* text,
|
||||||
TokenKind kind);
|
TokenKind kind);
|
||||||
|
struct token* lexer_try_scan_keyword(struct lexer* self,
|
||||||
|
char const* keyword,
|
||||||
|
TokenKind kind,
|
||||||
|
char const* value);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -16,6 +16,6 @@ void module_free(struct module* self);
|
||||||
void module_load_source(struct module* self,
|
void module_load_source(struct module* self,
|
||||||
char const* path);
|
char const* path);
|
||||||
|
|
||||||
void module_compile(struct module* self);
|
int module_compile(struct module* self);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -8,7 +8,7 @@ G(NODE_ROOT), \
|
||||||
G(NODE_INT), \
|
G(NODE_INT), \
|
||||||
G(NODE_ADD), G(NODE_SUB), G(NODE_MUL),\
|
G(NODE_ADD), G(NODE_SUB), G(NODE_MUL),\
|
||||||
G(NODE_DIV), G(NODE_POW), G(NODE_MOD),\
|
G(NODE_DIV), G(NODE_POW), G(NODE_MOD),\
|
||||||
G(NODE_USUB)
|
G(NODE_USUB), G(NODE_ASSERT_EQ)
|
||||||
|
|
||||||
SK_ENUM_H(NodeKind, NODE_KIND);
|
SK_ENUM_H(NodeKind, NODE_KIND);
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ struct node* parser_try(struct parser* self,
|
||||||
struct node* parser_try_parse(struct parser* self);
|
struct node* parser_try_parse(struct parser* self);
|
||||||
struct node* parser_try_root(struct parser* self);
|
struct node* parser_try_root(struct parser* self);
|
||||||
struct node* parser_try_expr(struct parser* self);
|
struct node* parser_try_expr(struct parser* self);
|
||||||
|
struct node* parser_try_assert(struct parser* self);
|
||||||
struct node* parser_try_term(struct parser* self);
|
struct node* parser_try_term(struct parser* self);
|
||||||
struct node* parser_try_factor(struct parser* self);
|
struct node* parser_try_factor(struct parser* self);
|
||||||
struct node* parser_try_usub(struct parser* self);
|
struct node* parser_try_usub(struct parser* self);
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
G(OP_PUSH), \
|
G(OP_PUSH), \
|
||||||
G(OP_ADD), G(OP_SUB), G(OP_MUL), \
|
G(OP_ADD), G(OP_SUB), G(OP_MUL), \
|
||||||
G(OP_DIV), G(OP_MOD), G(OP_POW), \
|
G(OP_DIV), G(OP_MOD), G(OP_POW), \
|
||||||
G(OP_USUB)
|
G(OP_USUB), G(OP_ASSERT_EQ)
|
||||||
|
|
||||||
SK_ENUM_H(Opcode, OPCODE);
|
SK_ENUM_H(Opcode, OPCODE);
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ struct value* state_try_get_value(struct state* self, SK value);
|
||||||
|
|
||||||
SK state_pop(struct state* self);
|
SK state_pop(struct state* self);
|
||||||
|
|
||||||
SK state_push_int(struct state* self, int integer);
|
SK state_push_int(struct state* self, int integer, int line);
|
||||||
|
|
||||||
SK state_add(struct state* self);
|
SK state_add(struct state* self);
|
||||||
SK state_sub(struct state* self);
|
SK state_sub(struct state* self);
|
||||||
|
|
|
@ -8,7 +8,8 @@ G(TOKEN_ROOT), \
|
||||||
G(TOKEN_INT), \
|
G(TOKEN_INT), \
|
||||||
G(TOKEN_ADD), G(TOKEN_SUB), \
|
G(TOKEN_ADD), G(TOKEN_SUB), \
|
||||||
G(TOKEN_MUL), G(TOKEN_DIV), G(TOKEN_MOD), \
|
G(TOKEN_MUL), G(TOKEN_DIV), G(TOKEN_MOD), \
|
||||||
G(TOKEN_POW), G(TOKEN_OPAR), G(TOKEN_CPAR)
|
G(TOKEN_POW), G(TOKEN_OPAR), G(TOKEN_CPAR), \
|
||||||
|
G(TOKEN_ASSERT), G(TOKEN_ASSERT_EQ)
|
||||||
|
|
||||||
SK_ENUM_H(TokenKind, TOKEN_KIND);
|
SK_ENUM_H(TokenKind, TOKEN_KIND);
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,18 @@ struct value
|
||||||
{
|
{
|
||||||
TypeKind type;
|
TypeKind type;
|
||||||
union val val;
|
union val val;
|
||||||
|
int line;
|
||||||
};
|
};
|
||||||
|
|
||||||
void value_init(struct value* self,
|
void value_init(struct value* self,
|
||||||
TypeKind type,
|
TypeKind type,
|
||||||
union val val);
|
union val val,
|
||||||
|
int line);
|
||||||
|
|
||||||
void value_free(struct value* self);
|
void value_free(struct value* self);
|
||||||
|
|
||||||
|
bool value_equals(struct value* self, struct value* rhs);
|
||||||
|
|
||||||
void value_str(struct value* self, struct str* dest);
|
void value_str(struct value* self, struct str* dest);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -29,11 +29,16 @@ void compiler_compile(struct compiler* self,
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_ASSERT_EQ: {
|
||||||
|
compiler_compile_children(self, node, prog);
|
||||||
|
prog_add_instr(prog, OP_ASSERT_EQ, SK_NO_PARAM);
|
||||||
|
} break;
|
||||||
|
|
||||||
case NODE_INT: {
|
case NODE_INT: {
|
||||||
struct value* value = malloc(sizeof(struct value));
|
struct value* value = malloc(sizeof(struct value));
|
||||||
union val val;
|
union val val;
|
||||||
val.integer = atoi(node->token->value);
|
val.integer = atoi(node->token->value);
|
||||||
value_init(value, TYPE_INT, val);
|
value_init(value, TYPE_INT, val, node->token->line);
|
||||||
|
|
||||||
size_t idx = prog_add_constant(prog, value);
|
size_t idx = prog_add_constant(prog, value);
|
||||||
prog_add_instr(prog, OP_PUSH, idx);
|
prog_add_instr(prog, OP_PUSH, idx);
|
||||||
|
|
|
@ -17,17 +17,61 @@ void exec_execute(struct exec* self,
|
||||||
{
|
{
|
||||||
while (self->pc < prog->opcodes.size)
|
while (self->pc < prog->opcodes.size)
|
||||||
{
|
{
|
||||||
|
if (!errors_ok())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Opcode opcode = (size_t) prog->opcodes.data[self->pc];
|
Opcode opcode = (size_t) prog->opcodes.data[self->pc];
|
||||||
size_t param = (size_t) prog->params.data[self->pc];
|
size_t param = (size_t) prog->params.data[self->pc];
|
||||||
|
|
||||||
switch (opcode)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
|
case OP_ASSERT_EQ: {
|
||||||
|
SK rhs = state_pop(state);
|
||||||
|
SK lhs = state_pop(state);
|
||||||
|
struct value* left =
|
||||||
|
state_try_get_value(state, lhs);
|
||||||
|
struct value* right =
|
||||||
|
state_try_get_value(state, rhs);
|
||||||
|
|
||||||
|
bool equals = value_equals(
|
||||||
|
left, right
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!equals)
|
||||||
|
{
|
||||||
|
struct str lstr;
|
||||||
|
str_init(&lstr);
|
||||||
|
value_str(left, &lstr);
|
||||||
|
|
||||||
|
struct str rstr;
|
||||||
|
str_init(&rstr);
|
||||||
|
value_str(right, &rstr);
|
||||||
|
|
||||||
|
errors_push(left->line,
|
||||||
|
"assertion failed"
|
||||||
|
"\n\texpected: %s"
|
||||||
|
"\n\tgot: %s",
|
||||||
|
rstr.value, lstr.value);
|
||||||
|
|
||||||
|
str_free(&lstr);
|
||||||
|
str_free(&rstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->pc++;
|
||||||
|
} break;
|
||||||
|
|
||||||
case OP_PUSH: {
|
case OP_PUSH: {
|
||||||
struct value* constant = prog->constants.data[param];
|
struct value* constant = prog->constants.data[param];
|
||||||
switch (constant->type)
|
switch (constant->type)
|
||||||
{
|
{
|
||||||
case TYPE_INT: {
|
case TYPE_INT: {
|
||||||
state_push_int(state, constant->val.integer);
|
state_push_int(
|
||||||
|
state,
|
||||||
|
constant->val.integer,
|
||||||
|
constant->line
|
||||||
|
);
|
||||||
} break;
|
} break;
|
||||||
default: {
|
default: {
|
||||||
fprintf(stderr, "cannot push value '%s'\n",
|
fprintf(stderr, "cannot push value '%s'\n",
|
||||||
|
|
|
@ -6,6 +6,12 @@ if ( (tok=lexer_try_scan_text(self, TXT, TOK)) )\
|
||||||
return tok;\
|
return tok;\
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SK_SCAN_KEYWORD(TXT, TOK, VAL) \
|
||||||
|
if ( (tok=lexer_try_scan_keyword(self, TXT, TOK, VAL)) )\
|
||||||
|
{\
|
||||||
|
return tok;\
|
||||||
|
}
|
||||||
|
|
||||||
void lexer_init(struct lexer* self, char const* source)
|
void lexer_init(struct lexer* self, char const* source)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
@ -114,6 +120,8 @@ struct token* lexer_try_new_next(struct lexer* self)
|
||||||
SK_SCAN_TEXT("%", TOKEN_MOD);
|
SK_SCAN_TEXT("%", TOKEN_MOD);
|
||||||
SK_SCAN_TEXT("(", TOKEN_OPAR);
|
SK_SCAN_TEXT("(", TOKEN_OPAR);
|
||||||
SK_SCAN_TEXT(")", TOKEN_CPAR);
|
SK_SCAN_TEXT(")", TOKEN_CPAR);
|
||||||
|
SK_SCAN_KEYWORD("assert", TOKEN_ASSERT, NULL);
|
||||||
|
SK_SCAN_KEYWORD("eq", TOKEN_ASSERT_EQ, NULL);
|
||||||
|
|
||||||
if (self->context.cursor < self->len)
|
if (self->context.cursor < self->len)
|
||||||
{
|
{
|
||||||
|
@ -199,3 +207,42 @@ struct token* lexer_try_scan_text(struct lexer* self,
|
||||||
self->context.cursor += txt_sz;
|
self->context.cursor += txt_sz;
|
||||||
return tok;
|
return tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct token* lexer_try_scan_keyword(struct lexer* self,
|
||||||
|
char const* keyword,
|
||||||
|
TokenKind kind,
|
||||||
|
char const* value)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
assert(keyword);
|
||||||
|
|
||||||
|
size_t txt_sz = strlen(keyword);
|
||||||
|
|
||||||
|
if (self->context.cursor + txt_sz > self->len)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<txt_sz; i++)
|
||||||
|
{
|
||||||
|
if (self->source[self->context.cursor + i]
|
||||||
|
!= keyword[i])
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(self->context.cursor > 0
|
||||||
|
&& !lexer_is_sep(self, self->context.cursor - 1))
|
||||||
|
|| (self->context.cursor + txt_sz <= self->len
|
||||||
|
&& !lexer_is_sep(self, self->context.cursor + txt_sz)))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct token* tok = malloc(sizeof(struct token));
|
||||||
|
token_init(tok, kind, value ? value : "", self->context.line);
|
||||||
|
self->context.cursor += txt_sz;
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
|
@ -35,14 +35,14 @@ void module_load_source(struct module* self,
|
||||||
fclose(file);
|
fclose(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
void module_compile(struct module* self)
|
int module_compile(struct module* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
struct parser parser;
|
struct parser parser;
|
||||||
|
|
||||||
parser_init(&parser, self->source.value);
|
parser_init(&parser, self->source.value);
|
||||||
struct node* root = parser_try_parse(&parser);
|
struct node* root = parser_try_parse(&parser);
|
||||||
|
|
||||||
if (!errors_ok())
|
if (!errors_ok())
|
||||||
{
|
{
|
||||||
errors_dump();
|
errors_dump();
|
||||||
|
@ -62,10 +62,16 @@ void module_compile(struct module* self)
|
||||||
|
|
||||||
exec_execute(&exec, &state, &self->prog);
|
exec_execute(&exec, &state, &self->prog);
|
||||||
|
|
||||||
|
if (!errors_ok())
|
||||||
|
{
|
||||||
|
errors_dump();
|
||||||
|
goto free_state;
|
||||||
|
}
|
||||||
|
|
||||||
if (state_has_top(&state))
|
if (state_has_top(&state))
|
||||||
{
|
{
|
||||||
struct value* value = state_try_get_value(
|
struct value* value = state_try_get_value(
|
||||||
&state,
|
&state,
|
||||||
state_top(&state)
|
state_top(&state)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -79,6 +85,7 @@ void module_compile(struct module* self)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free
|
// Free
|
||||||
|
free_state:
|
||||||
state_free(&state);
|
state_free(&state);
|
||||||
exec_free(&exec);
|
exec_free(&exec);
|
||||||
compiler_free(&compiler);
|
compiler_free(&compiler);
|
||||||
|
@ -86,4 +93,7 @@ free_node:
|
||||||
node_free(root);
|
node_free(root);
|
||||||
free(root);
|
free(root);
|
||||||
parser_free(&parser);
|
parser_free(&parser);
|
||||||
|
|
||||||
|
|
||||||
|
return errors_ok() ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,9 +68,51 @@ struct node* parser_try_root(struct parser* self)
|
||||||
|
|
||||||
struct node* parser_try_expr(struct parser* self)
|
struct node* parser_try_expr(struct parser* self)
|
||||||
{
|
{
|
||||||
|
if (lexer_next_is(&self->lexer, TOKEN_ASSERT))
|
||||||
|
{
|
||||||
|
return SK_TRY(parser_try_assert);
|
||||||
|
}
|
||||||
|
|
||||||
return SK_TRY(parser_try_term);
|
return SK_TRY(parser_try_term);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct node* parser_try_assert(struct parser* self)
|
||||||
|
{
|
||||||
|
assert(self);
|
||||||
|
|
||||||
|
if (!lexer_next_is(&self->lexer, TOKEN_ASSERT))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct token* tok = lexer_try_new_next(&self->lexer);
|
||||||
|
|
||||||
|
struct node* lhs = SK_TRY(parser_try_expr);
|
||||||
|
if (!lhs) { return NULL; }
|
||||||
|
|
||||||
|
if (!lexer_next_is(&self->lexer, TOKEN_ASSERT_EQ))
|
||||||
|
{
|
||||||
|
token_free(tok);
|
||||||
|
free(tok);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer_consume_next(&self->lexer);
|
||||||
|
struct node* rhs = SK_TRY(parser_try_expr);
|
||||||
|
if (!rhs)
|
||||||
|
{
|
||||||
|
token_free(tok);
|
||||||
|
free(tok);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct node* node = malloc(sizeof(struct node));
|
||||||
|
node_init(node, NODE_ASSERT_EQ, tok);
|
||||||
|
node_push_new_child(node, lhs);
|
||||||
|
node_push_new_child(node, rhs);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
struct node* parser_try_term(struct parser* self)
|
struct node* parser_try_term(struct parser* self)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
|
|
@ -107,14 +107,14 @@ SK state_pop(struct state* self)
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
SK state_push_int(struct state* self, int integer)
|
SK state_push_int(struct state* self, int integer, int line)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
|
||||||
struct value* value = malloc(sizeof(struct value));
|
struct value* value = malloc(sizeof(struct value));
|
||||||
union val val;
|
union val val;
|
||||||
val.integer = integer;
|
val.integer = integer;
|
||||||
value_init(value, TYPE_INT, val);
|
value_init(value, TYPE_INT, val, line);
|
||||||
|
|
||||||
struct frame* frame = state_frame(self);
|
struct frame* frame = state_frame(self);
|
||||||
struct local* local = malloc(sizeof(struct local));
|
struct local* local = malloc(sizeof(struct local));
|
||||||
|
@ -137,7 +137,8 @@ SK state_add(struct state* self)
|
||||||
|
|
||||||
return state_push_int(
|
return state_push_int(
|
||||||
self,
|
self,
|
||||||
left->val.integer + right->val.integer
|
left->val.integer + right->val.integer,
|
||||||
|
left->line
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +152,8 @@ SK state_sub(struct state* self)
|
||||||
|
|
||||||
return state_push_int(
|
return state_push_int(
|
||||||
self,
|
self,
|
||||||
left->val.integer - right->val.integer
|
left->val.integer - right->val.integer,
|
||||||
|
left->line
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +165,8 @@ SK state_usub(struct state* self)
|
||||||
|
|
||||||
return state_push_int(
|
return state_push_int(
|
||||||
self,
|
self,
|
||||||
-left->val.integer
|
-left->val.integer,
|
||||||
|
left->line
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +180,8 @@ SK state_mul(struct state* self)
|
||||||
|
|
||||||
return state_push_int(
|
return state_push_int(
|
||||||
self,
|
self,
|
||||||
left->val.integer * right->val.integer
|
left->val.integer * right->val.integer,
|
||||||
|
left->line
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +195,8 @@ SK state_div(struct state* self)
|
||||||
|
|
||||||
return state_push_int(
|
return state_push_int(
|
||||||
self,
|
self,
|
||||||
left->val.integer / right->val.integer
|
left->val.integer / right->val.integer,
|
||||||
|
left->line
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +210,8 @@ SK state_mod(struct state* self)
|
||||||
|
|
||||||
return state_push_int(
|
return state_push_int(
|
||||||
self,
|
self,
|
||||||
fmod(left->val.integer, right->val.integer)
|
fmod(left->val.integer, right->val.integer),
|
||||||
|
left->line
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,6 +225,7 @@ SK state_pow(struct state* self)
|
||||||
|
|
||||||
return state_push_int(
|
return state_push_int(
|
||||||
self,
|
self,
|
||||||
powf(left->val.integer, right->val.integer)
|
powf(left->val.integer, right->val.integer),
|
||||||
|
left->line
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,13 @@ SK_ENUM_C(TypeKind, TYPE_KIND);
|
||||||
|
|
||||||
void value_init(struct value* self,
|
void value_init(struct value* self,
|
||||||
TypeKind type,
|
TypeKind type,
|
||||||
union val val)
|
union val val,
|
||||||
|
int line)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
self->type = type;
|
self->type = type;
|
||||||
self->val = val;
|
self->val = val;
|
||||||
|
self->line = line;
|
||||||
}
|
}
|
||||||
|
|
||||||
void value_free(struct value* self)
|
void value_free(struct value* self)
|
||||||
|
@ -16,6 +18,24 @@ void value_free(struct value* self)
|
||||||
assert(self);
|
assert(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool value_equals(struct value* self, struct value* rhs)
|
||||||
|
{
|
||||||
|
struct str left;
|
||||||
|
str_init(&left);
|
||||||
|
value_str(self, &left);
|
||||||
|
|
||||||
|
struct str right;
|
||||||
|
str_init(&right);
|
||||||
|
value_str(rhs, &right);
|
||||||
|
|
||||||
|
bool result = strcmp(left.value, right.value) == 0;
|
||||||
|
|
||||||
|
str_free(&left);
|
||||||
|
str_free(&right);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void value_str(struct value* self, struct str* dest)
|
void value_str(struct value* self, struct str* dest)
|
||||||
{
|
{
|
||||||
assert(self);
|
assert(self);
|
||||||
|
|
|
@ -38,22 +38,33 @@ static void test_lexer(char const* source, int count, ...)
|
||||||
|
|
||||||
static void test_lexer_int()
|
static void test_lexer_int()
|
||||||
{
|
{
|
||||||
test_lexer(" 4 -23 720 ", 3,
|
test_lexer(" 4 -23 720 ", 3,
|
||||||
"INT[4]",
|
"INT[4]",
|
||||||
"INT[-23]",
|
"INT[-23]",
|
||||||
"INT[720]"
|
"INT[720]"
|
||||||
);
|
);
|
||||||
|
|
||||||
test_lexer("+-*/^%()", 8,
|
test_lexer("+-*/^%()", 8,
|
||||||
"ADD", "SUB", "MUL", "DIV",
|
"ADD", "SUB", "MUL", "DIV",
|
||||||
"POW", "MOD", "OPAR", "CPAR"
|
"POW", "MOD", "OPAR", "CPAR"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_lexer_assert()
|
||||||
|
{
|
||||||
|
test_lexer("assert 2 eq 1", 4,
|
||||||
|
"ASSERT",
|
||||||
|
"INT[2]",
|
||||||
|
"ASSERT_EQ",
|
||||||
|
"INT[1]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void register_lexer()
|
void register_lexer()
|
||||||
{
|
{
|
||||||
CU_pSuite suite = CU_add_suite("Lexer", 0, 0);
|
CU_pSuite suite = CU_add_suite("Lexer", 0, 0);
|
||||||
CU_add_test(suite, "Integers", test_lexer_int);
|
CU_add_test(suite, "Integers", test_lexer_int);
|
||||||
|
CU_add_test(suite, "Assertions", test_lexer_assert);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,7 +15,11 @@ static void test_parser(char const* oracle, char const* source)
|
||||||
struct str str;
|
struct str str;
|
||||||
str_init(&str);
|
str_init(&str);
|
||||||
node_str(node, &str);
|
node_str(node, &str);
|
||||||
CU_ASSERT_STRING_EQUAL(oracle, str.value);
|
if (strcmp(oracle, str.value) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "\n%s <> %s\n", oracle, str.value);
|
||||||
|
}
|
||||||
|
CU_ASSERT_STRING_EQUAL_FATAL(oracle, str.value);
|
||||||
str_free(&str);
|
str_free(&str);
|
||||||
|
|
||||||
node_free(node);
|
node_free(node);
|
||||||
|
@ -38,10 +42,17 @@ static void test_parser_int()
|
||||||
test_parser("ROOT(USUB(INT[32]))", " -(32) ");
|
test_parser("ROOT(USUB(INT[32]))", " -(32) ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_parser_assert()
|
||||||
|
{
|
||||||
|
test_parser("ROOT(ASSERT_EQ(INT[32],INT[12]))",
|
||||||
|
" assert 32 eq 12 ");
|
||||||
|
}
|
||||||
|
|
||||||
void register_parser()
|
void register_parser()
|
||||||
{
|
{
|
||||||
CU_pSuite suite = CU_add_suite("Parser", 0, 0);
|
CU_pSuite suite = CU_add_suite("Parser", 0, 0);
|
||||||
CU_add_test(suite, "Integers", test_parser_int);
|
CU_add_test(suite, "Integers", test_parser_int);
|
||||||
|
CU_add_test(suite, "Assertions", test_parser_assert);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue