lang: codegen: redesign variable definition/resolution system to support capturing

the codegen scope system has been removed. instead, each generator state in the stack,
from the current state backwards, is informed when a variable is defined, resolved, or
captured.

when a variable is defined, the state stack is traversed back-to-front (current generator
first). each state has a chance to record the variable definition. once one state has
signalled that it has recorded the variable definition, the traversal ends.

when a variable is resolved, the state stack is traversed back-to-front (current generator
first). each state is asked whether or not it recognises the variable identifier being resolved.
if a state has the variable in question defined, it returns information about the variable
definition, and the traversal stops.

once a variable has been resolved, the state stack is traversed front-to-back (current generator
last), starting from the generator /after/ the one that provided the variable definition. each
generator in the iteration is given the chance to adjust the variable information, or generate
IR in response to the variable being accessed. this is used to implement variable capture,
where the state of a variable in the enclosing context is captured for later use.
This commit is contained in:
2025-04-22 15:23:42 +01:00
parent dc000ae8ef
commit 7483b32c25
8 changed files with 289 additions and 134 deletions

View File

@@ -45,8 +45,6 @@ static enum ivy_status state_init(
uintptr_t argv, void *argp)
{
struct block_codegen_state *block = (struct block_codegen_state *)state;
block->s_scope = codegen_push_scope(gen);
return IVY_OK;
}
@@ -54,8 +52,6 @@ static enum ivy_status state_fini(
struct ivy_codegen *gen, struct code_generator_state *state,
struct mie_value **result)
{
codegen_pop_scope(gen);
return IVY_OK;
}

View File

