]> git.donarmstrong.com Git - lilypond.git/blob - lily/lexer.ll
b4e3697a5078cf2385aacd0093b918b65c7f980a
[lilypond.git] / lily / lexer.ll
1 %{ // -*-Fundamental-*-
2 /*
3   lexer.ll -- implement the Flex lexer
4
5   source file of the LilyPond music typesetter
6
7   (c) 1996--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
8            Jan Nieuwenhuizen <janneke@gnu.org>
9 */
10
11
12 /*
13   backup rules
14
15   after making a change to the lexer rules, run 
16       flex -b <this lexer file>
17   and make sure that 
18       lex.backup
19   contains no backup states, but only the reminder
20       Compressed tables always back up.
21  (don-t forget to rm lex.yy.cc :-)
22  */
23
24
25
26 #include <cstdio>
27 #include <cctype>
28 #include <cerrno>
29
30 /* Flex >= 2.5.29 fix; FlexLexer.h's multiple include bracing breaks
31    when building the actual lexer.  */
32
33 #define LEXER_CC
34
35 #include <iostream>
36 using namespace std;
37
38 #include "music-function.hh"
39 #include "source-file.hh"
40 #include "parse-scm.hh"
41 #include "lily-guile.hh"
42 #include "string.hh"
43 #include "string-convert.hh"
44 #include "lily-lexer.hh"
45 #include "interval.hh"
46 #include "lily-guile.hh"
47 #include "parser.hh"
48 #include "warn.hh"
49 #include "main.hh"
50 #include "version.hh"
51 #include "lilypond-input-version.hh"
52 #include "context-def.hh"
53 #include "identifier-smob.hh"
54
55 /*
56 RH 7 fix (?)
57 */
58 #define isatty HORRIBLEKLUDGE
59
60 void strip_trailing_white (String&);
61 void strip_leading_white (String&);
62 String lyric_fudge (String s);
63 int music_function_type (SCM);
64 SCM lookup_markup_command (String s);
65 bool is_valid_version (String s);
66
67
68 #define start_quote()   \
69         yy_push_state (quote);\
70         yylval.string = new String
71
72 #define start_lyric_quote()     \
73         yy_push_state (lyric_quote);\
74         yylval.string = new String
75
76 #define yylval \
77         (*(YYSTYPE*)lexval)
78
79 #define yylloc \
80         (*(YYLTYPE*)lexloc)
81
82 #define YY_USER_ACTION  add_lexed_char (YYLeng ());
83 /*
84
85 LYRICS          ({AA}|{TEX})[^0-9 \t\n\f]*
86
87 */
88
89
90 SCM scan_fraction (String);
91 SCM (* scm_parse_error_handler) (void *);
92
93
94
95 %}
96
97 %option c++
98 %option noyywrap
99 %option nodefault
100 %option debug
101 %option yyclass="Lily_lexer"
102 %option stack
103 %option never-interactive 
104 %option warn
105
106 %x chords
107 %x figures
108 %x incl
109 %x lyrics
110 %x lyric_quote
111 %x longcomment
112 %x markup
113 %x notes
114 %x quote
115 %x renameinput
116 %x version
117
118 A               [a-zA-Z]
119 AA              {A}|_
120 N               [0-9]
121 AN              {AA}|{N}
122 PUNCT           [?!:'`]
123 ACCENT          \\[`'"^]
124 NATIONAL        [\001-\006\021-\027\031\036\200-\377]
125 TEX             {AA}|-|{PUNCT}|{ACCENT}|{NATIONAL}
126 WORD            {A}{AN}*
127 ALPHAWORD       {A}+
128 DIGIT           {N}
129 UNSIGNED        {N}+
130 E_UNSIGNED      \\{N}+
131 FRACTION        {N}+\/{N}+
132 INT             -?{UNSIGNED}
133 REAL            ({INT}\.{N}*)|(-?\.{N}+)
134 KEYWORD         \\{WORD}
135 WHITE           [ \n\t\f\r]
136 HORIZONTALWHITE         [ \t]
137 BLACK           [^ \n\t\f\r]
138 RESTNAME        [rs]
139 NOTECOMMAND     \\{A}+
140 MARKUPCOMMAND   \\({A}|[-_])+
141 LYRICS          ({AA}|{TEX})[^0-9 \t\n\r\f]*
142 ESCAPED         [nt\\'"]
143 EXTENDER        __
144 HYPHEN          --
145 BOM_UTF8        \357\273\277
146 %%
147
148
149 <*>\r           {
150         // windows-suck-suck-suck
151 }
152
153 <INITIAL,chords,lyrics,figures,notes>{BOM_UTF8} {
154   if (this->lexloc->line_number () != 1 || this->lexloc->column_number () != 0)
155     {
156       LexerError (_ ("stray UTF-8 BOM encountered").to_str0 ());
157       exit (1);
158     }
159   if (be_verbose_global)
160      message (_ ("Skipping UTF-8 BOM"));
161 }
162
163 <INITIAL,chords,figures,incl,lyrics,markup,notes>{
164   "%{"  {
165         yy_push_state (longcomment);
166   }
167   %[^{\n\r].*[\n\r]     {
168   }
169   %[^{\n\r]     { // backup rule
170   }
171   %[\n\r]       {
172   }
173   %[^{\n\r].*   {
174   }
175   {WHITE}+      {
176
177   }
178 }
179
180 <INITIAL,chords,lyrics,notes,figures>\\version{WHITE}*  {
181         yy_push_state (version);
182 }
183 <INITIAL,chords,lyrics,notes,figures>\\renameinput{WHITE}*      {
184         yy_push_state (renameinput);
185 }
186 <version>\"[^"]*\"     { /* got the version number */
187         String s (YYText () + 1);
188         s = s.left_string (s.index_last ('\"'));
189
190         yy_pop_state ();
191         if (!is_valid_version (s))
192                 return INVALID;
193
194         SCM top_scope = scm_car (scm_last_pair (scopes_));
195         scm_module_define (top_scope, ly_symbol2scm ("version-seen?"), SCM_BOOL_T);
196
197 }
198 <renameinput>\"[^"]*\"     {
199         String s (YYText () + 1);
200         s = s.left_string (s.index_last ('\"'));
201
202         yy_pop_state ();
203         this->here_input().get_source_file ()->name_ = s;
204         message (_f ("Renaming input to: `%s'", s.to_str0 ()));
205         progress_indication ("\n");
206         scm_module_define (scm_car (scopes_),
207                      ly_symbol2scm ("input-file-name"),
208                      scm_makfrom0str (s.to_str0 ()));
209
210 }
211 <version>.      {
212         LexerError (_ ("quoted string expected after \\version").to_str0 ());
213         yy_pop_state ();
214 }
215 <renameinput>.  {
216         LexerError (_ ("quoted string expected after \\renameinput").to_str0 ());
217         yy_pop_state ();
218 }
219 <longcomment>{
220         [^\%]*          {
221         }
222         \%*[^}%]*               {
223
224         }
225         "%"+"}"         {
226                 yy_pop_state ();
227         }
228         <<EOF>>         {
229                 LexerError (_ ("EOF found inside a comment").to_str0 ());
230                 is_main_input_ = false;
231                 if (! close_input ()) 
232                   yyterminate (); // can't move this, since it actually rets a YY_NULL
233         }
234 }
235
236
237 <INITIAL,chords,lyrics,notes,figures>\\maininput           {
238         if (!is_main_input_)
239         {
240                 start_main_input ();
241                 is_main_input_ = true;
242         }
243         else
244                 error (_ ("\\maininput not allowed outside init files"));
245 }
246
247 <INITIAL,chords,lyrics,figures,notes>\\include           {
248         yy_push_state (incl);
249 }
250 <incl>\"[^"]*\"   { /* got the include file name */
251         String s (YYText ()+1);
252         s = s.left_string (s.index_last ('"'));
253
254         new_input (s, sources_);
255         yy_pop_state ();
256 }
257 <incl>\\{BLACK}*{WHITE} { /* got the include identifier */
258         String s = YYText () + 1;
259         strip_trailing_white (s);
260         if (s.length () && (s[s.length () - 1] == ';'))
261           s = s.left_string (s.length () - 1);
262
263         SCM sid = lookup_identifier (s);
264         if (scm_is_string (sid)) {
265                 new_input (ly_scm2string (sid), sources_);
266                 yy_pop_state ();
267         } else { 
268             String msg (_f ("wrong or undefined identifier: `%s'", s ));
269
270             LexerError (msg.to_str0 ());
271             SCM err = scm_current_error_port ();
272             scm_puts ("This value was found in the table: ", err);
273             scm_display (sid, err);
274           }
275 }
276 <incl>\"[^"]*   { // backup rule
277         error (_ ("end quote missing"));
278         exit (1);
279 }
280 <chords,notes,figures>{RESTNAME}        {
281         char const *s = YYText ();
282         yylval.scm = scm_makfrom0str (s);
283         return RESTNAME;
284 }
285 <chords,notes,figures>R         {
286         return MULTI_MEASURE_REST;
287 }
288 <INITIAL,chords,figures,lyrics,markup,notes>#   { //embedded scm
289         int n = 0;
290         Input hi = here_input();
291         hi.step_forward ();
292         SCM sval = ly_parse_scm (hi.start (), &n, hi,
293                 be_safe_global && is_main_input_);
294
295         if (sval == SCM_UNDEFINED)
296         {
297                 sval = SCM_UNSPECIFIED;
298                 error_level_ = 1;
299         }
300
301         for (int i = 0; i < n; i++)
302         {
303                 yyinput ();
304         }
305         char_count_stack_.top () += n;
306
307         if (unpack_identifier (sval) != SCM_UNDEFINED)
308         {
309                 yylval.scm = unpack_identifier(sval);
310                 return identifier_type (yylval.scm);
311         }
312                 
313         yylval.scm = sval;
314         return SCM_T;
315 }
316 <INITIAL,notes,lyrics>{ 
317         \<\<   {
318                 return DOUBLE_ANGLE_OPEN;
319         }
320         \>\>   {
321                 return DOUBLE_ANGLE_CLOSE;
322         }
323 }
324 <figures>{
325         _       {
326                 return FIGURE_SPACE;
327         }
328         \>              {
329                 return FIGURE_CLOSE;
330         }
331         \<      {
332                 return FIGURE_OPEN;
333         }
334 }
335
336 <notes,figures>{
337         {ALPHAWORD}     {
338                 return scan_bare_word (YYText ());
339         }
340
341         {NOTECOMMAND}   {
342                 return scan_escaped_word (YYText () + 1); 
343         }
344         {FRACTION}      {
345                 yylval.scm =  scan_fraction (YYText ());
346                 return FRACTION;
347         }
348
349         {DIGIT}         {
350                 yylval.i = String_convert::dec2int (String (YYText ()));
351                 return DIGIT;
352         }
353         {UNSIGNED}              {
354                 yylval.i = String_convert::dec2int (String (YYText ()));
355                 return UNSIGNED;
356         }
357         {E_UNSIGNED}    {
358                 yylval.i = String_convert::dec2int (String (YYText () +1));
359                 return E_UNSIGNED;
360         }
361         \" {
362                 start_quote ();
363         }
364 }
365
366 \"              {
367         start_quote ();
368 }
369 <quote>{
370         \\{ESCAPED}     {
371                 *yylval.string += to_string (escaped_char (YYText ()[1]));
372         }
373         [^\\"]+ {
374                 *yylval.string += YYText ();
375         }
376         \"      {
377
378                 yy_pop_state ();
379
380                 /* yylval is union. Must remember STRING before setting SCM*/
381                 String *sp = yylval.string;
382                 yylval.scm = scm_makfrom0str (sp->to_str0 ());
383                 delete sp;
384                 return STRING;
385         }
386         .       {
387                 *yylval.string += YYText ();
388         }
389 }
390 <lyric_quote>{
391         \\{ESCAPED}     {
392                 *yylval.string += to_string (escaped_char (YYText ()[1]));
393         }
394         [^\\"]+ {
395                 *yylval.string += YYText ();
396         }
397         \"      {
398
399                 yy_pop_state ();
400
401                 /* yylval is union. Must remember STRING before setting SCM*/
402                 String *sp = yylval.string;
403                 yylval.scm = scm_makfrom0str (sp->to_str0 ());
404                 delete sp;
405                 return LYRICS_STRING;
406         }
407         .       {
408                 *yylval.string += YYText ();
409         }
410 }
411
412 <lyrics>{
413         \" {
414                 start_lyric_quote ();
415         }
416         {FRACTION}      {
417                 yylval.scm =  scan_fraction (YYText ());
418                 return FRACTION;
419         }
420         {UNSIGNED}              {
421                 yylval.i = String_convert::dec2int (String (YYText ()));
422                 return UNSIGNED;
423         }
424         {NOTECOMMAND}   {
425                 return scan_escaped_word (YYText () + 1);
426         }
427         {LYRICS} {
428                 /* ugr. This sux. */
429                 String s (YYText ()); 
430                 if (s == "__")
431                         return yylval.i = EXTENDER;
432                 if (s == "--")
433                         return yylval.i = HYPHEN;
434                 s = lyric_fudge (s);
435
436                 char c = s[s.length () - 1];
437                 if (c == '{' ||  c == '}') // brace open is for not confusing dumb tools.
438                         here_input ().warning (
439                                 _ ("Brace found at end of lyric.  Did you forget a space?"));
440                 yylval.scm = scm_makfrom0str (s.to_str0 ());
441
442
443                 return LYRICS_STRING;
444         }
445         . {
446                 return YYText ()[0];
447         }
448 }
449 <chords>{
450         {ALPHAWORD}     {
451                 return scan_bare_word (YYText ());
452         }
453         {NOTECOMMAND}   {
454                 return scan_escaped_word (YYText () + 1);
455         }
456         {FRACTION}      {
457                 yylval.scm =  scan_fraction (YYText ());
458                 return FRACTION;
459         }
460         {UNSIGNED}              {
461                 yylval.i = String_convert::dec2int (String (YYText ()));
462                 return UNSIGNED;
463         }
464         \" {
465                 start_quote ();
466         }
467         -  {
468                 return CHORD_MINUS;
469         }
470         :  {
471                 return CHORD_COLON;
472         }
473         \/\+ {
474                 return CHORD_BASS;
475         }
476         \/  {
477                 return CHORD_SLASH;
478         }
479         \^  {
480                 return CHORD_CARET;
481         }
482         . {
483                 return YYText ()[0];
484         }
485 }
486
487
488 <markup>{
489         \" {
490                 start_quote ();
491         }
492         \\score {
493                 return SCORE;
494         }
495         {MARKUPCOMMAND} {
496                 String str (YYText () + 1);
497                 SCM s = lookup_markup_command (str);
498
499                 if (scm_is_pair (s) && scm_is_symbol (scm_cdr (s)) ) {
500                         yylval.scm = scm_car(s);
501                         SCM tag = scm_cdr(s);
502                         if (tag == ly_symbol2scm("markup0"))
503                                 return MARKUP_HEAD_MARKUP0;
504                         if (tag == ly_symbol2scm("empty"))
505                                 return MARKUP_HEAD_EMPTY;
506                         else if (tag == ly_symbol2scm ("markup0-markup1"))
507                                 return MARKUP_HEAD_MARKUP0_MARKUP1;
508                         else if (tag == ly_symbol2scm ("markup-list0"))
509                                 return MARKUP_HEAD_LIST0;
510                         else if (tag == ly_symbol2scm ("scheme0"))
511                                 return MARKUP_HEAD_SCM0;
512                         else if (tag == ly_symbol2scm ("scheme0-scheme1"))
513                                 return MARKUP_HEAD_SCM0_SCM1;
514                         else if (tag == ly_symbol2scm ("scheme0-markup1"))
515                                 return MARKUP_HEAD_SCM0_MARKUP1;
516                         else if (tag == ly_symbol2scm ("scheme0-scheme1-markup2"))
517                                 return MARKUP_HEAD_SCM0_SCM1_MARKUP2;
518                         else if (tag == ly_symbol2scm ("scheme0-scheme1-scheme2"))
519                                 return MARKUP_HEAD_SCM0_SCM1_SCM2;
520                         else {
521                                 programming_error ("no parser tag defined for this markup signature"); 
522                                 ly_display_scm (s);
523                                 assert(false);
524                         }
525                 } else
526                         return scan_escaped_word (str);
527         }
528         [{}]    {
529                 return YYText ()[0];
530         }
531         [^#{}"\\ \t\n\r\f]+ {
532                 String s (YYText ()); 
533
534                 char c = s[s.length () - 1];
535                 /* brace open is for not confusing dumb tools.  */
536                 if (c == '{' ||  c == '}')
537                         here_input ().warning (
538                                 _ ("Brace found at end of markup.  Did you forget a space?"));
539                 yylval.scm = scm_makfrom0str (s.to_str0 ());
540
541
542                 return STRING;
543         }
544         .  {
545                 return YYText()[0];
546         }
547 }
548
549 <<EOF>> {
550         if (is_main_input_)
551         {
552                 is_main_input_ = false;
553                 if (!close_input ())
554                 /* Returns YY_NULL */
555                         yyterminate ();
556         }
557         else if (!close_input ())
558                 /* Returns YY_NULL */
559                 yyterminate ();
560 }
561
562
563 {WORD}  {
564         return scan_bare_word (YYText ());
565 }
566 {KEYWORD}       {
567         return scan_escaped_word (YYText () + 1);
568 }
569 {REAL}          {
570         Real r;
571         int cnv = sscanf (YYText (), "%lf", &r);
572         assert (cnv == 1);
573
574         yylval.scm = scm_from_double (r);
575         return REAL;
576 }
577
578 {UNSIGNED}      {
579         yylval.i = String_convert::dec2int (String (YYText ()));
580         return UNSIGNED;
581 }
582
583
584 [{}]    {
585
586         return YYText ()[0];
587 }
588 [*:=]           {
589         char c = YYText ()[0];
590
591         return c;
592 }
593
594 <INITIAL,notes,figures>.        {
595         return YYText ()[0];
596 }
597
598 <INITIAL,lyrics,notes,figures>\\. {
599     char c = YYText ()[1];
600
601     switch (c) {
602     case '>':
603         return E_ANGLE_CLOSE;
604     case '<':
605         return E_ANGLE_OPEN;
606     case '!':
607         return E_EXCLAMATION;
608     case '(':
609         return E_OPEN;
610     case ')':
611         return E_CLOSE;
612     case '[':
613         return E_BRACKET_OPEN;
614     case '+':
615         return E_PLUS;
616     case ']':
617         return E_BRACKET_CLOSE;
618     case '~':
619         return E_TILDE;
620     case '\\':
621         return E_BACKSLASH;
622
623     default:
624         return E_CHAR;
625     }
626 }
627
628 <*>.            {
629         String msg = _f ("invalid character: `%c'", YYText ()[0]);
630         LexerError (msg.to_str0 ());
631         return YYText ()[0];
632 }
633
634 %%
635
636 void
637 Lily_lexer::push_chord_state (SCM tab)
638 {
639         pitchname_tab_stack_ = scm_cons (tab, pitchname_tab_stack_);
640         yy_push_state (chords);
641 }
642
643 void
644 Lily_lexer::push_figuredbass_state ()
645 {
646         yy_push_state (figures);
647 }
648
649 void
650 Lily_lexer::push_initial_state ()
651 {
652         yy_push_state (INITIAL);
653 }
654
655 void
656 Lily_lexer::push_lyric_state ()
657 {
658         yy_push_state (lyrics);
659 }
660
661 void
662 Lily_lexer::push_markup_state ()
663 {
664         yy_push_state (markup);
665 }
666
667 void
668 Lily_lexer::push_note_state (SCM tab)
669 {
670         pitchname_tab_stack_ = scm_cons (tab, pitchname_tab_stack_);
671         yy_push_state (notes);
672 }
673
674 void
675 Lily_lexer::pop_state ()
676 {
677         if (YYSTATE == notes || YYSTATE == chords)
678                 pitchname_tab_stack_ = scm_cdr (pitchname_tab_stack_);
679         yy_pop_state ();
680 }
681
682 int
683 Lily_lexer::identifier_type (SCM sid)
684 {
685         int k = try_special_identifiers (&yylval.scm , sid);
686         return k >= 0  ? k : SCM_IDENTIFIER;
687 }
688
689
690 int
691 Lily_lexer::scan_escaped_word (String str)
692 {
693         // use more SCM for this.
694
695 //      SCM sym = ly_symbol2scm (str.to_str0 ());
696
697         int i = lookup_keyword (str);
698         if (i == MARKUP && is_lyric_state ())
699                 return LYRIC_MARKUP;
700         if (i != -1)
701                 return i;
702
703         SCM sid = lookup_identifier (str);
704         if (is_music_function (sid))
705         {
706                 yylval.scm = get_music_function_transform (sid);
707                 return music_function_type (yylval.scm);
708         }
709
710         if (sid != SCM_UNDEFINED)
711         {
712                 yylval.scm = sid;
713                 return identifier_type (sid);
714         }
715
716         String msg (_f ("unknown escaped string: `\\%s'", str));        
717         LexerError (msg.to_str0 ());
718
719         yylval.scm = scm_makfrom0str (str.to_str0 ());
720
721         return STRING;
722 }
723
724 int
725 Lily_lexer::scan_bare_word (String str)
726 {
727         SCM sym = ly_symbol2scm (str.to_str0 ());
728         if ((YYSTATE == notes) || (YYSTATE == chords)) {
729                 SCM handle = SCM_BOOL_F;
730                 if (scm_is_pair (pitchname_tab_stack_))
731                         handle = scm_hashq_get_handle (scm_car (pitchname_tab_stack_), sym);
732                 
733                 if (scm_is_pair (handle)) {
734                         yylval.scm = scm_cdr (handle);
735                         if (unsmob_pitch (yylval.scm)) 
736                             return (YYSTATE == notes) ? NOTENAME_PITCH : TONICNAME_PITCH;
737                         else if (scm_is_symbol (yylval.scm))
738                             return DRUM_PITCH;
739                 }
740                 else if ((handle = scm_hashq_get_handle (chordmodifier_tab_, sym))!= SCM_BOOL_F)
741                 {
742                     yylval.scm = scm_cdr (handle);
743                     return CHORD_MODIFIER;
744                 }
745         }
746
747         yylval.scm = scm_makfrom0str (str.to_str0 ());
748         return STRING;
749 }
750
751 bool
752 Lily_lexer::is_note_state () const
753 {
754         return YY_START == notes;
755 }
756
757 bool
758 Lily_lexer::is_chord_state () const
759 {
760         return YY_START == chords;
761 }
762
763 bool
764 Lily_lexer::is_lyric_state () const
765 {
766         return YY_START == lyrics;
767 }
768
769 bool
770 Lily_lexer::is_figure_state () const
771 {
772         return YY_START == figures;
773 }
774
775 /*
776  urg, belong to String (_convert)
777  and should be generalised 
778  */
779 void
780 strip_leading_white (String&s)
781 {
782         int i = 0;
783         for (;  i < s.length (); i++) 
784                 if (!isspace (s[i]))
785                         break;
786
787         s = s.nomid_string (0, i);
788 }
789
790 void
791 strip_trailing_white (String&s)
792 {
793         int i = s.length ();    
794         while (i--) 
795                 if (!isspace (s[i]))
796                         break;
797
798         s = s.left_string (i+1);
799 }
800
801
802
803 /* 2.1.2x something -> \property -> \set. */ 
804 Lilypond_version oldest_version ("2.3.22");
805
806
807 bool
808 is_valid_version (String s)
809 {
810   Lilypond_version current ( MAJOR_VERSION "." MINOR_VERSION "." PATCH_LEVEL );
811   Lilypond_version ver (s);
812   if (! ((ver >= oldest_version) && (ver <= current)))
813         {       
814                 non_fatal_error (_f ("Incorrect lilypond version: %s (%s, %s)", ver.to_string (), oldest_version.to_string (), current.to_string ()));
815                 non_fatal_error (_ ("Consider updating the input with the convert-ly script")); 
816                 return false;
817     }
818   return true;
819 }
820         
821
822 /*
823   substitute _ and \,
824 */
825 String
826 lyric_fudge (String s)
827 {
828   char  * chars  = s.get_copy_str0 ();
829
830   for (char * p = chars; *p ; p++)
831     {
832       if (*p == '_' && (p == chars || *(p-1) != '\\'))
833         *p = ' ';
834     }
835   
836   s = String (chars);
837   delete[] chars;
838
839   int i = 0;    
840   if ((i = s.index ("\\,")) != -1)   // change "\," to TeX's "\c "
841     {
842       * (s.get_str0 () + i + 1) = 'c';
843       s = s.left_string (i+2) + " " + s.right_string (s.length ()-i-2);
844     }
845
846   return s;
847 }
848
849 /*
850 Convert "NUM/DEN" into a '(NUM . DEN) cons.
851 */
852 SCM
853 scan_fraction (String frac)
854 {
855         int i = frac.index ('/');
856         int l = frac.length ();
857         String left = frac.left_string (i);
858         String right = frac.right_string (l - i - 1);
859
860         int n = String_convert::dec2int (left);
861         int d = String_convert::dec2int (right);
862         return scm_cons (scm_from_int (n), scm_from_int (d));
863 }
864
865 // Breaks for flex 2.5.31
866 #if 0
867 /* avoid silly flex induced gcc warnings */
868 static void yy_push_state (int) {;}
869 static void yy_pop_state () {;}
870 static int yy_top_state () { return 0; }
871
872 static void
873 avoid_silly_flex_induced_gcc_warnings ()
874 {
875         (void)yy_start_stack_ptr;
876         (void)yy_start_stack_depth;
877         (void)yy_start_stack;
878         yy_push_state (0);
879         yy_pop_state ();
880         yy_top_state ();
881         avoid_silly_flex_induced_gcc_warnings ();
882 }
883 #endif
884
885 SCM
886 lookup_markup_command (String s)
887 {
888         SCM proc = ly_lily_module_constant ("lookup-markup-command");
889         return scm_call_1 (proc, scm_makfrom0str (s.to_str0 ()));
890 }
891
892
893 int
894 music_function_type (SCM func)
895 {
896         SCM type = scm_object_property (func, ly_symbol2scm ("music-function-signature-keyword"));
897         if (type == ly_symbol2scm ("scm"))
898         {
899                 return MUSIC_FUNCTION_SCM;
900         }
901         else if (type == ly_symbol2scm ("music"))
902         {
903                 return MUSIC_FUNCTION_MUSIC;
904         }
905         else if (type == ly_symbol2scm ("scm-music"))
906         {
907                 return MUSIC_FUNCTION_SCM_MUSIC;
908         }
909         else if (type == ly_symbol2scm ("scm-scm"))
910         {
911                 return MUSIC_FUNCTION_SCM_SCM;
912         }
913         else if (type == ly_symbol2scm ("music-music"))
914         {
915                 return MUSIC_FUNCTION_MUSIC_MUSIC;
916         }
917         else if (type == ly_symbol2scm ("scm-music-music"))
918         {
919                 return MUSIC_FUNCTION_SCM_MUSIC_MUSIC;
920         }
921         else if (type == ly_symbol2scm ("scm-scm-music"))
922         {
923                 return MUSIC_FUNCTION_SCM_SCM_MUSIC;
924         }
925         else if (type == ly_symbol2scm ("scm-scm-scm"))
926         {
927                 return MUSIC_FUNCTION_SCM_SCM_SCM;
928         }
929         else if (type == ly_symbol2scm ("markup"))
930         {
931                 return MUSIC_FUNCTION_MARKUP;
932         }
933         else if (type == ly_symbol2scm ("markup-music"))
934         {
935                 return MUSIC_FUNCTION_MARKUP_MUSIC;
936         }
937         else if (type == ly_symbol2scm ("markup-markup"))
938         {
939                 return MUSIC_FUNCTION_MARKUP_MARKUP;
940         }
941         else if (type == ly_symbol2scm ("markup-music-music"))
942         {
943                 return MUSIC_FUNCTION_MARKUP_MUSIC_MUSIC;
944         }
945         else if (type == ly_symbol2scm ("markup-markup-music"))
946         {
947                 return MUSIC_FUNCTION_MARKUP_MARKUP_MUSIC;
948         }
949         else if (type == ly_symbol2scm ("noarg"))
950         {
951                 return MUSIC_FUNCTION;
952         }
953         else
954                 {
955                 /* TODO: print location */
956                 error (_ ("can't find signature for music function"));
957                 }
958
959         return MUSIC_FUNCTION_SCM;
960 }
961
962 /* Shut up lexer warnings.  */
963 #if YY_STACK_USED
964
965 static void
966 yy_push_state (int)
967 {
968 }
969
970 static void
971 yy_pop_state ()
972 {
973 }
974
975 static int
976 yy_top_state ()
977 {
978   return 0;
979 }
980
981 static void
982 silence_lexer_warnings ()
983 {
984    (void) yy_start_stack_ptr;
985    (void) yy_start_stack_depth;
986    (void) yy_start_stack;
987    (void) yy_push_state;
988    (void) yy_pop_state;
989    (void) yy_top_state;
990    (void) silence_lexer_warnings;
991 }
992 #endif