From 6062529d3fc7c7d29c63d0726d2449d4b56f33ac Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 19 Jun 2011 20:12:00 +0000 Subject: 29492: add argument handling to anonymous functions --- ChangeLog | 5 +++- Doc/Zsh/func.yo | 14 +++++++---- Src/exec.c | 65 ++++++++++++++++++++++++++++++---------------------- Src/parse.c | 30 ++++++++++++++++++++++-- Src/text.c | 30 +++++++++++++++++++++--- Test/C04funcdef.ztst | 29 ++++++++++++++++++++++- 6 files changed, 133 insertions(+), 40 deletions(-) diff --git a/ChangeLog b/ChangeLog index 446bff14b..8eca2d9cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2011-06-19 Peter Stephenson + * 29492: Doc/Zsh/func.yo, Src/exec.c, Src/parse.c, Src/text.c, + Test/C04funcdef.ztst: add argument handling to anonymous functions. + * unposted: Src/Zle/zle_refresh.c: remove additional loop noticed by Mikael. @@ -15019,5 +15022,5 @@ ***************************************************** * This is used by the shell to define $ZSH_PATCHLEVEL -* $Revision: 1.5375 $ +* $Revision: 1.5376 $ ***************************************************** diff --git a/Doc/Zsh/func.yo b/Doc/Zsh/func.yo index 28bc6329a..89b956cb4 100644 --- a/Doc/Zsh/func.yo +++ b/Doc/Zsh/func.yo @@ -158,9 +158,13 @@ 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 +is not stored for future use. The function name is set to `tt((anon))'. + +Arguments to the function may be specified as words following the +closing brace defining the function, hence if there are none no +arguments (other than tt($0)) are set. 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 @@ -172,13 +176,13 @@ For example, example(variable=outside function { local variable=inside - print "I am $variable" -} + print "I am $variable with arguments $*" +} this and that print "I am $variable") outputs the following: -example(I am inside +example(I am inside with arguments this and that I am outside) Note that function definitions with arguments that expand to nothing, diff --git a/Src/exec.c b/Src/exec.c index 2558185c8..87a167ba6 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -50,20 +50,20 @@ int noerrexit; * noerrs = 1: suppress error messages * noerrs = 2: don't set errflag on parse error, either */ - + /**/ mod_export int noerrs; - + /* do not save history on exec and exit */ /**/ int nohistsave; - + /* error/break flag */ - + /**/ mod_export int errflag; - + /* * State of trap return value. Value is from enum trap_state. */ @@ -88,23 +88,23 @@ int trap_state; * - non-negative in a trap once it was triggered. It should remain * non-negative until restored after execution of the trap. */ - + /**/ int trap_return; - + /* != 0 if this is a subshell */ - + /**/ int subsh; - + /* != 0 if we have a return pending */ - + /**/ mod_export int retflag; /**/ long lastval2; - + /* The table of file descriptors. A table element is zero if the * * corresponding fd is not used by the shell. It is greater than * * 1 if the fd is used by a <(...) or >(...) substitution and 1 if * @@ -148,12 +148,12 @@ int fdtable_flocks; mod_export int zleactive; /* pid of process undergoing 'process substitution' */ - + /**/ pid_t cmdoutpid; - + /* exit status of process undergoing 'process substitution' */ - + /**/ int cmdoutval; @@ -166,7 +166,7 @@ int cmdoutval; /**/ int use_cmdoutval; -/* The context in which a shell function is called, see SFC_* in zsh.h. */ +/* The context in which a shell function is called, see SFC_* in zsh.h. */ /**/ mod_export int sfcontext; @@ -239,7 +239,7 @@ parse_string(char *s, int reset_lineno) /**/ mod_export struct rlimit current_limits[RLIM_NLIMITS], limits[RLIM_NLIMITS]; - + /**/ mod_export int zsetlimit(int limnum, char *nam) @@ -340,7 +340,7 @@ zfork(struct timeval *tv) * * (when waiting for the grep, ignoring execpline2 for now). At this time, * zsh has built two job-table entries for it: one for the cat and one for - * the grep. If the user hits ^Z at this point (and jobbing is used), the + * the grep. If the user hits ^Z at this point (and jobbing is used), the * shell is notified that the grep was suspended. The list_pipe flag is * used to tell the execpline where it was waiting that it was in a pipeline * with a shell construct at the end (which may also be a shell function or @@ -351,7 +351,7 @@ zfork(struct timeval *tv) * shell (its pid and the text for it) in the job entry of the cat. The pid * is passed down in the list_pipe_pid variable. * But there is a problem: the suspended grep is a child of the parent shell - * and can't be adopted by the sub-shell. So the parent shell also has to + * and can't be adopted by the sub-shell. So the parent shell also has to * keep the information about this process (more precisely: this pipeline) * by keeping the job table entry it created for it. The fact that there * are two jobs which have to be treated together is remembered by setting @@ -528,10 +528,10 @@ isgooderr(int e, char *dir) { /* * Maybe the directory was unreadable, or maybe it wasn't - * even a directory. + * even a directory. */ return ((e != EACCES || !access(dir, X_OK)) && - e != ENOENT && e != ENOTDIR); + e != ENOENT && e != ENOTDIR); } /* @@ -639,7 +639,7 @@ execute(LinkList args, int flags, int defpath) break; } - /* for command -p, search the default path */ + /* for command -p, search the default path */ if (defpath) { char *s, pbuf[PATH_MAX]; char *dptr, *pe, *ps = DEFAULT_PATH; @@ -676,7 +676,7 @@ execute(LinkList args, int flags, int defpath) eno = ee; } else { - + if ((cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0))) { char nn[PATH_MAX], *dptr; @@ -1312,9 +1312,9 @@ sublist_done: donetrap = 1; } if (lastval) { - int errreturn = isset(ERRRETURN) && + int errreturn = isset(ERRRETURN) && (isset(INTERACTIVE) || locallevel || sourcelevel); - int errexit = isset(ERREXIT) || + int errexit = isset(ERREXIT) || (isset(ERRRETURN) && !errreturn); if (errexit) { if (sigtrapped[SIGEXIT]) @@ -1536,7 +1536,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) else if (pid) { char dummy; - lpforked = + lpforked = (killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1); list_pipe_pid = pid; list_pipe_start = bgtime; @@ -3112,7 +3112,7 @@ execcmd(Estate state, int input, int output, int how, int last1) ESUB_PGRP | ESUB_FAKE; if (type != WC_SUBSH) flags |= ESUB_KEEPTRAP; - if ((do_exec || (type >= WC_CURSH && last1 == 1)) + if ((do_exec || (type >= WC_CURSH && last1 == 1)) && !forked) flags |= ESUB_REVERTPGRP; entersubsh(flags); @@ -4184,10 +4184,19 @@ execfuncdef(Estate state, UNUSED(int do_exec)) * Anonymous function, execute immediately. * Function name is "(anon)", parameter list is empty. */ - LinkList args = newlinklist(); + LinkList args; + + state->pc = end; + end += *state->pc++; + args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok); + + if (htok && args) + execsubst(args); + if (!args) + args = newlinklist(); shf->node.nam = "(anon)"; - addlinknode(args, shf->node.nam); + pushnode(args, shf->node.nam); execshfunc(shf, args); ret = lastval; diff --git a/Src/parse.c b/Src/parse.c index 4720dc3cf..5b6f09949 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -1480,12 +1480,25 @@ par_funcdef(void) ecbuf[p + num + 4] = ecnpats; ecbuf[p + 1] = num; - lineno += oldlineno; ecnpats = onp; ecssub = oecssub; ecnfunc++; ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); + + if (num == 0) { + /* Unnamed function */ + int parg = ecadd(0); + ecadd(0); + while (tok == STRING) { + ecstr(tokstr); + num++; + zshlex(); + } + ecbuf[parg] = ecused - parg; /*?*/ + ecbuf[parg+1] = num; + } + lineno += oldlineno; } /* @@ -1730,13 +1743,26 @@ par_simple(int *complex, int nr) ecbuf[p + argc + 3] = ecsoffs - so; ecbuf[p + argc + 4] = ecnpats; - lineno += oldlineno; ecnpats = onp; ecssub = oecssub; ecnfunc++; ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); + if (argc == 0) { + /* Unnamed function */ + int parg = ecadd(0); + ecadd(0); + while (tok == STRING) { + ecstr(tokstr); + argc++; + zshlex(); + } + ecbuf[parg] = ecused - parg; /*?*/ + ecbuf[parg+1] = argc; + } + lineno += oldlineno; + isfunc = 1; isnull = 0; break; diff --git a/Src/text.c b/Src/text.c index 669037a2d..f55553ed0 100644 --- a/Src/text.c +++ b/Src/text.c @@ -253,6 +253,7 @@ struct tstack { struct { char *strs; Wordcode end; + int nargs; } _funcdef; struct { Wordcode end; @@ -456,19 +457,31 @@ gettext2(Estate state) if (!s) { Wordcode p = state->pc; Wordcode end = p + WC_FUNCDEF_SKIP(code); + int nargs = *state->pc++; - taddlist(state, *state->pc++); + taddlist(state, nargs); + if (nargs) + taddstr(" "); if (tjob) { - taddstr(" () { ... }"); + taddstr("() { ... }"); state->pc = end; + if (!nargs) { + /* + * Unnamed fucntion. + * We're not going to pull any arguments off + * later, so skip them now... + */ + state->pc += *end; + } stack = 1; } else { - taddstr(" () {"); + taddstr("() {"); tindent++; taddnl(1); n = tpush(code, 1); n->u._funcdef.strs = state->strs; n->u._funcdef.end = end; + n->u._funcdef.nargs = nargs; state->strs += *state->pc; state->pc += 3; } @@ -478,6 +491,17 @@ gettext2(Estate state) dec_tindent(); taddnl(0); taddstr("}"); + if (s->u._funcdef.nargs == 0) { + /* Unnamed function with post-arguments */ + int nargs; + s->u._funcdef.end += *state->pc++; + nargs = *state->pc++; + if (nargs) { + taddstr(" "); + taddlist(state, nargs); + } + state->pc = s->u._funcdef.end; + } stack = 1; } break; diff --git a/Test/C04funcdef.ztst b/Test/C04funcdef.ztst index f71e5ce77..0cc5e5a2f 100644 --- a/Test/C04funcdef.ztst +++ b/Test/C04funcdef.ztst @@ -26,7 +26,7 @@ print regress expansion of function names } f$$ -0:Regression test: `function f$$ () { ... }' +0:Regression test: 'function f$$ () { ... }' >regress expansion of function names function foo () print bar @@ -109,6 +109,8 @@ >really useful >args +# ' deconfuse emacs + command_not_found_handler() { print "Your command:" >&2 print "$1" >&2 @@ -201,6 +203,31 @@ >Da de da >Do be do + () { print This has arguments $*; } of all sorts; print After the function + function { print More stuff $*; } and why not; print Yet more +0:Anonymous function with arguments +>This has arguments of all sorts +>After the function +>More stuff and why not +>Yet more + + fn() { + (){ print Anonymous function 1 $*; } with args + function { print Anonymous function 2 $*; } with more args + print Following bit + } + functions fn +0:Text representation of anonymous function with arguments +>fn () { +> () { +> print Anonymous function 1 $* +> } with args +> () { +> print Anonymous function 2 $* +> } with more args +> print Following bit +>} + %clean rm -f file.in file.out -- cgit 1.4.1