#include "ww.h" #include #define INIT_BUCKETS 16 static u64 hashstr(const char *s) { /* FNV-1a 64-bit; small fixed footprint, decent distribution */ u64 h = 0xcbf29ce484222325ULL; for (; *s; s++) { h ^= (unsigned char)*s; h *= 0x100000001b3ULL; } return h; } Scope * newscope(Arena *a, Scope *parent) { Scope *s = amalloc(a, sizeof *s); s->parent = parent; s->a = a; s->nbuckets = INIT_BUCKETS; s->buckets = amalloc(a, s->nbuckets * sizeof(Sym *)); return s; } Sym * scope_lookup_local(Scope *s, const char *name) { if (s == NULL) return NULL; u64 h = hashstr(name) % s->nbuckets; for (Sym *b = s->buckets[h]; b; b = b->hashnext) if (strcmp(b->name, name) == 0) return b; return NULL; } Sym * scope_lookup(Scope *s, const char *name) { for (; s; s = s->parent) { Sym *r = scope_lookup_local(s, name); if (r) return r; } return NULL; } /* * Used by resolve_typename and the cexpr N_DOT branch to pick the * right same-leaf-name type when two imports each export it * (`bufio.stream` vs `io.stream`). */ Sym * scope_lookup_in_module(Scope *s, const char *mod, const char *name) { if (mod == NULL) return scope_lookup(s, name); for (; s; s = s->parent) { u64 h = hashstr(name) % s->nbuckets; for (Sym *b = s->buckets[h]; b; b = b->hashnext) { if (strcmp(b->name, name) != 0) continue; if (b->mod && strcmp(b->mod, mod) == 0) return b; } } return NULL; } /* * Within each scope's bucket: Pass 1 prefers entries whose `sym.mod` * matches the caller's `mod`; Pass 2 accepts only a source-visible bare * entry. For a primary package that means mod==NULL. For an imported * package it means a lexical local (a child scope) or a decl-less builtin, * never another package's flattened interface value. We only * descend to the parent scope when the current scope has no matching * entry at all — so a local binding in a closer scope still shadows a * same-name fn from a parent scope, even when the parent entry * mod-matches. * * Used at bare-leaf lookup sites inside a known current module so that * a bare `read` inside lib/os resolves to os.read rather than the * io.read that happens to hash earlier into the flat scope. * Qualified-lookup paths (`mod.name`) stay on scope_lookup_in_module. */ Sym * scope_lookup_prefer(Scope *s, const char *mod, const char *name) { for (Scope *p = s; p; p = p->parent) { u64 h = hashstr(name) % p->nbuckets; Sym *fallback = NULL; for (Sym *b = p->buckets[h]; b; b = b->hashnext) { if (strcmp(b->name, name) != 0) continue; if (mod && b->mod && strcmp(b->mod, mod) == 0) return b; if (b->mod == NULL && (mod == NULL || p->parent != NULL || b->decl == NULL) && fallback == NULL) fallback = b; } if (fallback) return fallback; } return NULL; } /* * Skips every Sym whose kind isn't SK_TYPE and KEEPS scanning — so it * returns the innermost SK_TYPE of `name`, looking past a same-named * value binding (SK_VAR/SK_PARAM/SK_FN) that shadows it in a closer * scope. ww keeps type and value namespaces separate (wwstage already * does; #225 conformance gap): a param `off` must not hide the global * `type off`. */ Sym * scope_lookup_type(Scope *s, const char *mod, const char *name) { for (Scope *p = s; p; p = p->parent) { u64 h = hashstr(name) % p->nbuckets; Sym *fallback = NULL; for (Sym *b = p->buckets[h]; b; b = b->hashnext) { if (b->kind != SK_TYPE) continue; if (strcmp(b->name, name) != 0) continue; if (mod && b->mod && strcmp(b->mod, mod) == 0) return b; if (b->mod == NULL && (mod == NULL || b->decl == NULL) && fallback == NULL) fallback = b; } if (fallback) return fallback; } return NULL; } Sym * scope_define(Scope *s, const char *name, Skind k, Type *t, Node *decl) { return scope_define_in_module(s, name, NULL, k, t, decl); } /* * The duplicate-rejection key is (name, mod) rather than name alone. * This lets two imports each * register their own `stream` SK_TYPE in the flat scope, and lets the * primary register `stream` (mod=NULL) alongside imported `stream`s. * * Within a single (name, mod) pair the first registration wins; later * attempts return NULL and the caller emits a duplicate-type error. */ Sym * scope_define_in_module(Scope *s, const char *name, const char *mod, Skind k, Type *t, Node *decl) { u64 h = hashstr(name) % s->nbuckets; for (Sym *b = s->buckets[h]; b; b = b->hashnext) { if (strcmp(b->name, name) != 0) continue; if (b->mod == NULL && mod == NULL) return NULL; if (b->mod && mod && strcmp(b->mod, mod) == 0) return NULL; } Sym *sy = amalloc(s->a, sizeof *sy); sy->name = name; sy->mod = mod; sy->kind = k; sy->type = t; sy->decl = decl; sy->scope = s; sy->hashnext = s->buckets[h]; s->buckets[h] = sy; if (s->first == NULL) s->first = sy; else s->last->next = sy; s->last = sy; return sy; }