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