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

PATCH: anonymous functions (no documentation yet)



I just spotted that it's very easy to add "anonymous" functions, which
are defined using normal function syntax, but executed immediately.
This creates a local variable context:

  (){
    local variable="no great"
    print "In anonymous function with $variable value";
  }

executes immediately but "variable" does not remain set.  The most
obvious use of this would be in initialization files: you get a local
variable scope that guarantees not to pollute the function table.

Currently the code gets copied as if for use in the shell structure,
but I can probably optimise that out.  This would make it slightly less
efficient than a normal "{ ... }", but not much.

This syntax has always been accepted, oddly, but the code used simply to
be ignored.  This is a syntax error in other shells (at least bash, ksh
88, pdksh, Solaris 8 sh), so there's no clash.  (If you want protection
against triggering advanced features you have come to completely the
wrong shell anyway.)

Note this does not affect cases like

  $emptyvariable() { ... }

since the patch requires that there be nothing before the () even before
expansion.  So you're protected against accidental execution.  (Should
this be a syntax error?  I would think it probably should.  This is a
separate issue, however.)

It turns out the same patch works for "function { ... }", too; same
protection against empty expansions; also a syntax error in other
shells.

Is there any interest in pursuing this?  (Note that, although I am open
to suggestions, I am quite definitely not asking "is there any interest
in pursuing a dozen more complicated additions I have no time to
write".)

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	26 Jun 2008 13:31:10 -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,10 +3871,10 @@
     plen = nprg * sizeof(wordcode);
     len = plen + (npats * sizeof(Patprog)) + nstrs;
 
-    if (htok)
+    if (htok && names)
 	execsubst(names);
 
-    while ((s = (char *) ugetnode(names))) {
+    while (!names || (s = (char *) ugetnode(names))) {
 	prog = (Eprog) zalloc(sizeof(*prog));
 	prog->npats = npats;
 	prog->nref = 1; /* allocated from permanent storage */
@@ -3906,23 +3903,38 @@
 	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.
 	     */
-	    removetrapnode(signum);
+	    LinkList args = newlinklist();
+
+	    shf->node.nam = "(anon)";
+	    addlinknode(args, shf->node.nam);
+	    execshfunc(shf, args);
+
+	    freeeprog(shf->funcdef);
+	    zfree(shf, sizeof(struct shfunc));
+	    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;

-- 
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