summaryrefslogtreecommitdiff
path: root/appl/cmd/mash/depends.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/mash/depends.b')
-rw-r--r--appl/cmd/mash/depends.b228
1 files changed, 228 insertions, 0 deletions
diff --git a/appl/cmd/mash/depends.b b/appl/cmd/mash/depends.b
new file mode 100644
index 00000000..dfb14ef5
--- /dev/null
+++ b/appl/cmd/mash/depends.b
@@ -0,0 +1,228 @@
+#
+# 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;
+}