zsh-workers
 help / color / mirror / code / Atom feed
* Parsing of "anonymous" functions
@ 2013-12-28 20:07 Bart Schaefer
  2014-01-02 20:35 ` Peter Stephenson
  0 siblings, 1 reply; 2+ messages in thread
From: Bart Schaefer @ 2013-12-28 20:07 UTC (permalink / raw)
  To: zsh-workers

I just noticed this:

torch% ()()()()()()()()
function function function function function function function function> 

Did we really mean for this to be possible or should the parser be pickier
about requiring an "ordinary" command structure as the first thing after
the empty parens?

The same doesn't quite work with the "function" keyword:

torch% function function function function function
function function function> print $funcstack
torch% functions
function () {
        function () {
                () {
                        print $funcstack
                }
        }
}

(However, you can't easily execute the function named "function" as the
keyword always takes precedence.)

Anyway, just an oddity it seemed interesting to point out.


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

* Re: Parsing of "anonymous" functions
  2013-12-28 20:07 Parsing of "anonymous" functions Bart Schaefer
@ 2014-01-02 20:35 ` Peter Stephenson
  0 siblings, 0 replies; 2+ messages in thread
From: Peter Stephenson @ 2014-01-02 20:35 UTC (permalink / raw)
  To: zsh-workers

On Sat, 28 Dec 2013 12:07:30 -0800
Bart Schaefer <schaefer@brasslantern.com> wrote:
> I just noticed this:
> 
> torch% ()()()()()()()()
> function function function function function function function function> 
> 
> Did we really mean for this to be possible or should the parser be pickier
> about requiring an "ordinary" command structure as the first thing after
> the empty parens?

It's not just anonymous functions:  you can put a name before the
parentheses.  It's triggered by the ability to write simple functions
without braces, one of those "who ordered that?" compatibility
nightmares, though I think this is more pukka than most such things
since it seems to come from ksh.

Given that, actually I don't think this really is a bug.  It's a bit
bizarre, but it's a logical syntax:

  fn()() echo foo

defines a function fn() that executes an anonymous function that
contains the code "echo foo".  This does appear to work, even though I
don't see any reason for a nested function context when the code inside
is limited to a simple command.  So I don't think I'm going to commit
the code below.

Anyway, a potential fix looks easy enough.  I spotted and fixed one side
effect, which the second regression test checks for, but this is already
obscure enough that I'm not too worried about further issues.

diff --git a/Src/parse.c b/Src/parse.c
index f0d0855..b434b46 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -726,7 +726,7 @@ par_pline(int *complex)
 
     p = ecadd(0);
 
-    if (!par_cmd(complex)) {
+    if (!par_cmd(complex, 0)) {
 	ecused--;
 	return 0;
     }
@@ -781,7 +781,7 @@ par_pline(int *complex)
 
 /**/
 static int
-par_cmd(int *complex)
+par_cmd(int *complex, int infuncdef)
 {
     int r, nr = 0;
 
@@ -877,7 +877,7 @@ par_cmd(int *complex)
 	{
 	    int sr;
 
-	    if (!(sr = par_simple(complex, nr))) {
+	    if (!(sr = par_simple(complex, nr, infuncdef))) {
 		if (!nr)
 		    return 0;
 	    } else {
@@ -1564,7 +1564,7 @@ par_dinbrack(void)
 
 /**/
 static int
-par_simple(int *complex, int nr)
+par_simple(int *complex, int nr, int infuncdef)
 {
     int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0;
     int c = *complex, nrediradd, assignments = 0;
@@ -1701,6 +1701,9 @@ par_simple(int *complex, int nr)
 	    /* Error if preceding assignments */
 	    if (assignments)
 		YYERROR(oecused);
+	    /* Error if we've already been here */
+	    if (infuncdef)
+		YYERROR(oecused);
 
 	    *complex = c;
 	    lineno = 0;
@@ -1746,7 +1749,7 @@ par_simple(int *complex, int nr)
 		sl = ecadd(0);
 		(void)ecadd(WCB_PIPE(WC_PIPE_END, 0));
 
-		if (!par_cmd(&c)) {
+		if (!par_cmd(&c, 1)) {
 		    cmdpop();
 		    YYERROR(oecused);
 		}
@@ -1787,6 +1790,10 @@ par_simple(int *complex, int nr)
 	} else
 	    break;
 	isnull = 0;
+	/*
+	 * "foo()() blah" is invalid, but "foo() bar() blah" is valid.
+	 */
+	infuncdef = 0;
     }
     if (isnull && !(sr + nr)) {
 	ecused = p;
diff --git a/Test/C04funcdef.ztst b/Test/C04funcdef.ztst
index 706aa28..2173119 100644
--- a/Test/C04funcdef.ztst
+++ b/Test/C04funcdef.ztst
@@ -267,6 +267,19 @@
 >ignorebraces is off
 >ignorebraces is still on here
 
+# ` emacs sh-mode deconfusion
+
+  fn()()
+1:multiple ()s in a function definition are an error
+?(eval): parse error near `()'
+
+# ` emacs sh-mode deconfusion
+ 
+  fnfoo() fnbar() echo this is silly but it does work.
+  fnfoo
+  fnbar
+0:multiple ()s in separate function definitions are OK
+>this is silly but it does work.
 
 %clean
 
-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


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

end of thread, other threads:[~2014-01-02 20:36 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-12-28 20:07 Parsing of "anonymous" functions Bart Schaefer
2014-01-02 20:35 ` 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).