1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-09-26 18:01:03 +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>
* 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 $
*****************************************************

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
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,

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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