zsh-workers
 help / color / mirror / code / Atom feed
* PATCH: anonymous functions (no documentation yet)
@ 2008-06-26 13:51 Peter Stephenson
  2008-06-26 14:48 ` Oliver Kiddle
  0 siblings, 1 reply; 12+ messages in thread
From: Peter Stephenson @ 2008-06-26 13:51 UTC (permalink / raw)
  To: Zsh hackers list

I just spotted that it's very easy to add "anonymous" functions, which
are defined using normal function syntax, but executed immediately.
This creates a local variable context:

  (){
    local variable="no great"
    print "In anonymous function with $variable value";
  }

executes immediately but "variable" does not remain set.  The most
obvious use of this would be in initialization files: you get a local
variable scope that guarantees not to pollute the function table.

Currently the code gets copied as if for use in the shell structure,
but I can probably optimise that out.  This would make it slightly less
efficient than a normal "{ ... }", but not much.

This syntax has always been accepted, oddly, but the code used simply to
be ignored.  This is a syntax error in other shells (at least bash, ksh
88, pdksh, Solaris 8 sh), so there's no clash.  (If you want protection
against triggering advanced features you have come to completely the
wrong shell anyway.)

Note this does not affect cases like

  $emptyvariable() { ... }

since the patch requires that there be nothing before the () even before
expansion.  So you're protected against accidental execution.  (Should
this be a syntax error?  I would think it probably should.  This is a
separate issue, however.)

It turns out the same patch works for "function { ... }", too; same
protection against empty expansions; also a syntax error in other
shells.

Is there any interest in pursuing this?  (Note that, although I am open
to suggestions, I am quite definitely not asking "is there any interest
in pursuing a dozen more complicated additions I have no time to
write".)

Index: Src/exec.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/exec.c,v
retrieving revision 1.132
diff -u -r1.132 exec.c
--- Src/exec.c	11 Jun 2008 09:27:55 -0000	1.132
+++ Src/exec.c	26 Jun 2008 13:31:10 -0000
@@ -3853,7 +3853,7 @@
 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 @@
     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,10 +3871,10 @@
     plen = nprg * sizeof(wordcode);
     len = plen + (npats * sizeof(Patprog)) + nstrs;
 
-    if (htok)
+    if (htok && names)
 	execsubst(names);
 
-    while ((s = (char *) ugetnode(names))) {
+    while (!names || (s = (char *) ugetnode(names))) {
 	prog = (Eprog) zalloc(sizeof(*prog));
 	prog->npats = npats;
 	prog->nref = 1; /* allocated from permanent storage */
@@ -3906,23 +3903,38 @@
 	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.
 	     */
-	    removetrapnode(signum);
+	    LinkList args = newlinklist();
+
+	    shf->node.nam = "(anon)";
+	    addlinknode(args, shf->node.nam);
+	    execshfunc(shf, args);
+
+	    freeeprog(shf->funcdef);
+	    zfree(shf, sizeof(struct shfunc));
+	    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;

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: PATCH: anonymous functions (no documentation yet)
  2008-06-26 13:51 PATCH: anonymous functions (no documentation yet) Peter Stephenson
@ 2008-06-26 14:48 ` Oliver Kiddle
  2008-06-26 15:06   ` Peter Stephenson
  0 siblings, 1 reply; 12+ messages in thread
From: Oliver Kiddle @ 2008-06-26 14:48 UTC (permalink / raw)
  To: zsh-workers

Peter wrote:
> Note this does not affect cases like
> 
>   $emptyvariable() { ... }

Out of interest, how does it affect:
  ""() { ... }

I partly ask because in the same way that
  f g() { ... }
defines two functions,
  f ''() { ... }

might have the effect of defining a function f and running it
immediately. And then you might write: f '' ''() { ... } and so on.

What effect does all this have on the positional parameters?

Given that this feature will be seen as similar to plain { ... },
people might find it confusing that redirections don't work. I know
that you already know this but in other shells, redirections on function
definitions are allowed and form part of the function.

Oliver


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: PATCH: anonymous functions (no documentation yet)
  2008-06-26 14:48 ` Oliver Kiddle
