Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Parsing of "anonymous" functions



On Sat, 28 Dec 2013 12:07:30 -0800
Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> 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@xxxxxxxxxxxx>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/



Messages sorted by: Reverse Date, Date, Thread, Author