David Kastrup <d...@gnu.org> writes:

> "Keith OHara" <k-ohara5...@oco.net> writes:
>
>>>>>  so it can take *optional* arguments,
>>>> I have not yet learned to use David's new facility for optional 
>>>> arguments...
>>>
>>> Please report the parts of the documentation you find hard to
>>> understand.  Optional arguments are quite easy to use: if reading the
>>> documentation gives you a headache, there is something wrong with it.
>>
>> The new docs are clear, mostly it took me some time to re-learn
>> everything in "Extending" up to your addition.  (I have done no
>> extending myself beyond adapting other people's Lisp-in-Lilypond.)
>>
>> It would be good to warn people that optional arguments must come
>> immediately before an argument that will be read in LilyPond syntax,
>> so that the parser knows which argument is missing.
>
> That's not exactly true: optional arguments can be _skipped_ only when
> the following actual non-optional argument looks differently to the
> parser.  
>
> Scheme arguments currently look all the same to the parser: letting the
> parser _call_ the actual predicate for deciding how to parse on is hard.
> It must not have made any irreversible decisions at that point of time.
> And what Bison's LALR(1) parser algorithm considers irreversible is a
> bit tricky.
>
> I'll take a look whether just implementing this predicate-based
> backtracking for optional-Scheme-before-Scheme is feasible on the
> current code base: in the long run, I'll make this much more reliable.

You could try the following patch.  It has to run around Bison's totally
broken YYBACKUP macro.  I am also pretty sure that if an optional
argument is given by a Scheme function that has a duration or pitch as
its last argument, you'll get a "cannot backup" error.  I am not
actually sure that it's worth reworking the grammar for this, since all
it will buy you is getting a syntax error of at most slightly better
comprehensibility (something like "unexpected "ly:pitch?" or so).

I have to think about whether there is a way to do the same using our
own token pushback mechanism, making dealing with the YYBACKUP bug
unnecessary.

diff --git a/lily/parser.yy b/lily/parser.yy
index b0a584f..74ac5da 100644
--- a/lily/parser.yy
+++ b/lily/parser.yy
@@ -116,6 +116,25 @@ using namespace std;
 #include "text-interface.hh"
 #include "warn.hh"
 
+#define MYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY)					\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yytoken = YYTRANSLATE (yychar);				\
+      YYPOPSTACK (yylen);					\
+      yystate = *yyssp;						\
+      yylen = 0;						\
+      goto yybackup;						\
+    }								\
+  else								\
+    {								\
+      yyerror (YY_("syntax error: cannot back up"));		\
+      YYERROR;							\
+    }								\
+while (YYID (0))
+
 %}
 
 
@@ -224,6 +243,7 @@ void set_music_properties (Music *p, SCM a);
 %token SEQUENTIAL "\\sequential"
 %token SET "\\set"
 %token SIMULTANEOUS "\\simultaneous"
+%token <scm> SKIPPED_SCM
 %token TEMPO "\\tempo"
 %token TIMES "\\times"
 %token TYPE "\\type"
@@ -1110,7 +1130,7 @@ composite_music:
 	| closed_music
 	;
 
-/* Music that can't be followed by additional events or durations */
+/* Music that can be parsed without lookahead */
 closed_music:
 	mode_changed_music
 	| MUSIC_IDENTIFIER
@@ -1137,6 +1157,11 @@ function_arglist:
 	{
 		$$ = check_scheme_arg (PARSER, @3, SCM_UNDEFINED, $3, $2, $1);
 	}
+	| EXPECT_SCM SKIPPED_SCM
+	{
+		$$ = check_scheme_arg (PARSER, @$, SCM_UNDEFINED,
+				       scm_car ($2), scm_cdr ($2), $1);
+	}
 	;
 
 function_arglist_optional:
@@ -1157,10 +1182,6 @@ function_arglist_optional:
 	{
 		$$ = scm_cons ($1, $3);
 	}
-	| EXPECT_OPTIONAL EXPECT_SCM function_arglist_optional
-	{
-		$$ = scm_cons (loc_on_music (@3, $1), $3);
-	}
 	;
 
 function_arglist_keep:
@@ -1176,17 +1197,41 @@ function_arglist_keep:
 	| EXPECT_OPTIONAL EXPECT_DURATION function_arglist_closed_keep duration_length {
 		$$ = scm_cons ($4, $3);
 	}
+	| EXPECT_OPTIONAL EXPECT_MUSIC function_arglist_keep closed_music
+	{
+		$$ = scm_cons ($4, $3);
+	}
 	| EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep simple_string
 	{
-		$$ = check_scheme_arg (PARSER, @4, $1, $4, $3, $2);
+		if (scm_is_true (scm_call_1 ($2, $4)))
+		{
+			$$ = check_scheme_arg (PARSER, @4, $1, $4, $3, $2);
+		} else {
+			$$ = scm_cons2 ($4, loc_on_music (@3, $1), $3);
+			MYBACKUP (SKIPPED_SCM, yyval);
+		}
 	}
