Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: anonymous functions, full patch



On Thu, 26 Jun 2008 18:28:53 +0100
Peter Stephenson <pws@xxxxxxx> wrote:
> > } Positional parameters from the surrounding area would be hidden and the
> > } local parameter list empty.  I could easily copy them in so that they
> > } could be used and modified without affecting the calling environment.
> > 
> > I'm of two minds on that score.  On the one hand it'd be nice to be able
> > to get at the surrounding $@.  On the other it may often be a needless
> > expense.
> 
> Indeed, though unless your parameter list is humongous it won't be a big
> effect.  On the other hand. humongous parameter lists are not unknown
> with things like zargs.

The way the internals work we would have to copy the list to pass down, but
we wouldn't have to copy the strings.  Anything cleverer (that makes a
significant difference) is quite a lot of work.  I've left this out for
now.

Here is a slightly more optimised patch with documentation and tests.
In principle I think it can be optimised more, but I don't think it's worth
it.

Index: Doc/Zsh/func.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/func.yo,v
retrieving revision 1.17
diff -u -r1.17 func.yo
--- Doc/Zsh/func.yo	10 Jun 2008 08:50:37 -0000	1.17
+++ Doc/Zsh/func.yo	30 Jun 2008 10:22:38 -0000
@@ -150,6 +150,42 @@
 
 example(autoload +X myfunc)
 
