import "cffc.hff"; import "map.hff"; import "util.hff"; import "common.hff"; struct StringKeyTraits { fn hash(s *const u8) u32 { return fnv1a_s(FNV1A_INI, s); } fn eq(a *const u8, b *const u8) bool { return streq(a, b); } } struct Env { parent *Env, alloc *Allocator, decls Map<*const u8, *DeclList, StringKeyTraits>, } extern fn mkenv(parent *Env, alloc *Allocator) *Env { let env *Env = xmalloc(sizeof Env); *env = { parent, alloc }; return env; } extern fn envparent(env *Env) *Env { return env.parent; } extern fn envput_alloc(env *Env, alloc *Allocator, decl Decl, old **const Decl) *Decl { let l **DeclList = env.decls->get_slot(decl.name); let n *DeclList = anew(alloc, DeclList); if *l { // decl with this name exists let old = (*old = &(*l).decl); switch { case old.u.#tag == :Fn and decl.u.#tag == :Fn and decl.u.Fn.ty == old.u.Fn.ty and (old.u.Fn.body->empty() or decl.u.Fn.body->empty()); decl.u.Fn.id = old.u.Fn.id; case old.u.#tag == :Fn and decl.u.#tag == :Fn and memcmp(&old.u.Fn, &decl.u.Fn, sizeof(old.u.Fn)) == 0; case old.u.#tag == :Ty and decl.u.#tag == :Ty and old.u.Ty == decl.u.Ty; case old.u.#tag == :Def and decl.u.#tag == :Def and memcmp(&old.u.Def, &decl.u.Def, sizeof(old.u.Def)) == 0; case old.u.#tag == :Static and decl.u.#tag == :Static and decl.u.Static.ty == old.u.Static.ty and (old.u.Static.fwd or decl.u.Static.fwd); decl.u.Static.id = old.u.Static.id; case old.u.#tag == :Static and decl.u.#tag == :Static and memcmp(&old.u.Static, &decl.u.Static, sizeof(old.u.Static)) == 0; case old.u.#tag == :Macro and decl.u.#tag == :Macro and memcmp(&old.u.Macro, &decl.u.Macro, sizeof(old.u.Macro)) == 0; case decl.u.#tag == :Let; case env != old.myenv; case else; return #null; } } n.link = *l; n.decl = decl; n.decl.myenv = env; *l = n; return &n.decl; } extern fn envput(env *Env, decl Decl, old **const Decl) *Decl { return envput_alloc(env, env.alloc, decl, old); } extern fn envfind(env *Env, name *const u8) *Decl { let l **DeclList = env.decls->get(name); if l == #null { if env.parent == #null { return #null; } return envfind(env.parent, name); } let l = *l; assert(l != #null, "l?"); return &l.decl; } extern fn envfind_noparent(env *Env, name *const u8) *Decl { let l **DeclList = env.decls->get(name); return l ? &(*l).decl : #null; } extern fn envfree(env *Env) void { env.decls->clear(); free(env); }