feat: implement AST walker
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
#include <common/fzl_builtins.h>
|
||||
#include <common/fzl_opcodes.h>
|
||||
#include <compiler/fzl_parser.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++)
|
||||
walk(walker, node->list.entries[i]);
|
||||
// NOTE: destroy block env here
|
||||
break;
|
||||
|
||||
case FZL_NODETYPE_CALL:
|
||||
if (node->list.entry_count > 0)
|
||||
{
|
||||
for (size_t i = 0; i < node->list.entry_count; i++)
|
||||
{
|
||||
walk(walker, node->list.entries[i]);
|
||||
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:
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Walk entire AST recursevily
|
||||
int fzl_walk(fzl_walker_t *walker)
|
||||
{
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user