feat: implement loops
This commit is contained in:
+101
-1
@@ -200,6 +200,96 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
{
|
||||
switch (node->type)
|
||||
{
|
||||
// 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)`)
|
||||
case FZL_NODETYPE_LOOP:
|
||||
{
|
||||
fzl_walk_ctx_t *newctx = nest_ctx(walker, FZL_WALK_CTX_TYPE_LOOP);
|
||||
if (!newctx)
|
||||
{
|
||||
snprintf(wlast_err, sizeof(wlast_err), "failed to nest context");
|
||||
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||
}
|
||||
|
||||
newctx->initial_env_lvl = walker->sym_top - 1;
|
||||
newctx->breaks_count = 0;
|
||||
newctx->breaks_remainder = 2;
|
||||
newctx->breaks = malloc(newctx->breaks_remainder * sizeof(size_t));
|
||||
|
||||
push_op(walker, FZL_OPCODE_MK_ENVTABLE);
|
||||
walker->syms[walker->sym_top++] = fzl_mksymt();
|
||||
|
||||
if (node->loop.init)
|
||||
{
|
||||
fzl_walk_res_t res = walk(walker, node->loop.init);
|
||||
if (res.error)
|
||||
{
|
||||
free(newctx->breaks);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
size_t start_ip = walker->op_count;
|
||||
if (node->loop.cond)
|
||||
{
|
||||
fzl_walk_res_t res = walk(walker, node->loop.cond);
|
||||
if (res.error)
|
||||
{
|
||||
free(newctx->breaks);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
push_op(walker, FZL_OPCODE_PUSHNUM);
|
||||
push_word(walker, 1);
|
||||
}
|
||||
|
||||
push_op(walker, FZL_OPCODE_JZ);
|
||||
newctx->breaks[newctx->breaks_count++] = walker->op_count;
|
||||
push_word(walker, 0);
|
||||
|
||||
fzl_walk_res_t loop_body_res = walk(walker, node->loop.body);
|
||||
if (loop_body_res.error)
|
||||
{
|
||||
free(newctx->breaks);
|
||||
return loop_body_res;
|
||||
}
|
||||
|
||||
if (node->loop.step)
|
||||
{
|
||||
fzl_walk_res_t res = walk(walker, node->loop.step);
|
||||
if (res.error)
|
||||
{
|
||||
free(newctx->breaks);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
push_op(walker, FZL_OPCODE_JMP);
|
||||
push_word(walker, start_ip);
|
||||
push_op(walker, FZL_OPCODE_RM_ENVTABLE);
|
||||
|
||||
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))
|
||||
{
|
||||
snprintf(wlast_err, sizeof(wlast_err),
|
||||
"failed to finish loop context");
|
||||
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||
}
|
||||
|
||||
current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false};
|
||||
break;
|
||||
}
|
||||
|
||||
case FZL_NODETYPE_CLASS:
|
||||
{
|
||||
fzl_walk_ctx_t *newctx = nest_ctx(walker, FZL_WALK_CTX_TYPE_CLASS);
|
||||
@@ -461,7 +551,16 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
|
||||
case FZL_NODETYPE_RETURN:
|
||||
{
|
||||
if (!walker->ctx || walker->ctx->type != FZL_WALK_CTX_TYPE_FN)
|
||||
fzl_walk_ctx_t *ctx = walker->ctx;
|
||||
while (ctx)
|
||||
{
|
||||
if (ctx && ctx->type == FZL_WALK_CTX_TYPE_FN)
|
||||
break;
|
||||
|
||||
ctx = ctx->parent;
|
||||
}
|
||||
|
||||
if (!ctx || ctx->type != FZL_WALK_CTX_TYPE_FN)
|
||||
{
|
||||
snprintf(
|
||||
wlast_err, sizeof(wlast_err),
|
||||
@@ -731,6 +830,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
}
|
||||
fzl_freesymt(walker->syms[--walker->sym_top]);
|
||||
push_op(walker, FZL_OPCODE_RM_ENVTABLE);
|
||||
current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false};
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_CALL:
|
||||
|
||||
Reference in New Issue
Block a user