]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/parser.yy
Issue 4779: Accept isolated durations as music function arguments
[lilypond.git] / lily / parser.yy
index df8e6bc209bd497b835ac6a786a4dae8a445fa10..3e7625f45f8cafbbe578bfa803e2d2a4819bedbe 100644 (file)
@@ -116,6 +116,7 @@ FIXME:
 #include <cctype>
 #include <cstdlib>
 #include <cstdio>
+using namespace std;
 
 #include "book.hh"
 #include "context-def.hh"
@@ -140,7 +141,7 @@ FIXME:
 #include "lily-imports.hh"
 
 void
-Lily_parser::parser_error (Input const *i, Lily_parser *parser, SCM *, const std::string &s)
+Lily_parser::parser_error (Input const *i, Lily_parser *parser, SCM *, const string &s)
 {
        parser->parser_error (*i, s);
 }
@@ -222,7 +223,6 @@ SCM get_next_unique_lyrics_context_id ();
 #define _(x) gettext (x)
 #endif
 
-using std::string;
 
 static Music *make_music_with_input (SCM name, Input where);
 SCM check_scheme_arg (Lily_parser *parser, Input loc,
@@ -232,7 +232,7 @@ SCM loc_on_music (Lily_parser *parser, 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);
-SCM make_duration (SCM t, int dots = 0);
+SCM make_duration (SCM t, int dots = 0, SCM factor = SCM_UNDEFINED);
 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);
@@ -347,7 +347,8 @@ If we give names, Bison complains.
 %token CHORD_REPETITION
 %token CONTEXT_MOD_IDENTIFIER
 %token DRUM_PITCH
-%token PITCH_IDENTIFIER
+ /* Artificial token for durations in argument lists */
+%token DURATION_ARG
 %token DURATION_IDENTIFIER
 %token EVENT_IDENTIFIER
 %token EVENT_FUNCTION
@@ -361,6 +362,7 @@ If we give names, Bison complains.
 %token MUSIC_IDENTIFIER
 %token NOTENAME_PITCH
 %token NUMBER_IDENTIFIER
+%token PITCH_IDENTIFIER
 %token REAL
 %token RESTNAME
 %token SCM_ARG
@@ -692,7 +694,7 @@ identifier_init_nonumber:
        | pitch_or_music
        | FRACTION
        | string
-        | embedded_scm
+       | embedded_scm
        | partial_markup
        | full_markup_list
         | context_modification
@@ -1282,10 +1284,10 @@ tempo_event:
        TEMPO steno_duration '=' tempo_range    {
                $$ = MAKE_SYNTAX (tempo, @$, SCM_EOL, $2, $4);
        }
-       | TEMPO scalar steno_duration '=' tempo_range   {
+       | TEMPO text steno_duration '=' tempo_range     {
                $$ = MAKE_SYNTAX (tempo, @$, $2, $3, $5);
        }
-       | TEMPO scalar {
+       | TEMPO text {
                $$ = MAKE_SYNTAX (tempo, @$, $2);
        } %prec ':'
        ;
@@ -1756,7 +1758,11 @@ function_arglist_nonbackup:
                                               (parser, @3, $3),
                                               $1, $2);
        }
-       | function_arglist_nonbackup_reparse REPARSE duration_length
+       | function_arglist_nonbackup_reparse REPARSE multiplied_duration
+       {
+               $$ = check_scheme_arg (parser, @3, $3, $1, $2);
+       }
+       | function_arglist_nonbackup_reparse REPARSE reparsed_rhythm
        {
                $$ = check_scheme_arg (parser, @3, $3, $1, $2);
        }
@@ -1778,6 +1784,20 @@ function_arglist_nonbackup:
        }
        ;
 