@ 2008-06-26 15:06   ` Peter Stephenson
  2008-06-26 15:32     ` Stephane Chazelas
  2008-06-26 17:12     ` Bart Schaefer
  0 siblings, 2 replies; 12+ messages in thread
From: Peter Stephenson @ 2008-06-26 15:06 UTC (permalink / raw)
  To: zsh-workers

Oliver Kiddle wrote:
> Peter wrote:
> > Note this does not affect cases like
> > 
> >   $emptyvariable() { ... }
> 
> Out of interest, how does it affect:
>   ""() { ... }

That also turns into an empty argument list that is ignored.  I would
have to check why since my first guess was it would produce an invalid
empty function name.

> I partly ask because in the same way that
>   f g() { ... }
> defines two functions,
>   f ''() { ... }
> 
> might have the effect of defining a function f and running it
> immediately. And then you might write: f '' ''() { ... } and so on.

That ought to be a relatively straightforward addition, I think, subject
to the query above.

> What effect does all this have on the positional parameters?

Positional parameters from the surrounding area would be hidden and the
local parameter list empty.  I could easily copy them in so that they
could be used and modified without affecting the calling environment.

> Given that this feature will be seen as similar to plain { ... },
> people might find it confusing that redirections don't work. I know
> that you already know this but in other shells, redirections on function
> definitions are allowed and form part of the function.

Actually redirections work OK... the reason they don't work as they do
in other shells is because they do work on the definition, so it behaves
very much like { ... } in this respect.

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: PATCH: anonymous functions (no documentation yet)
  2008-06-26 15:06   ` Peter Stephenson
@ 2008-06-26 15:32     ` Stephane Chazelas
  2008-06-26 15:42       ` Peter Stephenson
  2008-06-26 17:12     ` Bart Schaefer
  1 sibling, 1 reply; 12+ messages in thread
From: Stephane Chazelas @ 2008-06-26 15:32 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: zsh-workers

On Thu, Jun 26, 2008 at 04:06:36PM +0100, Peter Stephenson wrote:
> Oliver Kiddle wrote:
> > Peter wrote:
> > > Note this does not affect cases like
> > > 
> > >   $emptyvariable() { ... }
> > 
> > Out of interest, how does it affect:
> >   ""() { ... }
> 
> That also turns into an empty argument list that is ignored.  I would
> have to check why since my first guess was it would produce an invalid
> empty function name.

Well,

""() echo test
''

Does echo "test".

I was finding it consistent of zsh that it would allow anything
as a function name, as there's no reason it shouldn't allow
anything that can be an argument to a simple command.

'{'() { echo test; }


Also note some funny alternatives:

$ bash -c 'a=c; a=a command eval "echo \$a; a=b; echo \$a"; echo $a'
a
b
c
$ ksh -c 'a=c; a=a command eval "echo \$a; a=b; echo \$a"; echo $a'
a
b
c
$ zsh -c 'a=c; a=a command eval "echo \$a; a=b; echo \$a"; echo $a'
zsh:1: command not found: eval
c
$ ARGV0=sh zsh -c 'a=c; a=a command eval "echo \$a; a=b; echo \$a"; echo $a'
a
b
c

$ bash -c 'a=c; a=a command . /dev/stdin << \EOF
echo $a; a=b; echo $a
EOF
echo $a'
a
b
c
$ ksh -c 'a=c; a=a command . /dev/stdin << \EOF
echo $a; a=b; echo $a
EOF
echo $a'
a
b
c
$ zsh -c 'a=c; a=a command . /dev/stdin << \EOF
echo $a; a=b; echo $a
EOF
echo $a'
zsh:1: permission denied: .
c
$ ARGV0=sh zsh -c 'a=c; a=a command . /dev/stdin << \EOF
echo $a; a=b; echo $a
EOF
echo $a'
a
b
c

Cheers,
Stéphane


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: PATCH: anonymous functions (no documentation yet)
  2008-06-26 15:32     ` Stephane Chazelas
