From d601a955160da8ac6b1df851c4a7cb22c00da886 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Thu, 28 Jul 2011 14:54:55 +0200 Subject: [PATCH] parser.yy: allow multiple trailing chord_body_elements and postevents as music function arguments. --- .../extending/programming-interface.itely | 79 ++++++++++++++----- lily/parser.yy | 50 +++++++++--- 2 files changed, 100 insertions(+), 29 deletions(-) diff --git a/Documentation/extending/programming-interface.itely b/Documentation/extending/programming-interface.itely index 72ca39e83d..71865e7788 100644 --- a/Documentation/extending/programming-interface.itely +++ b/Documentation/extending/programming-interface.itely @@ -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 diff --git a/lily/parser.yy b/lily/parser.yy index 9c1e38db41..2a4032e4bf 100644 --- a/lily/parser.yy +++ b/lily/parser.yy @@ -392,6 +392,7 @@ If we give names, Bison complains. %type full_markup_list %type function_scm_argument %type function_arglist +%type function_arglist_nonmusic_last %type closed_function_arglist %type open_function_arglist %type identifier_init @@ -414,7 +415,9 @@ If we give names, Bison complains. %type mode_changing_head_with_context %type multiplied_duration %type music_function_event +%type music_function_event_arglist %type music_function_chord_body +%type music_function_chord_body_arglist %type new_chord %type new_lyrics %type 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))); } ; -- 2.39.5