X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fparser.yy;h=37d5fa4e770c4d954375bc584b899a75d4f6c499;hb=fea0441e6d856d653aebcdb8d293e45bdec7844c;hp=270961ffd0fd2d1c68980e361cf7075d2efa936d;hpb=5f508003be854d1f6ef3bf5cca09a99d4877ad91;p=lilypond.git diff --git a/lily/parser.yy b/lily/parser.yy index 270961ffd0..37d5fa4e77 100644 --- a/lily/parser.yy +++ b/lily/parser.yy @@ -224,14 +224,14 @@ 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); @@ -328,7 +328,6 @@ If we give names, Bison complains. %token HYPHEN "--" %token CHORDMODIFIERS -%token LYRIC_MARKUP %token MULTI_MEASURE_REST @@ -362,7 +361,6 @@ If we give names, Bison complains. %token EVENT_IDENTIFIER %token EVENT_FUNCTION %token FRACTION -%token LYRICS_STRING %token LYRIC_ELEMENT %token MARKUP_FUNCTION %token MARKUP_LIST_FUNCTION @@ -379,8 +377,8 @@ If we give names, Bison complains. %token SCM_FUNCTION %token SCM_IDENTIFIER %token SCM_TOKEN -%token SCORE_IDENTIFIER %token STRING +%token SYMBOL_LIST %token TONICNAME_PITCH %left '-' '+' @@ -499,7 +497,6 @@ embedded_scm_bare_arg: { $$ = parser->lexer_->eval_scm_token ($1); } - | full_markup | full_markup_list | context_modification | score_block @@ -575,7 +572,6 @@ lilypond_header: */ assignment_id: STRING { $$ = $1; } - | LYRICS_STRING { $$ = $1; } ; assignment: @@ -645,7 +641,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 ($$); @@ -828,8 +825,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 { @@ -1012,13 +1017,24 @@ music: music_arg music_embedded: music + { + if (unsmob_music ($1)->is_mus_type ("post-event")) { + parser->parser_error (@1, _ ("unexpected post-event")); + $$ = SCM_UNSPECIFIED; + } + } | embedded_scm { - if (unsmob_music ($1) - || scm_is_eq ($1, SCM_UNSPECIFIED)) + if (scm_is_eq ($1, SCM_UNSPECIFIED)) $$ = $1; - else - { + else if (Music *m = unsmob_music ($1)) { + if (m->is_mus_type ("post-event")) { + parser->parser_error + (@1, _ ("unexpected post-event")); + $$ = SCM_UNSPECIFIED; + } else + $$ = $1; + } else { @$.warning (_ ("Ignoring non-music expression")); $$ = SCM_UNSPECIFIED; } @@ -1115,7 +1131,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); @@ -1158,6 +1175,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 @@ -1198,11 +1243,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); } } @@ -1232,34 +1274,60 @@ function_arglist_closed_nonbackup: } | EXPECT_OPTIONAL EXPECT_SCM function_arglist SCM_IDENTIFIER { - SCM res = try_string_variants ($2, $4); - if (SCM_UNBNDP (res)) - $$ = check_scheme_arg (parser, @4, $4, $3, $2); - else - $$ = scm_cons (res, $3); + $$ = check_scheme_arg (parser, @4, + try_string_variants ($2, $4), + $3, $2, $4); } | EXPECT_OPTIONAL EXPECT_SCM function_arglist STRING { - SCM res = try_string_variants ($2, $4); - if (SCM_UNBNDP (res)) - $$ = check_scheme_arg (parser, @4, $4, $3, $2); - else - $$ = scm_cons (res, $3); + $$ = check_scheme_arg (parser, @4, + try_string_variants ($2, $4), + $3, $2, $4); } - | EXPECT_OPTIONAL EXPECT_SCM function_arglist LYRICS_STRING + | EXPECT_OPTIONAL EXPECT_SCM function_arglist full_markup { - SCM res = try_string_variants ($2, $4); - if (SCM_UNBNDP (res)) - $$ = check_scheme_arg (parser, @4, $4, $3, $2); - else - $$ = scm_cons (res, $3); + $$ = check_scheme_arg (parser, @4, $4, $3, $2); } - | EXPECT_OPTIONAL EXPECT_SCM function_arglist lyric_markup + ; + +symbol_list_arg: + SYMBOL_LIST + | SYMBOL_LIST '.' symbol_list_rev { - $$ = check_scheme_arg (parser, @4, $4, $3, $2); + $$ = 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 + | embedded_scm_bare + ; + + function_arglist_nonbackup: function_arglist_nonbackup_common | EXPECT_OPTIONAL EXPECT_SCM function_arglist embedded_scm_arg @@ -1270,14 +1338,6 @@ function_arglist_nonbackup: { $$ = check_scheme_arg (parser, @4, $4, $3, $2); } - | EXPECT_OPTIONAL EXPECT_SCM function_arglist STRING - { - SCM res = try_string_variants ($2, $4); - if (SCM_UNBNDP (res)) - $$ = check_scheme_arg (parser, @4, $4, $3, $2); - else - $$ = scm_cons (res, $3); - } | function_arglist_nonbackup_reparse REPARSE SCM_ARG { $$ = check_scheme_arg (parser, @3, $3, $1, $2); @@ -1286,6 +1346,10 @@ function_arglist_nonbackup: { $$ = 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: @@ -1294,30 +1358,36 @@ function_arglist_nonbackup_reparse: $$ = $3; SCM res = try_string_variants ($2, $4); if (!SCM_UNBNDP (res)) - MYREPARSE (@4, $2, SCM_ARG, 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); + MYREPARSE (@4, $2, STRING, $4); else MYREPARSE (@4, $2, SCM_ARG, $4); } - | EXPECT_OPTIONAL EXPECT_SCM function_arglist LYRICS_STRING + | EXPECT_OPTIONAL EXPECT_SCM function_arglist STRING { $$ = $3; SCM res = try_string_variants ($2, $4); if (!SCM_UNBNDP (res)) - MYREPARSE (@4, $2, SCM_ARG, 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); + MYREPARSE (@4, $2, STRING, $4); else MYREPARSE (@4, $2, SCM_ARG, $4); } - | EXPECT_OPTIONAL EXPECT_SCM function_arglist lyric_markup + | EXPECT_OPTIONAL EXPECT_SCM function_arglist full_markup { $$ = $3; if (scm_is_true (scm_call_1 ($2, $4))) @@ -1326,7 +1396,7 @@ function_arglist_nonbackup_reparse: (scm_call_1 ($2, make_music_from_simple (parser, @4, $4)))) - MYREPARSE (@4, $2, LYRICS_STRING, $4); + MYREPARSE (@4, $2, STRING, $4); else MYREPARSE (@4, $2, SCM_ARG, $4); } @@ -1363,7 +1433,7 @@ function_arglist_backup: MYBACKUP (EVENT_IDENTIFIER, $4, @4); } } - | EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep lyric_markup + | EXPECT_OPTIONAL EXPECT_SCM function_arglist_keep full_markup { if (scm_is_true (scm_call_1 ($2, $4))) $$ = scm_cons ($4, $3); @@ -1467,7 +1537,12 @@ function_arglist_backup: { SCM res = try_string_variants ($2, $4); if (!SCM_UNBNDP (res)) - $$ = scm_cons (res, $3); + 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); @@ -1477,22 +1552,17 @@ function_arglist_backup: { SCM res = try_string_variants ($2, $4); if (!SCM_UNBNDP (res)) - $$ = scm_cons (res, $3); + 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)) - $$ = 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); @@ -1503,6 +1573,10 @@ function_arglist_backup: $$ = check_scheme_arg (parser, @3, $3, $1, $2); } + | function_arglist_backup REPARSE symbol_list_arg + { + $$ = check_scheme_arg (parser, @3, $3, $1, $2); + } ; function_arglist: @@ -1537,17 +1611,6 @@ function_arglist_common: SCM n = scm_difference ($4, SCM_UNDEFINED); $$ = check_scheme_arg (parser, @4, n, $2, $1); } - | EXPECT_SCM function_arglist_optional STRING - { - SCM res = try_string_variants ($1, $3); - if (!SCM_UNBNDP (res)) - $$ = scm_cons ($3, $2); - else - // This is going to flag a syntax error, we - // know the predicate to be false. - $$ = check_scheme_arg (parser, @3, - $3, $2, $1); - } | function_arglist_common_reparse REPARSE SCM_ARG { $$ = check_scheme_arg (parser, @3, @@ -1563,6 +1626,10 @@ function_arglist_common: $$ = 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_reparse: @@ -1571,38 +1638,40 @@ function_arglist_common_reparse: $$ = $2; SCM res = try_string_variants ($1, $3); if (!SCM_UNBNDP (res)) - MYREPARSE (@3, $1, SCM_ARG, 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)))) + (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 LYRICS_STRING + | EXPECT_SCM function_arglist_optional STRING { $$ = $2; SCM res = try_string_variants ($1, $3); if (!SCM_UNBNDP (res)) - MYREPARSE (@3, $1, SCM_ARG, 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)))) + (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 + | EXPECT_SCM function_arglist_optional full_markup { $$ = $2; - // We check how the predicate thinks about the - // unmodified lyric element. If it would be accepted - // in this form, we don't try interpreting is as lyric - // music. if (scm_is_true (scm_call_1 ($1, $3))) MYREPARSE (@3, $1, SCM_ARG, $3); else if (scm_is_true @@ -1672,17 +1741,6 @@ function_arglist_closed_common: $$ = check_scheme_arg (parser, @3, $3, $2, $1); } - | EXPECT_SCM function_arglist_optional STRING - { - SCM res = try_string_variants ($1, $3); - if (!SCM_UNBNDP (res)) - $$ = scm_cons ($3, $2); - else - // This is going to flag a syntax error, we - // know the predicate to be false. - $$ = check_scheme_arg (parser, @3, - $3, $2, $1); - } | function_arglist_common_reparse REPARSE SCM_ARG { $$ = check_scheme_arg (parser, @3, @@ -1693,6 +1751,10 @@ function_arglist_closed_common: $$ = 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_optional: @@ -1923,19 +1985,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: @@ -1947,15 +2003,71 @@ 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 revert_arg { + $$ = scm_cons (ly_symbol2scm ("pop"), $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 simple_string embedded_scm { - $$ = scm_list_3 (ly_symbol2scm ("pop"), - scm_string_to_symbol ($2), $3); + | 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: @@ -1993,52 +2105,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)); } ; @@ -2063,9 +2264,6 @@ string: simple_string: STRING { $$ = $1; } - | LYRICS_STRING { - $$ = $1; - } | embedded_scm_bare { if (scm_is_string ($1)) { @@ -2083,7 +2281,6 @@ scalar: | bare_number | FRACTION | lyric_element - | STRING ; scalar_closed: @@ -2092,7 +2289,6 @@ scalar_closed: | bare_number | FRACTION | lyric_element - | STRING ; @@ -2515,13 +2711,7 @@ gen_text_def: make_simple_markup ($1)); $$ = t->unprotect (); } - | LYRICS_STRING { - Music *t = MY_MAKE_MUSIC ("TextScriptEvent", @$); - t->set_property ("text", - 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")) @@ -2852,10 +3042,10 @@ simple_chord_elements: ; lyric_element: - lyric_markup { + full_markup { $$ = $1; } - | LYRICS_STRING { + | STRING { $$ = $1; } | LYRIC_ELEMENT @@ -2863,6 +3053,8 @@ lyric_element: lyric_element_music: lyric_element optional_notemode_duration post_events { + if (!parser->lexer_->is_lyric_state ()) + parser->parser_error (@1, _ ("have to be in Lyric mode for lyrics")); $$ = MAKE_SYNTAX ("lyric-event", @$, $1, $2); if (scm_is_pair ($3)) unsmob_music ($$)->set_property @@ -3023,19 +3215,6 @@ questions: } ; -/* -This should be done more dynamically if possible. -*/ - -lyric_markup: - LYRIC_MARKUP - { parser->lexer_->push_markup_state (); } - markup_top { - $$ = $3; - parser->lexer_->pop_state (); - } - ; - full_markup_list: MARKUPLIST { parser->lexer_->push_markup_state (); } @@ -3239,12 +3418,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 (); @@ -3288,14 +3461,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; } @@ -3314,18 +3507,47 @@ SCM loc_on_music (Input loc, SCM 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; - if (!is_regular_identifier (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_to_symbol (str); + + 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; @@ -3341,7 +3563,7 @@ is_regular_identifier (SCM id) || (c >= 'A' && c <= 'Z') || c > 0x7f) middle = true; - else if (middle && (c == '-' || c == '_')) + else if (middle && (c == '-' || c == '_' || (multiple && c == '.'))) middle = false; else return false; @@ -3355,17 +3577,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,