]> git.donarmstrong.com Git - lilypond.git/blob - lily/lexer.ll
release: 1.4.12
[lilypond.git] / lily / lexer.ll
1 %{ // -*-Fundamental-*-
2 /*
3   lexer.l -- implement the Flex lexer
4
5   source file of the LilyPond music typesetter
6
7   (c) 1996--2000 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 #include <stdio.h>
26 #include <ctype.h>
27 #include <iostream.h> /* gcc 3.0 */
28 #include <errno.h>
29
30
31 #include "score.hh"
32 #include "lily-guile.hh"
33 #include "string.hh"
34 #include "string-convert.hh"
35 #include "my-lily-lexer.hh"
36 #include "array.hh"
37 #include "interval.hh"
38 #include "lily-guile.hh"
39 #include "parser.hh"
40 #include "debug.hh"
41 #include "main.hh"
42 #include "musical-request.hh"
43 #include "version.hh"
44 #include "lilypond-input-version.hh"
45 #include "translator-def.hh"
46 #include "music-output-def.hh"
47
48 /*
49 RH 7 fix (?)
50 */
51 #define isatty HORRIBLEKLUDGE
52
53 void strip_trailing_white (String&);
54 void strip_leading_white (String&);
55 String lyric_fudge (String s);
56
57
58 bool
59 valid_version_b (String s);
60
61
62
63 #define start_quote()   \
64         yy_push_state (quote);\
65         yylval.string = new String
66
67 #define yylval \
68         (*(YYSTYPE*)lexval_l)
69
70 #define YY_USER_ACTION  add_lexed_char (YYLeng ());
71 /*
72
73 LYRICS          ({AA}|{TEX})[^0-9 \t\n\f]*
74
75 */
76
77
78 SCM scan_fraction (String);
79
80 %}
81
82 %option c++
83 %option noyywrap
84 %option nodefault
85 %option debug
86 %option yyclass="My_lily_lexer"
87 %option stack
88 %option never-interactive 
89 %option warn
90
91 %x version
92 %x chords
93 %x incl
94 %x lyrics
95 %x notes
96 %x quote
97 %x longcomment
98
99
100 A               [a-zA-Z]
101 AA              {A}|_
102 N               [0-9]
103 AN              {AA}|{N}
104 PUNCT           [?!:'`]
105 ACCENT          \\[`'"^]
106 NATIONAL        [\001-\006\021-\027\031\036\200-\377]
107 TEX             {AA}|-|{PUNCT}|{ACCENT}|{NATIONAL}
108 WORD            {A}{AN}*
109 ALPHAWORD       {A}+
110 DIGIT           {N}
111 UNSIGNED        {N}+
112 FRACTION        {N}+\/{N}+
113 INT             -?{UNSIGNED}
114 REAL            ({INT}\.{N}*)|(-?\.{N}+)
115 KEYWORD         \\{WORD}
116 WHITE           [ \n\t\f\r]
117 HORIZONTALWHITE         [ \t]
118 BLACK           [^ \n\t\f\r]
119 RESTNAME        [rs]
120 NOTECOMMAND     \\{A}+
121 LYRICS          ({AA}|{TEX})[^0-9 \t\n\f]*
122 ESCAPED         [nt\\'"]
123 EXTENDER        __
124 HYPHEN          --
125 %%
126
127
128 <*>\r           {
129         // windows-suck-suck-suck
130 }
131
132 <INITIAL,chords,incl,lyrics,notes>{
133   "%{"  {
134         yy_push_state (longcomment);
135   }
136   %[^{\n].*\n   {
137   }
138   %[^{\n]       { // backup rule
139   }
140   %\n   {
141   }
142   %[^{\n].*     {
143   }
144   {WHITE}+      {
145
146   }
147 }
148
149 <INITIAL,chords,lyrics,notes>\\version{WHITE}*  {
150         yy_push_state (version);
151 }
152 <version>\"[^"]*\"     { /* got the version number */
153         String s (YYText ()+1);
154         s = s.left_str (s.index_last_i ('"'));
155
156         yy_pop_state ();
157         if (!valid_version_b (s))
158                 return INVALID;
159 }
160 <version>.      {
161         LexerError ("No quoted string found after \\version");
162         yy_pop_state ();
163 }
164 <longcomment>{
165         [^\%]*          {
166         }
167         \%*[^}%]*               {
168
169         }
170         "%"+"}"         {
171                 yy_pop_state ();
172         }
173         <<EOF>>         {
174                 LexerError (_ ("EOF found inside a comment").ch_C ());
175                 if (! close_input ()) 
176                   yyterminate (); // can't move this, since it actually rets a YY_NULL
177         }
178 }
179
180
181 <INITIAL,chords,lyrics,notes>\\maininput           {
182         if (!main_input_b_)
183         {
184                 start_main_input ();
185                 main_input_b_ = true;
186         }
187         else
188                 error (_ ("\\maininput disallowed outside init files"));
189 }
190
191 <INITIAL,chords,lyrics,notes>\\include           {
192         yy_push_state (incl);
193 }
194 <incl>\"[^"]*\";?   { /* got the include file name */
195         String s (YYText ()+1);
196         s = s.left_str (s.index_last_i ('"'));
197
198         new_input (s,source_global_l);
199         yy_pop_state ();
200 }
201 <incl>\\{BLACK}*;?{WHITE} { /* got the include identifier */
202         String s = YYText () + 1;
203         strip_trailing_white (s);
204         if (s.length_i () && (s[s.length_i () - 1] == ';'))
205           s = s.left_str (s.length_i () - 1);
206
207         SCM sid = lookup_identifier (s);
208         if (gh_string_p (sid)) {
209                 new_input (ly_scm2string (sid), source_global_l);
210                 yy_pop_state ();
211         } else { 
212             String msg (_f ("wrong or undefined identifier: `%s'", s ));
213
214             LexerError (msg.ch_C ());
215             SCM err = scm_current_error_port ();
216             scm_puts ("This value was found in the table: ", err);
217             scm_display (sid, err);
218           }
219 }
220 <incl>\"[^"]*   { // backup rule
221         cerr << _ ("Missing end quote") << endl;
222         exit (1);
223 }
224 <chords,notes>{RESTNAME}        {
225         const char *s = YYText ();
226         yylval.scm = ly_str02scm (s);
227         return RESTNAME;
228 }
229 <chords,notes>R         {
230         return MULTI_MEASURE_REST;
231 }
232 <INITIAL,chords,lyrics,notes>\\\${BLACK}*{WHITE}        {
233         String s=YYText () + 2;
234         s=s.left_str (s.length_i () - 1);
235         return scan_escaped_word (s); 
236 }
237 <INITIAL,chords,lyrics,notes>\${BLACK}*{WHITE}          {
238         String s=YYText () + 1;
239         s=s.left_str (s.length_i () - 1);
240         return scan_bare_word (s);
241 }
242 <INITIAL,chords,lyrics,notes>\\\${BLACK}*               { // backup rule
243         cerr << _ ("white expected") << endl;
244         exit (1);
245 }
246 <INITIAL,chords,lyrics,notes>\${BLACK}*         { // backup rule
247         cerr << _ ("white expected") << endl;
248         exit (1);
249 }
250
251 <INITIAL,chords,lyrics,notes>#  { //embedded scm
252         //char const* s = YYText () + 1;
253         char const* s = here_ch_C ();
254         int n = 0;
255         if (main_input_b_ && safe_global_b) {
256                 error (_ ("Can't evaluate Scheme in safe mode"));
257                 yylval.scm =  SCM_EOL;
258                 return SCM_T;
259         }
260         yylval.scm = ly_parse_scm (s, &n);
261         
262         for (int i=0; i < n; i++)
263         {
264                 yyinput ();
265         }
266         char_count_stack_.top () += n;
267
268         return SCM_T;
269 }
270 <notes>{
271         {ALPHAWORD}     {
272                 return scan_bare_word (YYText ());
273         }
274
275         {NOTECOMMAND}   {
276                 return scan_escaped_word (YYText () + 1); 
277         }
278         {FRACTION}      {
279                 yylval.scm =  scan_fraction (YYText ());
280                 return FRACTION;
281         }
282
283         {DIGIT}         {
284                 yylval.i = String_convert::dec2_i (String (YYText ()));
285                 return DIGIT;
286         }
287         {UNSIGNED}              {
288                 yylval.i = String_convert::dec2_i (String (YYText ()));
289                 return UNSIGNED;
290         }
291
292         \" {
293                 start_quote ();
294         }
295 }
296
297 \"              {
298         start_quote ();
299 }
300 <quote>{
301         \\{ESCAPED}     {
302                 *yylval.string += to_str (escaped_char (YYText ()[1]));
303         }
304         [^\\"]+ {
305                 *yylval.string += YYText ();
306         }
307         \"      {
308
309                 yy_pop_state ();
310
311                 /* yylval is union. Must remember STRING before setting SCM*/
312                 String *sp = yylval.string;
313                 yylval.scm = ly_str02scm (sp->ch_C ());
314                 delete sp;
315                 return STRING;
316         }
317         .       {
318                 *yylval.string += YYText ();
319         }
320 }
321
322 <lyrics>{
323         \" {
324                 start_quote ();
325         }
326         {FRACTION}      {
327                 yylval.scm =  scan_fraction (YYText ());
328                 return FRACTION;
329         }
330         {UNSIGNED}              {
331                 yylval.i = String_convert::dec2_i (String (YYText ()));
332                 return UNSIGNED;
333         }
334         {NOTECOMMAND}   {
335                 return scan_escaped_word (YYText () + 1);
336         }
337         {LYRICS} {
338                 /* ugr. This sux. */
339                 String s (YYText ()); 
340                 if (s == "__")
341                         return yylval.i = EXTENDER;
342                 if (s == "--")
343                         return yylval.i = HYPHEN;
344                 s = lyric_fudge (s);
345
346                 char c = s[s.length_i () - 1];
347                 if (c == '{' ||  c == '}') // brace open is for not confusing dumb tools.
348                         here_input ().warning (
349                                 _ ("Brace found at end of lyric. Did you forget a space?"));
350                 yylval.scm = ly_str02scm (s.ch_C ());
351
352
353                 return STRING;
354         }
355         . {
356                 return YYText ()[0];
357         }
358 }
359 <chords>{
360         {ALPHAWORD}     {
361                 return scan_bare_word (YYText ());
362         }
363         {NOTECOMMAND}   {
364                 return scan_escaped_word (YYText () + 1);
365         }
366         {FRACTION}      {
367                 yylval.scm =  scan_fraction (YYText ());
368                 return FRACTION;
369         }
370         {UNSIGNED}              {
371                 yylval.i = String_convert::dec2_i (String (YYText ()));
372                 return UNSIGNED;
373         }
374         \" {
375                 start_quote ();
376         }
377         -  {
378                 return CHORD_MINUS;
379         }
380         :  {
381                 return CHORD_COLON;
382         }
383         \/\+ {
384                 return CHORD_BASS;
385         }
386         \^  {
387                 return CHORD_CARET;
388         }
389         . {
390                 return YYText ()[0];
391         }
392 }
393
394 <<EOF>> {
395
396
397         if (! close_input ()) { 
398           yyterminate (); // can't move this, since it actually rets a YY_NULL
399         }
400 }
401
402
403 {WORD}  {
404         return scan_bare_word (YYText ());
405 }
406 {KEYWORD}       {
407         return scan_escaped_word (YYText () + 1);
408 }
409 {REAL}          {
410         Real r;
411         int cnv=sscanf (YYText (), "%lf", &r);
412         assert (cnv == 1);
413
414         yylval.scm = gh_double2scm (r);
415         return REAL;
416 }
417
418 {UNSIGNED}      {
419         yylval.i = String_convert::dec2_i (String (YYText ()));
420         return UNSIGNED;
421 }
422
423 [{}]    {
424
425         return YYText ()[0];
426 }
427 [*:=]           {
428         char c = YYText ()[0];
429
430         return c;
431 }
432
433 <INITIAL,notes>.        {
434         return YYText ()[0];
435 }
436
437 <INITIAL,lyrics,notes>\\. {
438     char c= YYText ()[1];
439
440     switch (c) {
441     case '>':
442         return E_BIGGER;
443     case '<':
444         return E_SMALLER;
445     case '!':
446         return E_EXCLAMATION;
447     case '\\':
448         return E_BACKSLASH;
449     case '(':
450         return E_OPEN;
451     case ')':
452         return E_CLOSE;
453     default:
454         return E_CHAR;
455     }
456 }
457
458 <*>.            {
459         String msg = _f ("invalid character: `%c'", YYText ()[0]);
460         LexerError (msg.ch_C ());
461         return YYText ()[0];
462 }
463
464 %%
465
466 void
467 My_lily_lexer::push_note_state ()
468 {
469         yy_push_state (notes);
470 }
471
472 void
473 My_lily_lexer::push_chord_state ()
474 {
475         yy_push_state (chords);
476 }
477
478 void
479 My_lily_lexer::push_lyric_state ()
480 {
481         yy_push_state (lyrics);
482 }
483
484 void
485 My_lily_lexer::pop_state ()
486 {
487         yy_pop_state ();
488 }
489
490 int
491 My_lily_lexer::scan_escaped_word (String str)
492 {
493         // use more SCM for this.
494
495         SCM sym = ly_symbol2scm (str.ch_C ());
496
497         int l = lookup_keyword (str);
498         if (l != -1) {
499                 return l;
500         }
501         SCM sid = lookup_identifier (str);
502         if (gh_string_p (sid)) {
503                 yylval.scm = sid; 
504                 return STRING_IDENTIFIER;
505         } else if (gh_number_p (sid)) {
506                 yylval.scm = sid;
507                 return NUMBER_IDENTIFIER;
508         } else if (unsmob_translator_def (sid)) {
509                 yylval.scm = sid;
510                 return TRANSLATOR_IDENTIFIER;
511         } else if (unsmob_score (sid)) {
512                 yylval.scm =sid;
513                 return SCORE_IDENTIFIER;
514         } else if (Music * mus =unsmob_music (sid)) {
515                 yylval.scm = sid;
516                 
517                 return dynamic_cast<Request*> (mus) ? REQUEST_IDENTIFIER : MUSIC_IDENTIFIER;
518         } else if (unsmob_duration (sid)) {
519                 yylval.scm = sid;
520                 return DURATION_IDENTIFIER;
521         } else if (unsmob_music_output_def (sid)) {
522                 yylval.scm = sid;
523                 return MUSIC_OUTPUT_DEF_IDENTIFIER;
524         }
525
526         if (sid != SCM_UNDEFINED) {
527                 yylval.scm = sid;
528                 return SCM_IDENTIFIER;
529         }
530
531         if ((YYSTATE != notes) && (YYSTATE != chords)) {
532                 SCM pitch = scm_hashq_get_handle (pitchname_tab_, sym);
533                 
534                 if (gh_pair_p (pitch))
535                 {
536                         yylval.scm = gh_cdr (pitch);
537                         return NOTENAME_PITCH;
538                 }
539         }
540         String msg (_f ("unknown escaped string: `\\%s'", str));        
541         LexerError (msg.ch_C ());
542
543         yylval.scm = ly_str02scm (str.ch_C ());
544
545         return STRING;
546 }
547
548 int
549 My_lily_lexer::scan_bare_word (String str)
550 {
551         SCM sym = ly_symbol2scm (str.ch_C ());
552         if ((YYSTATE == notes) || (YYSTATE == chords)) {
553                 SCM pitch = scm_hashq_get_handle (pitchname_tab_, sym);
554                 if (gh_pair_p (pitch)) {
555                     yylval.scm = gh_cdr (pitch);
556                     return (YYSTATE == notes) ? NOTENAME_PITCH : TONICNAME_PITCH;
557                 } else if ((pitch = scm_hashq_get_handle (chordmodifier_tab_, sym))!= SCM_BOOL_F)
558                 {
559                     yylval.scm = gh_cdr (pitch);
560                     return CHORDMODIFIER_PITCH;
561                 }
562         }
563
564         yylval.scm = ly_str02scm (str.ch_C ());
565         return STRING;
566 }
567
568 bool
569 My_lily_lexer::note_state_b () const
570 {
571         return YY_START == notes;
572 }
573
574 bool
575 My_lily_lexer::chord_state_b () const
576 {
577         return YY_START == chords;
578 }
579
580 bool
581 My_lily_lexer::lyric_state_b () const
582 {
583         return YY_START == lyrics;
584 }
585
586 /*
587  urg, belong to String (_convert)
588  and should be generalised 
589  */
590 void
591 strip_leading_white (String&s)
592 {
593         int i=0;
594         for (;  i < s.length_i (); i++) 
595                 if (!isspace (s[i]))
596                         break;
597
598         s = s.nomid_str (0, i);
599 }
600
601 void
602 strip_trailing_white (String&s)
603 {
604         int i=s.length_i ();    
605         while (i--) 
606                 if (!isspace (s[i]))
607                         break;
608
609         s = s.left_str (i+1);
610 }
611
612
613
614 Lilypond_version oldest_version ("1.3.59");
615
616 void
617 print_lilypond_versions (ostream &os)
618 {
619   os << _f ("Oldest supported input version: %s", oldest_version.str ()) 
620     << endl;
621 }
622
623
624 bool
625 valid_version_b (String s)
626 {
627   Lilypond_version current ( MAJOR_VERSION "." MINOR_VERSION "." PATCH_LEVEL );
628   Lilypond_version ver (s);
629   if (! ((ver >= oldest_version) && (ver <= current)))
630         {       
631                 non_fatal_error (_f ("incorrect lilypond version: %s (%s, %s)", ver.str (), oldest_version.str (), current.str ()));
632                 non_fatal_error (_ ("Consider converting the input with the convert-ly script")); 
633                 return false;
634     }
635   return true;
636 }
637         
638
639 String
640 lyric_fudge (String s)
641 {
642   char  * chars  =s.copy_ch_p ();
643
644   for (char * p = chars; *p ; p++)
645     {
646       if (*p == '_' && (p == chars || *(p-1) != '\\'))
647         *p = ' ';
648     }
649   
650   s = String (chars);
651   delete[] chars;
652
653   int i =0;     
654   if ((i=s.index_i ("\\,")) != -1)   // change "\," to TeX's "\c "
655     {
656       * (s.ch_l () + i + 1) = 'c';
657       s = s.left_str (i+2) + " " + s.right_str (s.length_i ()-i-2);
658     }
659
660   return s;
661 }
662
663 /*
664 Convert "NUM/DEN" into a '(NUM . DEN) cons.
665 */
666 SCM
667 scan_fraction (String frac)
668 {
669         int i = frac.index_i ('/');
670         int l = frac.length_i ();
671         String left = frac.left_str (i);
672         String right = frac.right_str (l - i - 1);
673
674         int n = String_convert::dec2_i (left);
675         int d = String_convert::dec2_i (right);
676         return gh_cons (gh_int2scm (n), gh_int2scm (d));
677 }
678