Hi, The attached patch cleans up music functions: - functions can have any arity, and parameters can be mixed in any way. - implementation: A music function generates a sequence of tokens, so e.g. a (markup? music? scm?) function generates tokens MUSIC_FUNCTION EXPECT_SCM EXPECT_MUSIC EXPECT_MARKUP. After that it's easy to write a grammar to parse the argument list. - there is still a restriction on 'event functions': A function can only be applied to a post-event or chord-element if the function has exactly one music parameter, and if that parameter is last. If this rule is violated, there will be confusing parser errors ("unexpected EXPECT_MUSIC") due my implementation, but I also added an error message which describes the problem in English.
-- Erik
? .sconf_temp ? build-stamp ? context-unique.diff ? def-rel-music-funciton.diff ? delay-music-functions.diff ? exjobb.diff3 ? fonts ? lib ? lilypond.kdevelop ? lilypond.kdevelop.pcs ? lilypond.kdevses ? optimized ? os ? ref1.diff ? ref2.diff ? repeat.diff ? scons.cache ? web-err ? web-out ? Documentation/out ? Documentation/out-www ? Documentation/bibliography/out ? Documentation/bibliography/out-www ? Documentation/misc/out ? Documentation/misc/out-www ? Documentation/pictures/out ? Documentation/pictures/out-www ? Documentation/topdocs/out ? Documentation/topdocs/out-www ? Documentation/user/out ? Documentation/user/out-www ? buildscripts/out ? buildscripts/out-www ? cygwin/out ? cygwin/out-www ? debian/out ? elisp/out ? elisp/out-www ? flower/out ? flower/out-scons ? flower/out-www ? flower/include/.sconsign ? flower/include/out ? flower/include/out-www ? input/Diagram1.dia.autosave ? input/les-nereides.pdf ? input/les-nereides.ps ? input/out ? input/out-www ? input/manual/out ? input/manual/out-www ? input/mutopia/out ? input/mutopia/out-www ? input/mutopia/E.Satie/out ? input/mutopia/E.Satie/out-www ? input/mutopia/F.Schubert/morgenlied.midi ? input/mutopia/F.Schubert/morgenlied.pdf ? input/mutopia/F.Schubert/morgenlied.ps ? input/mutopia/F.Schubert/out ? input/mutopia/F.Schubert/out-www ? input/mutopia/J.S.Bach/out ? input/mutopia/J.S.Bach/out-www ? input/mutopia/R.Schumann/out ? input/mutopia/R.Schumann/out-www ? input/mutopia/W.A.Mozart/out ? input/mutopia/W.A.Mozart/out-www ? input/no-notation/out ? input/no-notation/out-www ? input/no-notation/to-xml.pdf ? input/no-notation/to-xml.ps ? input/regression/chord-tremolo.pdf ? input/regression/chord-tremolo.ps ? input/regression/out ? input/regression/out-www ? input/template/out ? input/test/new ? input/test/out ? input/test/out-www ? input/tutorial/out ? input/tutorial/out-www ? kpath-guile/out ? kpath-guile/out-scons ? lily/On ? lily/busy-playing-listener.cc ? lily/foo.pdf ? lily/foo.ps ? lily/lilypond ? lily/lilypond.gdt ? lily/lilypond.gpr ? lily/out ? lily/out-scons ? lily/out-www ? lily/include/.sconsign ? lily/include/busy-playing-listener.hh ? lily/include/out ? lily/include/out-www ? ly/out ? ly/out-www ? make/out ? make/out-www ? mf/feta-alphabet11.600pk ? mf/feta-alphabet13.600pk ? mf/feta-alphabet14.600pk ? mf/feta-alphabet16.600pk ? mf/feta-alphabet18.600pk ? mf/feta-alphabet20.600pk ? mf/feta-alphabet23.600pk ? mf/feta-alphabet26.600pk ? mf/feta-braces-a.600pk ? mf/feta-braces-b.600pk ? mf/feta-braces-c.600pk ? mf/feta-braces-d.600pk ? mf/feta-braces-e.600pk ? mf/feta-braces-f.600pk ? mf/feta-braces-g.600pk ? mf/feta-braces-h.600pk ? mf/feta-braces-i.600pk ? mf/feta11.600pk ? mf/feta13.600pk ? mf/feta14.600pk ? mf/feta16.600pk ? mf/feta18.600pk ? mf/feta20.600pk ? mf/feta23.600pk ? mf/feta26.600pk ? mf/out ? mf/out-scons ? mf/out-www ? mf/parmesan11.600pk ? mf/parmesan13.600pk ? mf/parmesan14.600pk ? mf/parmesan16.600pk ? mf/parmesan18.600pk ? mf/parmesan20.600pk ? mf/parmesan23.600pk ? mf/parmesan26.600pk ? po/out ? po/out-www ? ps/out ? ps/out-www ? python/convertrules.pyc ? python/fontextract.pyc ? python/lilylib.pyc ? python/out ? python/out-www ? scm/out ? scm/out-www ? scripts/lilypond-book-36.py ? scripts/lilypond-book.py.new ? scripts/out ? scripts/out-www ? scripts/stat ? stepmake/out ? stepmake/out-www ? stepmake/bin/out ? stepmake/bin/out-www ? stepmake/bin/packagepython.pyc ? stepmake/stepmake/out ? stepmake/stepmake/out-www ? tex/out ? tex/out-www ? ttftool/out ? ttftool/out-scons ? ttftool/include/.sconsign ? ttftool/include/out ? vim/out ? vim/out-www Index: lily/lexer.ll =================================================================== RCS file: /sources/lilypond/lilypond/lily/lexer.ll,v retrieving revision 1.196 diff -u -r1.196 lexer.ll --- lily/lexer.ll 23 Aug 2006 20:43:29 -0000 1.196 +++ lily/lexer.ll 25 Aug 2006 06:13:45 -0000 @@ -43,6 +43,7 @@ #include "lily-lexer.hh" #include "lilypond-input-version.hh" #include "main.hh" +#include "music.hh" #include "music-function.hh" #include "parse-scm.hh" #include "parser.hh" @@ -60,7 +61,6 @@ void strip_trailing_white (string&); void strip_leading_white (string&); string lyric_fudge (string s); -int music_function_type (SCM); SCM lookup_markup_command (string s); bool is_valid_version (string s); @@ -103,6 +103,7 @@ %option never-interactive %option warn +%x extratoken %x chords %x figures %x incl @@ -153,6 +154,22 @@ // windows-suck-suck-suck } +<extratoken>. { + /* Generate a token without swallowing anything */ + + /* First unswallow the eaten character */ + add_lexed_char (-YYLeng ()); + yyless (0); + + /* produce requested token */ + int type = extra_token_types_.back (); + extra_token_types_.pop_back (); + if (extra_token_types_.empty ()) + yy_pop_state (); + + return type; +} + <INITIAL,chords,lyrics,figures,notes>{BOM_UTF8} { if (this->lexloc->line_number () != 1 || this->lexloc->column_number () != 0) { @@ -646,6 +663,18 @@ %% +/* Make the lexer generate a token of the given type as the next token. + TODO: make it possible to define a value for the token as well */ +void +Lily_lexer::push_extra_token (int token_type) +{ + if (extra_token_types_.empty ()) + { + yy_push_state (extratoken); + } + extra_token_types_.push_back (token_type); +} + void Lily_lexer::push_chord_state (SCM tab) { @@ -717,7 +746,19 @@ if (is_music_function (sid)) { yylval.scm = get_music_function_transform (sid); - return music_function_type (yylval.scm); + + SCM s = scm_object_property (yylval.scm, ly_symbol2scm ("music-function-signature")); + for (; scm_is_pair (s); s = scm_cdr (s)) + { + if (scm_car (s) == ly_music_p_proc) + push_extra_token (EXPECT_MUSIC); + else if (scm_car (s) == ly_lily_module_constant ("markup?")) + push_extra_token (EXPECT_MARKUP); + else if (ly_is_procedure (scm_car (s))) + push_extra_token (EXPECT_SCM); + else programming_error ("Function parameter without type-checking predicate"); + } + return MUSIC_FUNCTION; } if (sid != SCM_UNDEFINED) @@ -880,59 +921,6 @@ return scm_call_1 (proc, scm_makfrom0str (s.c_str ())); } -struct Parser_signature -{ - char *symbol; - int token_type; -}; -static SCM signature_hash_table; - -static void init_signature_hash_table () -{ - signature_hash_table = scm_gc_protect_object (scm_c_make_hash_table (31)); - Parser_signature sigs[] = { - {"scm", MUSIC_FUNCTION_SCM}, - {"music", MUSIC_FUNCTION_MUSIC}, - {"scm-music", MUSIC_FUNCTION_SCM_MUSIC}, - {"scm-scm", MUSIC_FUNCTION_SCM_SCM}, - {"music-music", MUSIC_FUNCTION_MUSIC_MUSIC}, - {"scm-music-music", MUSIC_FUNCTION_SCM_MUSIC_MUSIC}, - {"scm-scm-music-music", MUSIC_FUNCTION_SCM_SCM_MUSIC_MUSIC}, - {"scm-scm-music", MUSIC_FUNCTION_SCM_SCM_MUSIC}, - {"scm-scm-scm-music", MUSIC_FUNCTION_SCM_SCM_SCM_SCM_MUSIC}, - {"scm-scm-scm-scm-music", MUSIC_FUNCTION_SCM_SCM_SCM_MUSIC}, - {"scm-scm-scm", MUSIC_FUNCTION_SCM_SCM_SCM}, - {"markup", MUSIC_FUNCTION_MARKUP}, - {"markup-music", MUSIC_FUNCTION_MARKUP_MUSIC}, - {"markup-markup", MUSIC_FUNCTION_MARKUP_MARKUP}, - {"markup-music-music", MUSIC_FUNCTION_MARKUP_MUSIC_MUSIC}, - {"markup-markup-music", MUSIC_FUNCTION_MARKUP_MARKUP_MUSIC}, - {"noarg", MUSIC_FUNCTION}, - {0,0} - }; - - for (int i = 0; sigs[i].symbol; i++) - scm_hashq_set_x (signature_hash_table, ly_symbol2scm (sigs[i].symbol), - scm_from_int (sigs[i].token_type)); -} - -int -music_function_type (SCM func) -{ - if (!signature_hash_table) - init_signature_hash_table (); - - SCM type = scm_object_property (func, ly_symbol2scm ("music-function-signature-keyword")); - SCM token_type = scm_hashq_ref (signature_hash_table, type, SCM_BOOL_F); - if (!scm_is_number (token_type)) - { - programming_error (_ ("can't find signature for music function")); - return MUSIC_FUNCTION_SCM; - } - - return scm_to_int (token_type); -} - /* Shut up lexer warnings. */ #if YY_STACK_USED Index: lily/music-function.cc =================================================================== RCS file: /sources/lilypond/lilypond/lily/music-function.cc,v retrieving revision 1.18 diff -u -r1.18 music-function.cc --- lily/music-function.cc 11 Feb 2006 11:35:17 -0000 1.18 +++ lily/music-function.cc 25 Aug 2006 06:13:45 -0000 @@ -35,26 +35,9 @@ { extern SCM ly_music_p_proc; - string str = ""; - for (SCM s = signature; scm_is_pair (s); s = scm_cdr (s)) - { - if (str != "") - str += "-"; - - if (scm_car (s) == ly_music_p_proc) - str += "music"; - else if (scm_car (s) == ly_lily_module_constant ("markup?")) - str += "markup"; - else if (ly_is_procedure (scm_car (s))) - str += "scm"; - } - if (str == "") str = "noarg"; scm_set_object_property_x (func, ly_symbol2scm ("music-function-signature"), signature); - scm_set_object_property_x (func, ly_symbol2scm ("music-function-signature-keyword"), - ly_symbol2scm (str.c_str ())); - SCM_RETURN_NEWSMOB (music_function_tag, func); } Index: lily/parser.yy =================================================================== RCS file: /sources/lilypond/lilypond/lily/parser.yy,v retrieving revision 1.526 diff -u -r1.526 parser.yy --- lily/parser.yy 23 Aug 2006 21:19:36 -0000 1.526 +++ lily/parser.yy 25 Aug 2006 06:13:45 -0000 @@ -256,6 +256,11 @@ %token <i> E_UNSIGNED %token <i> UNSIGNED +/* Artificial tokens, for more generic function syntax */ +%token <i> EXPECT_MARKUP; +%token <i> EXPECT_MUSIC; +%token <i> EXPECT_SCM; + %token <scm> BOOK_IDENTIFIER %token <scm> CHORDMODIFIER_PITCH %token <scm> CHORD_MODIFIER @@ -277,22 +282,6 @@ %token <scm> MARKUP_HEAD_SCM0_SCM1_SCM2 %token <scm> MARKUP_IDENTIFIER %token <scm> MUSIC_FUNCTION -%token <scm> MUSIC_FUNCTION_MARKUP -%token <scm> MUSIC_FUNCTION_MARKUP_MARKUP -%token <scm> MUSIC_FUNCTION_MARKUP_MARKUP_MUSIC -%token <scm> MUSIC_FUNCTION_MARKUP_MUSIC -%token <scm> MUSIC_FUNCTION_MARKUP_MUSIC_MUSIC -%token <scm> MUSIC_FUNCTION_MUSIC -%token <scm> MUSIC_FUNCTION_MUSIC_MUSIC -%token <scm> MUSIC_FUNCTION_SCM -%token <scm> MUSIC_FUNCTION_SCM_MUSIC -%token <scm> MUSIC_FUNCTION_SCM_MUSIC_MUSIC -%token <scm> MUSIC_FUNCTION_SCM_SCM_MUSIC_MUSIC -%token <scm> MUSIC_FUNCTION_SCM_SCM -%token <scm> MUSIC_FUNCTION_SCM_SCM_MUSIC -%token <scm> MUSIC_FUNCTION_SCM_SCM_SCM -%token <scm> MUSIC_FUNCTION_SCM_SCM_SCM_MUSIC -%token <scm> MUSIC_FUNCTION_SCM_SCM_SCM_SCM_MUSIC %token <scm> MUSIC_IDENTIFIER %token <scm> NOTENAME_PITCH %token <scm> NUMBER_IDENTIFIER @@ -361,10 +350,6 @@ %type <scm> absolute_pitch %type <scm> assignment_id %type <scm> bare_number -%type <scm> music_function_event -%type <scm> music_function_chord_body -%type <scm> music_function_musicless_prefix -%type <scm> music_function_musicless_function %type <scm> bass_figure %type <scm> figured_bass_modification %type <scm> br_bass_figure @@ -386,6 +371,11 @@ %type <scm> figure_spec %type <scm> fraction %type <scm> full_markup +%type <scm> function_scm_argument +%type <scm> function_arglist +%type <scm> function_arglist_music_last +%type <scm> function_arglist_nonmusic_last +%type <scm> function_arglist_nonmusic %type <scm> identifier_init %type <scm> lilypond_header %type <scm> lilypond_header_body @@ -402,6 +392,9 @@ %type <scm> mode_changing_head %type <scm> mode_changing_head_with_context %type <scm> multiplied_duration +%type <scm> music_function_identifier_musicless_prefix +%type <scm> music_function_event +%type <scm> music_function_chord_body %type <scm> new_chord %type <scm> new_lyrics %type <scm> number_expression @@ -427,7 +420,6 @@ %type <scm> step_number %type <scm> step_numbers %type <scm> string -%type <scm> function_scm_argument %type <score> score_block %type <score> score_body @@ -921,72 +913,46 @@ | simple_string ; -/* -TODO: use code generation for this -*/ -music_function_musicless_function: - MUSIC_FUNCTION { - $$ = scm_list_2 ($1, make_input (@$)); - } - | MUSIC_FUNCTION_SCM function_scm_argument { - $$ = scm_list_3 ($1, make_input (@$), $2); - } - | MUSIC_FUNCTION_MARKUP full_markup { - $$ = scm_list_3 ($1, make_input (@$), $2); - } - | MUSIC_FUNCTION_SCM_SCM function_scm_argument function_scm_argument { - $$ = scm_list_4 ($1, make_input (@$), $2, $3); - } - | MUSIC_FUNCTION_SCM_SCM_SCM function_scm_argument function_scm_argument function_scm_argument { - $$ = scm_list_5 ($1, make_input (@$), $2, $3, $4); - } - | MUSIC_FUNCTION_MARKUP_MARKUP full_markup full_markup { - $$ = scm_list_4 ($1, make_input (@$), $2, $3); +/* An argument list. If a function \foo expects scm scm music, then the lexer expands \foo into the token sequence: + MUSIC_FUNCTION EXPECT_MUSIC EXPECT_SCM EXPECT_SCM +and this rule returns the reversed list of arguments. */ + +function_arglist_music_last: + EXPECT_MUSIC function_arglist music { + $$ = scm_cons ($3, $2); } ; -/* -TODO: use code generation for this -*/ -music_function_musicless_prefix: - MUSIC_FUNCTION_MUSIC { - $$ = scm_list_2 ($1, make_input (@$)); +function_arglist_nonmusic_last: + EXPECT_MARKUP function_arglist full_markup { + $$ = scm_cons ($3, $2); } - | MUSIC_FUNCTION_SCM_MUSIC function_scm_argument { - $$ = scm_list_3 ($1, make_input (@$), $2); + | EXPECT_SCM function_arglist function_scm_argument { + $$ = scm_cons ($3, $2); } - | MUSIC_FUNCTION_SCM_SCM_MUSIC function_scm_argument function_scm_argument { - $$ = scm_list_4 ($1, make_input (@$), $2, $3); + ; + +function_arglist_nonmusic: /*empty*/ { + $$ = SCM_EOL; } - | MUSIC_FUNCTION_SCM_SCM_SCM_MUSIC function_scm_argument function_scm_argument function_scm_argument { - $$ = scm_list_5 ($1, make_input (@$), $2, $3, $4); + | EXPECT_MARKUP function_arglist_nonmusic full_markup { + $$ = scm_cons ($3, $2); } - | MUSIC_FUNCTION_SCM_SCM_SCM_SCM_MUSIC function_scm_argument function_scm_argument function_scm_argument function_scm_argument { - $$ = scm_list_n ($1, make_input (@$), $2, $3, $4, $5, SCM_UNDEFINED); + | EXPECT_SCM function_arglist_nonmusic function_scm_argument { + $$ = scm_cons ($3, $2); } - | MUSIC_FUNCTION_MARKUP_MUSIC full_markup { - $$ = scm_list_3 ($1, make_input (@$), $2); + ; + +function_arglist: /*empty*/ { + $$ = SCM_EOL; } + | function_arglist_music_last + | function_arglist_nonmusic_last ; generic_prefix_music_scm: - music_function_musicless_function { - $$ = $1; - } - | music_function_musicless_prefix music { - $$ = ly_append2 ($1, scm_list_1 ($2)); - } - | MUSIC_FUNCTION_MUSIC_MUSIC music music { - $$ = scm_list_4 ($1, make_input (@$), $2, $3); - } - | MUSIC_FUNCTION_SCM_MUSIC_MUSIC function_scm_argument music music { - $$ = scm_list_5 ($1, make_input (@$), $2, $3, $4); - } - | MUSIC_FUNCTION_SCM_SCM_MUSIC_MUSIC function_scm_argument function_scm_argument music music { - $$ = scm_list_n ($1, make_input (@$), $2, $3, $4, $5, SCM_UNDEFINED); - } - | MUSIC_FUNCTION_MARKUP_MUSIC_MUSIC full_markup music music { - $$ = scm_list_5 ($1, make_input (@$), $2, $3, $4); + MUSIC_FUNCTION function_arglist { + $$ = ly_append2 (scm_list_2 ($1, make_input (@$)), scm_reverse_x ($2, SCM_EOL)); } ; @@ -1393,24 +1359,38 @@ } ; +music_function_identifier_musicless_prefix: MUSIC_FUNCTION { + SCM sig = scm_object_property (yylval.scm, ly_symbol2scm ("music-function-signature")); + if (scm_is_pair (sig) && scm_memq (ly_music_p_proc, scm_cdr (scm_reverse (sig)))) + { + PARSER->parser_error (@$, "Music function applied to event may not have a Music argument, except as the last argument."); + } + } + ; + music_function_chord_body: - music_function_musicless_function { - $$ = $1; + /* We could allow chord functions to have multiple music arguments, + but it's more consistent with music_function_event if we + prohibit it here too */ + music_function_identifier_musicless_prefix EXPECT_MUSIC function_arglist_nonmusic chord_body_element { + $$ = ly_append2 (scm_list_2 ($1, make_input (@$)), scm_reverse_x ($3, scm_list_1 ($4))); } - | music_function_musicless_prefix chord_body_element { - $$ = ly_append2 ($1, scm_list_1 ($2)); + | music_function_identifier_musicless_prefix function_arglist_nonmusic { + $$ = ly_append2 (scm_list_2 ($1, make_input (@$)), scm_reverse_x ($2, SCM_EOL)); } ; music_function_event: - music_function_musicless_prefix post_event { - $$ = ly_append2 ($1, scm_list_1 ($2)); + /* Post-events can only have the last argument as music, without this + restriction we get a shift/reduce conflict from e.g. + c8-\partcombine c8 -. */ + music_function_identifier_musicless_prefix EXPECT_MUSIC function_arglist_nonmusic post_event { + $$ = ly_append2 (scm_list_2 ($1, make_input (@$)), scm_reverse_x ($3, scm_list_1 ($4))); } - | music_function_musicless_function { - $$ = $1; + | music_function_identifier_musicless_prefix function_arglist_nonmusic { + $$ = ly_append2 (scm_list_2 ($1, make_input (@$)), scm_reverse_x ($2, SCM_EOL)); } ; - command_element: command_event { Index: lily/include/lily-lexer.hh =================================================================== RCS file: /sources/lilypond/lilypond/lily/include/lily-lexer.hh,v retrieving revision 1.18 diff -u -r1.18 lily-lexer.hh --- lily/include/lily-lexer.hh 11 Feb 2006 11:35:16 -0000 1.18 +++ lily/include/lily-lexer.hh 25 Aug 2006 06:13:45 -0000 @@ -35,6 +35,7 @@ SCM scopes_; SCM start_module_; public: + vector<int> extra_token_types_; string main_input_name_; void *lexval; Input *lexloc; @@ -67,6 +68,7 @@ SCM lookup_identifier (string s); SCM lookup_identifier_symbol (SCM s); + void push_extra_token (int token_type); void push_chord_state (SCM tab); void push_figuredbass_state (); void push_lyric_state ();
_______________________________________________ lilypond-devel mailing list lilypond-devel@gnu.org http://lists.gnu.org/mailman/listinfo/lilypond-devel