]> git.donarmstrong.com Git - lilypond.git/commitdiff
2006-09-22 Erik Sandberg <mandolaerik@gmail.com>
authorErik Sandberg <mandolaerik@gmail.com>
Fri, 22 Sep 2006 06:18:00 +0000 (06:18 +0000)
committerErik Sandberg <mandolaerik@gmail.com>
Fri, 22 Sep 2006 06:18:00 +0000 (06:18 +0000)
* lily/lexer.ll: remove limitation on music function arity. New
mode extratoken, which inserts extra EXPECT_* tokens after
MUSIC_FUNCTION token. Junk all MUSIC_FUNCTION_* tokens.

* lily/parser.yy: Change grammar for music function accordingly.

* lily/include/lily-parser.hh: New method get_state, new member
hidden_state. Works around a problem when parser fetches
MUSIC_FUNCTION token but not the following EXPECT_* token.

ChangeLog
lily/include/lily-lexer.hh
lily/lexer.ll
lily/music-function.cc
lily/parser.yy

index 894e7e8dbc1597908f3f1305ddf8619030147f64..999a91a5cac0b2615aa9080a8f7899597dd9c90c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2006-09-22  Erik Sandberg  <mandolaerik@gmail.com>
+
+       * lily/lexer.ll: remove limitation on music function arity. New
+       mode extratoken, which inserts extra EXPECT_* tokens after
+       MUSIC_FUNCTION token. Junk all MUSIC_FUNCTION_* tokens.
+
+       * lily/parser.yy: Change grammar for music function accordingly.
+
+       * lily/include/lily-parser.hh: New method get_state, new member
+       hidden_state. Works around a problem when parser fetches
+       MUSIC_FUNCTION token but not the following EXPECT_* token.
+
 2006-09-21  Graham Percival  <gpermus@gmail.com>
 
        * Documentation/user/tweaks.itely (Fitting music onto fewer
index d0a188ab69883d77fca0c278266a74ff67f9e4ac..b22038fbeb0c5e35505f0af0e4e24105d7262ef8 100644 (file)
@@ -34,7 +34,9 @@ private:
   Keyword_table *keytable_;
   SCM scopes_;
   SCM start_module_;
+  int hidden_state_;
 public:
+  vector<int> extra_token_types_;
   string main_input_name_;
   void *lexval;
   Input *lexloc;
@@ -67,6 +69,7 @@ public:
 
   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 ();
@@ -76,6 +79,7 @@ public:
   void pop_state ();
   void LexerError (char const *);
   void set_identifier (SCM name_string, SCM);
+  int get_state () const;
   bool is_note_state () const;
   bool is_chord_state () const;
   bool is_lyric_state () const;
index 178ea88e97eb9d070e22359aa0458f2487158fab..9d333ff511fe68dbf31fc68441ce47b2d28610d8 100644 (file)
@@ -43,6 +43,7 @@ using namespace std;
 #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 @@ RH 7 fix (?)
 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 @@ SCM (* scm_parse_error_handler) (void *);
 %option never-interactive 
 %option warn
 
+%x extratoken
 %x chords
 %x figures
 %x incl
@@ -120,6 +121,7 @@ A           [a-zA-Z]
 AA             {A}|_
 N              [0-9]
 AN             {AA}|{N}
+ANY_CHAR       (.|\n)
 PUNCT          [?!:'`]
 ACCENT         \\[`'"^]
 NATIONAL       [\001-\006\021-\027\031\036\200-\377]
@@ -153,6 +155,22 @@ BOM_UTF8   \357\273\277
        // windows-suck-suck-suck
 }
 
+<extratoken>{ANY_CHAR} {
+  /* 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)
     {
@@ -647,6 +665,20 @@ BOM_UTF8   \357\273\277
 
 %%
 
+/* 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 ())
+       {
+               if (YY_START != extratoken)
+                       hidden_state_ = YY_START;
+               yy_push_state (extratoken);
+       }
+       extra_token_types_.push_back (token_type);
+}
+
 void
 Lily_lexer::push_chord_state (SCM tab)
 {
@@ -690,6 +722,7 @@ Lily_lexer::pop_state ()
 {
        if (YYSTATE == notes || YYSTATE == chords)
                pitchname_tab_stack_ = scm_cdr (pitchname_tab_stack_);
+
        yy_pop_state ();
 }
 
@@ -718,7 +751,19 @@ Lily_lexer::scan_escaped_word (string str)
        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)
@@ -762,28 +807,37 @@ Lily_lexer::scan_bare_word (string str)
        return STRING;
 }
 
+int
+Lily_lexer::get_state () const
+{
+       if (YY_START == extratoken)
+               return hidden_state_;
+       else
+               return YY_START;
+}
+
 bool
 Lily_lexer::is_note_state () const
 {
-       return YY_START == notes;
+       return get_state () == notes;
 }
 
 bool
 Lily_lexer::is_chord_state () const
 {
-       return YY_START == chords;
+       return get_state () == chords;
 }
 
 bool
 Lily_lexer::is_lyric_state () const
 {
-       return YY_START == lyrics;
+       return get_state () == lyrics;
 }
 
 bool
 Lily_lexer::is_figure_state () const
 {
-       return YY_START == figures;
+       return get_state () == figures;
 }
 
 /*
@@ -881,59 +935,6 @@ lookup_markup_command (string s)
        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 8f0f7813d9d96b82333f773de3cc82da7cbe67b8..c9b9a07658cdf01ab1b4b9185934fa9d07c639d4 100644 (file)
@@ -35,26 +35,9 @@ LY_DEFINE (ly_make_music_function, "ly:make-music-function", 2, 0, 0,
 {
   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 ef215c327a2cf24e024abff0e422be17e9dece18..3f06738a64b0d7beaba98106a93d753cb529e87f 100644 (file)
@@ -256,6 +256,11 @@ If we give names, Bison complains.
 %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 @@ If we give names, Bison complains.
 %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 @@ If we give names, Bison complains.
 %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 @@ If we give names, Bison complains.
 %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 @@ If we give names, Bison complains.
 %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 @@ If we give names, Bison complains.
 %type <scm> step_number
 %type <scm> step_numbers 
 %type <scm> string
-%type <scm> function_scm_argument
 
 %type <score> score_block
 %type <score> score_body
@@ -925,72 +917,46 @@ function_scm_argument:
        | 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));
        }
        ;
 
@@ -1397,24 +1363,38 @@ chord_body_element:
        }
        ;
 
+music_function_identifier_musicless_prefix: MUSIC_FUNCTION {
+               SCM sig = scm_object_property (yylval.scm, ly_symbol2scm ("music-function-signature"));
+               if (scm_is_pair (sig) && to_boolean (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 {