@ 2008-06-26 15:42       ` Peter Stephenson
  0 siblings, 0 replies; 12+ messages in thread
From: Peter Stephenson @ 2008-06-26 15:42 UTC (permalink / raw)
  To: zsh-workers

Stephane Chazelas wrote:
> On Thu, Jun 26, 2008 at 04:06:36PM +0100, Peter Stephenson wrote:
> > > Out of interest, how does it affect:
> > >   ""() { ... }
> > 
> > That also turns into an empty argument list that is ignored.  I would
> > have to check why since my first guess was it would produce an invalid
> > empty function name.
> 
> Well,
> 
> ""() echo test
> ''
> 
> Does echo "test".

You're right; I didn't try it out.  I'd forgotten functions (unlike
variables) could be called absolutely any string of characters.

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: PATCH: anonymous functions (no documentation yet)
  2008-06-26 15:06   ` Peter Stephenson
  2008-06-26 15:32     ` Stephane Chazelas
@ 2008-06-26 17:12     ` Bart Schaefer
  2008-06-26 17:28       ` Peter Stephenson
  1 sibling, 1 reply; 12+ messages in thread
From: Bart Schaefer @ 2008-06-26 17:12 UTC (permalink / raw)
  To: Zsh hackers list

On Jun 26,  2:51pm, Peter Stephenson wrote:
}
} I just spotted that it's very easy to add "anonymous" functions, which
} are defined using normal function syntax, but executed immediately.

I like this idea although I prefer the "function { ... }" syntax to the
empty parens with nothing before them.

With FUNCTION_ARGZERO set, what's the value of $0 inside an anonymous
function?  What's the value of $functions[$0] ?

On Jun 26,  4:06pm, Peter Stephenson wrote:
} Subject: Re: PATCH: anonymous functions (no documentation yet)
}
} Oliver Kiddle wrote:
} >   f ''() { ... }
} > 
} > might have the effect of defining a function f and running it
} > immediately. And then you might write: f '' ''() { ... } and so on.
} 
} That ought to be a relatively straightforward addition, I think, subject
} to the query above.

What would the form with multiple empty-string arguments mean?  Call
the function multiple times?  (Anyway I take it from later remarks
that ''() already defines a function with the empty name, so probably
this "name it but also call it" idea won't work.)
 
} > What effect does all this have on the positional parameters?
} 
} Positional parameters from the surrounding area would be hidden and the
} local parameter list empty.  I could easily copy them in so that they
} could be used and modified without affecting the calling environment.

I'm of two minds on that score.  On the one hand it'd be nice to be able
to get at the surrounding $@.  On the other it may often be a needless
expense.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: PATCH: anonymous functions (no documentation yet)
  2008-06-26 17:12     ` Bart Schaefer
@ 2008-06-26 17:28       ` Peter Stephenson
  2008-06-30 10:27         ` PATCH: anonymous functions, full patch Peter Stephenson
  0 siblings, 1 reply; 12+ messages in thread
From: Peter Stephenson @ 2008-06-26 17:28 UTC (permalink / raw)
  To: Zsh hackers list

Bart Schaefer wrote:
> I like this idea although I prefer the "function { ... }" syntax to the
> empty parens with nothing before them.

It's pretty much inevitable they'll both be available.

> With FUNCTION_ARGZERO set, what's the value of $0 inside an anonymous
> function?  What's the value of $functions[$0] ?

$0 will be "(anon)" which is similar to "(eval)" in debug prompts.
There's no entry in the function table, so $functions[$0] won't be set
unless someone as defined a real function called "(anon)" which is
eccentric but valid (underlying problem is as below: *any* string
whatsoever is a valid function name; in principle you can even have
embedded nulls because this is all done internally).  $funcstack[1] will
be "(argv)".