@@ -81,73 +81,6 @@ enum ivy_status codegen_pop_generator(
return IVY_OK;
}
static struct codegen_var *code_generator_scope_get_variable(
struct code_generator_scope *scope, const char *ident)
{
b_queue_iterator it;
b_queue_foreach (&it, &scope->s_vars) {
struct codegen_var *var
= b_unbox(struct codegen_var, it.entry, v_entry);
if (!strcmp(var->v_ident->n_content->t_str, ident)) {
return var;
}
}
return NULL;
}
static bool code_generator_scope_is_top_level(struct code_generator_scope *scope)
{
switch (scope->s_type) {
case CODE_GENERATOR_SCOPE_FUNC:
case CODE_GENERATOR_SCOPE_UNIT:
return true;
default:
return false;
}
}
static void code_generator_scope_destroy(struct code_generator_scope *scope)
{
/* TODO clean up variables */
free(scope);
}
struct code_generator_scope *codegen_push_scope(struct ivy_codegen *gen)
{
struct code_generator_scope *scope = malloc(sizeof *scope);
if (!scope) {
return NULL;
}
memset(scope, 0x0, sizeof *scope);
b_queue_push_back(&gen->c_scope, &scope->s_entry);
return scope;
}
void codegen_pop_scope(struct ivy_codegen *gen)
{
b_queue_entry *entry = b_queue_pop_back(&gen->c_scope);
if (!entry) {
return;
}
struct code_generator_scope *scope
= b_unbox(struct code_generator_scope, entry, s_entry);
code_generator_scope_destroy(scope);
}
struct code_generator_scope *codegen_get_current_scope(struct ivy_codegen *gen)
{
b_queue_entry *entry = b_queue_last(&gen->c_scope);
if (!entry) {
return NULL;
}
return b_unbox(struct code_generator_scope, entry, s_entry);
}
struct codegen_value *codegen_value_create(
struct ivy_ast_node *node, struct mie_value *val)
{
@@ -184,49 +117,112 @@ struct codegen_value *codegen_pop_value(struct ivy_codegen *gen)
return b_unbox(struct codegen_value, entry, v_entry);
}
struct codegen_var *codegen_resolve_variable(
struct ivy_codegen *gen, const char *ident)
enum ivy_status codegen_define_variable(
struct ivy_codegen *gen, const char *ident, const struct codegen_var *var)
{
b_queue_entry *cur = b_queue_last(&gen->c_scope);
while (cur) {
struct code_generator_scope *scope
= b_unbox(struct code_generator_scope, cur, s_entry);
struct codegen_var *var
= code_generator_scope_get_variable(scope, ident);
b_queue_entry *entry = b_queue_last(&gen->c_state);
if (!entry) {
return IVY_ERR_NO_ENTRY;
}
if (var) {
return var;
bool resolved = false;
while (entry) {
struct code_generator_state *state
= b_unbox(struct code_generator_state, entry, s_entry);
const struct code_generator *generator = state->s_gen;
enum ivy_status status = IVY_ERR_NO_ENTRY;
if (generator->g_define_var) {
status = generator->g_define_var(gen, state, ident, var);
}
if (code_generator_scope_is_top_level(scope)) {
if (status == IVY_OK) {
break;
}
cur = b_queue_prev(cur);
entry = b_queue_prev(entry);
}
return NULL;
return IVY_ERR_NOT_SUPPORTED;
}
enum ivy_status code_generator_scope_put_variable(
struct code_generator_scope *scope, struct ivy_ast_ident_node *ident,
struct mie_value *ptr)
enum ivy_status resolve_global_variable(
struct ivy_codegen *gen, const char *ident, struct codegen_var *out)
{
struct codegen_var *var = malloc(sizeof *var);
if (!var) {
return IVY_ERR_NO_MEMORY;
}
struct mie_value *var_ptr
= mie_builder_get_data_ptr(gen->c_builder, ident);
memset(var, 0x0, sizeof *var);
var->v_ident = ident;
var->v_ptr = ptr;
b_queue_push_back(&scope->s_vars, &var->v_entry);
out->v_type = mie_ctx_get_type(gen->c_ctx, MIE_TYPE_ID);
out->v_node = NULL;
out->v_flags = CODEGEN_VAR_F_PTR;
out->v_value = var_ptr;
return IVY_OK;
}
enum ivy_status codegen_resolve_variable(
struct ivy_codegen *gen, const char *ident, struct codegen_var *out)
{
b_queue_entry *entry = b_queue_last(&gen->c_state);
if (!entry) {
return IVY_ERR_NO_ENTRY;
}
bool resolved = false;
while (entry) {
struct code_generator_state *state
= b_unbox(struct code_generator_state, entry, s_entry);
const struct code_generator *generator = state->s_gen;
enum ivy_status status = IVY_ERR_NO_ENTRY;
if (generator->g_resolve_var) {
status = generator->g_resolve_var(gen, state, ident, out);
}
if (status == IVY_OK) {
resolved = true;
break;
}
entry = b_queue_prev(entry);
}
if (!resolved) {
return resolve_global_variable(gen, ident, out);
}
if (entry) {
entry = b_queue_next(entry);
}
while (entry) {
struct code_generator_state *state
= b_unbox(struct code_generator_state, entry, s_entry);
const struct code_generator *generator = state->s_gen;
enum ivy_status status = IVY_OK;
if (generator->g_capture_var) {
status = generator->g_capture_var(gen, state, ident, out);
}
if (status != IVY_OK) {
return status;
}
entry = b_queue_next(entry);
}
return IVY_OK;
}
struct mie_value *codegen_load_variable(
struct ivy_codegen *gen, struct codegen_var *var)
{
return mie_builder_load(gen->c_builder, var->v_type, var->v_value, NULL);
}
static struct code_generator_state *get_current_generator_state(
struct ivy_codegen *gen)
{

View File

@@ -1,6 +1,8 @@
#ifndef _LANG_CODEGEN_H_
#define _LANG_CODEGEN_H_
#include "var-map.h"
#include <blue/core/queue.h>
#include <ivy/lang/ast.h>
#include <ivy/lang/lex.h>
@@ -13,6 +15,8 @@ struct ivy_ast_node;
struct mie_value;
struct mie_func;
struct codegen_var;
#define CODEGEN_RESULT_OK(flags) \
(struct code_generator_result) \
{ \
@@ -27,18 +31,6 @@ struct mie_func;
#define __AST_INDEX(type) IVY_AST_##type
#define NODE_CODEGEN(type, func) [__AST_INDEX(type)] = func
struct codegen_value {
b_queue_entry v_entry;
struct ivy_ast_node *v_node;
struct mie_value *v_value;
};
struct codegen_var {
b_queue_entry v_entry;
struct ivy_ast_ident_node *v_ident;
struct mie_value *v_ptr;
};
enum code_generator_flags {
CODEGEN_F_IGNORE_RESULT = 0x01u,
};
@@ -70,6 +62,12 @@ struct code_generator_result {
} r_flags;
};
struct codegen_value {
b_queue_entry v_entry;
struct ivy_ast_node *v_node;
struct mie_value *v_value;
};
struct code_generator_state;
typedef enum ivy_status (*code_generator_state_init_callback)(
@@ -83,6 +81,15 @@ typedef struct code_generator_result (*code_generator_node_callback)(
struct ivy_ast_node *, size_t);
typedef struct code_generator_result (*code_generator_value_received_callback)(
struct ivy_codegen *, struct code_generator_state *, struct mie_value *);
typedef enum ivy_status (*code_generator_define_variable_callback)(
struct ivy_codegen *, struct code_generator_state *, const char *,
const struct codegen_var *);
typedef enum ivy_status (*code_generator_resolve_variable_callback)(
struct ivy_codegen *, struct code_generator_state *, const char *,
struct codegen_var *);
typedef enum ivy_status (*code_generator_capture_variable_callback)(
struct ivy_codegen *, struct code_generator_state *, const char *,
struct codegen_var *);
struct code_generator {
enum code_generator_type g_type;
@@ -93,6 +100,9 @@ struct code_generator {
code_generator_node_callback g_node_generators[IVY_AST_TYPE_COUNT];
code_generator_node_callback g_expr_generator;
code_generator_value_received_callback g_value_received;
code_generator_define_variable_callback g_define_var;
code_generator_resolve_variable_callback g_resolve_var;
code_generator_capture_variable_callback g_capture_var;
};
struct code_generator_state {
@@ -104,13 +114,6 @@ struct code_generator_state {
size_t s_depth;
};
struct code_generator_scope {
enum code_generator_scope_type s_type;
b_queue_entry s_entry;
/* TODO turn this into a bst */
b_queue s_vars;
};
struct ivy_codegen {
b_queue c_values;
b_queue c_state;
@@ -131,11 +134,6 @@ extern enum ivy_status codegen_push_generator(
extern enum ivy_status codegen_pop_generator(
struct ivy_codegen *gen, struct mie_value **result);
extern struct code_generator_scope *codegen_push_scope(struct ivy_codegen *gen);
extern void codegen_pop_scope(struct ivy_codegen *gen);
extern struct code_generator_scope *codegen_get_current_scope(
struct ivy_codegen *gen);
extern struct codegen_value *codegen_value_create(
struct ivy_ast_node *node, struct mie_value *val);
extern void codegen_value_destroy(struct codegen_value *val);
@@ -143,11 +141,11 @@ extern void codegen_value_destroy(struct codegen_value *val);
extern void codegen_push_value(struct ivy_codegen *gen, struct codegen_value *val);
extern struct codegen_value *codegen_pop_value(struct ivy_codegen *gen);
extern struct codegen_var *codegen_resolve_variable(
struct ivy_codegen *gen, const char *ident);
extern enum ivy_status code_generator_scope_put_variable(
struct code_generator_scope *scope, struct ivy_ast_ident_node *ident,
struct mie_value *ptr);
extern enum ivy_status codegen_define_variable(
struct ivy_codegen *gen, const char *ident, const struct codegen_var *var);
extern enum ivy_status codegen_resolve_variable(
struct ivy_codegen *gen, const char *ident, struct codegen_var *out);
extern struct mie_value *codegen_load_variable(
struct ivy_codegen *gen, struct codegen_var *var);
#endif

View File

@@ -178,8 +178,14 @@ static struct code_generator_result gen_var_reference(
struct mie_value *var_ptr = NULL;
struct ivy_ast_ident_node *ident = (struct ivy_ast_ident_node *)node;
struct codegen_var *var
= codegen_resolve_variable(gen, ident->n_content->t_str);
struct codegen_var var = {};
enum ivy_status status
= codegen_resolve_variable(gen, ident->n_content->t_str, &var);
if (status != IVY_OK) {
return CODEGEN_RESULT_ERR(status);
}
#if 0
if (var) {
/* this is a local variable */
var_ptr = var->v_ptr;
@@ -192,8 +198,13 @@ static struct code_generator_result gen_var_reference(
struct mie_type *id = mie_ctx_get_type(gen->c_ctx, MIE_TYPE_ID);
struct mie_value *var_value
= mie_builder_load(gen->c_builder, id, var_ptr, NULL);
#endif
struct mie_value *var_value = codegen_load_variable(gen, &var);
if (!var_value) {
return CODEGEN_RESULT_ERR(IVY_ERR_INTERNAL_FAILURE);
}
enum ivy_status status = push_operand(expr, node, var_value);
status = push_operand(expr, node, var_value);
if (status != IVY_OK) {
return CODEGEN_RESULT_ERR(status);

View File

@@ -1,4 +1,5 @@
#include "codegen.h"
#include "var-map.h"
#include <mie/block.h>
#include <mie/func.h>
@@ -10,6 +11,7 @@ struct unit_codegen_state {
/* function for top-level statements */
// struct mie_block *s_immediate_alloca;
struct mie_func *s_immediate;
struct codegen_var_map s_vars;
};
static enum ivy_status switch_to_immediate_func(
@@ -151,7 +153,7 @@ static enum ivy_status state_init(
uintptr_t argv, void *argp)
{
struct unit_codegen_state *unit = (struct unit_codegen_state *)state;
unit->s_scope = codegen_push_scope(gen);
codegen_var_map_init(&unit->s_vars);
return IVY_OK;
}
@@ -160,11 +162,37 @@ static enum ivy_status state_fini(
struct ivy_codegen *gen, struct code_generator_state *state,
struct mie_value **result)
{
codegen_pop_scope(gen);
struct unit_codegen_state *unit = (struct unit_codegen_state *)state;
codegen_var_map_fini(&unit->s_vars);
return IVY_OK;
}
static enum ivy_status define_var(
struct ivy_codegen *gen, struct code_generator_state *state,
const char *ident, const struct codegen_var *var)
{
struct unit_codegen_state *unit = (struct unit_codegen_state *)state;
return codegen_var_map_put(&unit->s_vars, ident, var);
}
static enum ivy_status resolve_var(
struct ivy_codegen *gen, struct code_generator_state *state,
const char *ident, struct codegen_var *var)
{
struct unit_codegen_state *unit = (struct unit_codegen_state *)state;
struct codegen_var *result = NULL;
enum ivy_status status
= codegen_var_map_get(&unit->s_vars, ident, &result);
if (status != IVY_OK) {
return status;
}
memcpy(var, result, sizeof *var);
return IVY_OK;
}
struct code_generator unit_generator = {
.g_type = CODE_GENERATOR_UNIT,
.g_state_size = sizeof(struct unit_codegen_state),
@@ -174,4 +202,6 @@ struct code_generator unit_generator = {
NODE_CODEGEN(VAR, gen_var_declaration),
},
.g_expr_generator = gen_expr,
.g_define_var = define_var,
.g_resolve_var = resolve_var,
};

73
lang/codegen/var-map.c Normal file
View File

@@ -0,0 +1,73 @@
#include "var-map.h"
#include <blue/object/hashmap.h>
#include <blue/object/string.h>
#include <stdlib.h>
#include <string.h>
enum ivy_status codegen_var_map_init(struct codegen_var_map *map)
{
memset(map, 0x0, sizeof *map);
map->m_map = b_hashmap_create(free, free);
return IVY_OK;
}
enum ivy_status codegen_var_map_fini(struct codegen_var_map *map)
{
b_hashmap_release(map->m_map);
return IVY_OK;
}
enum ivy_status codegen_var_map_get(
struct codegen_var_map *map, const char *ident, struct codegen_var **out)
{
b_hashmap_key key = {
.key_data = ident,
.key_size = strlen(ident),
};
const b_hashmap_value *value = b_hashmap_get(map->m_map, &key);
if (!value) {
return IVY_ERR_NO_ENTRY;
}
*out = value->value_data;
return IVY_OK;
}
enum ivy_status codegen_var_map_put(
struct codegen_var_map *map, const char *ident,
const struct codegen_var *var)
{
b_hashmap_key key = {
.key_data = b_strdup(ident),
.key_size = strlen(ident),
};
if (!key.key_data) {
return IVY_ERR_NO_MEMORY;
}
b_hashmap_value value = {
.value_data = malloc(sizeof *var),
.value_size = sizeof *var,
};
if (!value.value_data) {
free((void *)key.key_data);
return IVY_ERR_NO_MEMORY;
}
memcpy(value.value_data, var, sizeof *var);
b_status status = b_hashmap_put(map->m_map, &key, &value);
if (!B_OK(status)) {
free((void *)key.key_data);
free(value.value_data);
}
return ivy_status_from_b_status(status);
}

35
lang/codegen/var-map.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef CODEGEN_VAR_MAP_H_
#define CODEGEN_VAR_MAP_H_
#include <ivy/status.h>
struct b_hashmap;
struct mie_value;
struct ivy_ast_node;
enum codegen_var_flags {
CODEGEN_VAR_F_PTR = 0x01u,
CODEGEN_VAR_F_CAPTURE = 0x02u,
};
struct codegen_var {
enum codegen_var_flags v_flags;
struct mie_type *v_type;
struct ivy_ast_node *v_node;
struct mie_value *v_value;
};
struct codegen_var_map {
struct b_hashmap *m_map;
};
extern enum ivy_status codegen_var_map_init(struct codegen_var_map *map);
extern enum ivy_status codegen_var_map_fini(struct codegen_var_map *map);
extern enum ivy_status codegen_var_map_get(
struct codegen_var_map *map, const char *ident, struct codegen_var **out);
extern enum ivy_status codegen_var_map_put(
struct codegen_var_map *map, const char *ident,
const struct codegen_var *var);
#endif

View File

@@ -106,8 +106,24 @@ static enum ivy_status state_fini(
debug_printf("codegen: end of var decl\n");
struct var_codegen_state *var = (struct var_codegen_state *)state;
struct code_generator_scope *scope = codegen_get_current_scope(gen);
code_generator_scope_put_variable(scope, var->s_var_ident, var->s_var_ptr);
const char *ident = var->s_var_ident->n_content->t_str;
struct mie_type *type = NULL;
if (var->s_value) {
type = mie_value_get_type(var->s_value);
} else {
type = mie_ctx_get_type(gen->c_ctx, MIE_TYPE_ID);
}
struct codegen_var var_info = {
.v_node = (struct ivy_ast_node *)var->s_var_ident,
.v_type = type,
.v_value = var->s_var_ptr,
};
codegen_define_variable(gen, ident, &var_info);
// struct code_generator_scope *scope = codegen_get_current_scope(gen);
// code_generator_scope_put_variable(scope, var->s_var_ident, var->s_var_ptr);
if (var->s_value) {
mie_builder_store(gen->c_builder, var->s_value, var->s_var_ptr);