+sect(Anonymous Functions)
+cindex(anonymous functions)
+cindex(functions, anonymous)
+
+If no name is given for a function, it is `anonymous' and is handled
+specially.  Either form of function definition may be used: a `tt(())' with
+no preceding name, or a `tt(function)' with an immediately following open
+brace.  The function is executed immediately at the point of definition and
+is not stored for future use.  The function name is set to `tt((anon))' and
+the parameter list passed to the function is empty.  Note that this means
+the argument list of any enclosing script or function is hidden.
+Redirections may be applied to the anonymous function in the same manner as
+to a current-shell structure enclosed in braces.  The main use of anonymous
+functions is to provide a scope for local variables.  This is particularly
+convenient in start-up files as these do not provide their own local
+variable scope.
+
+For example,
+
+example(variable=outside
+function {
+  local variable=inside
+  print "I am $variable"
+}
+print "I am $variable")
+
+outputs the following:
+
+example(I am inside
+I am outside)
+
+Note that function definitions with arguments that expand to nothing,
+for example `tt(name=; function $name { )var(...)tt( })', are not
+treated as anonymous functions.  Instead, they are treated as normal
+function definitions where the definition is silently discarded.
+
 sect(Special Functions)
 Certain functions, if defined, have special meaning to the shell.
 
Index: Src/exec.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/exec.c,v
retrieving revision 1.132
diff -u -r1.132 exec.c
--- Src/exec.c	11 Jun 2008 09:27:55 -0000	1.132
+++ Src/exec.c	30 Jun 2008 10:22:39 -0000
@@ -3853,7 +3853,7 @@
 execfuncdef(Estate state, UNUSED(int do_exec))
 {
     Shfunc shf;
-    char *s;
+    char *s = NULL;
     int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0;
     Wordcode beg = state->pc, end;
     Eprog prog;
@@ -3861,10 +3861,7 @@
     LinkList names;
 
     end = beg + WC_FUNCDEF_SKIP(state->pc[-1]);
-    if (!(names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok))) {
-	state->pc = end;
-	return 0;
-    }
+    names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok);
     nprg = end - beg;
     sbeg = *state->pc++;
     nstrs = *state->pc++;
@@ -3874,21 +3871,32 @@
     plen = nprg * sizeof(wordcode);
     len = plen + (npats * sizeof(Patprog)) + nstrs;
 
-    if (htok)
+    if (htok && names)
 	execsubst(names);
 
-    while ((s = (char *) ugetnode(names))) {
-	prog = (Eprog) zalloc(sizeof(*prog));
+    while (!names || (s = (char *) ugetnode(names))) {
+	if (!names) {
+	    prog = (Eprog) zhalloc(sizeof(*prog));
+	    prog->nref = -1; /* on the heap */
+	} else {
+	    prog = (Eprog) zalloc(sizeof(*prog));
+	    prog->nref = 1; /* allocated from permanent storage */
+	}
 	prog->npats = npats;
-	prog->nref = 1; /* allocated from permanent storage */
 	prog->len = len;
-	if (state->prog->dump) {
-	    prog->flags = EF_MAP;
-	    incrdumpcount(state->prog->dump);
-	    prog->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog));
+	if (state->prog->dump || !names) {
+	    if (!names) {
+		prog->flags = EF_HEAP;
+		prog->dump = NULL;
+		prog->pats = pp = (Patprog *) zhalloc(npats * sizeof(Patprog));
+	    } else {
+		prog->flags = EF_MAP;
+		incrdumpcount(state->prog->dump);
+		prog->dump = state->prog->dump;
+		prog->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog));
+	    }
 	    prog->prog = state->pc;
 	    prog->strs = state->strs + sbeg;
-	    prog->dump = state->prog->dump;
 	} else {
 	    prog->flags = EF_REAL;
 	    prog->pats = pp = (Patprog *) zalloc(len);
@@ -3906,23 +3914,37 @@
 	shf->funcdef = prog;
 	shf->node.flags = 0;
 
-	/* is this shell function a signal trap? */
-	if (!strncmp(s, "TRAP", 4) &&
-	    (signum = getsignum(s + 4)) != -1) {
-	    if (settrap(signum, NULL, ZSIG_FUNC)) {
-		freeeprog(shf->funcdef);
-		zfree(shf, sizeof(*shf));
-		state->pc = end;
-		return 1;
-	    }
-
+	if (!names) {
 	    /*
-	     * Remove the old node explicitly in case it has
-	     * an alternative name
+	     * Anonymous function, execute immediately.
+	     * Function name is "(anon)", parameter list is empty.
 	     */
-	    removetrapnode(signum);
+	    LinkList args = newlinklist();
+
+	    shf->node.nam = "(anon)";
+	    addlinknode(args, shf->node.nam);
+
+	    execshfunc(shf, args);
+	    break;
+	} else {
+	    /* is this shell function a signal trap? */
+	    if (!strncmp(s, "TRAP", 4) &&
+		(signum = getsignum(s + 4)) != -1) {
+		if (settrap(signum, NULL, ZSIG_FUNC)) {
+		    freeeprog(shf->funcdef);
+		    zfree(shf, sizeof(*shf));
+		    state->pc = end;
+		    return 1;
+		}
+
+		/*
+		 * Remove the old node explicitly in case it has
+		 * an alternative name
+		 */
+		removetrapnode(signum);
+	    }
+	    shfunctab->addnode(shfunctab, ztrdup(s), shf);
 	}
-	shfunctab->addnode(shfunctab, ztrdup(s), shf);
     }
     state->pc = end;
     return 0;
Index: Test/C04funcdef.ztst
===================================================================
RCS file: /cvsroot/zsh/zsh/Test/C04funcdef.ztst,v
retrieving revision 1.3
diff -u -r1.3 C04funcdef.ztst
--- Test/C04funcdef.ztst	13 Jul 2007 22:28:00 -0000	1.3
+++ Test/C04funcdef.ztst	30 Jun 2008 10:22:39 -0000
@@ -99,3 +99,82 @@
 ?ThisCommandDoesNotExistEither
 ?has gone down the tubes.  Sorry.
 ?(eval):7: command not found: ThisCommandDoesNotExistEither
+
+  local variable=outside
+  print "I am $variable"
+  function {
+    local variable=inside
+    print "I am $variable"
+  }
+  print "I am $variable"
+  () {
+    local variable="inside again"
+    print "I am $variable"
+  }
+  print "I am $variable"
+0:Anonymous function scope
+>I am outside
+>I am inside
+>I am outside
+>I am inside again
+>I am outside
+
+  integer i
+  for (( i = 0; i < 10; i++ )); do function {
+    case $i in
+    ([13579])
+    print $i is odd
+    ;|
+    ([2468])
+    print $i is even
+    ;|
+    ([2357])
+    print $i is prime
+    ;;
+    esac
+  }; done
+0:Anonymous function with patterns in loop
+>1 is odd
+>2 is even
+>2 is prime
+>3 is odd
+>3 is prime
+>4 is even
+>5 is odd
+>5 is prime
+>6 is even
+>7 is odd
+>7 is prime
+>8 is even
+>9 is odd
+
+  echo stuff in file >file.in
+  function {
+    sed 's/stuff/rubbish/'
+  } <file.in >file.out
+  cat file.out
+0:Anonymous function redirection
+>rubbish in file
+
+  variable="Do be do"
+  print $variable
+  function {
+     print $variable
+     local variable="Da de da"
+     print $variable
+     function {
+       print $variable
+       local variable="Dum da dum"
+       print $variable
+     }
+     print $variable
+  }
+  print $variable
+0:Nested anonymous functions
+>Do be do
+>Do be do
+>Da de da
+>Da de da
+>Dum da dum
+>Da de da
+>Do be do

-- 
Peter Stephenson <pws@xxxxxxx>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070



Messages sorted by: Reverse Date, Date, Thread, Author