feat: implement AST walker

This commit is contained in:
samsonium
2026-10-10 02:40:53 +03:00
parent 2fabd0ec2d
commit e979a22739
9 changed files with 469 additions and 17 deletions
+2
View File
@@ -12,6 +12,8 @@ add_executable(fzc
src/compiler/fzl_reader.c
src/compiler/fzl_parser.c
src/compiler/fzl_debug.c
src/compiler/fzl_walker.c
src/compiler/utils/fzl_const_pool.c
src/compiler/main.c)
# VM
+9
View File
@@ -0,0 +1,9 @@
#ifndef FZL_BUILTINS_H
#define FZL_BUILTINS_H
typedef enum
{
FZL_BUILTIN_PRINTLINE // Print message in arg with newline at end
} fzl_builtin_t;
#endif // FZL_BUILTINS_H
+12
View File
@@ -0,0 +1,12 @@
#ifndef FZL_OPCODES_H
#define FZL_OPCODES_H
// Available opcodes
typedef enum
{
FZL_OPCODE_LOADCONST, // Load const into T (temp) reg
FZL_OPCODE_PUSHARG, // Push argument from T into args stack
FZL_OPCODE_CALL_BUILTIN // Call builtin function
} fzl_opcode_t;
#endif // FZL_OPCODES_H
+34
View File
@@ -0,0 +1,34 @@
#ifndef FZL_WALKER_H
#define FZL_WALKER_H
#include <common/fzl_opcodes.h>
#include <compiler/fzl_parser.h>
#include <compiler/utils/fzl_const_pool.h>
#include <stddef.h>
// Walker descriptor
typedef struct
{
fzl_node_t *root;
fzl_const_hash_pool_t *consts;
uint64_t *ops;
size_t op_count;
size_t op_remainder;
} fzl_walker_t;
// Allocate new walker
fzl_walker_t *fzl_mkwalker(fzl_node_t *root);
// Walk throught the entire AST and build:
// 1. Constants table
// 2. Operations
int fzl_walk(fzl_walker_t *walker);
// Get last error message
char *fzl_walker_geterror();
// Free walker
void fzl_freewalker(fzl_walker_t *walker);
#endif // FZL_WALKER_H
+64
View File
@@ -0,0 +1,64 @@
#ifndef FZL_CONST_SET_H
#define FZL_CONST_SET_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
// Constant types
typedef enum
{
FZL_CONSTTYPE_STRING
} fzl_consttype_t;
// Constant descriptor
typedef struct
{
fzl_consttype_t type;
union
{
struct
{
const char *chars;
size_t length;
} string;
} as;
} fzl_const_t;
typedef struct
{
size_t pool_index;
} fzl_const_hash_slot_t;
// Constant pool descriptor
typedef struct
{
fzl_const_t *array;
size_t size;
size_t capacity;
fzl_const_hash_slot_t *slots;
size_t slots_count;
size_t slots_capacity;
} fzl_const_hash_pool_t;
// Compare two constants for equality
bool fzl_consts_equal(fzl_const_t a, fzl_const_t b);
// Get hash of specified constant
static uint32_t fzl_hash_const(fzl_const_t c);
// Allocate constants hash pool
fzl_const_hash_pool_t *fzl_mkconstpool();
// Resize the hash table
static void fzl_consts_pool_resize_hash(fzl_const_hash_pool_t *pool);
// Push constant. Returns unique index
size_t fzl_consts_pool_add(fzl_const_hash_pool_t *pool, fzl_const_t c);
// Free constants hash pool
void fzl_consts_pool_free(fzl_const_hash_pool_t *pool);
#endif // FZL_CONST_SET_H
+16 -17
View File
@@ -2,13 +2,13 @@
#include <stdio.h>
#include <stdlib.h>
char last_err[256];
char plast_err[256];
fzl_parser_t *fzl_mkparser(fzl_reader_ll_t *first)
{
if (!first)
{
snprintf(last_err, sizeof(last_err), "token is null");
snprintf(plast_err, sizeof(plast_err), "token is null");
return NULL;
}
@@ -81,7 +81,7 @@ fzl_node_t *parse_primary(fzl_parser_t *parser)
return fzl_mknode(FZL_NODETYPE_LITERAL, tok);
}
snprintf(last_err, sizeof(last_err), "failed to parse primary at %d",
snprintf(plast_err, sizeof(plast_err), "failed to parse primary at %d",
peek(parser)->i0);
return NULL;
}
@@ -126,7 +126,7 @@ fzl_node_t *parse_call(fzl_parser_t *parser)
consume(parser);
else if (peek(parser) && peek(parser)->type != FZL_TOKTYPE_RPAIR)
{
snprintf(last_err, sizeof(last_err),
snprintf(plast_err, sizeof(plast_err),
"expected ',' or ')' after arg list");
return NULL;
}
@@ -147,7 +147,7 @@ fzl_node_t *parse_expression(fzl_parser_t *parser, int precedence)
else if (peek(parser) && peek(parser)->type == FZL_TOKTYPE_STR_LITERAL)
return parse_primary(parser);
snprintf(last_err, sizeof(last_err), "failed to parse expression at %d",
snprintf(plast_err, sizeof(plast_err), "failed to parse expression at %d",
peek(parser)->i0);
return NULL;
}
@@ -184,7 +184,7 @@ fzl_node_t *parse_program(fzl_parser_t *parser)
if (!first)
{
snprintf(last_err, sizeof(last_err), "empty ;(");
snprintf(plast_err, sizeof(plast_err), "empty ;(");
return NULL;
}
@@ -204,7 +204,7 @@ int fzl_parse(fzl_parser_t *parser)
// --- Parse zone end ---
char *fzl_parser_geterror() { return last_err; }
char *fzl_parser_geterror() { return plast_err; }
void fzl_freenode(fzl_node_t *node)
{
@@ -220,17 +220,16 @@ void fzl_freeast(fzl_node_t *root)
while (root)
{
if (root->type == FZL_NODETYPE_BLOCK)
if (root->type == FZL_NODETYPE_BLOCK || root->type == FZL_NODETYPE_CALL)
{
for (size_t i = 0; i < root->list.entry_count; i++)
if (root->list.entries[i])
fzl_freeast(root->list.entries[i]);
}
else if (root->type == FZL_NODETYPE_CALL)
{
for (size_t i = 0; i < root->list.entry_count; i++)
if (root->list.entries[i])
fzl_freeast(root->list.entries[i]);
if (root->list.entries)
{
for (size_t i = 0; i < root->list.entry_count; i++)
if (root->list.entries[i])
fzl_freeast(root->list.entries[i]);
free(root->list.entries);
}
}
fzl_freenode(root);
+168
View File
@@ -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);
}
+24
View File
@@ -1,6 +1,7 @@
#include <compiler/fzl_debug.h>
#include <compiler/fzl_parser.h>
#include <compiler/fzl_reader.h>
#include <compiler/fzl_walker.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -155,12 +156,35 @@ int main(int argc, char **argv)
{
printf("ok!\n");
fzl_debug_print_ast(parser->root);
printf("-> walking AST...");
}
fzl_walker_t *walker = fzl_mkwalker(parser->root);
result = fzl_walk(walker);
if (result != 0)
{
printf("Error: %s\n", fzl_walker_geterror());
return -1;
}
if (args->verbose)
{
printf("ok!\n");
printf("--> Constants table:\n");
for (size_t i = 0; i < walker->consts->size; i++)
printf("---> %s\n", walker->consts->array[i].as.string.chars);
printf("--> Ops:\n");
for (size_t i = 0; i < walker->op_count; i++)
printf("---> %lu\n", walker->ops[i]);
}
free(args);
fzl_freereader(rd);
fzl_freeparser(parser);
fzl_freeast(parser->root);
fzl_freewalker(walker);
printf("done.\n");
return 0;
+140
View File
@@ -0,0 +1,140 @@
#include <compiler/utils/fzl_const_pool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
bool fzl_consts_equal(fzl_const_t a, fzl_const_t b)
{
if (a.type != b.type)
return false;
switch (a.type)
{
case FZL_CONSTTYPE_STRING:
{
if (a.as.string.length != b.as.string.length)
return false;
return memcmp(a.as.string.chars, b.as.string.chars, a.as.string.length) ==
0;
}
}
return false;
}
static uint32_t fzl_hash_const(fzl_const_t c)
{
uint32_t hash = 2166136261u;
const uint8_t *data = NULL;
size_t size = 0;
switch (c.type)
{
case FZL_CONSTTYPE_STRING:
data = (const uint8_t *)&c.as.string.chars;
size = c.as.string.length;
break;
}
for (size_t i = 0; i < size; i++)
{
hash ^= data[i];
hash *= 16777619u;
}
return hash;
}
fzl_const_hash_pool_t *fzl_mkconstpool()
{
fzl_const_hash_pool_t *pool = malloc(sizeof(fzl_const_hash_pool_t));
pool->size = 0;
pool->capacity = 8;
pool->array = malloc(pool->capacity * sizeof(fzl_const_t));
pool->slots_count = 0;
pool->slots_capacity = 16;
pool->slots = malloc(pool->slots_capacity * sizeof(fzl_const_hash_slot_t));
for (int i = 0; i < pool->slots_capacity; i++)
pool->slots[i].pool_index = -1;
return pool;
}
static void fzl_consts_pool_resize_hash(fzl_const_hash_pool_t *pool)
{
int old_capacity = pool->slots_capacity;
fzl_const_hash_slot_t *old_slots = pool->slots;
pool->slots_capacity = old_capacity * 2;
pool->slots = malloc(pool->slots_capacity * sizeof(fzl_const_hash_slot_t));
for (int i = 0; i < pool->slots_capacity; i++)
pool->slots[i].pool_index = -1;
pool->slots_count = 0;
for (int i = 0; i < old_capacity; i++)
{
if (old_slots[i].pool_index == -1)
continue;
int index_in_pool = old_slots[i].pool_index;
fzl_const_t c = pool->array[index_in_pool];
uint32_t hash = fzl_hash_const(c);
uint32_t bucket = hash & (pool->slots_capacity - 1);
while (pool->slots[bucket].pool_index != -1)
bucket = (bucket + 1) & (pool->slots_capacity - 1);
pool->slots[bucket].pool_index = index_in_pool;
pool->slots_count++;
}
free(old_slots);
}
size_t fzl_consts_pool_add(fzl_const_hash_pool_t *pool, fzl_const_t c)
{
if (pool->slots_count * 4 >= pool->slots_capacity * 3)
fzl_consts_pool_resize_hash(pool);
uint32_t hash = fzl_hash_const(c);
uint32_t bucket = hash & (pool->slots_capacity - 1);
while (pool->slots[bucket].pool_index != -1)
{
size_t existing_index = pool->slots[bucket].pool_index;
if (fzl_consts_equal(pool->array[existing_index], c))
return existing_index;
bucket = (bucket + 1) & (pool->slots_capacity - 1);
}
if (pool->size >= pool->capacity)
{
pool->capacity *= 2;
pool->array = (fzl_const_t *)realloc(pool->array,
pool->capacity * sizeof(fzl_const_t));
}
size_t new_index = pool->size;
pool->array[new_index] = c;
pool->size++;
pool->slots[bucket].pool_index = new_index;
pool->slots_count++;
return new_index;
}
void fzl_consts_pool_free(fzl_const_hash_pool_t *pool)
{
if (!pool)
return;
free(pool->array);
free(pool->slots);
free(pool);
}