> On Jun 26,  4:06pm, Peter Stephenson wrote:
> } Subject: Re: PATCH: anonymous functions (no documentation yet)
> }
> } Oliver Kiddle wrote:
> } >   f ''() { ... }
> } > 
> } > might have the effect of defining a function f and running it
> } > immediately. And then you might write: f '' ''() { ... } and so on.
> } 
> } That ought to be a relatively straightforward addition, I think, subject
> } to the query above.
> 
> What would the form with multiple empty-string arguments mean?  Call
> the function multiple times?  (Anyway I take it from later remarks
> that ''() already defines a function with the empty name, so probably
> this "name it but also call it" idea won't work.)

The last sentence appears to be correct.

> } > What effect does all this have on the positional parameters?
> } 
> } Positional parameters from the surrounding area would be hidden and the
> } local parameter list empty.  I could easily copy them in so that they
> } could be used and modified without affecting the calling environment.
> 
> I'm of two minds on that score.  On the one hand it'd be nice to be able
> to get at the surrounding $@.  On the other it may often be a needless
> expense.

Indeed, though unless your parameter list is humongous it won't be a big
effect.  On the other hand. humongous parameter lists are not unknown
with things like zargs.

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


^ permalink raw reply	[flat|nested] 12+ messages in thread

* PATCH: anonymous functions, full patch
  2008-06-26 17:28       ` Peter Stephenson
@ 2008-06-30 10:27         ` Peter Stephenson
  2008-06-30 13:57           ` Bart Schaefer
  2008-07-01 17:43           ` Mikael Magnusson
  0 siblings, 2 replies; 12+ messages in thread
From: Peter Stephenson @ 2008-06-30 10:27 UTC (permalink / raw)
  To: Zsh hackers list

On Thu, 26 Jun 2008 18:28:53 +0100
Peter Stephenson <pws@csr.com> wrote:
> > } Positional parameters from the surrounding area would be hidden and the
> > } local parameter list empty.  I could easily copy them in so that they
> > } could be used and modified without affecting the calling environment.
> > 
> > I'm of two minds on that score.  On the one hand it'd be nice to be able
> > to get at the surrounding $@.  On the other it may often be a needless
> > expense.
> 
> Indeed, though unless your parameter list is humongous it won't be a big
> effect.  On the other hand. humongous parameter lists are not unknown
> with things like zargs.

The way the internals work we would have to copy the list to pass down, but
we wouldn't have to copy the strings.  Anything cleverer (that makes a
significant difference) is quite a lot of work.  I've left this out for
now.

Here is a slightly more optimised patch with documentation and tests.
In principle I think it can be optimised more, but I don't think it's worth
it.

Index: Doc/Zsh/func.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/func.yo,v
retrieving revision 1.17
diff -u -r1.17 func.yo
--- Doc/Zsh/func.yo	10 Jun 2008 08:50:37 -0000	1.17
+++ Doc/Zsh/func.yo	30 Jun 2008 10:22:38 -0000
@@ -150,6 +150,42 @@
 
 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.
 
Index: Src/exec.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/exec.c,v
retrieving revision 1.132
diff -u -r1.132 exec.c
--- Src/exec.c	11 Jun 2008 09:27:55 -0000	1.132
+++ Src/exec.c	30 Jun 2008 10:22:39 -0000
@@ -3853,7 +3853,7 @@
 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 @@
     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 @@
     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 @@
 	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;
