feat: implement functions

This commit is contained in:
samsonium
2026-10-15 02:23:43 +03:00
parent cc3087296f
commit 2c206822ca
15 changed files with 391 additions and 36 deletions
+16
View File
@@ -96,6 +96,22 @@ void print_node(fzl_node_t *node, int level)
print_node(node->unary_op.operand, level + 1);
break;
case FZL_NODETYPE_FUNCTION:
printf("%s Function named '%s' with ret type '%s':\n", levelbuf,
(char *)node->fn.name->value, (char *)node->fn.type->value);
printf("-%s Args:\n", levelbuf);
for (size_t i = 0; i < node->fn.args_count; i++)
print_node(node->fn.args[i], level + 2);
printf("-%s Body:\n", levelbuf);
print_node(node->fn.body, level + 2);
break;
case FZL_NODETYPE_RETURN:
printf("%s Return:\n", levelbuf);
if (node->ret.value)
print_node(node->ret.value, level + 1);
break;
case FZL_NODETYPE_EQUAL:
case FZL_NODETYPE_NEQUAL:
case FZL_NODETYPE_LESS:
+101 -1
View File
@@ -514,6 +514,79 @@ fzl_node_t *parse_statement(fzl_parser_t *parser)
if (type->type == FZL_TOKTYPE_TYPE)
consume(parser);
if (type->type == FZL_TOKTYPE_TYPE &&
next(parser)->type == FZL_TOKTYPE_LPAREN)
{
fzl_token_t *tok = consume(parser);
if (tok->type != FZL_TOKTYPE_NAME)
{
snprintf(plast_err, sizeof(plast_err),
"expected name in function definition at %ld:%ld", tok->ln,
tok->cn);
return NULL;
}
fzl_token_t *lparen = consume(parser);
if (lparen->type != FZL_TOKTYPE_LPAREN)
{
snprintf(plast_err, sizeof(plast_err),
"expected '(' after name at %ld:%ld", lparen->ln, lparen->cn);
return NULL;
}
size_t remainder = 1;
size_t count = 0;
fzl_node_t **args = malloc(remainder * sizeof(fzl_node_t *));
while (peek(parser) && !is(parser, FZL_TOKTYPE_RPAREN))
{
fzl_node_t *arg = parse_statement(parser);
if (!arg)
return NULL;
if (remainder <= count)
{
remainder *= 2;
args = realloc(args, remainder * sizeof(fzl_node_t *));
}
args[count++] = arg;
if (is(parser, FZL_TOKTYPE_COMMA))
consume(parser);
}
fzl_token_t *rparen = consume(parser);
if (rparen->type != FZL_TOKTYPE_RPAREN)
{
snprintf(plast_err, sizeof(plast_err),
"expected ')' after argument list at %ld:%ld", rparen->ln,
rparen->cn);
return NULL;
}
if (!is(parser, FZL_TOKTYPE_LBRACE))
{
fzl_token_t *tok = peek(parser);
snprintf(plast_err, sizeof(plast_err), "expected '{' at %ld:%ld",
tok->ln, tok->cn);
return NULL;
}
fzl_node_t *body = parse_statement(parser);
if (!body)
return NULL;
fzl_node_t *node = fzl_mknode(FZL_NODETYPE_FUNCTION, tok);
node->fn.args = args;
node->fn.args_count = count;
node->fn.body = body;
node->fn.name = tok;
node->fn.type = type;
return node;
}
if (type->type == FZL_TOKTYPE_TYPE || next(parser)->type == FZL_TOKTYPE_EQ)
{
@@ -574,6 +647,25 @@ fzl_node_t *parse_statement(fzl_parser_t *parser)
return left;
}
if (is(parser, FZL_TOKTYPE_RETURN))
{
fzl_token_t *ret = consume(parser);
fzl_node_t *node = fzl_mknode(FZL_NODETYPE_RETURN, ret);
if (is(parser, FZL_TOKTYPE_DOT))
{
consume(parser);
return node;
}
fzl_node_t *retval = parse_expression(parser);
if (!retval)
return NULL;
node->ret.value = retval;
return node;
}
if (is(parser, FZL_TOKTYPE_IF))
{
fzl_token_t *if_tok = consume(parser);
@@ -637,7 +729,7 @@ fzl_node_t *parse_statement(fzl_parser_t *parser)
if (remainder <= count)
{
remainder *= 2;
stmts = realloc(stmts, remainder);
stmts = realloc(stmts, remainder * sizeof(fzl_node_t *));
}
stmts[count++] = node;
@@ -758,6 +850,14 @@ void fzl_freeast(fzl_node_t *root)
if (root->unary_op.operand)
fzl_freeast(root->unary_op.operand);
}
else if (root->type == FZL_NODETYPE_FUNCTION)
{
if (root->fn.args_count)
for (size_t i = 0; i < root->fn.args_count; i++)
fzl_freenode(root->fn.args[i]);
free(root->fn.args);
fzl_freenode(root->fn.body);
}
fzl_freenode(root);
root = root->next;
+3 -1
View File
@@ -74,7 +74,9 @@ fzl_token_t *parse_name(fzl_reader_t *reader)
size_t line = reader->line_n;
size_t pos = start - reader->line_start;
if (strcmp(value, "if") == 0)
if (strcmp(value, "return") == 0)
tok = fzl_mktoken(FZL_TOKTYPE_RETURN, line, pos, value, size);
else if (strcmp(value, "if") == 0)
tok = fzl_mktoken(FZL_TOKTYPE_IF, line, pos, value, size);
else if (strcmp(value, "else") == 0)
tok = fzl_mktoken(FZL_TOKTYPE_ELSE, line, pos, value, size);
+142 -12
View File
@@ -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),
+4 -2
View File
@@ -1,6 +1,7 @@
#include <compiler/utils/fzl_symtable.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -20,7 +21,7 @@ fzl_symtable_t *fzl_mksymt()
}
fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, char *name,
fzl_type_t type)
fzl_type_t type, uint64_t ip)
{
if (!symt)
return FZL_SYMTABLE_RESULT_ERR;
@@ -35,7 +36,8 @@ fzl_symtable_res_t fzl_symt_put(fzl_symtable_t *symt, char *name,
symt->symbols = realloc(symt->symbols, symt->remainder * sizeof(fzl_sym_t));
}
symt->symbols[symt->size++] = (fzl_sym_t){.name = name, .type = type};
symt->symbols[symt->size++] =
(fzl_sym_t){.name = name, .type = type, .ip = ip};
return FZL_SYMTABLE_RESULT_OK;
}