Compare commits
4
Commits
5dde75523d
...
7cfb8efef4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cfb8efef4 | ||
|
|
b5ae959d14 | ||
|
|
0e319308a7 | ||
|
|
a20743a835 |
@@ -58,5 +58,9 @@ foreach (TEST_FILE IN LISTS TEST_PROGS)
|
||||
set_tests_properties("progs_compile_${TEST_NAME}" PROPERTIES WILL_FAIL TRUE)
|
||||
set_tests_properties("progs_runtime_${TEST_NAME}" PROPERTIES WILL_FAIL TRUE)
|
||||
endif()
|
||||
|
||||
if (TEST_NAME MATCHES "neg1_")
|
||||
set_tests_properties("progs_runtime_${TEST_NAME}" PROPERTIES WILL_FAIL TRUE)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ typedef enum
|
||||
FZL_NODETYPE_FIELD,
|
||||
FZL_NODETYPE_CONSTRUCT,
|
||||
FZL_NODETYPE_LOOP,
|
||||
FZL_NODETYPE_BREAK,
|
||||
|
||||
FZL_NODETYPE_ADD,
|
||||
FZL_NODETYPE_SUB,
|
||||
|
||||
@@ -16,6 +16,7 @@ typedef enum
|
||||
|
||||
FZL_TOKTYPE_FOR, // for
|
||||
FZL_TOKTYPE_WHILE, // while
|
||||
FZL_TOKTYPE_BREAK, // break
|
||||
|
||||
FZL_TOKTYPE_IF, // if
|
||||
FZL_TOKTYPE_ELSE, // else
|
||||
|
||||
@@ -6,42 +6,209 @@
|
||||
#include <compiler/utils/fzl_symtable.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
ssize_t check_top;
|
||||
fzl_nodetype_t check_tree[256];
|
||||
|
||||
void ghost_walk_changed(fzl_node_t *node, bool *changed, fzl_sym_t *sym)
|
||||
{
|
||||
if (!node || !changed || *changed)
|
||||
return;
|
||||
|
||||
check_tree[check_top++] = node->type;
|
||||
|
||||
switch (node->type)
|
||||
{
|
||||
case FZL_NODETYPE_BREAK:
|
||||
case FZL_NODETYPE_DECLARATION:
|
||||
case FZL_NODETYPE_CONSTRUCT:
|
||||
case FZL_NODETYPE_CLASS:
|
||||
case FZL_NODETYPE_FUNCTION:
|
||||
case FZL_NODETYPE_LITERAL:
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_UNARY_PLUS:
|
||||
case FZL_NODETYPE_UNARY_MINUS:
|
||||
case FZL_NODETYPE_NOT:
|
||||
ghost_walk_changed(node->unary_op.operand, changed, sym);
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_ADD:
|
||||
case FZL_NODETYPE_SUB:
|
||||
case FZL_NODETYPE_MUL:
|
||||
case FZL_NODETYPE_DIV:
|
||||
case FZL_NODETYPE_POW:
|
||||
case FZL_NODETYPE_MOD:
|
||||
case FZL_NODETYPE_OR:
|
||||
case FZL_NODETYPE_AND:
|
||||
case FZL_NODETYPE_EQUAL:
|
||||
case FZL_NODETYPE_NEQUAL:
|
||||
case FZL_NODETYPE_LESS:
|
||||
case FZL_NODETYPE_LESS_EQUAL:
|
||||
case FZL_NODETYPE_GREATER:
|
||||
case FZL_NODETYPE_GREATER_EQUAL:
|
||||
case FZL_NODETYPE_ASSIGN:
|
||||
ghost_walk_changed(node->binary_op.left, changed, sym);
|
||||
ghost_walk_changed(node->binary_op.right, changed, sym);
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_FIELD:
|
||||
ghost_walk_changed(node->memb.parent, changed, sym);
|
||||
ghost_walk_changed(node->memb.field, changed, sym);
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_RETURN:
|
||||
ghost_walk_changed(node->ret.value, changed, sym);
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_CONDITIONAL:
|
||||
ghost_walk_changed(node->cond.condition, changed, sym);
|
||||
ghost_walk_changed(node->cond.true_block, changed, sym);
|
||||
ghost_walk_changed(node->cond.else_block, changed, sym);
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_LOOP:
|
||||
ghost_walk_changed(node->loop.cond, changed, sym);
|
||||
ghost_walk_changed(node->loop.step, changed, sym);
|
||||
ghost_walk_changed(node->loop.body, changed, sym);
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_BLOCK:
|
||||
for (size_t i = 0; i < node->list.entry_count; i++)
|
||||
ghost_walk_changed(node->list.entries[i], changed, sym);
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_CALL:
|
||||
ghost_walk_changed(node->call.callee, changed, sym);
|
||||
for (size_t i = 0; i < node->call.args_count; i++)
|
||||
ghost_walk_changed(node->call.args[i], changed, sym);
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_IDENTIFIER:
|
||||
if (strcmp(node->token->value, sym->name) == 0)
|
||||
{
|
||||
*changed = true;
|
||||
}
|
||||
|
||||
if (*changed)
|
||||
{
|
||||
*changed = false;
|
||||
for (ssize_t i = check_top - 1; i >= 0; i--)
|
||||
{
|
||||
fzl_nodetype_t cur_t = check_tree[i];
|
||||
if (cur_t == FZL_NODETYPE_ADD || cur_t == FZL_NODETYPE_SUB ||
|
||||
cur_t == FZL_NODETYPE_MUL || cur_t == FZL_NODETYPE_DIV ||
|
||||
cur_t == FZL_NODETYPE_POW || cur_t == FZL_NODETYPE_MOD ||
|
||||
cur_t == FZL_NODETYPE_UNARY_MINUS)
|
||||
{
|
||||
*changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
check_tree[--check_top] = 0;
|
||||
}
|
||||
|
||||
// Whether value changed in loop body
|
||||
bool value_is_changed(fzl_node_t *node, fzl_sym_t *sym)
|
||||
{
|
||||
if (!node || !sym)
|
||||
return false;
|
||||
|
||||
bool changed = false;
|
||||
check_top = 0;
|
||||
memset(check_tree, 0, sizeof(check_tree));
|
||||
ghost_walk_changed(node, &changed, sym);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
// Try to find symbol in loop condition node
|
||||
bool retrieve_cond_sym(fzl_walker_t *walker, fzl_node_t *node, fzl_sym_t *sym)
|
||||
{
|
||||
if (!walker || !node)
|
||||
return false;
|
||||
|
||||
switch (node->type)
|
||||
{
|
||||
case FZL_NODETYPE_BREAK:
|
||||
case FZL_NODETYPE_BLOCK:
|
||||
case FZL_NODETYPE_CALL:
|
||||
case FZL_NODETYPE_LITERAL:
|
||||
case FZL_NODETYPE_CONDITIONAL:
|
||||
case FZL_NODETYPE_FUNCTION:
|
||||
case FZL_NODETYPE_RETURN:
|
||||
case FZL_NODETYPE_CLASS:
|
||||
case FZL_NODETYPE_FIELD:
|
||||
case FZL_NODETYPE_CONSTRUCT:
|
||||
case FZL_NODETYPE_LOOP:
|
||||
case FZL_NODETYPE_ASSIGN:
|
||||
case FZL_NODETYPE_DECLARATION:
|
||||
return false;
|
||||
|
||||
case FZL_NODETYPE_UNARY_MINUS:
|
||||
case FZL_NODETYPE_UNARY_PLUS:
|
||||
case FZL_NODETYPE_NOT:
|
||||
return retrieve_cond_sym(walker, node->unary_op.operand, sym);
|
||||
|
||||
case FZL_NODETYPE_ADD:
|
||||
case FZL_NODETYPE_SUB:
|
||||
case FZL_NODETYPE_MUL:
|
||||
case FZL_NODETYPE_DIV:
|
||||
case FZL_NODETYPE_POW:
|
||||
case FZL_NODETYPE_MOD:
|
||||
case FZL_NODETYPE_AND:
|
||||
case FZL_NODETYPE_OR:
|
||||
case FZL_NODETYPE_LESS:
|
||||
case FZL_NODETYPE_LESS_EQUAL:
|
||||
case FZL_NODETYPE_GREATER:
|
||||
case FZL_NODETYPE_GREATER_EQUAL:
|
||||
case FZL_NODETYPE_EQUAL:
|
||||
case FZL_NODETYPE_NEQUAL:
|
||||
if (retrieve_cond_sym(walker, node->binary_op.left, sym))
|
||||
return true;
|
||||
if (retrieve_cond_sym(walker, node->binary_op.right, sym))
|
||||
return true;
|
||||
return false;
|
||||
|
||||
case FZL_NODETYPE_IDENTIFIER:
|
||||
{
|
||||
bool exists;
|
||||
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
|
||||
{
|
||||
if ((exists = fzl_symt_get(walker->syms[i], (char *)node->token->value,
|
||||
sym)))
|
||||
break;
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for infinite loop
|
||||
bool is_infinite_loop(fzl_walker_t *walker, size_t start_ip, size_t end_ip)
|
||||
bool is_infinite_loop(fzl_walker_t *walker, fzl_node_t *node, size_t start_ip,
|
||||
size_t end_ip)
|
||||
{
|
||||
if (!walker)
|
||||
return false;
|
||||
|
||||
end_ip = (end_ip >= walker->op_count) ? (walker->op_count - 1) : end_ip;
|
||||
|
||||
// Can we determine an init block?
|
||||
size_t maybe_init_start = start_ip + 1;
|
||||
size_t maybe_init_end = start_ip + 6;
|
||||
fzl_const_t *declared = NULL;
|
||||
for (size_t i = maybe_init_start; i <= maybe_init_end; i++)
|
||||
{
|
||||
if (walker->ops[i] != FZL_OPCODE_STORE)
|
||||
continue;
|
||||
|
||||
declared = &walker->consts->array[walker->ops[i - 1]];
|
||||
break;
|
||||
}
|
||||
|
||||
if (declared)
|
||||
{
|
||||
fzl_sym_t sym;
|
||||
bool found = false;
|
||||
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
|
||||
if ((found = fzl_symt_get(walker->syms[i],
|
||||
(char *)declared->as.string.chars, &sym)))
|
||||
break;
|
||||
|
||||
if (!found)
|
||||
return false; // suspicious
|
||||
|
||||
bool sym_exists = retrieve_cond_sym(walker, node->loop.cond, &sym);
|
||||
if (sym_exists)
|
||||
{
|
||||
if (sym.type >= FZL_TYPE_INT8)
|
||||
{
|
||||
bool value_changed = value_is_changed(node, &sym);
|
||||
if (value_changed)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1198,6 +1198,13 @@ fzl_node_t *parse_loop(fzl_parser_t *parser)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// `break` keyword parsing
|
||||
fzl_node_t *parse_break(fzl_parser_t *parser)
|
||||
{
|
||||
fzl_token_t *tok = consume(parser);
|
||||
return fzl_mknode(FZL_NODETYPE_BREAK, tok);
|
||||
}
|
||||
|
||||
// Statement parsing
|
||||
fzl_node_t *parse_statement(fzl_parser_t *parser)
|
||||
{
|
||||
@@ -1218,6 +1225,8 @@ fzl_node_t *parse_statement(fzl_parser_t *parser)
|
||||
return parse_block(parser);
|
||||
else if (is(parser, FZL_TOKTYPE_FOR) || is(parser, FZL_TOKTYPE_WHILE))
|
||||
return parse_loop(parser);
|
||||
else if (is(parser, FZL_TOKTYPE_BREAK))
|
||||
res = parse_break(parser);
|
||||
|
||||
if (!res)
|
||||
res = parse_expression(parser);
|
||||
@@ -1303,6 +1312,7 @@ void fzl_freeast(fzl_node_t *root)
|
||||
{
|
||||
switch (root->type)
|
||||
{
|
||||
case FZL_NODETYPE_BREAK:
|
||||
case FZL_NODETYPE_IDENTIFIER:
|
||||
case FZL_NODETYPE_LITERAL:
|
||||
break;
|
||||
|
||||
@@ -78,6 +78,9 @@ fzl_token_t *parse_name(fzl_reader_t *reader)
|
||||
else if (strcmp(value, "while") == 0)
|
||||
tok =
|
||||
fzl_mktoken(FZL_TOKTYPE_WHILE, l.line, l.col, value, size, l.filename);
|
||||
else if (strcmp(value, "break") == 0)
|
||||
tok =
|
||||
fzl_mktoken(FZL_TOKTYPE_BREAK, l.line, l.col, value, size, l.filename);
|
||||
else if (strcmp(value, "new") == 0)
|
||||
tok = fzl_mktoken(FZL_TOKTYPE_NEW, l.line, l.col, value, size, l.filename);
|
||||
else if (strcmp(value, "fn") == 0)
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern bool is_infinite_loop(fzl_walker_t *walker, size_t ls, size_t le);
|
||||
extern bool is_infinite_loop(fzl_walker_t *walker, fzl_node_t *node, size_t ls,
|
||||
size_t le);
|
||||
|
||||
char *null_name = "<null>";
|
||||
char wlast_err[256];
|
||||
@@ -202,9 +203,45 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
{
|
||||
switch (node->type)
|
||||
{
|
||||
case FZL_NODETYPE_BREAK:
|
||||
{
|
||||
fzl_walk_ctx_t *ctx = walker->ctx;
|
||||
while (ctx)
|
||||
{
|
||||
if (ctx->type == FZL_WALK_CTX_TYPE_LOOP)
|
||||
break;
|
||||
|
||||
ctx = ctx->parent;
|
||||
}
|
||||
|
||||
if (!ctx || ctx->type != FZL_WALK_CTX_TYPE_LOOP)
|
||||
{
|
||||
snprintf(wlast_err, sizeof(wlast_err),
|
||||
"'%s': usage of 'break' outsude the loop at %ld:%ld",
|
||||
node->token->filename, node->token->ln, node->token->cn);
|
||||
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||
}
|
||||
|
||||
for (size_t i = walker->sym_top - 1; i > ctx->initial_env_lvl + 1; i--)
|
||||
push_op(walker, FZL_OPCODE_RM_ENVTABLE);
|
||||
|
||||
if (ctx->breaks_remainder <= ctx->breaks_count)
|
||||
{
|
||||
ctx->breaks_remainder *= 2;
|
||||
ctx->breaks =
|
||||
realloc(ctx->breaks, ctx->breaks_remainder * sizeof(size_t));
|
||||
}
|
||||
|
||||
push_op(walker, FZL_OPCODE_JMP);
|
||||
ctx->breaks[ctx->breaks_count++] = walker->op_count;
|
||||
push_word(walker, 0);
|
||||
|
||||
current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false};
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: add new levels for infinite loop check:
|
||||
// 1. Dead condition (f.e. `while (i < 5)` w/o `i` increment)
|
||||
// 2. Step direction (f.e. `for (; i > 5; i = i - 1)`)
|
||||
// 1. Step direction (f.e. `for (; i > 5; i = i - 1)`)
|
||||
case FZL_NODETYPE_LOOP:
|
||||
{
|
||||
fzl_walk_ctx_t *newctx = nest_ctx(walker, FZL_WALK_CTX_TYPE_LOOP);
|
||||
@@ -221,6 +258,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
|
||||
push_op(walker, FZL_OPCODE_MK_ENVTABLE);
|
||||
walker->syms[walker->sym_top++] = fzl_mksymt();
|
||||
size_t loop_begin_ip = walker->op_count;
|
||||
|
||||
if (node->loop.init)
|
||||
{
|
||||
@@ -271,12 +309,18 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
|
||||
push_op(walker, FZL_OPCODE_JMP);
|
||||
push_word(walker, start_ip);
|
||||
|
||||
// backpatch `break`s
|
||||
for (size_t i = 0; i < newctx->breaks_count; i++)
|
||||
walker->ops[newctx->breaks[i]] = walker->op_count;
|
||||
|
||||
push_op(walker, FZL_OPCODE_RM_ENVTABLE);
|
||||
|
||||
bool is_infinite =
|
||||
is_infinite_loop(walker, start_ip, walker->op_count - 2);
|
||||
is_infinite_loop(walker, node, loop_begin_ip, walker->op_count - 2);
|
||||
if (is_infinite)
|
||||
{
|
||||
free(newctx->breaks);
|
||||
snprintf(wlast_err, sizeof(wlast_err),
|
||||
"'%s': infinite loop detected at %ld:%ld",
|
||||
node->token->filename, node->token->ln, node->token->cn);
|
||||
@@ -286,10 +330,6 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
for (size_t i = walker->sym_top - 1; i > newctx->initial_env_lvl; i--)
|
||||
fzl_freesymt(walker->syms[--walker->sym_top]);
|
||||
|
||||
// backpatch `break`s
|
||||
for (size_t i = 0; i < newctx->breaks_count; i++)
|
||||
walker->ops[newctx->breaks[i]] = walker->op_count - 1;
|
||||
|
||||
free(newctx->breaks);
|
||||
if (!fin_ctx(walker))
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <common/fzl_value.h>
|
||||
#include <compiler/utils/fzl_const_pool.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -13,6 +14,11 @@
|
||||
#include <vm/fzl_vm_object.h>
|
||||
|
||||
char last_err[256];
|
||||
size_t last_jmp_ip = 0;
|
||||
|
||||
// NOTE: add `--max-iter` option for limit
|
||||
size_t max_iter_limit = 100000;
|
||||
size_t total_jmp_on_same = 0;
|
||||
|
||||
static char *expect_hdr = "FZL\0\1";
|
||||
|
||||
@@ -667,7 +673,22 @@ int fzl_vm_run(fzl_vm_t *vm)
|
||||
return -1;
|
||||
}
|
||||
uint64_t jmp_ip = vm->ops[++vm->ip];
|
||||
if (jmp_ip == last_jmp_ip)
|
||||
total_jmp_on_same++;
|
||||
else
|
||||
total_jmp_on_same = 0;
|
||||
|
||||
if (total_jmp_on_same > max_iter_limit)
|
||||
{
|
||||
snprintf(last_err, sizeof(last_err),
|
||||
"runtime error - exceeded max loop jumps (infinite loop?)\n"
|
||||
"If you thinks there's mistake, change jumps limit with "
|
||||
"--max-iter=N");
|
||||
return -1;
|
||||
}
|
||||
|
||||
vm->ip = jmp_ip - 1;
|
||||
last_jmp_ip = jmp_ip;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
for (int i = 0; i < 5; i = i + 0)
|
||||
{}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
for (int i = 0; i < 5; i = i - 1)
|
||||
{}
|
||||
|
||||
Reference in New Issue
Block a user