ref/hare/ ships only ast/lex/parse/module/unparse — no checker. Every bail-fix landing in A.6.2.1 (helper bail audit, N_IDENT bail audit, enum fold) needs a `ref/harec/...` cite anchor so divergence rationale isn't claimed against memory. PLAN.md L16, L35. Snapshot of upstream commit ee640c6b2c17f57e8014d277a3028b262ffb7952 (`rt/+openbsd/start.ha: remove outdated comment about crt0`). 20 files: 5 src (check.c, eval.c, scope.c, types.c, type_store.c), 14 transitive include/ headers (no parser/codegen surface), and COPYING. License GPLv3 (COPYING included). Vendored as read-only reference per CLAUDE.md rule 6 — no redistribution as binary, no edits.
137 lines
2.6 KiB
C
137 lines
2.6 KiB
C
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "expr.h"
|
|
#include "identifier.h"
|
|
#include "scope.h"
|
|
#include "util.h"
|
|
|
|
static uint32_t
|
|
name_hash(uint32_t init, const struct ident *ident)
|
|
{
|
|
return fnv1a_s(init, ident->name);
|
|
}
|
|
|
|
struct scope *
|
|
scope_push(struct scope **stack, enum scope_class class)
|
|
{
|
|
struct scope *new = xcalloc(1, sizeof(struct scope));
|
|
new->class = class;
|
|
new->results.types = NULL;
|
|
new->next = &new->objects;
|
|
new->parent = *stack;
|
|
*stack = new;
|
|
return new;
|
|
}
|
|
|
|
struct scope *
|
|
scope_pop(struct scope **stack)
|
|
{
|
|
struct scope *prev = *stack;
|
|
assert(prev);
|
|
*stack = prev->parent;
|
|
return prev;
|
|
}
|
|
|
|
struct scope *
|
|
scope_lookup_class(struct scope *scope, enum scope_class class)
|
|
{
|
|
while (scope) {
|
|
if (scope->class == class) {
|
|
break;
|
|
}
|
|
scope = scope->parent;
|
|
}
|
|
return scope;
|
|
}
|
|
|
|
struct scope *
|
|
scope_lookup_label(struct scope *scope, const char *label)
|
|
{
|
|
while (scope) {
|
|
if (scope->label && strcmp(scope->label, label) == 0) {
|
|
break;
|
|
}
|
|
scope = scope->parent;
|
|
}
|
|
return scope;
|
|
}
|
|
|
|
void
|
|
scope_free(struct scope *scope)
|
|
{
|
|
if (!scope) {
|
|
return;
|
|
}
|
|
|
|
struct scope_object *obj = scope->objects;
|
|
while (obj) {
|
|
struct scope_object *next = obj->lnext;
|
|
free(obj);
|
|
obj = next;
|
|
}
|
|
|
|
free(scope);
|
|
}
|
|
|
|
void
|
|
scope_free_all(struct scopes *scopes)
|
|
{
|
|
while (scopes) {
|
|
struct scopes *next = scopes->next;
|
|
scope_free(scopes->scope);
|
|
free(scopes);
|
|
scopes = next;
|
|
}
|
|
}
|
|
|
|
struct scope_object *
|
|
scope_insert(struct scope *scope, enum object_type otype,
|
|
struct ident *ident, struct ident *name, const struct type *type,
|
|
struct expression *value)
|
|
{
|
|
assert(otype == O_SCAN || !type != !value);
|
|
struct scope_object *obj = xcalloc(1, sizeof(struct scope_object));
|
|
obj->ident = ident;
|
|
obj->name = name;
|
|
obj->otype = otype;
|
|
if (type) {
|
|
obj->type = type;
|
|
} else if (value) {
|
|
obj->value = value;
|
|
assert(otype == O_CONST);
|
|
assert(value->type == EXPR_LITERAL);
|
|
}
|
|
flexible_refer(type, &obj->type);
|
|
|
|
// Linked list
|
|
*scope->next = obj;
|
|
scope->next = &obj->lnext;
|
|
|
|
// Hash map
|
|
uint32_t hash = name_hash(FNV1A_INIT, obj->name);
|
|
struct scope_object **bucket = &scope->buckets[hash % SCOPE_BUCKETS];
|
|
if (*bucket) {
|
|
obj->mnext = *bucket;
|
|
}
|
|
*bucket = obj;
|
|
return obj;
|
|
}
|
|
|
|
struct scope_object *
|
|
scope_lookup(struct scope *scope, struct ident *ident)
|
|
{
|
|
uint32_t hash = name_hash(FNV1A_INIT, ident);
|
|
struct scope_object *bucket = scope->buckets[hash % SCOPE_BUCKETS];
|
|
while (bucket) {
|
|
if (bucket->name == ident) {
|
|
return bucket;
|
|
}
|
|
bucket = bucket->mnext;
|
|
}
|
|
if (scope->parent) {
|
|
return scope_lookup(scope->parent, ident);
|
|
}
|
|
return NULL;
|
|
}
|