zsh-workers
 help / color / mirror / code / Atom feed
From: Peter Stephenson <pws@csr.com>
To: zsh-workers@sunsite.dk (Zsh hackers list)
Subject: PATCH: anonymous functions, full patch
Date: Mon, 30 Jun 2008 11:27:22 +0100	[thread overview]
Message-ID: <20080630112722.3e7eb215@news01> (raw)
In-Reply-To: <200806261729.m5QHSr5F021201@news01.csr.com>

On Thu, 26 Jun 2008 18:28:53 +0100
Peter Stephenson <pws@csr.com> 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@csr.com>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


  reply	other threads:[~2008-06-30 10:27 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-06-26 13:51 PATCH: anonymous functions (no documentation yet) Peter Stephenson
2008-06-26 14:48 ` Oliver Kiddle
2008-06-26 15:06   ` Peter Stephenson
2008-06-26 15:32     ` Stephane Chazelas
2008-06-26 15:42       ` Peter Stephenson
2008-06-26 17:12     ` Bart Schaefer
2008-06-26 17:28       ` Peter Stephenson
2008-06-30 10:27         ` Peter Stephenson [this message]
2008-06-30 13:57           ` PATCH: anonymous functions, full patch Bart Schaefer
2008-06-30 15:04             ` Peter Stephenson
2008-07-01 17:43           ` Mikael Magnusson
2008-07-01 18:32             ` Peter Stephenson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20080630112722.3e7eb215@news01 \
    --to=pws@csr.com \
    --cc=zsh-workers@sunsite.dk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).