236 lines
5.3 KiB
C
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);
|
|
}
|