feat: implement functions
This commit is contained in:
+142
-12
@@ -52,6 +52,8 @@ fzl_walker_t *fzl_mkwalker(fzl_node_t *root)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
walker->ctx = NULL;
|
||||
|
||||
return walker;
|
||||
}
|
||||
|
||||
@@ -152,6 +154,101 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
{
|
||||
switch (node->type)
|
||||
{
|
||||
case FZL_NODETYPE_FUNCTION:
|
||||
{
|
||||
push_op(walker, FZL_OPCODE_JMP);
|
||||
size_t skip_ip = walker->op_count;
|
||||
push_word(walker, (uint64_t)0);
|
||||
|
||||
fzl_type_t fntype = tok_to_type(node->fn.type);
|
||||
fzl_symtable_res_t putres =
|
||||
fzl_symt_put(walker->syms[walker->sym_top - 1],
|
||||
node->fn.name->value, fntype, walker->op_count);
|
||||
if (putres != FZL_SYMTABLE_RESULT_OK)
|
||||
{
|
||||
if (putres == FZL_SYMTABLE_RESULT_EXISTS)
|
||||
snprintf(wlast_err, sizeof(wlast_err),
|
||||
"cannot define already declared name '%s' at %ld:%ld",
|
||||
(char *)node->fn.name->value, node->token->ln,
|
||||
node->token->cn);
|
||||
else
|
||||
snprintf(wlast_err, sizeof(wlast_err), "failed to store name '%s'",
|
||||
(char *)node->fn.name->value);
|
||||
|
||||
return (fzl_walk_res_t){.error = true};
|
||||
}
|
||||
|
||||
// NOTE: add sym_top limit check
|
||||
size_t lvl = walker->sym_top;
|
||||
walker->syms[walker->sym_top++] = fzl_mksymt();
|
||||
walker->ctx = malloc(sizeof(fzl_walk_ctx_t));
|
||||
walker->ctx->fn = node;
|
||||
walker->ctx->initial_env_lvl = lvl;
|
||||
|
||||
push_op(walker, FZL_OPCODE_MK_ENVTABLE);
|
||||
|
||||
for (ssize_t i = node->fn.args_count - 1; i >= 0; i--)
|
||||
{
|
||||
fzl_walk_res_t res = walk(walker, node->fn.args[i]);
|
||||
if (res.error)
|
||||
return res;
|
||||
push_op(walker, FZL_OPCODE_WRITE);
|
||||
}
|
||||
|
||||
fzl_walk_res_t bodyres = walk(walker, node->fn.body);
|
||||
if (bodyres.error)
|
||||
return bodyres;
|
||||
|
||||
// NOTE: ghost walk (w/o opcodes write) for arg check should be there in
|
||||
// near future
|
||||
|
||||
push_op(walker, FZL_OPCODE_RM_ENVTABLE);
|
||||
walker->ops[skip_ip] = walker->op_count;
|
||||
|
||||
walker->ctx = NULL; // NOTE: think about nested function defs (not now)
|
||||
for (ssize_t i = walker->sym_top - 1; i >= lvl; i--)
|
||||
fzl_freesymt(walker->syms[--walker->sym_top]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case FZL_NODETYPE_RETURN:
|
||||
{
|
||||
if (!walker->ctx)
|
||||
{
|
||||
snprintf(wlast_err, sizeof(wlast_err),
|
||||
"return cannot be used outside of functions");
|
||||
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||
}
|
||||
|
||||
if (node->ret.value)
|
||||
{
|
||||
fzl_walk_res_t retres = walk(walker, node->ret.value);
|
||||
if (retres.error)
|
||||
return retres;
|
||||
|
||||
if (!is_compatible(retres.type,
|
||||
tok_to_type(walker->ctx->fn->fn.type)))
|
||||
{
|
||||
snprintf(wlast_err, sizeof(wlast_err),
|
||||
"incompatible types between return and function type at "
|
||||
"%ld:%ld",
|
||||
node->token->ln, node->token->cn);
|
||||
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||
}
|
||||
|
||||
current_res = (fzl_walk_res_t){.type = retres.type, .error = false};
|
||||
}
|
||||
else
|
||||
current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false};
|
||||
|
||||
for (size_t i = 0; i < (walker->sym_top - walker->ctx->initial_env_lvl);
|
||||
i++)
|
||||
push_op(walker, FZL_OPCODE_RM_ENVTABLE);
|
||||
push_op(walker, FZL_OPCODE_RET);
|
||||
break;
|
||||
}
|
||||
|
||||
case FZL_NODETYPE_NOT:
|
||||
{
|
||||
fzl_walk_res_t res = walk(walker, node->unary_op.operand);
|
||||
@@ -294,8 +391,12 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
.as.string.length = node->token->value_sz});
|
||||
|
||||
fzl_sym_t sym;
|
||||
bool exists = fzl_symt_get(walker->syms[walker->sym_top - 1],
|
||||
node->token->value, &sym);
|
||||
bool exists = false;
|
||||
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
|
||||
if ((exists =
|
||||
fzl_symt_get(walker->syms[i], node->token->value, &sym)))
|
||||
break;
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
snprintf(wlast_err, sizeof(wlast_err),
|
||||
@@ -314,6 +415,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
}
|
||||
|
||||
case FZL_NODETYPE_BLOCK:
|
||||
walker->syms[walker->sym_top++] = fzl_mksymt();
|
||||
push_op(walker, FZL_OPCODE_MK_ENVTABLE);
|
||||
if (node->list.entry_count > 0)
|
||||
for (size_t i = 0; i < node->list.entry_count; i++)
|
||||
@@ -322,6 +424,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
if (res.error)
|
||||
return res;
|
||||
}
|
||||
fzl_freesymt(walker->syms[--walker->sym_top]);
|
||||
push_op(walker, FZL_OPCODE_RM_ENVTABLE);
|
||||
break;
|
||||
|
||||
@@ -335,11 +438,10 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
// NOTE: you need to implement dynamic names call too
|
||||
push_op(walker, FZL_OPCODE_CALL_BUILTIN);
|
||||
|
||||
if (strcmp(node->token->value, "printline") == 0)
|
||||
{
|
||||
push_op(walker, FZL_OPCODE_CALL_BUILTIN);
|
||||
push_word(walker, FZL_BUILTIN_PRINTLINE);
|
||||
if (node->call.args_count < 1)
|
||||
{
|
||||
@@ -348,14 +450,37 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
node->token->ln, node->token->cn);
|
||||
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||
}
|
||||
current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false};
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(wlast_err, sizeof(wlast_err),
|
||||
"call to unknown function '%s'", (char *)node->token->value);
|
||||
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||
fzl_sym_t res;
|
||||
bool found = false;
|
||||
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
|
||||
if ((found =
|
||||
fzl_symt_get(walker->syms[i], node->token->value, &res)))
|
||||
break;
|
||||
|
||||
if (!found)
|
||||
{
|
||||
snprintf(wlast_err, sizeof(wlast_err),
|
||||
"call to unknown function '%s'",
|
||||
(char *)node->token->value);
|
||||
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||
}
|
||||
|
||||
if (!res.ip)
|
||||
{
|
||||
snprintf(wlast_err, sizeof(wlast_err),
|
||||
"cannot jump to address 0x0");
|
||||
return (fzl_walk_res_t){.error = true, .error_msg = wlast_err};
|
||||
}
|
||||
|
||||
push_op(walker, FZL_OPCODE_CALL);
|
||||
push_word(walker, res.ip);
|
||||
|
||||
current_res = (fzl_walk_res_t){.type = res.type, .error = false};
|
||||
}
|
||||
current_res = (fzl_walk_res_t){.type = FZL_TYPE_NULL, .error = false};
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_LITERAL:
|
||||
@@ -475,7 +600,7 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
{
|
||||
fzl_symtable_res_t res = fzl_symt_put(
|
||||
walker->syms[walker->sym_top - 1],
|
||||
node->var.identifier->token->value, tok_to_type(node->var.type));
|
||||
node->var.identifier->token->value, tok_to_type(node->var.type), 0);
|
||||
|
||||
fzl_token_t *id_tok = node->var.identifier->token;
|
||||
size_t const_n = add_const(
|
||||
@@ -509,9 +634,14 @@ fzl_walk_res_t walk(fzl_walker_t *walker, fzl_node_t *node)
|
||||
else if (node->binary_op.left->type == FZL_NODETYPE_IDENTIFIER)
|
||||
{
|
||||
fzl_sym_t sym;
|
||||
bool res = fzl_symt_get(walker->syms[walker->sym_top - 1],
|
||||
node->binary_op.left->token->value, &sym);
|
||||
if (!res) // NOTE: refactor this before implement blocks, fns, etc
|
||||
bool found = false;
|
||||
for (ssize_t i = walker->sym_top - 1; i >= 0; i--)
|
||||
if ((found =
|
||||
fzl_symt_get(walker->syms[i],
|
||||
node->binary_op.left->token->value, &sym)))
|
||||
break;
|
||||
|
||||
if (!found)
|
||||
{
|
||||
fzl_token_t *tok = node->binary_op.left->token->value;
|
||||
snprintf(wlast_err, sizeof(wlast_err),
|
||||
|
||||
Reference in New Issue
Block a user