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