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