diff options
Diffstat (limited to 'appl/cmd/mash/symb.b')
| -rw-r--r-- | appl/cmd/mash/symb.b | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/appl/cmd/mash/symb.b b/appl/cmd/mash/symb.b new file mode 100644 index 00000000..8d317b37 --- /dev/null +++ b/appl/cmd/mash/symb.b @@ -0,0 +1,265 @@ +# +# Symbol table routines. A symbol table becomes copy-on-write +# when it is cloned. The first modification will copy the hash table. +# Every list is then copied on first modification. +# + +# +# Copy a hash list. +# +cpsymbs(l: list of ref Symb): list of ref Symb +{ + r: list of ref Symb; + while (l != nil) { + r = (ref *hd l) :: r; + l = tl l; + } + return r; +} + +# +# New symbol table. +# +Stab.new(): ref Stab +{ + return ref Stab(array[SHASH] of list of ref Symb, 0, 0); +} + +# +# Clone a symbol table. Copy Stab and mark contents copy-on-write. +# +Stab.clone(t: self ref Stab): ref Stab +{ + t.copy = 1; + t.wmask = SMASK; + return ref *t; +} + +# +# Update symbol table entry, or add new entry. +# +Stab.update(t: self ref Stab, s: string, tag: int, v: list of string, f: ref Cmd, b: Mashbuiltin): ref Symb +{ + if (t.copy) { + a := array[SHASH] of list of ref Symb; + a[:] = t.tab[:]; + t.tab = a; + t.copy = 0; + } + x := hash->fun1(s, SHASH); + l := t.tab[x]; + if (t.wmask & (1 << x)) { + l = cpsymbs(l); + t.tab[x] = l; + t.wmask &= ~(1 << x); + } + r := l; + while (r != nil) { + h := hd r; + if (h.name == s) { + case tag { + Svalue => + h.value = v; + Sfunc => + h.func = f; + Sbuiltin => + h.builtin = b; + } + return h; + } + r = tl r; + } + n := ref Symb(s, v, f, b, 0); + t.tab[x] = n :: l; + return n; +} + +# +# Make a list of a symbol table's contents. +# +Stab.all(t: self ref Stab): list of ref Symb +{ + r: list of ref Symb; + for (i := 0; i < SHASH; i++) { + for (l := t.tab[i]; l != nil; l = tl l) + r = (ref *hd l) :: r; + } + return r; +} + +# +# Assign a list of strings to a variable. The distinguished value +# "empty" is used to distinguish nil value from undefined. +# +Stab.assign(t: self ref Stab, s: string, v: list of string) +{ + if (v == nil) + v = empty; + t.update(s, Svalue, v, nil, nil); +} + +# +# Define a builtin. +# +Stab.defbuiltin(t: self ref Stab, s: string, b: Mashbuiltin) +{ + t.update(s, Sbuiltin, nil, nil, b); +} + +# +# Define a function. +# +Stab.define(t: self ref Stab, s: string, f: ref Cmd) +{ + t.update(s, Sfunc, nil, f, nil); +} + +# +# Symbol table lookup. +# +Stab.find(t: self ref Stab, s: string): ref Symb +{ + l := t.tab[hash->fun1(s, SHASH)]; + while (l != nil) { + h := hd l; + if (h.name == s) + return h; + l = tl l; + } + return nil; +} + +# +# Function lookup. +# +Stab.func(t: self ref Stab, s: string): ref Cmd +{ + v := t.find(s); + if (v == nil) + return nil; + return v.func; +} + +# +# New environment. +# +Env.new(): ref Env +{ + return ref Env(Stab.new(), nil, ETop, nil, nil, nil, nil, nil, nil, 0); +} + +# +# Clone environment. No longer top-level or interactive. +# +Env.clone(e: self ref Env): ref Env +{ + e = e.copy(); + e.flags &= ~(ETop | EInter); + e.global = e.global.clone(); + if (e.local != nil) + e.local = e.local.clone(); + return e; +} + +# +# Copy environment. +# +Env.copy(e: self ref Env): ref Env +{ + return ref *e; +} + +# +# Fetch $n argument. +# +Env.arg(e: self ref Env, s: string): string +{ + n := int s; + if (e.args == nil || n >= len e.args) + return "$" + s; + else + return e.args[n]; +} + +# +# Lookup builtin. +# +Env.builtin(e: self ref Env, s: string): Mashbuiltin +{ + v := e.global.find(s); + if (v == nil) + return nil; + return v.builtin; +} + +# +# Define a builtin. +# +Env.defbuiltin(e: self ref Env, s: string, b: Mashbuiltin) +{ + e.global.defbuiltin(s, b); +} + +# +# Define a function. +# +Env.define(e: self ref Env, s: string, f: ref Cmd) +{ + e.global.define(s, f); +} + +# +# Value of a shell variable (check locals then globals). +# +Env.dollar(e: self ref Env, s: string): ref Symb +{ + if (e.local != nil) { + l := e.local.find(s); + if (l != nil && l.value != nil) + return l; + } + g := e.global.find(s); + if (g != nil && g.value != nil) + return g; + return nil; +} + +# +# Lookup a function. +# +Env.func(e: self ref Env, s: string): ref Cmd +{ + v := e.global.find(s); + if (v == nil) + return nil; + return v.func; +} + +# +# Local assignment. +# +Env.let(e: self ref Env, s: string, v: list of string) +{ + if (e.local == nil) + e.local = Stab.new(); + e.local.assign(s, v); +} + +# +# Assignment. Update local or define global. +# +Env.set(e: self ref Env, s: string, v: list of string) +{ + if (e.local != nil && e.local.find(s) != nil) + e.local.assign(s, v); + else + e.global.assign(s, v); +} + +# +# Report undefined. +# +Env.undefined(e: self ref Env, s: string) +{ + e.report(s + ": undefined"); +} |