Index: Test/C04funcdef.ztst
===================================================================
RCS file: /cvsroot/zsh/zsh/Test/C04funcdef.ztst,v
retrieving revision 1.3
diff -u -r1.3 C04funcdef.ztst
--- Test/C04funcdef.ztst	13 Jul 2007 22:28:00 -0000	1.3
+++ Test/C04funcdef.ztst	30 Jun 2008 10:22:39 -0000
@@ -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

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: PATCH: anonymous functions, full patch
  2008-06-30 10:27         ` PATCH: anonymous functions, full patch Peter Stephenson
@ 2008-06-30 13:57           ` Bart Schaefer
  2008-06-30 15:04             ` Peter Stephenson
  2008-07-01 17:43           ` Mikael Magnusson
  1 sibling, 1 reply; 12+ messages in thread
From: Bart Schaefer @ 2008-06-30 13:57 UTC (permalink / raw)
  To: Zsh hackers list

On Jun 30, 11:27am, Peter Stephenson wrote:
}
} > > I'm of two minds on that score.  On the one hand it'd be nice to be able
} > > to get at the surrounding $@.  On the other it may often be a needless
} > > expense.
} 
} The way the internals work we would have to copy the list to pass down, but
} we wouldn't have to copy the strings.  Anything cleverer (that makes a
} significant difference) is quite a lot of work.  I've left this out for
} now.

Seems a little odd, though, to call something a function when you can't
pass any arguments to it.  I'm not going to have time to look today, but
how difficult is this?

    function { print $@ } args go here

Currently that's a parse error, so would not conflict with any existing
scripts.  [Of course (){ print oops } is NOT a parse error, which means
in some theoretical world somebody could be using it {un}intentionally
as a no-op block and we're about to mess them up.  Not a great worry.]

There's another syntax that this anonymous functions patch creates:

torch% function() { echo $0 }  
(anon)

Since only empty parens are currently valid there [anything else is a
parse error] it'd also be possible to use

    function(args go here) { print $@ }

but in other languages that would be naming the positional parameters
of a closure, not passing it arguments, so I suspect we should reserve
this syntax for that future enhancement.

Put a space between "function" and "()" and you have a fourth syntax,
where non-empty parens are parsed as a glob qualifier on an empty file
name and therefore either produce an error or always fail to match.  I
don't see anything useful there.

Two final remarks:  One: should this be considered a bug?

torch% torch% () ()
function function> {                 
function function> print oops
function function> }
zsh: parse error near `\n'

Once you've got the double empty parens there seems to be nowhere to go
that doesn't end in a parse error.  [If the second set of parens is not
empty you get a function whose body is a subshell.]

Two:  As usual with function definitions, the body doesn't have to be a
block.

torch% () print $0
(anon)
torch% x=3
torch% () local x=4
torch% print $x
3


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: PATCH: anonymous functions, full patch
  2008-06-30 13:57           ` Bart Schaefer
@ 2008-06-30 15:04             ` Peter Stephenson
  0 siblings, 0 replies; 12+ messages in thread
From: Peter Stephenson @ 2008-06-30 15:04 UTC (permalink / raw)
  To: Zsh hackers list

Bart Schaefer wrote:
> Seems a little odd, though, to call something a function when you can't
> pass any arguments to it.  I'm not going to have time to look today, but
> how difficult is this?
> 
>     function { print $@ } args go here

Shouldn't be too horrific, but will require parsing changes, which is
why I didn't puruse it.

> Two final remarks:  One: should this be considered a bug?
> 
> torch% torch% () ()
> function function> {                 
> function function> print oops
> function function> }
> zsh: parse error near `\n'
> 
> Once you've got the double empty parens there seems to be nowhere to go
> that doesn't end in a parse error.

Doesn't really surprise me.

-- 
Peter Stephenson <pws@csr.com>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: PATCH: anonymous functions, full patch
  2008-06-30 10:27         ` PATCH: anonymous functions, full patch Peter Stephenson
  2008-06-30 13:57           ` Bart Schaefer
@ 2008-07-01 17:43           ` Mikael Magnusson
  2008-07-01 18:32             ` Peter Stephenson
  1 sibling, 1 reply; 12+ messages in thread
From: Mikael Magnusson @ 2008-07-01 17:43 UTC (permalink / raw)
  To: Zsh hackers list

2008/6/30 Peter Stephenson <pws@csr.com>:
> On Thu, 26 Jun 2008 18:28:53 +0100
> Peter Stephenson <pws@csr.com> wrote:
>> > } Positional parameters from the surrounding area would be hidden and the
>> > } local parameter list empty.  I could easily copy them in so that they
>> > } could be used and modified without affecting the calling environment.
>> >
>> > I'm of two minds on that score.  On the one hand it'd be nice to be able
>> > to get at the surrounding $@.  On the other it may often be a needless
>> > expense.
>>
>> Indeed, though unless your parameter list is humongous it won't be a big
>> effect.  On the other hand. humongous parameter lists are not unknown
>> with things like zargs.
>
> The way the internals work we would have to copy the list to pass down, but
> we wouldn't have to copy the strings.  Anything cleverer (that makes a
> significant difference) is quite a lot of work.  I've left this out for
> now.
>
> Here is a slightly more optimised patch with documentation and tests.
> In principle I think it can be optimised more, but I don't think it's worth
> it.

