shithub: purgatorio

ref: a411870ee4640241e3c494367d922847da84f972
dir: /appl/cmd/mash/symb.b/

View raw version
#
#	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");
}