-	| EXPECT_OPTIONAL EXPECT_MUSIC function_arglist_keep closed_music
+	| EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep embedded_scm_closed
 	{
-		$$ = scm_cons ($4, $3);
+		if (scm_is_true (scm_call_1 ($2, $4)))
+		{
+			$$ = check_scheme_arg (PARSER, @4, $1, $4, $3, $2);
+		} else {
+			$$ = scm_cons2 ($4, loc_on_music (@3, $1), $3);
+			MYBACKUP (SKIPPED_SCM, yyval);
+		}
 	}
-	| EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep embedded_scm
+	| EXPECT_OPTIONAL EXPECT_SCM SKIPPED_SCM
 	{
-		$$ = check_scheme_arg (PARSER, @4, $1, $4, $3, $2);
+		if (scm_is_true (scm_call_1 ($2, scm_car ($3))))
+		{
+			$$ = check_scheme_arg (PARSER, @3, $1, scm_car ($3),
+					       scm_cdr ($3), $2);
+		} else {
+			$$ = scm_cons2 (scm_car ($3), loc_on_music (@3, $1),
+					scm_cdr ($3));
+			MYBACKUP (SKIPPED_SCM, yyval);
+		}
 	}
 	| function_arglist
 	;
@@ -1202,6 +1247,11 @@ function_arglist_closed:
 	{
 		$$ = check_scheme_arg (PARSER, @3, SCM_UNDEFINED, $3, $2, $1);
 	}
+	| EXPECT_SCM SKIPPED_SCM
+	{
+		$$ = check_scheme_arg (PARSER, @$, SCM_UNDEFINED,
+				       scm_car ($2), scm_cdr ($2), $1);
+	}
 	;
 
 function_arglist_closed_optional:
@@ -1241,17 +1291,41 @@ function_arglist_closed_keep:
 	| EXPECT_OPTIONAL EXPECT_DURATION function_arglist_closed_keep duration_length {
 		$$ = scm_cons ($4, $3);
 	}
-	| EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep simple_string
-	{
-		$$ = check_scheme_arg (PARSER, @4, $1, $4, $3, $2);
-	}
 	| EXPECT_OPTIONAL EXPECT_MUSIC function_arglist_keep closed_music
 	{
 		$$ = scm_cons ($4, $3);
 	}
+	| EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep simple_string
+	{
+		if (scm_is_true (scm_call_1 ($2, $4)))
+		{
+			$$ = check_scheme_arg (PARSER, @4, $1, $4, $3, $2);
+		} else {
+			$$ = scm_cons2 ($4, loc_on_music (@3, $1), $3);
+			MYBACKUP (SKIPPED_SCM, yyval);
+		}
+	}
 	| EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep embedded_scm_closed
 	{
-		$$ = check_scheme_arg (PARSER, @4, $1, $4, $3, $2);
+		if (scm_is_true (scm_call_1 ($2, $4)))
+		{
+			$$ = check_scheme_arg (PARSER, @4, $1, $4, $3, $2);
+		} else {
+			$$ = scm_cons2 ($4, loc_on_music (@3, $1), $3);
+			MYBACKUP (SKIPPED_SCM, yyval);
+		}
+	}
+	| EXPECT_OPTIONAL EXPECT_SCM SKIPPED_SCM
+	{
+		if (scm_is_true (scm_call_1 ($2, scm_car ($3))))
+		{
+			$$ = check_scheme_arg (PARSER, @3, $1, scm_car ($3),
+					       scm_cdr ($3), $2);
+		} else {
+			$$ = scm_cons2 (scm_car ($3), loc_on_music (@3, $1),
+					scm_cdr ($3));
+			MYBACKUP (SKIPPED_SCM, yyval);
+		}
 	}
 	| function_arglist_closed
 	;
@@ -1747,6 +1821,11 @@ music_function_chord_body_arglist:
 	| EXPECT_SCM function_arglist_optional embedded_scm_chord_body {
 		$$ = check_scheme_arg (PARSER, @3, SCM_UNDEFINED, $3, $2, $1);
 	}
+	| EXPECT_SCM SKIPPED_SCM
+	{
+		$$ = check_scheme_arg (PARSER, @$, SCM_UNDEFINED,
+				       scm_car ($2), scm_cdr ($2), $1);
+	}
 	;
 
 embedded_scm_chord_body:
@@ -1777,6 +1856,11 @@ music_function_event_arglist:
 	| EXPECT_SCM function_arglist_optional embedded_scm_event {
 		$$ = check_scheme_arg (PARSER, @3, SCM_UNDEFINED, $3, $2, $1);
 	}
+	| EXPECT_SCM SKIPPED_SCM
+	{
+		$$ = check_scheme_arg (PARSER, @$, SCM_UNDEFINED,
+				       scm_car ($2), scm_cdr ($2), $1);
+	}
 	;
 
 embedded_scm_event:

-- 
David Kastrup
_______________________________________________
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel

Reply via email to