ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/mash/depends.b/
#
# 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;
}