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