/** * @file scope.c * @brief Scope management implementation for Baba Yaga * @author eli_oat * @version 0.0.1 * @date 2025 * * This file implements scope management for the Baba Yaga language. */ #include #include #include #include "baba_yaga.h" /* ============================================================================ * Scope Entry Structure * ============================================================================ */ typedef struct ScopeEntry { char* name; Value value; bool is_constant; struct ScopeEntry* next; } ScopeEntry; /* ============================================================================ * Scope Structure * ============================================================================ */ struct Scope { struct Scope* parent; ScopeEntry* entries; int entry_count; int capacity; }; /* ============================================================================ * Scope Management Functions * ============================================================================ */ /** * @brief Create a new scope * * @param parent Parent scope, or NULL for global scope * @return New scope instance, or NULL on failure */ Scope* scope_create(Scope* parent) { Scope* scope = malloc(sizeof(Scope)); if (scope == NULL) { return NULL; } scope->parent = parent; scope->entries = NULL; scope->entry_count = 0; scope->capacity = 0; return scope; } /** * @brief Destroy a scope and all its entries * * @param scope Scope to destroy */ void scope_destroy(Scope* scope) { if (scope == NULL) { return; } /* Free all entries */ ScopeEntry* entry = scope->entries; while (entry != NULL) { ScopeEntry* next = entry->next; /* Destroy the value */ baba_yaga_value_destroy(&entry->value); /* Free the entry */ free(entry->name); free(entry); entry = next; } free(scope); } /** * @brief Get the global scope (root scope with no parent) * * @param scope Starting scope * @return Global scope, or NULL if not found */ Scope* scope_get_global(Scope* scope) { if (scope == NULL) { return NULL; } /* Traverse up the scope chain until we find a scope with no parent */ while (scope->parent != NULL) { scope = scope->parent; } return scope; } /** * @brief Find an entry in the scope chain * * @param scope Starting scope * @param name Variable name to find * @return Scope entry if found, NULL otherwise */ static ScopeEntry* scope_find_entry(Scope* scope, const char* name) { while (scope != NULL) { ScopeEntry* entry = scope->entries; while (entry != NULL) { if (strcmp(entry->name, name) == 0) { return entry; } entry = entry->next; } scope = scope->parent; } return NULL; } /** * @brief Get a value from the scope chain * * @param scope Starting scope * @param name Variable name * @return Value if found, nil otherwise */ Value scope_get(Scope* scope, const char* name) { if (scope == NULL || name == NULL) { return baba_yaga_value_nil(); } ScopeEntry* entry = scope_find_entry(scope, name); if (entry == NULL) { DEBUG_DEBUG("scope_get: variable '%s' not found in scope", name); return baba_yaga_value_nil(); } DEBUG_DEBUG("scope_get: found variable '%s' in scope with type %d", name, entry->value.type); /* Return a copy of the value */ return baba_yaga_value_copy(&entry->value); } /** * @brief Set a value in the current scope (creates if doesn't exist) * * @param scope Current scope * @param name Variable name * @param value Value to set * @return true on success, false on failure */ bool scope_set(Scope* scope, const char* name, Value value) { if (scope == NULL || name == NULL) { return false; } /* Look for existing entry in current scope only */ ScopeEntry* entry = scope->entries; while (entry != NULL) { if (strcmp(entry->name, name) == 0) { /* Update existing entry */ baba_yaga_value_destroy(&entry->value); entry->value = baba_yaga_value_copy(&value); return true; } entry = entry->next; } /* Create new entry */ entry = malloc(sizeof(ScopeEntry)); if (entry == NULL) { return false; } entry->name = strdup(name); if (entry->name == NULL) { free(entry); return false; } entry->value = baba_yaga_value_copy(&value); entry->is_constant = false; entry->next = scope->entries; scope->entries = entry; scope->entry_count++; return true; } /** * @brief Define a new variable in the current scope * * @param scope Current scope * @param name Variable name * @param value Initial value * @param is_constant Whether the variable is constant * @return true on success, false on failure */ bool scope_define(Scope* scope, const char* name, Value value, bool is_constant) { if (scope == NULL || name == NULL) { return false; } /* Check if variable already exists in current scope */ ScopeEntry* entry = scope->entries; while (entry != NULL) { if (strcmp(entry->name, name) == 0) { /* Variable already exists */ return false; } entry = entry->next; } /* Create new entry */ entry = malloc(sizeof(ScopeEntry)); if (entry == NULL) { return false; } entry->name = strdup(name); if (entry->name == NULL) { free(entry); return false; } entry->value = baba_yaga_value_copy(&value); entry->is_constant = is_constant; entry->next = scope->entries; scope->entries = entry; scope->entry_count++; DEBUG_DEBUG("scope_define: defined variable '%s' in scope with type %d", name, entry->value.type); return true; } /** * @brief Check if a variable exists in the scope chain * * @param scope Starting scope * @param name Variable name * @return true if variable exists, false otherwise */ bool scope_has(Scope* scope, const char* name) { if (scope == NULL || name == NULL) { return false; } return scope_find_entry(scope, name) != NULL; } /** * @brief Get all variable names in the current scope * * @param scope Current scope * @param names Output array for variable names * @param max_names Maximum number of names to return * @return Number of names returned */ int scope_get_names(Scope* scope, char** names, int max_names) { if (scope == NULL || names == NULL || max_names <= 0) { return 0; } int count = 0; ScopeEntry* entry = scope->entries; while (entry != NULL && count < max_names) { names[count] = strdup(entry->name); count++; entry = entry->next; } return count; } /** * @brief Print scope contents for debugging * * @param scope Scope to print * @param indent Indentation level */ void scope_print(Scope* scope, int indent) { if (scope == NULL) { return; } /* Print indentation */ for (int i = 0; i < indent; i++) { printf(" "); } printf("Scope (entries: %d):\n", scope->entry_count); /* Print entries */ ScopeEntry* entry = scope->entries; while (entry != NULL) { for (int i = 0; i < indent + 1; i++) { printf(" "); } char* value_str = baba_yaga_value_to_string(&entry->value); printf("%s%s = %s\n", entry->is_constant ? "const " : "", entry->name, value_str); free(value_str); entry = entry->next; } /* Print parent scope */ if (scope->parent != NULL) { for (int i = 0; i < indent; i++) { printf(" "); } printf("Parent scope:\n"); scope_print(scope->parent, indent + 1); } }