]> git.donarmstrong.com Git - lilypond.git/commitdiff
parser.yy: allow multiple trailing chord_body_elements and postevents as music functi...
authorDavid Kastrup <dak@gnu.org>
Thu, 28 Jul 2011 12:54:55 +0000 (14:54 +0200)
committerDavid Kastrup <dak@gnu.org>
Fri, 29 Jul 2011 14:24:55 +0000 (16:24 +0200)
Documentation/extending/programming-interface.itely
lily/parser.yy

index 72ca39e83d01a25f17d90614e10513c50fa8dae6..71865e7788d009dbe7bfe55f25f539128fb3cf39 100644 (file)
@@ -35,7 +35,8 @@ expressions automatically, and can be used to greatly simplify the
 input file.
 
 @menu
-* Music function syntax::
+* Music function definitions::
+* Music function usage::
 * Simple substitution functions::
 * Intermediate substitution functions::
 * Mathematics in functions::
@@ -44,10 +45,10 @@ input file.
 @end menu
 
 
-@node Music function syntax
-@subsection Music function syntax
+@node Music function definitions
+@subsection Music function definitions
 
-The general form for music functions is:
+The general form for defining music functions is:
 
 @example
 function =
@@ -67,17 +68,7 @@ where
 @item @code{@var{typeN?}}
 @tab a scheme @emph{type predicate} for which @code{@var{argN}}
 must return @code{#t}.  Some of these predicates are specially
-recognized by the parser, making the respective values be read in
-Lilypond syntax rather than in Scheme syntax.  Currently these are
-@code{ly:music?}, @code{markup?}, @code{ly:pitch?}, and
-@code{ly:duration?}.  Not all combinations are allowed: i.e., you can't
-look for a duration after music since music may end @emph{optionally}
-with a duration.
-
-If you really want to input some of those items as a Scheme rather than
-a Lilypond expression, you may wrap them in a call of @code{ly:export}.
-@c TODO: document what kind of argument lists are permitted in what
-@c syntactical context of a music function call.
+recognized by the parser, see below.
 
 @item @code{@var{music}}
 @tab A music expression, optionally written in scheme, with any
@@ -90,10 +81,20 @@ function arguments (eg., @w{@samp{$(cons arg1 arg2)}}).
 @end multitable
 
 @noindent
-For a list of available type predicates, see
-@ruser{Predefined type predicates}.  User-defined type predicates
-are also allowed.
+Some type predicates are specially recognized by the parser and will
+make the parser look for the respective arguments in Lilypond syntax
+rather than in Scheme syntax.  Currently these are @code{ly:music?},
+@code{markup?}, @code{ly:pitch?}, and @code{ly:duration?}.
+
+If you really want to input one of those items as a Scheme rather than a
+Lilypond expression, you may write them as a Scheme expression that
+calls @code{ly:export} at its outermost level.
+
+Other type predicates, including user-defined ones, will make the
+respective argument only be accepted as a Scheme expression.
 
+For a list of available type predicates, see
+@ruser{Predefined type predicates}.
 
 @seealso
 
@@ -106,6 +107,48 @@ Installed Files:
 @file{scm/lily.scm}.
 
 
+@node Music function usage
+@subsection Music function usage
+Music functions may currently be used in three places.  Depending on
+where they are used, restrictions apply in order to be able to parse
+them unambiguously.  The result a music function returns must be
+compatible with the context in which it is called.
+
+@itemize
+@item
+At top level in a music expression.  There are no special restrictions
+on the argument list.  Using the @code{#@{}@dots{}@code{#@}} construct
+produces a sequential music expression and consequently can only applied
+in this context.
+
+@item
+As a post-event.  All trailing arguments of the music function with the
+predicate @code{ly:music?} will get parsed also as post-events.  Note
+that returning post-events will also be acceptable for music functions
+called at top level, leading to a result roughly equivalent to
+@example
+s 1*0-\fun
+@end example
+
+@item
+As a chord constituent.  All trailing arguments of the music function
+with the predicate @code{ly:music?} will get parsed also as chord
+constituents.
+@end itemize
+
+@noindent
+The special rules for trailing arguments make it possible to write
+polymorphic functions like @code{\tweak} that can be applied to
+different constructs.
+
+There is another somewhat special rule: if you have a predicate
+@code{ly:music?} directly before a @code{ly:duration?} predicate, then
+the corresponding music expression must be either a music identifier, or
+literal sequential or parallel music enclosed in
+@code{@{}@dots{}@code{@}} or @code{<<}@dots{}@code{>>} explicitly.
+Otherwise, Lilypond could get confused about where the music ends and
+the duration starts.
+
 @node Simple substitution functions
 @subsection Simple substitution functions
 
index 9c1e38db41e33bca01281c8f10d54a989c04ad4c..2a4032e4bf65ba3faa46f047454d3b0949b16a23 100644 (file)
@@ -392,6 +392,7 @@ If we give names, Bison complains.
 %type <scm> full_markup_list
 %type <scm> function_scm_argument
 %type <scm> function_arglist
+%type <scm> function_arglist_nonmusic_last
 %type <scm> closed_function_arglist
 %type <scm> open_function_arglist
 %type <scm> identifier_init
@@ -414,7 +415,9 @@ If we give names, Bison complains.
 %type <scm> mode_changing_head_with_context
 %type <scm> multiplied_duration
 %type <scm> music_function_event
+%type <scm> music_function_event_arglist
 %type <scm> music_function_chord_body
+%type <scm> music_function_chord_body_arglist
 %type <scm> new_chord
 %type <scm> new_lyrics
 %type <scm> number_expression
@@ -1098,6 +1101,13 @@ open_function_arglist:
    expression that could still take a duration or event */
 
 closed_function_arglist:
+       function_arglist_nonmusic_last
+       | EXPECT_MUSIC function_arglist closed_music {
+               $$ = scm_cons ($3, $2);
+               }
+       ;
+
+function_arglist_nonmusic_last:
        EXPECT_NO_MORE_ARGS {
                /* This is for 0-ary functions, so they don't need to
                   read a lookahead token */
@@ -1118,9 +1128,6 @@ closed_function_arglist:
        | EXPECT_SCM function_arglist function_scm_argument {
                $$ = scm_cons ($3, $2);
        }
-       | EXPECT_MUSIC function_arglist closed_music {
-               $$ = scm_cons ($3, $2);
-               }
        ;
 
 generic_prefix_music_scm:
@@ -1575,21 +1582,42 @@ chord_body_element:
        }
        ;
 
+/* We can't accept a music argument, not even a closed one,
+ * immediately before chord_body_elements, otherwise a function \fun
+ * with a signature of two music arguments can't be sorted out
+ * properly in a construct like
+ * <\fun { c } \fun { c } c>
+ * The second call could be interpreted either as a chord constituent
+ * or a music expression.
+ */
 
-music_function_chord_body:
-       MUSIC_FUNCTION EXPECT_MUSIC function_arglist chord_body_element {
-               $$ = scm_cons ($1, scm_cons (make_input (@$), scm_reverse_x ($3, scm_list_1 ($4))));
+music_function_chord_body_arglist:
+       function_arglist_nonmusic_last
+       | EXPECT_MUSIC music_function_chord_body_arglist chord_body_element {
+               $$ = scm_cons ($3, $2);
        }
-       | MUSIC_FUNCTION closed_function_arglist {
+       ;
+
+music_function_chord_body:
+       MUSIC_FUNCTION music_function_chord_body_arglist {
                $$ = scm_cons ($1, scm_cons (make_input (@$), scm_reverse_x ($2, SCM_EOL)));
        }
        ;
 
-music_function_event:
-       MUSIC_FUNCTION EXPECT_MUSIC closed_function_arglist post_event {
-               $$ = scm_cons ($1, scm_cons (make_input (@$), scm_reverse_x ($3, scm_list_1 ($4))));
+/* We could accept a closed music argument before the post events
+ * indicated by a trailing argument list.  For symmetry with chord
+ * bodies and in order to avoid too tricky and complex behavior, we
+ * refrain from doing so.
+ */
+music_function_event_arglist:
+       function_arglist_nonmusic_last
+       | EXPECT_MUSIC music_function_event_arglist post_event {
+               $$ = scm_cons ($3, $2);
        }
-       | MUSIC_FUNCTION closed_function_arglist {
+       ;
+
+music_function_event:
+       MUSIC_FUNCTION music_function_event_arglist {
                $$ = scm_cons ($1, scm_cons (make_input (@$), scm_reverse_x ($2, SCM_EOL)));
        }
        ;