summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2008-06-30 10:37:11 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2008-06-30 10:37:11 +0000
commit950756d57943a0bf104d9f015b8da604171a956e (patch)
treec1555284c1e0009d89f9fe70f5757ee1e994b43c
parentd09e51d6090df6e7c052d48543dcbd32af1a986f (diff)
downloadzsh-950756d57943a0bf104d9f015b8da604171a956e.tar.gz
zsh-950756d57943a0bf104d9f015b8da604171a956e.zip
25255: add anonymous function capability
-rw-r--r--ChangeLog5
-rw-r--r--Doc/Zsh/func.yo36
-rw-r--r--Src/exec.c78
-rw-r--r--Test/C04funcdef.ztst79
4 files changed, 170 insertions, 28 deletions
diff --git a/ChangeLog b/ChangeLog
index a5d5bd9eb..7a49bf6ce 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2008-06-30 Peter Stephenson <pws@csr.com>
+
+ * 25255: Doc/Zsh/func.yo, Src/exec.c, Test/C04funcdef.ztst:
+ anonymous functions using "() { ... }" and "function { ... }".
+
2008-06-24 Clint Adams <clint@zsh.org>
* 25243: Completion/X/Command/_xrandr: patch from Chris Lamb to add
diff --git a/Doc/Zsh/func.yo b/Doc/Zsh/func.yo
index 6b6202831..65bcd9abc 100644
--- a/Doc/Zsh/func.yo
+++ b/Doc/Zsh/func.yo
@@ -150,6 +150,42 @@ executing tt(myfunc), use:
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.
diff --git a/Src/exec.c b/Src/exec.c
index 74f389a8d..7ce657032 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -3853,7 +3853,7 @@ static int
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 @@ execfuncdef(Estate state, UNUSED(int do_exec))
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 @@ execfuncdef(Estate state, UNUSED(int do_exec))
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 @@ execfuncdef(Estate state, UNUSED(int do_exec))
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;
diff --git a/Test/C04funcdef.ztst b/Test/C04funcdef.ztst
index d297ea418..69b09686a 100644
--- a/Test/C04funcdef.ztst
+++ b/Test/C04funcdef.ztst
@@ -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