+
+reparsed_rhythm:
+       DURATION_ARG dots multipliers post_events
+       {
+               $$ = make_music_from_simple (parser, @$,
+                                            make_duration ($1, scm_to_int ($2), $3));
+               Music *m = unsmob<Music> ($$);
+               assert (m);
+               if (scm_is_pair ($4))
+                       m->set_property ("articulations",
+                                        scm_reverse_x ($4, SCM_EOL));
+       } %prec ':'
+       ;
+
 function_arglist_nonbackup_reparse:
        EXPECT_OPTIONAL EXPECT_SCM function_arglist_nonbackup SCM_IDENTIFIER
        {
@@ -1852,18 +1872,34 @@ function_arglist_nonbackup_reparse:
        {
                $$ = $3;
                if (scm_is_true (scm_call_1 ($2, $4)))
+                       // May be 3 \cm or similar
                        MYREPARSE (@4, $2, REAL, $4);
                else {
                        SCM d = make_duration ($4);
-                       if (SCM_UNBNDP (d) || scm_is_false (scm_call_1 ($2, d)))
-                               MYREPARSE (@4, $2, REAL, $4); // trigger error
-                       else
-                               MYREPARSE (@4, $2, DURATION_IDENTIFIER, d);
+                       if (!SCM_UNBNDP (d)) {
+                               if (scm_is_true (scm_call_1 ($2, d)))
+                                       MYREPARSE (@4, $2, DURATION_IDENTIFIER, d);
+                               else if (scm_is_true
+                                        (scm_call_1
+                                         ($2, make_music_from_simple (parser, @4, d))))
+                                       MYREPARSE (@4, $2, DURATION_ARG, d);
+                               else
+                                       MYREPARSE (@4, $2, SCM_ARG, $4); // trigger error
+                       } else
+                               MYREPARSE (@4, $2, SCM_ARG, $4); // trigger error
                }
        }
-       | EXPECT_OPTIONAL EXPECT_SCM function_arglist_nonbackup DURATION_IDENTIFIER {
+       | EXPECT_OPTIONAL EXPECT_SCM function_arglist_nonbackup DURATION_IDENTIFIER
+       {
                $$ = $3;
-               MYREPARSE (@4, $2, DURATION_IDENTIFIER, $4);
+               if (scm_is_true (scm_call_1 ($2, $4)))
+                       MYREPARSE (@4, $2, DURATION_IDENTIFIER, $4);
+               else if (scm_is_true
+                        (scm_call_1
+                         ($2, make_music_from_simple (parser, @4, $4))))
+                       MYREPARSE (@4, $2, DURATION_ARG, $4);
+               else
+                       MYREPARSE (@4, $2, SCM_ARG, $4); // trigger error
        }
        ;
 
@@ -1940,19 +1976,26 @@ function_arglist_backup:
        }
        | EXPECT_OPTIONAL EXPECT_SCM function_arglist_backup UNSIGNED
        {
+               $$ = $3;
                if (scm_is_true (scm_call_1 ($2, $4)))
-               {
+                       // May be 3 \cm or similar
                        MYREPARSE (@4, $2, REAL, $4);
-                       $$ = $3;
-               } else {
+               else {
                        SCM d = make_duration ($4);
-                       if (SCM_UNBNDP (d) || scm_is_false (scm_call_1 ($2, d)))
-                       {
+                       if (!SCM_UNBNDP (d)) {
+                               if (scm_is_true (scm_call_1 ($2, d)))
+                                       MYREPARSE (@4, $2, DURATION_IDENTIFIER, d);
+                               else if (scm_is_true
+                                        (scm_call_1
+                                         ($2, make_music_from_simple (parser, @4, d))))
+                                       MYREPARSE (@4, $2, DURATION_ARG, d);
+                               else {
+                                       $$ = scm_cons (loc_on_music (parser, @3, $1), $3);
+                                       MYBACKUP (UNSIGNED, $4, @4);
+                               }
+                       } else {
                                $$ = scm_cons (loc_on_music (parser, @3, $1), $3);
                                MYBACKUP (UNSIGNED, $4, @4);
-                       } else {
-                               MYREPARSE (@4, $2, DURATION_IDENTIFIER, d);
-                               $$ = $3;
                        }
                }
        }
@@ -2019,11 +2062,14 @@ function_arglist_backup:
        }
        | EXPECT_OPTIONAL EXPECT_SCM function_arglist_backup DURATION_IDENTIFIER
        {
+               $$ = $3;
                if (scm_is_true (scm_call_1 ($2, $4)))
-               {
                        MYREPARSE (@4, $2, DURATION_IDENTIFIER, $4);
-                       $$ = $3;
-               } else {
+               else if (scm_is_true
+                        (scm_call_1
+                         ($2, make_music_from_simple (parser, @4, $4))))
+                       MYREPARSE (@4, $2, DURATION_ARG, $4);
+               else {
                        $$ = scm_cons (loc_on_music (parser, @3, $1), $3);
                        MYBACKUP (DURATION_IDENTIFIER, $4, @4);
                }
@@ -2073,7 +2119,12 @@ function_arglist_backup:
                $$ = check_scheme_arg (parser, @3,
                                       $3, $1, $2);
        }
-       | function_arglist_backup REPARSE duration_length
+       | function_arglist_backup REPARSE multiplied_duration
+       {
+               $$ = check_scheme_arg (parser, @3,
+                                      $3, $1, $2);
+       }
+       | function_arglist_backup REPARSE reparsed_rhythm
        {
                $$ = check_scheme_arg (parser, @3,
                                       $3, $1, $2);
@@ -2204,7 +2255,12 @@ function_arglist_common:
                $$ = check_scheme_arg (parser, @3,
                                       $3, $1, $2);
        }
-       | function_arglist_common_reparse REPARSE duration_length
+       | function_arglist_common_reparse REPARSE multiplied_duration
+       {
+               $$ = check_scheme_arg (parser, @3,
+                                      $3, $1, $2);
+       }
+       | function_arglist_common_reparse REPARSE reparsed_rhythm
        {
                $$ = check_scheme_arg (parser, @3,
                                       $3, $1, $2);
@@ -2292,19 +2348,34 @@ function_arglist_common_reparse:
        {
                $$ = $2;
                if (scm_is_true (scm_call_1 ($1, $3)))
+                       // May be 3 \cm or similar
                        MYREPARSE (@3, $1, REAL, $3);
                else {
                        SCM d = make_duration ($3);
-                       if (SCM_UNBNDP (d) || scm_is_false (scm_call_1 ($1, d)))
-                               MYREPARSE (@3, $1, REAL, $3);
-                       else
-                               MYREPARSE (@3, $1, DURATION_IDENTIFIER, d);
+                       if (!SCM_UNBNDP (d)) {
+                               if (scm_is_true (scm_call_1 ($1, d)))
+                                       MYREPARSE (@3, $1, DURATION_IDENTIFIER, d);
+                               else if (scm_is_true
+                                        (scm_call_1
+                                         ($1, make_music_from_simple (parser, @3, d))))
+                                       MYREPARSE (@3, $1, DURATION_ARG, d);
+                               else
+                                       MYREPARSE (@3, $1, SCM_ARG, $3); // trigger error
+                       } else
+                               MYREPARSE (@3, $1, SCM_ARG, $3); // trigger error
                }
        }
        | EXPECT_SCM function_arglist_optional DURATION_IDENTIFIER
        {
                $$ = $2;
-               MYREPARSE (@3, $1, DURATION_IDENTIFIER, $3);
+               if (scm_is_true (scm_call_1 ($1, $3)))
+                       MYREPARSE (@3, $1, DURATION_IDENTIFIER, $3);
+               else if (scm_is_true
+                        (scm_call_1
+                         ($1, make_music_from_simple (parser, @3, $3))))
+                       MYREPARSE (@3, $1, DURATION_ARG, $3);
+               else
+                       MYREPARSE (@3, $1, SCM_ARG, $3); // trigger error
        }
        | EXPECT_SCM function_arglist_optional '-' UNSIGNED
        {
@@ -2716,15 +2787,25 @@ music_property_def:
        ;
 
 string:
-       STRING {
-               $$ = $1;
-       }
+       STRING
        | full_markup
        ;
 
-simple_string: STRING {
-               $$ = $1;
+text:
+       STRING
+       | full_markup
+       | embedded_scm_bare
+       {
+               if (Text_interface::is_markup ($1)) {
+                       $$ = $1;
+               } else {
+                       parser->parser_error (@1, (_ ("markup expected")));
+                       $$ = scm_string (SCM_EOL);
+               }
        }
+       ;
+
+simple_string: STRING
        | embedded_scm_bare
        {
                if (scm_is_string ($1)) {
@@ -3169,12 +3250,6 @@ script_dir:
        | '-'   { $$ = SCM_UNDEFINED; }
        ;
 
-duration_length:
-       multiplied_duration {
-               $$ = $1;
-       }
-       ;
-
 maybe_notemode_duration:
        {
                $$ = SCM_UNDEFINED;
@@ -3204,26 +3279,13 @@ steno_duration:
                }
        }
        | DURATION_IDENTIFIER dots      {
-               Duration *d = unsmob<Duration> ($1);
-               Duration k (d->duration_log (),
-                            d->dot_count () + scm_to_int ($2));
-               k = k.compressed (d->factor ());
-                scm_remember_upto_here_1 ($1);
-               $$ = k.smobbed_copy ();
+               $$ = make_duration ($1, scm_to_int ($2));
        }
        ;
 
 multiplied_duration:
-       steno_duration {
-               $$ = $1;
-       }
-       | multiplied_duration '*' UNSIGNED {
-               $$ = unsmob<Duration> ($$)->compressed (scm_to_int ($3)).smobbed_copy ();
-       }
-       | multiplied_duration '*' FRACTION {
-               Rational  m (scm_to_int (scm_car ($3)), scm_to_int (scm_cdr ($3)));
-
-               $$ = unsmob<Duration> ($$)->compressed (m).smobbed_copy ();
+       steno_duration multipliers {
+               $$ = make_duration ($1, 0, $2);
        }
        ;
 
@@ -3236,6 +3298,28 @@ dots:
        }
        ;
 
+multipliers:
+       /* empty */
+       {
+               $$ = SCM_UNDEFINED;
+       }
+       | multipliers '*' UNSIGNED
+       {
+               if (!SCM_UNBNDP ($1))
+                       $$ = scm_product ($1, $3);
+               else
+                       $$ = $3;
+       }
+       | multipliers '*' FRACTION
+       {
+               if (!SCM_UNBNDP ($1))
+                       $$ = scm_product ($1, scm_divide (scm_car ($3),
+                                                         scm_cdr ($3)));
+               else
+                       $$ = scm_divide (scm_car ($3), scm_cdr ($3));
+       }
+       ;
+
 tremolo_type:
        ':'     {
                $$ = scm_from_int (parser->default_tremolo_type_);
@@ -3661,7 +3745,7 @@ full_markup:
        ;
 
 partial_markup:
-       markup_mode markup_head_1_list ETC
+       markup_mode markup_partial_function ETC
        {
                $$ = MAKE_SYNTAX (partial_markup, @2, $2);
                parser->lexer_->pop_state ();
@@ -3683,7 +3767,7 @@ markup_top:
        ;
 
 markup_scm:
-       embedded_scm_bare
+       embedded_scm
        {
                if (Text_interface::is_markup ($1))
                        MYBACKUP (MARKUP_IDENTIFIER, $1, @1);
@@ -3779,6 +3863,37 @@ markup_command_list_arguments:
        }
        ;
 
+markup_partial_function:
+       MARKUP_FUNCTION markup_arglist_partial
+       {
+               $$ = scm_list_1 (scm_cons ($1, scm_reverse_x ($2, SCM_EOL)));
+       }
+       | markup_head_1_list MARKUP_FUNCTION markup_arglist_partial
+       {
+               $$ = scm_cons (scm_cons ($2, scm_reverse_x ($3, SCM_EOL)),
+                              $1);
+       }
+       ;
+
+markup_arglist_partial:
+       EXPECT_MARKUP markup_arglist_partial
+       {
+               $$ = $2;
+       }
+       | EXPECT_SCM markup_arglist_partial
+       {
+               $$= $2;
+       }
+       | EXPECT_MARKUP markup_command_list_arguments
+       {
+               $$ = $2;
+       }
+       | EXPECT_SCM markup_command_list_arguments
+       {
+               $$ = $2;
+       }
+       ;
+
 markup_head_1_item:
        MARKUP_FUNCTION EXPECT_MARKUP markup_command_list_arguments {
          $$ = scm_cons ($1, scm_reverse_x ($3, SCM_EOL));
@@ -4051,6 +4166,14 @@ make_music_from_simple (Lily_parser *parser, Input loc, SCM simple)
                        n->set_property ("pitch", simple);
                        return n->unprotect ();
                }
+               SCM d = simple;
+               if (scm_is_integer (simple))
+                       d = make_duration (simple);
+               if (unsmob<Duration> (d)) {
+                       Music *n = MY_MAKE_MUSIC ("NoteEvent", loc);
+                       n->set_property ("duration", d);
+                       return n->unprotect ();
+               }
                return simple;
        } else if (parser->lexer_->is_lyric_state ()) {
                if (Text_interface::is_markup (simple))
@@ -4083,13 +4206,29 @@ make_simple_markup (SCM a)
 }
 
 SCM
-make_duration (SCM d, int dots)
+make_duration (SCM d, int dots, SCM factor)
 {
-       int t = scm_to_int (d);
-       if (t > 0 && (t & (t-1)) == 0)
-               return Duration (intlog2 (t), dots).smobbed_copy ();
-       else
-               return SCM_UNDEFINED;
+       Duration k;
+
+       if (Duration *dur = unsmob<Duration> (d)) {
+               if (!dots && SCM_UNBNDP (factor))
+                       return d;
+               k = *dur;
+               if (dots)
+                       k = Duration (k.duration_log (), k.dot_count () + dots)
+                               .compressed (k.factor ());
+       } else {
+               int t = scm_to_int (d);
+               if (t > 0 && (t & (t-1)) == 0)
+                       k = Duration (intlog2 (t), dots);
+               else
+                       return SCM_UNDEFINED;
+       }
+
+       if (!SCM_UNBNDP (factor))
+               k = k.compressed (ly_scm2rational (factor));
+
+       return k.smobbed_copy ();
 }
 
 SCM