code: 9ferno

ref: 6bfe4cf8107de7a01ff8a0667db7f28f08e4d238
dir: /appl/cmd/mash/depends.b/

View raw version
#
#	Dependency/rule routines.
#

DHASH:	con 127;	# dephash size

#
#	Initialize.  "make -clear" calls this.
#
initdep()
{
	dephash = array[DHASH] of list of ref Target;
	rules = nil;
}

#
#	Lookup a target in dephash, maybe add it.
#
target(s: string, insert: int): ref Target
{
	h := hash->fun1(s, DHASH);
	l := dephash[h];
	while (l != nil) {
		if ((hd l).target == s)
			return hd l;
		l = tl l;
	}
	if (!insert)
		return nil;
	t := ref Target(s, nil);
	dephash[h] = t :: dephash[h];
	return t;
}

adddep(s: string, d: ref Depend)
{
	t := target(s, 1);
	t.depends = d :: t.depends;
}

#
#	Dependency (:) command.
#	Evaluate lhs and rhs, make dependency, and add to the targets.
#
Cmd.depend(c: self ref Cmd, e: ref Env)
{
	if ((e.flags & ETop) == 0) {
		e.report("dependency not at top level");
		return;
	}
	if (dephash == nil)
		initdep();
	w := pass1(e, c.words);
	if (w == nil)
		return;
	l := pass2(e, w);
	if (l == nil)
		return;
	r: list of string;
	if (c.left.words != nil) {
		w = pass1(e, c.left.words);
		if (w == nil)
			return;
		r = pass2(e, w);
		if (r == nil)
			return;
	}
	d := ref Depend(l, r, c.left.op, c.left.left, 0);
	while (l != nil) {
		adddep(hd l, d);
		l = tl l;
	}
}

#
#	Evaluate rule lhs and break into path components.
#
rulelhs(e: ref Env, i: ref Item): ref Lhs
{
	i = i.ieval1(e);
	if (i == nil)
		return nil;
	(s, l, nil) := i.ieval2(e);
	if (l != nil) {
		e.report("rule pattern evaluates to a list");
		return nil;
	}
	if (s == nil) {
		e.report("rule pattern evaluates to nil");
		return nil;
	}
	(n, p) := sys->tokenize(s, "/");
	return ref Lhs(s, p, n);
}

#
#	Rule (:~) command.
#	First pass of rhs evaluation is done here.
#
Cmd.rule(c: self ref Cmd, e: ref Env)
{
	if (e.flags & ETop) {
		l := rulelhs(e, c.item);
		if (l == nil)
			return;
		r := c.left.item.ieval1(e);
		if (r == nil)
			return;
		rules = ref Rule(l, r, c.left.op, c.left.left) :: rules;
	} else
		e.report("rule not at top level");
}

Target.find(s: string): ref Target
{
	if (dephash == nil)
		return nil;
	return target(s, 0);
}

#
#	Match a path element.
#
matchelem(p, s: string): int
{
	m := len p;
	n := len s;
	if (m == n && p == s)
		return 1;
	for (i := 0; i < m; i++) {
		if (p[i] == '*') {
			j := i + 1;
			if (j == m)
				return 1;
			q := p[j:];
			do {
				if (matchelem(q, s[i:]))
					return 1;
			} while (++i < n);
			return 0;
		} else if (i >= n || p[i] != s[i])
			return 0;
	}
	return 0;
}

#
#	Match a path element and return a list of sub-matches.
#
matches(p, s: string): (int, list of string)
{
	m := len p;
	n := len s;
	for (i := 0; i < m; i++) {
		if (p[i] == '*') {
			j := i + 1;
			if (j == m)
				return (1, s[i:] :: nil);
			q := p[j:];
			do {
				(r, l) := matches(q, s[i:]);
				if (r)
					return (1, s[j - 1: i] :: l);
			} while (++i < n);
			return (0, nil);
		} else if (i >= n || p[i] != s[i])
			return (0, nil);
	}
	return (m == n, nil);
}

#
#	Rule match.
#
Rule.match(r: self ref Rule, a, n: int, t: list of string): int
{
	l := r.lhs;
	if (l.count != n || (l.text[0] == '/') != a)
		return 0;
	for (e := l.elems; e != nil; e = tl e) {
		if (!matchelem(hd e, hd t))
			return 0;
		t = tl t;
	}
	return 1;
}

#
#	Rule match with array of sub-matches.
#
Rule.matches(r: self ref Rule, t: list of string): array of string
{
	m: list of list of string;
	c := 1;
	for (e := r.lhs.elems; e != nil; e = tl e) {
		(x, l) := matches(hd e, hd t);
		if (!x)
			return nil;
		if (l != nil) {
			c += len l;
			m = revstrs(l) :: m;
		}
		t = tl t;
	}
	a := array[c] of string;
	while (m != nil) {
		for (l := hd m; l != nil; l = tl l)
			a[--c] = hd l;
		m = tl m;
	}
	return a;
}

#
#	Return list of rules that match a string.
#
rulematch(s: string): list of ref Rule
{
	m: list of ref Rule;
	a := s[0] == '/';
	(n, t) := sys->tokenize(s, "/");
	for (l := rules; l != nil; l = tl l) {
		r := hd l;
		if (r.match(a, n, t))
			m = r :: m;
	}
	return m;
}