1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-09-30 07:10:58 +02:00

29492: add argument handling to anonymous functions

This commit is contained in:
Peter Stephenson 2011-06-19 20:12:00 +00:00
parent 437d5d98f6
commit 6062529d3f
6 changed files with 133 additions and 40 deletions

View file

@ -1,5 +1,8 @@
2011-06-19 Peter Stephenson <p.w.stephenson@ntlworld.com> 2011-06-19 Peter Stephenson <p.w.stephenson@ntlworld.com>
* 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 * unposted: Src/Zle/zle_refresh.c: remove additional loop
noticed by Mikael. noticed by Mikael.
@ -15019,5 +15022,5 @@
***************************************************** *****************************************************
* This is used by the shell to define $ZSH_PATCHLEVEL * This is used by the shell to define $ZSH_PATCHLEVEL
* $Revision: 1.5375 $ * $Revision: 1.5376 $
***************************************************** *****************************************************

View file

@ -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 specially. Either form of function definition may be used: a `tt(())' with
no preceding name, or a `tt(function)' with an immediately following open no preceding name, or a `tt(function)' with an immediately following open
brace. The function is executed immediately at the point of definition and 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 is not stored for future use. The function name is set to `tt((anon))'.
the parameter list passed to the function is empty. Note that this means
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. the argument list of any enclosing script or function is hidden.
Redirections may be applied to the anonymous function in the same manner as 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 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 functions is to provide a scope for local variables. This is particularly
@ -172,13 +176,13 @@ For example,
example(variable=outside example(variable=outside
function { function {
local variable=inside local variable=inside
print "I am $variable" print "I am $variable with arguments $*"
} } this and that
print "I am $variable") print "I am $variable")
outputs the following: outputs the following:
example(I am inside example(I am inside with arguments this and that
I am outside) I am outside)
Note that function definitions with arguments that expand to nothing, Note that function definitions with arguments that expand to nothing,

View file

@ -50,20 +50,20 @@ int noerrexit;
* noerrs = 1: suppress error messages * noerrs = 1: suppress error messages
* noerrs = 2: don't set errflag on parse error, either * noerrs = 2: don't set errflag on parse error, either
*/ */
/**/ /**/
mod_export int noerrs; mod_export int noerrs;
/* do not save history on exec and exit */ /* do not save history on exec and exit */
/**/ /**/
int nohistsave; int nohistsave;
/* error/break flag */ /* error/break flag */
/**/ /**/
mod_export int errflag; mod_export int errflag;
/* /*
* State of trap return value. Value is from enum trap_state. * 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 in a trap once it was triggered. It should remain
* non-negative until restored after execution of the trap. * non-negative until restored after execution of the trap.
*/ */
/**/ /**/
int trap_return; int trap_return;
/* != 0 if this is a subshell */ /* != 0 if this is a subshell */
/**/ /**/
int subsh; int subsh;
/* != 0 if we have a return pending */ /* != 0 if we have a return pending */
/**/ /**/
mod_export int retflag; mod_export int retflag;
/**/ /**/
long lastval2; long lastval2;
/* The table of file descriptors. A table element is zero if the * /* The table of file descriptors. A table element is zero if the *
* corresponding fd is not used by the shell. It is greater than * * 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 * * 1 if the fd is used by a <(...) or >(...) substitution and 1 if *
@ -148,12 +148,12 @@ int fdtable_flocks;
mod_export int zleactive; mod_export int zleactive;
/* pid of process undergoing 'process substitution' */ /* pid of process undergoing 'process substitution' */
/**/ /**/
pid_t cmdoutpid; pid_t cmdoutpid;
/* exit status of process undergoing 'process substitution' */ /* exit status of process undergoing 'process substitution' */
/**/ /**/
int cmdoutval; int cmdoutval;
@ -166,7 +166,7 @@ int cmdoutval;
/**/ /**/
int use_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; 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 struct rlimit current_limits[RLIM_NLIMITS], limits[RLIM_NLIMITS];
/**/ /**/
mod_export int mod_export int
zsetlimit(int limnum, char *nam) 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, * (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 * 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 * 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 * 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 * 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 * 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. * is passed down in the list_pipe_pid variable.
* But there is a problem: the suspended grep is a child of the parent shell * 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) * keep the information about this process (more precisely: this pipeline)
* by keeping the job table entry it created for it. The fact that there * 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 * 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 * Maybe the directory was unreadable, or maybe it wasn't
* even a directory. * even a directory.
*/ */
return ((e != EACCES || !access(dir, X_OK)) && 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; break;
} }
/* for command -p, search the default path */ /* for command -p, search the default path */
if (defpath) { if (defpath) {
char *s, pbuf[PATH_MAX]; char *s, pbuf[PATH_MAX];
char *dptr, *pe, *ps = DEFAULT_PATH; char *dptr, *pe, *ps = DEFAULT_PATH;
@ -676,7 +676,7 @@ execute(LinkList args, int flags, int defpath)
eno = ee; eno = ee;
} else { } else {
if ((cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0))) { if ((cn = (Cmdnam) cmdnamtab->getnode(cmdnamtab, arg0))) {
char nn[PATH_MAX], *dptr; char nn[PATH_MAX], *dptr;
@ -1312,9 +1312,9 @@ sublist_done:
donetrap = 1; donetrap = 1;
} }
if (lastval) { if (lastval) {
int errreturn = isset(ERRRETURN) && int errreturn = isset(ERRRETURN) &&
(isset(INTERACTIVE) || locallevel || sourcelevel); (isset(INTERACTIVE) || locallevel || sourcelevel);
int errexit = isset(ERREXIT) || int errexit = isset(ERREXIT) ||
(isset(ERRRETURN) && !errreturn); (isset(ERRRETURN) && !errreturn);
if (errexit) { if (errexit) {
if (sigtrapped[SIGEXIT]) if (sigtrapped[SIGEXIT])
@ -1536,7 +1536,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
else if (pid) { else if (pid) {
char dummy; char dummy;
lpforked = lpforked =
(killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1); (killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1);
list_pipe_pid = pid; list_pipe_pid = pid;
list_pipe_start = bgtime; list_pipe_start = bgtime;
@ -3112,7 +3112,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
ESUB_PGRP | ESUB_FAKE; ESUB_PGRP | ESUB_FAKE;
if (type != WC_SUBSH) if (type != WC_SUBSH)
flags |= ESUB_KEEPTRAP; flags |= ESUB_KEEPTRAP;
if ((do_exec || (type >= WC_CURSH && last1 == 1)) if ((do_exec || (type >= WC_CURSH && last1 == 1))
&& !forked) && !forked)
flags |= ESUB_REVERTPGRP; flags |= ESUB_REVERTPGRP;
entersubsh(flags); entersubsh(flags);
@ -4184,10 +4184,19 @@ execfuncdef(Estate state, UNUSED(int do_exec))
* Anonymous function, execute immediately. * Anonymous function, execute immediately.
* Function name is "(anon)", parameter list is empty. * 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)"; shf->node.nam = "(anon)";
addlinknode(args, shf->node.nam); pushnode(args, shf->node.nam);
execshfunc(shf, args); execshfunc(shf, args);
ret = lastval; ret = lastval;

View file

@ -1480,12 +1480,25 @@ par_funcdef(void)
ecbuf[p + num + 4] = ecnpats; ecbuf[p + num + 4] = ecnpats;
ecbuf[p + 1] = num; ecbuf[p + 1] = num;
lineno += oldlineno;
ecnpats = onp; ecnpats = onp;
ecssub = oecssub; ecssub = oecssub;
ecnfunc++; ecnfunc++;
ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); 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 + 3] = ecsoffs - so;
ecbuf[p + argc + 4] = ecnpats; ecbuf[p + argc + 4] = ecnpats;
lineno += oldlineno;
ecnpats = onp; ecnpats = onp;
ecssub = oecssub; ecssub = oecssub;
ecnfunc++; ecnfunc++;
ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); 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; isfunc = 1;
isnull = 0; isnull = 0;
break; break;

View file

@ -253,6 +253,7 @@ struct tstack {
struct { struct {
char *strs; char *strs;
Wordcode end; Wordcode end;
int nargs;
} _funcdef; } _funcdef;
struct { struct {
Wordcode end; Wordcode end;
@ -456,19 +457,31 @@ gettext2(Estate state)
if (!s) { if (!s) {
Wordcode p = state->pc; Wordcode p = state->pc;
Wordcode end = p + WC_FUNCDEF_SKIP(code); Wordcode end = p + WC_FUNCDEF_SKIP(code);
int nargs = *state->pc++;
taddlist(state, *state->pc++); taddlist(state, nargs);
if (nargs)
taddstr(" ");
if (tjob) { if (tjob) {
taddstr(" () { ... }"); taddstr("() { ... }");
state->pc = end; 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; stack = 1;
} else { } else {
taddstr(" () {"); taddstr("() {");
tindent++; tindent++;
taddnl(1); taddnl(1);
n = tpush(code, 1); n = tpush(code, 1);
n->u._funcdef.strs = state->strs; n->u._funcdef.strs = state->strs;
n->u._funcdef.end = end; n->u._funcdef.end = end;
n->u._funcdef.nargs = nargs;
state->strs += *state->pc; state->strs += *state->pc;
state->pc += 3; state->pc += 3;
} }
@ -478,6 +491,17 @@ gettext2(Estate state)
dec_tindent(); dec_tindent();
taddnl(0); taddnl(0);
taddstr("}"); 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; stack = 1;
} }
break; break;

View file

@ -26,7 +26,7 @@
print regress expansion of function names print regress expansion of function names
} }
f$$ f$$
0:Regression test: `function f$$ () { ... }' 0:Regression test: 'function f$$ () { ... }'
>regress expansion of function names >regress expansion of function names
function foo () print bar function foo () print bar
@ -109,6 +109,8 @@
>really useful >really useful
>args >args
# ' deconfuse emacs
command_not_found_handler() { command_not_found_handler() {
print "Your command:" >&2 print "Your command:" >&2
print "$1" >&2 print "$1" >&2
@ -201,6 +203,31 @@
>Da de da >Da de da
>Do be do >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 %clean
rm -f file.in file.out rm -f file.in file.out