aboutsummaryrefslogtreecommitdiff
path: root/src/env.cff
blob: 1c525b981d6634cadd6f84c813099b898e3dafdf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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);
}