I played a bit with this to see what works and what doesn't, and I
came across this non-working syntax (which is also obviously useless,
but still):

() a=b
zsh: parse error near `\n'

while these work:

() { a=b }
setopt shortloops; for a in a; a=b

-- 
Mikael Magnusson


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: PATCH: anonymous functions, full patch
  2008-07-01 17:43           ` Mikael Magnusson
@ 2008-07-01 18:32             ` Peter Stephenson
  0 siblings, 0 replies; 12+ messages in thread
From: Peter Stephenson @ 2008-07-01 18:32 UTC (permalink / raw)
  To: Zsh hackers list

On Tue, 1 Jul 2008 19:43:49 +0200
"Mikael Magnusson" <mikachu@gmail.com> wrote:
> I played a bit with this to see what works and what doesn't, and I
> came across this non-working syntax (which is also obviously useless,
> but still):
> 
> () a=b
> zsh: parse error near `\n'

This isn't new, you always got this, even if there was a function name
in front.  It's there because there's a test that a function body
consists of sufficiently complex code that it can't be simplified to
executing a single shell structure.  As this can, you get the error.

That seems an odd thing to test at this point in the code, and the more
I look at it the more I think it should be testing the return value of
the function instead...  It's odd this hasn't shown up before, but I
suppose there was a reasonable chance of a parse error occurring before
the parameter had been set to 1.

Index: Src/parse.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/parse.c,v
retrieving revision 1.69
diff -u -r1.69 parse.c
--- Src/parse.c	14 Mar 2008 11:40:59 -0000	1.69
+++ Src/parse.c	1 Jul 2008 18:29:00 -0000
@@ -1687,8 +1687,7 @@
 		sl = ecadd(0);
 		pl = ecadd(WCB_PIPE(WC_PIPE_END, 0));
 
-		par_cmd(&c);
-		if (!c) {
+		if (!par_cmd(&c)) {
 		    cmdpop();
 		    YYERROR(oecused);
 		}

-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2008-07-01 18:32 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-06-26 13:51 PATCH: anonymous functions (no documentation yet) Peter Stephenson
2008-06-26 14:48 ` Oliver Kiddle
2008-06-26 15:06   ` Peter Stephenson
2008-06-26 15:32     ` Stephane Chazelas
2008-06-26 15:42       ` Peter Stephenson
2008-06-26 17:12     ` Bart Schaefer
2008-06-26 17:28       ` Peter Stephenson
2008-06-30 10:27         ` PATCH: anonymous functions, full patch Peter Stephenson
2008-06-30 13:57           ` Bart Schaefer
2008-06-30 15:04             ` Peter Stephenson
2008-07-01 17:43           ` Mikael Magnusson
2008-07-01 18:32             ` Peter Stephenson

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).