From 950756d57943a0bf104d9f015b8da604171a956e Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 30 Jun 2008 10:37:11 +0000 Subject: 25255: add anonymous function capability --- ChangeLog | 5 ++++ Doc/Zsh/func.yo | 36 ++++++++++++++++++++++++ Src/exec.c | 78 ++++++++++++++++++++++++++++++++------------------- Test/C04funcdef.ztst | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 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 + + * 25255: Doc/Zsh/func.yo, Src/exec.c, Test/C04funcdef.ztst: + anonymous functions using "() { ... }" and "function { ... }". + 2008-06-24 Clint Adams * 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.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 -- cgit 1.4.1