X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fparser.yy;h=0804ce575b59f240ff3b43fcd1487ad7c9511fe0;hb=01df8ad908c92687d0c352e5ad5f067e52809423;hp=13f071a2881286008e95e752b23fa638698ed9f9;hpb=f0fe9c843e926066299c1f9a33004649f42e1f24;p=lilypond.git diff --git a/lily/parser.yy b/lily/parser.yy index 13f071a288..0804ce575b 100644 --- a/lily/parser.yy +++ b/lily/parser.yy @@ -224,14 +224,15 @@ SCM get_next_unique_lyrics_context_id (); static Music *make_music_with_input (SCM name, Input where); SCM check_scheme_arg (Lily_parser *parser, Input loc, - SCM arg, SCM args, SCM pred); + SCM arg, SCM args, SCM pred, SCM disp = SCM_UNDEFINED); SCM make_music_from_simple (Lily_parser *parser, Input loc, SCM pitch); SCM loc_on_music (Input loc, SCM arg); SCM make_chord_elements (Input loc, SCM pitch, SCM dur, SCM modification_list); SCM make_chord_step (SCM step, Rational alter); SCM make_simple_markup (SCM a); bool is_duration (int t); -bool is_regular_identifier (SCM id); +bool is_regular_identifier (SCM id, bool multiple=false); +SCM try_string_variants (SCM pred, SCM str); int yylex (YYSTYPE *s, YYLTYPE *loc, Lily_parser *parser); %} @@ -374,11 +375,12 @@ If we give names, Bison complains. %token OUTPUT_DEF_IDENTIFIER %token REAL %token RESTNAME +%token SCM_ARG %token SCM_FUNCTION %token SCM_IDENTIFIER %token SCM_TOKEN -%token SCORE_IDENTIFIER %token STRING +%token SYMBOL_LIST %token TONICNAME_PITCH %left '-' '+' @@ -492,8 +494,11 @@ embedded_scm_active: ; embedded_scm_bare_arg: - embedded_scm_bare - | STRING + SCM_ARG + | SCM_TOKEN + { + $$ = parser->lexer_->eval_scm_token ($1); + } | full_markup | full_markup_list | context_modification @@ -640,7 +645,8 @@ context_def_spec_body: unsmob_context_def ($$)->origin ()->set_spot (@$); } | context_def_spec_body context_mod { - unsmob_context_def ($$)->add_context_mod ($2); + if (!SCM_UNBNDP ($2)) + unsmob_context_def ($$)->add_context_mod ($2); } | context_def_spec_body context_modification { Context_def *td = unsmob_context_def ($$); @@ -823,8 +829,16 @@ score_body: unsmob_score ($$)->origin ()->set_spot (@$); } - | SCORE_IDENTIFIER { + | embedded_scm_active { + Score *score; + if (unsmob_score ($1)) + score = new Score (*unsmob_score ($1)); + else { + score = new Score; + parser->parser_error (@1, _("score expected")); + } unsmob_score ($$)->origin ()->set_spot (@$); + $$ = score->unprotect (); } | score_body { @@ -1033,7 +1047,6 @@ music_arg: music_assign: simple_music | composite_music %prec COMPOSITE - | lyric_element_music ; repeated_music: @@ -1111,7 +1124,8 @@ context_mod_list: $$ = Context_mod ().smobbed_copy (); } | context_mod_list context_mod { - unsmob_context_mod ($1)->add_context_mod ($2); + if (!SCM_UNBNDP ($2)) + unsmob_context_mod ($1)->add_context_mod ($2); } | context_mod_list CONTEXT_MOD_IDENTIFIER { Context_mod *md = unsmob_context_mod ($2); @@ -1154,6 +1168,34 @@ grouped_music_list: MUSIC_FUNCTION EXPECT_PITCH EXPECT_SCM EXPECT_SCM EXPECT_NO_MORE_ARGS and this rule returns the reversed list of arguments. */ +/* Function argument lists come in a number of flavors. Whenever + * LilyPond has to pick between different flavors, the decision is + * either made because of tokens it has already seen, or it is + * postponed until tokens suitable for making the decision come up. + * For postponing decisions, it may be necessary that the competing + * rules are written in a way making them compatible until a decision + * can be made. Sometimes this is done by putting common traits into + * a separate "common" rule set. + * + * function_arglist: a full argument list. Optional arguments at the + * end of the list can only be skipped by writing \default in their + * place. + * + * function_arglist_backup: an argument list ending in an optional + * argument that may be skipped depending on its predicate. + * + * function_arglist_skip: an argument list _not_ ending in an optional + * argument that is actually taken. + * + * function_arglist_nonbackup: an argument list ending in an optional + * argument that may not be skipped because it is in end position and + * has not been shortcircuited with \default. + * + * function_arglist* / function_arglist_closed*: The closed variants + * don't end in simple music expressions that might still accept + * things like a duration or a postevent. + */ + function_arglist_skip: function_arglist_common | EXPECT_OPTIONAL EXPECT_PITCH function_arglist_skip @@ -1178,7 +1220,7 @@ function_arglist_nonbackup_common: | EXPECT_OPTIONAL EXPECT_DURATION function_arglist_closed duration_length { $$ = scm_cons ($4, $3); } - | EXPECT_OPTIONAL EXPECT_SCM function_arglist_closed FRACTION + | EXPECT_OPTIONAL EXPECT_SCM function_arglist FRACTION { $$ = check_scheme_arg (parser, @4, $4, $3, $2); } @@ -1194,11 +1236,8 @@ function_arglist_nonbackup_common: else { Music *t = MY_MAKE_MUSIC ("FingeringEvent", @5); t->set_property ("digit", $5); - $$ = t->unprotect (); - if (scm_is_true (scm_call_1 ($2, $$))) - $$ = scm_cons ($$, $3); - else - $$ = check_scheme_arg (parser, @4, n, $3, $2); + $$ = check_scheme_arg (parser, @4, t->unprotect (), + $3, $2, n); } } @@ -1226,8 +1265,69 @@ function_arglist_closed_nonbackup: { $$ = check_scheme_arg (parser, @4, $4, $3, $2); } + | EXPECT_OPTIONAL EXPECT_SCM function_arglist SCM_IDENTIFIER + { + $$ = check_scheme_arg (parser, @4, + try_string_variants ($2, $4), + $3, $2, $4); + } + | EXPECT_OPTIONAL EXPECT_SCM function_arglist STRING + { + $$ = check_scheme_arg (parser, @4, + try_string_variants ($2, $4), + $3, $2, $4); + } + | EXPECT_OPTIONAL EXPECT_SCM function_arglist LYRICS_STRING + { + $$ = check_scheme_arg (parser, @4, + try_string_variants ($2, $4), + $3, $2, $4); + } + | EXPECT_OPTIONAL EXPECT_SCM function_arglist lyric_markup + { + $$ = check_scheme_arg (parser, @4, $4, $3, $2); + } + ; + +symbol_list_arg: + SYMBOL_LIST + | SYMBOL_LIST '.' symbol_list_rev + { + $$ = scm_append (scm_list_2 ($1, scm_reverse_x ($3, SCM_EOL))); + } + ; + +symbol_list_rev: + symbol_list_part + | symbol_list_rev '.' symbol_list_part + { + $$ = scm_append_x (scm_list_2 ($3, $1)); + } + ; + +// symbol_list_part delivers elements in reverse copy. + +symbol_list_part: + symbol_list_element + { + SCM sym_l_p = ly_lily_module_constant ("symbol-list?"); + $$ = try_string_variants (sym_l_p, $1); + if (SCM_UNBNDP ($$)) { + parser->parser_error (@1, _("not a symbol")); + $$ = SCM_EOL; + } else + $$ = scm_reverse ($$); + } + ; + + +symbol_list_element: + STRING + | LYRICS_STRING + | embedded_scm_bare ; + function_arglist_nonbackup: function_arglist_nonbackup_common | EXPECT_OPTIONAL EXPECT_SCM function_arglist embedded_scm_arg @@ -1238,6 +1338,80 @@ function_arglist_nonbackup: { $$ = check_scheme_arg (parser, @4, $4, $3, $2); } + | function_arglist_nonbackup_reparse REPARSE SCM_ARG + { + $$ = check_scheme_arg (parser, @3, $3, $1, $2); + } + | function_arglist_nonbackup_reparse REPARSE lyric_element_music + { + $$ = check_scheme_arg (parser, @3, $3, $1, $2); + } + | function_arglist_nonbackup_reparse REPARSE symbol_list_arg + { + $$ = check_scheme_arg (parser, @3, $3, $1, $2); + } + ; + +function_arglist_nonbackup_reparse: + EXPECT_OPTIONAL EXPECT_SCM function_arglist SCM_IDENTIFIER + { + $$ = $3; + SCM res = try_string_variants ($2, $4); + if (!SCM_UNBNDP (res)) + if (scm_is_pair (res)) + MYREPARSE (@4, $2, SYMBOL_LIST, res); + else + MYREPARSE (@4, $2, SCM_ARG, res); + else if (scm_is_true + (scm_call_1 + ($2, make_music_from_simple + (parser, @4, $4)))) + MYREPARSE (@4, $2, LYRICS_STRING, $4); + else + MYREPARSE (@4, $2, SCM_ARG, $4); + } + | EXPECT_OPTIONAL EXPECT_SCM function_arglist LYRICS_STRING + { + $$ = $3; + SCM res = try_string_variants ($2, $4); + if (!SCM_UNBNDP (res)) + if (scm_is_pair (res)) + MYREPARSE (@4, $2, SYMBOL_LIST, res); + else + MYREPARSE (@4, $2, SCM_ARG, res); + else if (scm_is_true + (scm_call_1 + ($2, make_music_from_simple + (parser, @4, $4)))) + MYREPARSE (@4, $2, LYRICS_STRING, $4); + else + MYREPARSE (@4, $2, SCM_ARG, $4); + } + | EXPECT_OPTIONAL EXPECT_SCM function_arglist STRING + { + $$ = $3; + SCM res = try_string_variants ($2, $4); + if (!SCM_UNBNDP (res)) + if (scm_is_pair (res)) + MYREPARSE (@4, $2, SYMBOL_LIST, res); + else + MYREPARSE (@4, $2, SCM_ARG, res); + else + MYREPARSE (@4, $2, SCM_ARG, $4); + } + | EXPECT_OPTIONAL EXPECT_SCM function_arglist lyric_markup + { + $$ = $3; + if (scm_is_true (scm_call_1 ($2, $4))) + MYREPARSE (@4, $2, SCM_ARG, $4); + else if (scm_is_true + (scm_call_1 + ($2, make_music_from_simple + (parser, @4, $4)))) + MYREPARSE (@4, $2, LYRICS_STRING, $4); + else + MYREPARSE (@4, $2, SCM_ARG, $4); + } ; function_arglist_keep: @@ -1258,7 +1432,7 @@ function_arglist_backup: $$ = scm_cons ($4, $3); } else { $$ = scm_cons (loc_on_music (@3, $1), $3); - MYBACKUP (SCM_IDENTIFIER, $4, @4); + MYBACKUP (SCM_ARG, $4, @4); } } | EXPECT_OPTIONAL EXPECT_SCM function_arglist_closed_keep post_event_nofinger @@ -1271,21 +1445,13 @@ function_arglist_backup: MYBACKUP (EVENT_IDENTIFIER, $4, @4); } } - | EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep lyric_element + | EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep lyric_markup { - // There is no point interpreting a lyrics string as - // an event, since we don't allow music possibly - // followed by durations or postevent into closed - // music, and we only accept closed music in optional - // arguments at the moment. If this changes, more - // complex schemes might become interesting here as - // well: see how we do this at the mandatory argument - // point. if (scm_is_true (scm_call_1 ($2, $4))) $$ = scm_cons ($4, $3); else { $$ = scm_cons (loc_on_music (@3, $1), $3); - MYBACKUP (LYRICS_STRING, $4, @4); + MYBACKUP (LYRIC_ELEMENT, $4, @4); } } | EXPECT_OPTIONAL EXPECT_SCM function_arglist_closed_keep UNSIGNED @@ -1320,7 +1486,7 @@ function_arglist_backup: MYBACKUP (NUMBER_IDENTIFIER, $4, @4); } } - | EXPECT_OPTIONAL EXPECT_SCM function_arglist_closed_keep FRACTION + | EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep FRACTION { if (scm_is_true (scm_call_1 ($2, $4))) { @@ -1357,6 +1523,7 @@ function_arglist_backup: MYREPARSE (@5, $2, REAL, n); $$ = $3; } else { + $$ = scm_cons (loc_on_music (@3, $1), $3); MYBACKUP (REAL, n, @5); } } @@ -1366,6 +1533,7 @@ function_arglist_backup: if (scm_is_true (scm_call_1 ($2, n))) { $$ = scm_cons (n, $3); } else { + $$ = scm_cons (loc_on_music (@3, $1), $3); MYBACKUP (NUMBER_IDENTIFIER, n, @5); } } @@ -1377,20 +1545,64 @@ function_arglist_backup: { $$ = scm_cons ($4, $3); } + | EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep SCM_IDENTIFIER + { + SCM res = try_string_variants ($2, $4); + if (!SCM_UNBNDP (res)) + if (scm_is_pair (res)) { + $$ = $3; + MYREPARSE (@4, $2, SYMBOL_LIST, res); + } + else + $$ = scm_cons (res, $3); + else { + $$ = scm_cons (loc_on_music (@3, $1), $3); + MYBACKUP (SCM_IDENTIFIER, $4, @4); + } + } + | EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep STRING + { + SCM res = try_string_variants ($2, $4); + if (!SCM_UNBNDP (res)) + if (scm_is_pair (res)) { + $$ = $3; + MYREPARSE (@4, $2, SYMBOL_LIST, res); + } + else + $$ = scm_cons (res, $3); + else { + $$ = scm_cons (loc_on_music (@3, $1), $3); + MYBACKUP (STRING, $4, @4); + } + } + | EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep LYRICS_STRING + { + SCM res = try_string_variants ($2, $4); + if (!SCM_UNBNDP (res)) + if (scm_is_pair (res)) { + $$ = $3; + MYREPARSE (@4, $2, SYMBOL_LIST, res); + } + else + $$ = scm_cons (res, $3); + else { + $$ = scm_cons (loc_on_music (@3, $1), $3); + MYBACKUP (LYRICS_STRING, $4, @4); + } + } | EXPECT_OPTIONAL EXPECT_SCM function_arglist_backup BACKUP { $$ = scm_cons ($1, $3); MYBACKUP(0, SCM_UNDEFINED, @3); } - | function_arglist_backup REPARSE embedded_scm_arg_closed + | function_arglist_backup REPARSE bare_number { $$ = check_scheme_arg (parser, @3, $3, $1, $2); } - | function_arglist_backup REPARSE bare_number + | function_arglist_backup REPARSE symbol_list_arg { - $$ = check_scheme_arg (parser, @3, - $3, $1, $2); + $$ = check_scheme_arg (parser, @3, $3, $1, $2); } ; @@ -1411,7 +1623,7 @@ function_arglist_common: $$ = check_scheme_arg (parser, @3, $3, $2, $1); } - | EXPECT_SCM function_arglist_closed_optional FRACTION + | EXPECT_SCM function_arglist_optional FRACTION { $$ = check_scheme_arg (parser, @3, $3, $2, $1); @@ -1421,65 +1633,111 @@ function_arglist_common: $$ = check_scheme_arg (parser, @3, $3, $2, $1); } - | function_arglist_common_minus - | function_arglist_common_lyric + | EXPECT_SCM function_arglist_closed_optional '-' NUMBER_IDENTIFIER + { + SCM n = scm_difference ($4, SCM_UNDEFINED); + $$ = check_scheme_arg (parser, @4, n, $2, $1); + } + | function_arglist_common_reparse REPARSE SCM_ARG + { + $$ = check_scheme_arg (parser, @3, + $3, $1, $2); + } + | function_arglist_common_reparse REPARSE lyric_element_music + { + $$ = check_scheme_arg (parser, @3, + $3, $1, $2); + } + | function_arglist_common_reparse REPARSE bare_number + { + $$ = check_scheme_arg (parser, @3, + $3, $1, $2); + } + | function_arglist_common_reparse REPARSE symbol_list_arg + { + $$ = check_scheme_arg (parser, @3, $3, $1, $2); + } ; -function_arglist_common_lyric: - EXPECT_SCM function_arglist_optional lyric_element +function_arglist_common_reparse: + EXPECT_SCM function_arglist_optional SCM_IDENTIFIER { - // We check how the predicate thinks about a lyrics - // event or about a markup. If it accepts neither, we - // backup the original token. Otherwise we commit to - // taking the token. Depending on what the predicate - // is willing to accept, we interpret as a string, as - // a lyric event, or ambiguously (meaning that if - // something looking like a duration or post event - // follows, we take the event, otherwise the string). - SCM lyric_event = MAKE_SYNTAX ("lyric-event", @3, $3, - parser->default_duration_.smobbed_copy ()); - if (scm_is_true (scm_call_1 ($1, $3))) - if (scm_is_true (scm_call_1 ($1, lyric_event))) - { - $$ = $2; - MYREPARSE (@3, $1, LYRICS_STRING, $3); - } else { - $$ = scm_cons ($3, $2); - } - else if (scm_is_true (scm_call_1 ($1, lyric_event))) - { - $$ = $2; + $$ = $2; + SCM res = try_string_variants ($1, $3); + if (!SCM_UNBNDP (res)) + if (scm_is_pair (res)) + MYREPARSE (@3, $1, SYMBOL_LIST, res); + else + MYREPARSE (@3, $1, SCM_ARG, res); + else if (scm_is_true + (scm_call_1 + ($1, make_music_from_simple (parser, @3, $3)))) MYREPARSE (@3, $1, LYRIC_ELEMENT, $3); - } else { + else // This is going to flag a syntax error, we // know the predicate to be false. - check_scheme_arg (parser, @3, - $3, $2, $1); - } + MYREPARSE (@3, $1, SCM_ARG, $3); } - | function_arglist_common_lyric REPARSE lyric_element_arg + | EXPECT_SCM function_arglist_optional STRING { - // This should never be false - $$ = check_scheme_arg (parser, @3, - $3, $1, $2); + $$ = $2; + SCM res = try_string_variants ($1, $3); + if (!SCM_UNBNDP (res)) + if (scm_is_pair (res)) + MYREPARSE (@3, $1, SYMBOL_LIST, res); + else + MYREPARSE (@3, $1, SCM_ARG, res); + else + // This is going to flag a syntax error, we + // know the predicate to be false. + MYREPARSE (@3, $1, SCM_ARG, $3); } - ; - -function_arglist_common_minus: - EXPECT_SCM function_arglist_closed_optional '-' UNSIGNED + | EXPECT_SCM function_arglist_optional LYRICS_STRING { + $$ = $2; + SCM res = try_string_variants ($1, $3); + if (!SCM_UNBNDP (res)) + if (scm_is_pair (res)) + MYREPARSE (@3, $1, SYMBOL_LIST, res); + else + MYREPARSE (@3, $1, SCM_ARG, res); + else if (scm_is_true + (scm_call_1 + ($1, make_music_from_simple (parser, @3, $3)))) + MYREPARSE (@3, $1, LYRIC_ELEMENT, $3); + else + // This is going to flag a syntax error, we + // know the predicate to be false. + MYREPARSE (@3, $1, SCM_ARG, $3); + } + | EXPECT_SCM function_arglist_optional lyric_markup + { + $$ = $2; + if (scm_is_true (scm_call_1 ($1, $3))) + MYREPARSE (@3, $1, SCM_ARG, $3); + else if (scm_is_true + (scm_call_1 + ($1, make_music_from_simple (parser, @3, $3)))) + MYREPARSE (@3, $1, LYRIC_ELEMENT, $3); + else + // This is going to flag a syntax error, we + // know the predicate to be false. + MYREPARSE (@3, $1, SCM_ARG, $3); + } + | EXPECT_SCM function_arglist_closed_optional '-' UNSIGNED + { + $$ = $2; SCM n = scm_difference ($4, SCM_UNDEFINED); - if (scm_is_true (scm_call_1 ($1, n))) { - $$ = $2; + if (scm_is_true (scm_call_1 ($1, n))) MYREPARSE (@4, $1, REAL, n); - } else { + else { Music *t = MY_MAKE_MUSIC ("FingeringEvent", @4); t->set_property ("digit", $4); - $$ = t->unprotect (); - if (scm_is_true (scm_call_1 ($1, $$))) - $$ = scm_cons ($$, $2); + SCM m = t->unprotect (); + if (scm_is_true (scm_call_1 ($1, m))) + MYREPARSE (@4, $1, SCM_ARG, m); else - $$ = check_scheme_arg (parser, @3, n, $2, $1); + MYREPARSE (@4, $1, SCM_ARG, $4); } } @@ -1489,15 +1747,6 @@ function_arglist_common_minus: SCM n = scm_difference ($4, SCM_UNDEFINED); MYREPARSE (@4, $1, REAL, n); } - | EXPECT_SCM function_arglist_closed_optional '-' NUMBER_IDENTIFIER - { - SCM n = scm_difference ($4, SCM_UNDEFINED); - $$ = check_scheme_arg (parser, @4, n, $2, $1); - } - | function_arglist_common_minus REPARSE bare_number - { - $$ = check_scheme_arg (parser, @3, $3, $1, $2); - } ; function_arglist_closed: @@ -1517,28 +1766,6 @@ function_arglist_closed_common: $$ = check_scheme_arg (parser, @3, $3, $2, $1); } - | EXPECT_SCM function_arglist_closed_optional '-' UNSIGNED - { - SCM n = scm_difference ($4, SCM_UNDEFINED); - if (scm_is_true (scm_call_1 ($1, n))) { - $$ = scm_cons (n, $2); - } else { - Music *t = MY_MAKE_MUSIC ("FingeringEvent", @4); - t->set_property ("digit", $4); - $$ = t->unprotect (); - if (scm_is_true (scm_call_1 ($1, $$))) - $$ = scm_cons ($$, $2); - else - $$ = check_scheme_arg (parser, @3, n, $2, $1); - } - - } - | EXPECT_SCM function_arglist_closed_optional '-' REAL - { - $$ = check_scheme_arg (parser, @3, - scm_difference ($4, SCM_UNDEFINED), - $2, $1); - } | EXPECT_SCM function_arglist_closed_optional '-' NUMBER_IDENTIFIER { $$ = check_scheme_arg (parser, @3, @@ -1550,15 +1777,24 @@ function_arglist_closed_common: $$ = check_scheme_arg (parser, @3, $3, $2, $1); } - | EXPECT_SCM function_arglist_closed_optional FRACTION + | EXPECT_SCM function_arglist_optional FRACTION { $$ = check_scheme_arg (parser, @3, $3, $2, $1); } - | EXPECT_SCM function_arglist_optional lyric_element + | function_arglist_common_reparse REPARSE SCM_ARG { $$ = check_scheme_arg (parser, @3, - $3, $2, $1); + $3, $1, $2); + } + | function_arglist_common_reparse REPARSE bare_number + { + $$ = check_scheme_arg (parser, @3, + $3, $1, $2); + } + | function_arglist_common_reparse REPARSE symbol_list_arg + { + $$ = check_scheme_arg (parser, @3, $3, $1, $2); } ; @@ -1790,19 +2026,13 @@ context_change: ; -property_path_revved: - embedded_scm_closed { - $$ = scm_cons ($1, SCM_EOL); - } - | property_path_revved embedded_scm_closed { - $$ = scm_cons ($2, $1); - } - ; - property_path: - property_path_revved { + symbol_list_rev { $$ = scm_reverse_x ($1, SCM_EOL); } + | symbol_list_rev property_path { + $$ = scm_reverse_x ($1, $2); + } ; property_operation: @@ -1814,15 +2044,73 @@ property_operation: $$ = scm_list_2 (ly_symbol2scm ("unset"), scm_string_to_symbol ($2)); } - | OVERRIDE simple_string property_path '=' scalar { - $$ = scm_append (scm_list_2 (scm_list_3 (ly_symbol2scm ("push"), - scm_string_to_symbol ($2), $5), - $3)); + | OVERRIDE property_path '=' scalar { + if (scm_ilength ($2) < 2) { + parser->parser_error (@2, _("bad grob property path")); + $$ = SCM_UNDEFINED; + } else { + $$ = scm_cons (ly_symbol2scm ("push"), + scm_cons2 (scm_car ($2), + $4, + scm_cdr ($2))); + } } - | REVERT simple_string embedded_scm { + | REVERT revert_arg { $$ = scm_list_3 (ly_symbol2scm ("pop"), - scm_string_to_symbol ($2), $3); + scm_car ($2), + scm_cdr ($2)); + } + ; + +// This is all quite awkward for the sake of substantial backward +// compatibility while at the same time allowing a more "natural" form +// of specification not separating grob specification from grob +// property path. The purpose of this definition of revert_arg is to +// allow the symbol list which specifies grob and property to revert +// to be optionally be split into two parts after the grob (which in +// this case is just the first element of the list). symbol_list_part +// is only one path component, but it can be parsed without lookahead, +// so we can follow it with a synthetic BACKUP token when needed. If +// the first symbol_list_part already contains multiple elements (only +// possible if a Scheme expression provides them), we just parse for +// additional elements introduced by '.', which is what the +// SYMBOL_LIST backup in connection with the immediately following +// rule using symbol_list_arg does. +// +// As long as we don't have our coffers filled with both grob and at +// least one grob property specification, the rest of the required +// symbol list chain may be provided either with or without a leading +// dot. This is for both allowing the traditional +// \revert Accidental #'color +// as well as well as the "naive" form +// \revert Accidental.color + +revert_arg: + revert_arg_part + { + if (scm_is_null ($1) + || scm_is_null (scm_cdr ($1))) + MYBACKUP (SCM_ARG, $1, @1); + else + MYBACKUP (SYMBOL_LIST, scm_reverse_x ($1, SCM_EOL), @1); + } + | revert_arg BACKUP symbol_list_arg + { + $$ = $3; + } + ; + +// revert_arg_part delivers results in reverse +revert_arg_part: + symbol_list_part + | revert_arg BACKUP SCM_ARG '.' symbol_list_part + { + $$ = scm_append_x (scm_list_2 ($5, $3)); } + | revert_arg BACKUP SCM_ARG symbol_list_part + { + $$ = scm_append_x (scm_list_2 ($4, $3)); + } ; context_def_mod: @@ -1860,52 +2148,141 @@ context_mod: } ; -context_prop_spec: - simple_string { - if (!is_regular_identifier ($1)) +// If defined, at least two members. +grob_prop_spec: + symbol_list_rev + { + SCM l = scm_reverse_x ($1, SCM_EOL); + if (scm_is_pair (l) + && to_boolean + (scm_object_property (scm_car (l), + ly_symbol2scm ("is-grob?")))) + l = scm_cons (ly_symbol2scm ("Bottom"), l); + if (scm_is_null (l) || scm_is_null (scm_cdr (l))) { + parser->parser_error (@1, _ ("bad grob property path")); + l = SCM_UNDEFINED; + } + $$ = l; + } + ; + +// If defined, at least three members +grob_prop_path: + grob_prop_spec + { + if (!SCM_UNBNDP ($1) && scm_is_null (scm_cddr ($1))) { - @$.error (_("Grob name should be alphanumeric")); + parser->parser_error (@1, _ ("bad grob property path")); + $$ = SCM_UNDEFINED; + } + } + | grob_prop_spec property_path + { + if (!SCM_UNBNDP ($1)) { + $$ = scm_append_x (scm_list_2 ($1, $2)); + if (scm_is_null (scm_cddr ($$))) { + parser->parser_error (@$, _ ("bad grob property path")); + $$ = SCM_UNDEFINED; + } } - $$ = scm_list_2 (ly_symbol2scm ("Bottom"), - scm_string_to_symbol ($1)); } - | simple_string '.' simple_string { - $$ = scm_list_2 (scm_string_to_symbol ($1), - scm_string_to_symbol ($3)); + ; + +// Exactly two elements or undefined +context_prop_spec: + symbol_list_rev + { + SCM l = scm_reverse_x ($1, SCM_EOL); + switch (scm_ilength (l)) { + case 1: + l = scm_cons (ly_symbol2scm ("Bottom"), l); + case 2: + break; + default: + parser->parser_error (@1, _ ("bad context property path")); + l = SCM_UNDEFINED; + } + $$ = l; } ; simple_music_property_def: - OVERRIDE context_prop_spec property_path '=' scalar { - $$ = scm_append (scm_list_2 (scm_list_n (scm_car ($2), - ly_symbol2scm ("OverrideProperty"), - scm_cadr ($2), - $5, SCM_UNDEFINED), - $3)); - } - | REVERT context_prop_spec embedded_scm { - $$ = scm_list_4 (scm_car ($2), - ly_symbol2scm ("RevertProperty"), - scm_cadr ($2), - $3); + OVERRIDE grob_prop_path '=' scalar { + if (SCM_UNBNDP ($2)) + $$ = SCM_UNDEFINED; + else { + $$ = scm_list_5 (scm_car ($2), + ly_symbol2scm ("OverrideProperty"), + scm_cadr ($2), + $4, + scm_cddr ($2)); + } + } + | REVERT simple_revert_context revert_arg { + $$ = scm_list_4 ($2, + ly_symbol2scm ("RevertProperty"), + scm_car ($3), + scm_cdr ($3)); } | SET context_prop_spec '=' scalar { - $$ = scm_list_4 (scm_car ($2), - ly_symbol2scm ("PropertySet"), - scm_cadr ($2), - $4); + if (SCM_UNBNDP ($2)) + $$ = SCM_UNDEFINED; + else + $$ = scm_list_4 (scm_car ($2), + ly_symbol2scm ("PropertySet"), + scm_cadr ($2), + $4); } | UNSET context_prop_spec { - $$ = scm_list_3 (scm_car ($2), - ly_symbol2scm ("PropertyUnset"), - scm_cadr ($2)); + if (SCM_UNBNDP ($2)) + $$ = SCM_UNDEFINED; + else + $$ = scm_list_3 (scm_car ($2), + ly_symbol2scm ("PropertyUnset"), + scm_cadr ($2)); + } + ; + + +// This is all quite awkward for the sake of substantial backward +// compatibility while at the same time allowing a more "natural" form +// of specification not separating grob specification from grob +// property path. The purpose of this definition of +// simple_revert_context is to allow the symbol list which specifies +// grob and property to revert to be optionally be split into two +// parts after the grob (which may be preceded by a context +// specification, a case which we distinguish by checking whether the +// first symbol is a valid grob symbol instead). +// +// See revert_arg above for the main work horse of this arrangement. +// simple_revert_context just caters for the context and delegates the +// rest of the job to revert_arg. + +simple_revert_context: + symbol_list_part + { + $1 = scm_reverse_x ($1, SCM_EOL); + if (scm_is_null ($1) + || to_boolean + (scm_object_property (scm_car ($1), + ly_symbol2scm ("is-grob?")))) { + $$ = ly_symbol2scm ("Bottom"); + parser->lexer_->push_extra_token (SCM_IDENTIFIER, $1); + } else { + $$ = scm_car ($1); + parser->lexer_->push_extra_token (SCM_IDENTIFIER, + scm_cdr ($1)); + } } ; music_property_def: simple_music_property_def { - $$ = LOWLEVEL_MAKE_SYNTAX (ly_lily_module_constant ("property-operation"), scm_cons2 (parser->self_scm (), make_input (@$), $1)); + if (SCM_UNBNDP ($1)) + $$ = MAKE_SYNTAX ("void-music", @1); + else + $$ = LOWLEVEL_MAKE_SYNTAX (ly_lily_module_constant ("property-operation"), scm_cons2 (parser->self_scm (), make_input (@$), $1)); } ; @@ -1946,16 +2323,20 @@ simple_string: STRING { scalar: embedded_scm_arg + | SCM_IDENTIFIER | bare_number | FRACTION | lyric_element + | STRING ; scalar_closed: embedded_scm_arg_closed + | SCM_IDENTIFIER | bare_number | FRACTION | lyric_element + | STRING ; @@ -2384,7 +2765,7 @@ gen_text_def: make_simple_markup ($1)); $$ = t->unprotect (); } - | embedded_scm_bare + | embedded_scm_closed { Music *m = unsmob_music ($1); if (m && m->is_mus_type ("post-event")) @@ -2721,31 +3102,9 @@ lyric_element: | LYRICS_STRING { $$ = $1; } + | LYRIC_ELEMENT ; -lyric_element_arg: - lyric_element - | lyric_element multiplied_duration post_events { - $$ = MAKE_SYNTAX ("lyric-event", @$, $1, $2); - if (scm_is_pair ($3)) - unsmob_music ($$)->set_property - ("articulations", scm_reverse_x ($3, SCM_EOL)); - } - | lyric_element post_event post_events { - $$ = MAKE_SYNTAX ("lyric-event", @$, $1, - parser->default_duration_.smobbed_copy ()); - unsmob_music ($$)->set_property - ("articulations", scm_cons ($2, scm_reverse_x ($3, SCM_EOL))); - } - | LYRIC_ELEMENT optional_notemode_duration post_events { - $$ = MAKE_SYNTAX ("lyric-event", @$, $1, $2); - if (scm_is_pair ($3)) - unsmob_music ($$)->set_property - ("articulations", scm_reverse_x ($3, SCM_EOL)); - } - ; - - lyric_element_music: lyric_element optional_notemode_duration post_events { $$ = MAKE_SYNTAX ("lyric-event", @$, $1, $2); @@ -3124,12 +3483,6 @@ Lily_lexer::try_special_identifiers (SCM *destination, SCM sid) *destination = unsmob_context_mod (sid)->smobbed_copy (); return CONTEXT_MOD_IDENTIFIER; - } else if (unsmob_score (sid)) { - Score *score = new Score (*unsmob_score (sid)); - *destination = score->self_scm (); - - score->unprotect (); - return SCORE_IDENTIFIER; } else if (Music *mus = unsmob_music (sid)) { mus = mus->clone (); *destination = mus->self_scm (); @@ -3173,14 +3526,34 @@ get_next_unique_lyrics_context_id () return scm_from_locale_string (s); } +// check_scheme_arg checks one argument with a given predicate for use +// in an argument list and throws a syntax error if it is unusable. +// The argument is prepended to the argument list in any case. After +// throwing a syntax error, the argument list is terminated with #f as +// its last cdr in order to mark it as uncallable while not losing +// track of its total length. +// +// There are a few special considerations: if optional argument disp +// is given (otherwise it defaults to SCM_UNDEFINED), it will be used +// instead of arg in a prospective error message. This is useful if +// arg is not the actual argument but rather a transformation of it. +// +// If arg itself is SCM_UNDEFINED, the predicate is considered false +// and an error message using disp is produced unconditionally. + SCM check_scheme_arg (Lily_parser *parser, Input loc, - SCM arg, SCM args, SCM pred) + SCM arg, SCM args, SCM pred, SCM disp) { - args = scm_cons (arg, args); - if (scm_is_true (scm_call_1 (pred, arg))) - return args; + if (SCM_UNBNDP (arg)) + args = scm_cons (disp, args); + else { + args = scm_cons (arg, args); + if (scm_is_true (scm_call_1 (pred, arg))) + return args; + } scm_set_cdr_x (scm_last_pair (args), SCM_EOL); - MAKE_SYNTAX ("argument-error", loc, scm_length (args), pred, arg); + MAKE_SYNTAX ("argument-error", loc, scm_length (args), pred, + SCM_UNBNDP (disp) ? arg : disp); scm_set_cdr_x (scm_last_pair (args), SCM_BOOL_F); return args; } @@ -3196,23 +3569,71 @@ SCM loc_on_music (Input loc, SCM arg) return arg; } +SCM +try_string_variants (SCM pred, SCM str) +{ + // a matching predicate is always ok + if (scm_is_true (scm_call_1 (pred, str))) + return str; + // a symbol may be interpreted as a list of symbols if it helps + if (scm_is_symbol (str)) { + str = scm_list_1 (str); + if (scm_is_true (scm_call_1 (pred, str))) + return str; + return SCM_UNDEFINED; + } + + // If this cannot be a string representation of a symbol list, + // we are through. + + if (!is_regular_identifier (str, true)) + return SCM_UNDEFINED; + + str = scm_string_split (str, SCM_MAKE_CHAR ('.')); + for (SCM p = str; scm_is_pair (p); p = scm_cdr (p)) + scm_set_car_x (p, scm_string_to_symbol (scm_car (p))); + + // Let's attempt the symbol list interpretation first. + + if (scm_is_true (scm_call_1 (pred, str))) + return str; + + // If there is just one symbol in the list, we might interpret + // it as a single symbol + + if (scm_is_null (scm_cdr (str))) + { + str = scm_car (str); + if (scm_is_true (scm_call_1 (pred, str))) + return str; + } + + return SCM_UNDEFINED; +} + bool -is_regular_identifier (SCM id) +is_regular_identifier (SCM id, bool multiple) { + if (!scm_is_string (id)) + return false; + string str = ly_scm2string (id); - char const *s = str.c_str (); - bool v = true; -#if 0 - isalpha (*s); - s++; -#endif - while (*s && v) - { - v = v && isalnum (*s); - s++; - } - return v; + bool middle = false; + + for (string::iterator it=str.begin(); it != str.end (); it++) + { + int c = *it & 0xff; + if ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || c > 0x7f) + middle = true; + else if (middle && (c == '-' || c == '_' || (multiple && c == '.'))) + middle = false; + else + return false; + } + return middle; } SCM @@ -3221,17 +3642,19 @@ make_music_from_simple (Lily_parser *parser, Input loc, SCM simple) if (unsmob_music (simple)) return simple; if (parser->lexer_->is_note_state ()) { - Music *n = MY_MAKE_MUSIC ("NoteEvent", loc); - n->set_property ("duration", parser->default_duration_.smobbed_copy ()); - if (scm_is_symbol (simple)) + if (scm_is_symbol (simple)) { + Music *n = MY_MAKE_MUSIC ("NoteEvent", loc); + n->set_property ("duration", parser->default_duration_.smobbed_copy ()); n->set_property ("drum-type", simple); - else if (unsmob_pitch (simple)) + return n->unprotect (); + } + if (unsmob_pitch (simple)) { + Music *n = MY_MAKE_MUSIC ("NoteEvent", loc); + n->set_property ("duration", parser->default_duration_.smobbed_copy ()); n->set_property ("pitch", simple); - else { - n->unprotect (); - return simple; + return n->unprotect (); } - return n->unprotect (); + return simple; } else if (parser->lexer_->is_lyric_state ()) { if (Text_interface::is_markup (simple)) return MAKE_SYNTAX ("lyric-event", loc, simple,