Files
fz-lang/src/compiler/fzl_walker.c
T
2026-10-11 03:12:35 +03:00

236 lines
5.3 KiB
C

#include <common/fzl_builtins.h>
#include <common/fzl_opcodes.h>
#include <compiler/fzl_parser.h>
#include <compiler/fzl_token.h>
#include <compiler/fzl_walker.h>
#include <compiler/utils/fzl_const_pool.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char wlast_err[256];
fzl_walker_t *fzl_mkwalker(fzl_node_t *root)
{
if (!root)
{
snprintf(wlast_err, sizeof(wlast_err), "root node is null");
return NULL;
}
fzl_walker_t *walker = malloc(sizeof(fzl_walker_t));
if (!walker)
{
snprintf(wlast_err, sizeof(wlast_err), "failed to allocated walker");
return NULL;
}
fzl_const_hash_pool_t *pool = fzl_mkconstpool();
if (!pool)
{
snprintf(wlast_err, sizeof(wlast_err), "failed to create constants pool");
return NULL;
}
walker->root = root;
walker->consts = pool;
walker->op_count = 0;
walker->op_remainder = 2;
walker->ops = malloc(walker->op_remainder * sizeof(uint64_t));
return walker;
}
// --- Walking zone ---
// Add const to consts table
size_t add_const(fzl_walker_t *walker, fzl_const_t c)
{
if (!walker)
return false;
return fzl_consts_pool_add(walker->consts, c);
}
// Resize ops if size is equals to capacity
void ops_resize_if_needed(fzl_walker_t *walker)
{
if (!walker)
return;
if (walker->op_count >= walker->op_remainder)
{
walker->op_remainder *= 2;
walker->ops = realloc(walker->ops, walker->op_remainder * sizeof(uint64_t));
}
}
// Push opcode to operations array
void push_op(fzl_walker_t *walker, fzl_opcode_t opcode)
{
if (!walker)
return;
ops_resize_if_needed(walker);
walker->ops[walker->op_count++] = (uint64_t)opcode;
}
// Push 64-bit integer to operations array
void push_word(fzl_walker_t *walker, uint64_t word)
{
if (!walker)
return;
ops_resize_if_needed(walker);
walker->ops[walker->op_count++] = word;
}
bool walk(fzl_walker_t *walker, fzl_node_t *node)
{
if (!walker || !node)
return false;
while (node != NULL)
{
switch (node->type)
{
case FZL_NODETYPE_BLOCK:
// NOTE: create block env here
if (node->list.entry_count > 0)
for (size_t i = 0; i < node->list.entry_count; i++)
if (!walk(walker, node->list.entries[i]))
return false;
// NOTE: destroy block env here
break;
case FZL_NODETYPE_CALL:
if (node->list.entry_count > 0)
{
for (size_t i = 0; i < node->call.args_count; i++)
{
if (!walk(walker, node->call.args[i]))
return false;
push_op(walker, FZL_OPCODE_PUSHARG);
}
}
// NOTE: you need to implement dynamic names call
push_op(walker, FZL_OPCODE_CALL_BUILTIN);
if (strcmp(node->token->value, "printline") == 0)
push_word(walker, FZL_BUILTIN_PRINTLINE);
else
{
snprintf(wlast_err, sizeof(wlast_err), "call to unknown function '%s'",
(char *)node->token->value);
return false;
}
break;
case FZL_NODETYPE_LITERAL:
if (node->token->type == FZL_TOKTYPE_STR_LITERAL)
{
size_t n = add_const(
walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING,
.as.string.chars = node->token->value,
.as.string.length = node->token->value_sz});
push_op(walker, FZL_OPCODE_LOADCONST);
push_word(walker, n);
}
else if (node->token->type == FZL_TOKTYPE_NUM_LITERAL)
{
push_op(walker, FZL_OPCODE_PUSHNUM);
uint64_t num = strtol(node->token->value, NULL, 10);
push_word(walker, num);
}
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:
if (!walk(walker, node->binary_op.left))
return false;
push_op(walker, FZL_OPCODE_PUSHARG);
if (!walk(walker, node->binary_op.right))
return false;
push_op(walker, FZL_OPCODE_PUSHARG);
switch (node->type)
{
case FZL_NODETYPE_ADD:
push_op(walker, FZL_OPCODE_ADD);
break;
case FZL_NODETYPE_SUB:
push_op(walker, FZL_OPCODE_SUB);
break;
case FZL_NODETYPE_MUL:
push_op(walker, FZL_OPCODE_MUL);
break;
case FZL_NODETYPE_DIV:
push_op(walker, FZL_OPCODE_DIV);
break;
case FZL_NODETYPE_POW:
push_op(walker, FZL_OPCODE_POW);
break;
case FZL_NODETYPE_MOD:
push_op(walker, FZL_OPCODE_MOD);
break;
default:
return false;
}
break;
case FZL_NODETYPE_UNARY_MINUS:
if (!walk(walker, node->unary_op.operand))
return false;
push_op(walker, FZL_OPCODE_PUSHARG);
push_op(walker, FZL_OPCODE_NEG);
break;
}
node = node->next;
}
return true;
}
// Walk entire AST recursevily
int fzl_walk(fzl_walker_t *walker)
{
if (!walker)
{
snprintf(wlast_err, sizeof(wlast_err), "walker is null");
return -1;
}
bool result = walk(walker, walker->root);
return !result && wlast_err[0] ? -1 : 0;
}
// --- Walking zone end ---
char *fzl_walker_geterror() { return wlast_err; }
void fzl_freewalker(fzl_walker_t *walker)
{
if (!walker)
return;
fzl_consts_pool_free(walker->consts);
if (walker->ops)
free(walker->ops);
free(walker);
}