diff options
Diffstat (limited to 'appl/cmd/mash/xeq.b')
| -rw-r--r-- | appl/cmd/mash/xeq.b | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/appl/cmd/mash/xeq.b b/appl/cmd/mash/xeq.b new file mode 100644 index 00000000..fd2f1e6f --- /dev/null +++ b/appl/cmd/mash/xeq.b @@ -0,0 +1,543 @@ +# +# Command execution. +# + +# +# Entry from parser. +# +Cmd.xeq(c: self ref Cmd, e: ref Env) +{ + if (e.flags & EDumping) { + s := c.text(); + f := e.outfile(); + f.puts(s); + if (s != nil && s[len s - 1] != '&') + f.putc(';'); + f.putc('\n'); + f.close(); + f = nil; + } + if ((e.flags & ENoxeq) == 0) + c.xeqit(e, 1); +} + +# +# Execute a command. Tail recursion. +# +Cmd.xeqit(c: self ref Cmd, e: ref Env, wait: int) +{ +tail: for (;;) { + if (c == nil) + return; + case c.op { + Csimple => + c.simple(e, wait); + Casync => + e = e.clone(); + e.in = e.devnull(); + e.wait = nil; + spawn c.left.xeqit(e, 1); + Cgroup => + if (c.redirs != nil) { + (ok, in, out) := mkredirs(e, c.redirs); + if (!ok) + return; + e = e.copy(); + e.in = in; + e.out = out; + c.left.xeqit(e, 1); + } else { + c = c.left; + continue tail; + } + Csubgroup => + e = e.clone(); + if (c.redirs != nil) { + (ok, in, out) := mkredirs(e, c.redirs); + if (!ok) + return; + e.in = in; + e.out = out; + } + c = c.left; + continue tail; + Cseq => + c.left.xeqit(e, 1); + c = c.right; + continue tail; + Cpipe => + do { + fds := e.pipe(); + if (fds == nil) + return; + n := e.clone(); + n.out = fds[0]; + c.left.xeqit(n, 0); + n = nil; + e = e.clone(); + e.in = fds[1]; + fds = nil; + c = c.right; + } while (c.op == Cpipe); + continue tail; + Cif => + t := c.left.truth(e); + if (c.right.op == Celse) { + if (t) + c.right.left.xeqit(e, wait); + else + c.right.right.xeqit(e, wait); + } else if (t) + c.right.xeqit(e, wait); + Celse => + panic("unexpected else"); + Cwhile => + while (c.left.truth(e)) + c.right.xeqit(e, wait); + Cfor => + (ok, l) := evalw(c.words, e); + if (!ok) + return; + s := c.item.word.text; + c = c.left; + while (l != nil) { + e.let(s, (hd l) :: nil); + c.xeqit(e, 1); + l = tl l; + } + Ccase => + (s1, l1) := c.left.eeval(e); + r := c.right; + while (r != nil) { + l := r.left; + (s2, l2) := l.left.eeval(e); + if (match2(e, s1, l1, s2, l2)) { + c = l.right; + continue tail; + } + r = r.right; + } + Ceq => + c.assign(e, 0); + Cdefeq => + c.assign(e, 1); + Cfn => + (s, nil, nil) := c.item.ieval(e); + if (!ident(s)) { + e.report("bad function name"); + return; + } + e.define(s, c.left); + Crescue => + e.report("rescue not implemented"); + Cdepend => + c.depend(e); + Crule => + c.rule(e); + * => + sys->print("number %d\n", c.op); + } return; } # tail recursion +} + +# +# Execute quote or backquote generator. Return generated item. +# +Cmd.quote(c: self ref Cmd, e: ref Env, back: int): ref Item +{ + e = e.copy(); + fds := e.pipe(); + if (fds == nil) + return nil; + e.out = fds[0]; + in := bufio->fopen(fds[1], Bufio->OREAD); + if (in == nil) + e.couldnot("fopen", "pipe"); + c.xeqit(e, 0); + fds = nil; + e = nil; + if (back) { + l: list of string; + while ((s := in.gets('\n')) != nil) { + (nil, r) := sys->tokenize(s, " \t\r\n"); + l = prepend(l, r); + } + return Item.iteml(revstrs(l)); + } else { + s := in.gets('\n'); + if (s != nil && s[len s - 1] == '\n') + s = s[:len s - 1]; + return Item.itemw(s); + } +} + +# +# Execute serve generator. +# +Cmd.serve(c: self ref Cmd, e: ref Env, write: int): ref Item +{ + e = e.clone(); + fds := e.pipe(); + if (fds == nil) + return nil; + if (write) + e.in = fds[0]; + else + e.out = fds[0]; + s := e.servefd(fds[1], write); + if (s == nil) + return nil; + c.xeqit(e, 0); + return Item.itemw(s); +} + +# +# Expression evaluation, first pass. +# Parse tree is copied and word items are evaluated. +# nil return for error is propagated. +# +Cmd.eeval1(c: self ref Cmd, e: ref Env): ref Cmd +{ + case c.op { + Cword => + l := c.item.ieval1(e); + if (l == nil) + return nil; + return Cmd.cmd1i(Cword, nil, l); + Chd or Ctl or Clen or Cnot => + l := c.left.eeval1(e); + if (l == nil) + return nil; + return Cmd.cmd1(c.op, l); + Ccaret or Ccons or Ceqeq or Cnoteq or Cmatch => + l := c.left.eeval1(e); + r := c.right.eeval1(e); + if (l == nil || r == nil) + return nil; + return Cmd.cmd2(c.op, l, r); + } + panic("expr1: bad op"); + return nil; +} + +# +# Expression evaluation, second pass. +# Returns a tuple (singleton, list, expand flag). +# +Cmd.eeval2(c: self ref Cmd, e: ref Env): (string, list of string, int) +{ + case c.op { + Cword => + return c.item.ieval2(e); + Clist => + return (nil, c.value, 0); + Ccaret => + (s1, l1, x1) := c.left.eeval2(e); + (s2, l2, x2) := c.right.eeval2(e); + return caret(s1, l1, x1, s2, l2, x2); + Chd => + (s, l, x) := c.left.eeval2(e); + if (s != nil) + return (s, nil, x); + if (l != nil) + return (hd l, nil, 0); + Ctl => + (s, l, nil) := c.left.eeval2(e); + if (s != nil) + break; + if (l != nil) + return (nil, tl l, 0); + Clen => + (s, l, nil) := c.left.eeval2(e); + if (s != nil) + return ("1", nil, 0); + return (string len l, nil, 0); + Cnot => + (s, l, nil) := c.left.eeval2(e); + if (s == nil && l == nil) + return (TRUE, nil, 0); + Ccons => + (s1, l1, nil) := c.left.eeval2(e); + (s2, l2, nil) := c.right.eeval2(e); + if (s1 != nil) { + if (s2 != nil) + return (nil, s1 :: s2 :: nil, 0); + if (l2 != nil) + return (nil, s1 :: l2, 0); + return (s1, nil, 0); + } else if (l1 != nil) { + if (s2 != nil) + return (nil, prepend(s2 :: nil, revstrs(l1)), 0); + if (l2 != nil) + return (nil, prepend(l2, revstrs(l1)), 0); + return (nil, l1, 0); + } else + return (s2, l2, 0); + Ceqeq => + if (c.evaleq(e)) + return (TRUE, nil, 0); + Cnoteq => + if (!c.evaleq(e)) + return (TRUE, nil, 0); + Cmatch => + if (c.evalmatch(e)) + return (TRUE, nil, 0); + * => + panic("expr2: bad op"); + } + return (nil, nil, 0); +} + +# +# Evaluate expression. 1st pass, 2nd pass, maybe glob. +# +Cmd.eeval(c: self ref Cmd, e: ref Env): (string, list of string) +{ + c = c.eeval1(e); + if (c == nil) + return (nil, nil); + (s, l, x) := c.eeval2(e); + if (x && s != nil) + (s, l) = glob(e, s); + return (s, l); +} + +# +# Assignment - let or set. +# +Cmd.assign(c: self ref Cmd, e: ref Env, def: int) +{ + i := c.item; + if (i == nil) + return; + (ok, v) := evalw(c.words, e); + if (!ok) + return; + s := c.item.word.text; + if (def) + e.let(s, v); + else + e.set(s, v); +} + +# +# Evaluate command and test for non-empty. +# +Cmd.truth(c: self ref Cmd, e: ref Env): int +{ + (s, l) := c.eeval(e); + return s != nil || l != nil; +} + +# +# Evaluate word. +# +evalw(l: list of ref Item, e: ref Env): (int, list of string) +{ + if (l == nil) + return (1, nil); + w := pass1(e, l); + if (w == nil) + return (0, nil); + return (1, pass2(e, w)); +} + +# +# Evaluate list of items, pass 1 - reverses. +# +pass1(e: ref Env, l: list of ref Item): list of ref Item +{ + r: list of ref Item; + while (l != nil) { + i := (hd l).ieval1(e); + if (i == nil) + return nil; + r = i :: r; + l = tl l; + } + return r; +} + +# +# Evaluate list of items, pass 2 with globbing - reverses (restores order). +# +pass2(e: ref Env, l: list of ref Item): list of string +{ + r: list of string; + while (l != nil) { + (s, t, x) := (hd l).ieval2(e); + if (x && s != nil) + (s, t) = glob(e, s); + if (s != nil) + r = s :: r; + else if (t != nil) + r = prepend(r, revstrs(t)); + l = tl l; + } + return r; +} + +# +# Simple command. Maybe a function. +# +Cmd.simple(c: self ref Cmd, e: ref Env, wait: int) +{ + w := pass1(e, c.words); + if (w == nil) + return; + s := pass2(e, w); + if (s == nil) + return; + if (e.flags & EEcho) + echo(e, s); + (ok, in, out) := mkredirs(e, c.redirs); + if (ok) + e.runit(s, in, out, wait); +} + +# +# Cmd name and arglist. Maybe a function. +# +Env.runit(e: self ref Env, s: list of string, in, out: ref Sys->FD, wait: int) +{ + d := e.func(hd s); + if (d != nil) { + if (e.level >= MAXELEV) { + e.report(hd s + ": function nesting too deep"); + return; + } + e = e.copy(); + e.level++; + e.in = in; + e.out = out; + e.local = Stab.new(); + e.local.assign(ARGS, tl s); + d.xeqit(e, wait); + } else + exec(s, e, in, out, wait); +} + +# +# Item evaluation, first pass. Copy parse tree. Expand variables. +# Call first pass of expression evaluation. Execute generators. +# +Item.ieval1(i: self ref Item, e: ref Env): ref Item +{ + if (i == nil) + return nil; + case i.op { + Icaret or Iicaret => + l := i.left.ieval1(e); + r := i.right.ieval1(e); + if (l == nil || r == nil) + return nil; + return Item.item2(i.op, l, r); + Idollar or Idollarq=> + s := e.dollar(i.word.text); + if (s == nil) { + e.undefined(i.word.text); + return nil; + } + if (s.value == empty) + return Item.itemw(nil); + if (i.op == Idollar) + return Item.iteml(s.value); + else + return Item.itemw(concat(s.value)); + Iword or Imatch => + return i; + Iexpr => + l := i.cmd.eeval1(e); + if (l == nil) + return nil; + return Item.itemc(Iexpr, l); + Ibackq => + return i.cmd.quote(e, 1); + Iquote => + return i.cmd.quote(e, 0); + Iinpipe => + return i.cmd.serve(e, 0); + Ioutpipe => + return i.cmd.serve(e, 1); + } + panic("ieval1: bad op"); + return nil; +} + +# +# Item evaluation, second pass. Outer level carets. Expand matches. +# Call second pass of expression evaluation. +# +Item.ieval2(i: self ref Item, e: ref Env): (string, list of string, int) +{ + case i.op { + Icaret or Iicaret => + return i.caret(e); + Imatch => + return (e.arg(i.word.text), nil, 0); + Idollar or Idollarq => + panic("ieval2: unexpected $"); + Iword => + return (i.word.text, nil, i.word.flags & Wexpand); + Iexpr => + return i.cmd.eeval2(e); + Ibackq or Iinpipe or Ioutpipe => + panic("ieval2: unexpected generator"); + } + panic("ieval2: bad op"); + return (nil, nil, 0); +} + +# +# Item evaluation. +# +Item.ieval(i: self ref Item, e: ref Env): (string, list of string, int) +{ + i = i.ieval1(e); + if (i == nil) + return (nil, nil, 0); + return i.ieval2(e); +} + +# +# Redirection item evaluation. +# +Item.reval(i: self ref Item, e: ref Env): (int, string) +{ + (s, l, nil) := i.ieval(e); + if (s == nil) { + if (l == nil) + e.report("null redirect"); + else + e.report("list for redirect"); + return (0, nil); + } + return (1, s); +} + +# +# Make redirection names. +# +mkrdnames(e: ref Env, l: list of ref Redir): (int, array of string) +{ + f := array[Rcount] of string; + while (l != nil) { + r := hd l; + (ok, s) := r.word.reval(e); + if (!ok) + return (0, nil); + f[r.op] = s; + l = tl l; + } + return (1, f); +} + +# +# Perform redirections. +# +mkredirs(e: ref Env, l: list of ref Redir): (int, ref Sys->FD, ref Sys->FD) +{ + (ok, f) := mkrdnames(e, l); + if (!ok) + return (0, nil, nil); + return redirect(e, f, e.in, e.out); +} |
