]> git.donarmstrong.com Git - lilypond.git/blob - lily/lexer.ll
release: 1.3.154
[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_OPEN;
449     case ')':
450         return E_CLOSE;
451     default:
452         return E_CHAR;
453     }
454 }
455
456 <*>.            {
457         String msg = _f ("invalid character: `%c'", YYText ()[0]);
458         LexerError (msg.ch_C ());
459         return YYText ()[0];
460 }
461
462 %%
463
464 void
465 My_lily_lexer::push_note_state ()
466 {
467         yy_push_state (notes);
468 }
469
470 void
471 My_lily_lexer::push_chord_state ()
472 {
473         yy_push_state (chords);
474 }
475
476 void
477 My_lily_lexer::push_lyric_state ()
478 {
479         yy_push_state (lyrics);
480 }
481
482 void
483 My_lily_lexer::pop_state ()
484 {
485         yy_pop_state ();
486 }
487
488 int
489 My_lily_lexer::scan_escaped_word (String str)
490 {
491         // use more SCM for this.
492
493         SCM sym = ly_symbol2scm (str.ch_C ());
494
495         int l = lookup_keyword (str);
496         if (l != -1) {
497                 return l;
498         }
499         SCM sid = lookup_identifier (str);
500         if (gh_string_p (sid)) {
501                 yylval.scm = sid; 
502                 return STRING_IDENTIFIER;
503         } else if (gh_number_p (sid)) {
504                 yylval.scm = sid;
505                 return NUMBER_IDENTIFIER;
506         } else if (unsmob_translator_def (sid)) {
507                 yylval.scm = sid;
508                 return TRANSLATOR_IDENTIFIER;
509         } else if (unsmob_score (sid)) {
510                 yylval.scm =sid;
511                 return SCORE_IDENTIFIER;
512         } else if (Music * mus =unsmob_music (sid)) {
513                 yylval.scm = sid;
514                 
515                 return dynamic_cast<Request*> (mus) ? REQUEST_IDENTIFIER : MUSIC_IDENTIFIER;
516         } else if (unsmob_duration (sid)) {
517                 yylval.scm = sid;
518                 return DURATION_IDENTIFIER;
519         } else if (unsmob_music_output_def (sid)) {
520                 yylval.scm = sid;
521                 return MUSIC_OUTPUT_DEF_IDENTIFIER;
522         }
523
524         if (sid != SCM_UNDEFINED) {
525                 yylval.scm = sid;
526                 return SCM_IDENTIFIER;
527         }
528
529         if ((YYSTATE != notes) && (YYSTATE != chords)) {
530                 SCM pitch = scm_hashq_get_handle (pitchname_tab_, sym);
531                 
532                 if (gh_pair_p (pitch))
533                 {
534                         yylval.scm = gh_cdr (pitch);
535                         return NOTENAME_PITCH;
536                 }
537         }
538         String msg (_f ("unknown escaped string: `\\%s'", str));        
539         LexerError (msg.ch_C ());
540
541         yylval.scm = ly_str02scm (str.ch_C ());
542
543         return STRING;
544 }
545
546 int
547 My_lily_lexer::scan_bare_word (String str)
548 {
549         SCM sym = ly_symbol2scm (str.ch_C ());
550         if ((YYSTATE == notes) || (YYSTATE == chords)) {
551                 SCM pitch = scm_hashq_get_handle (pitchname_tab_, sym);
552                 if (gh_pair_p (pitch)) {
553                     yylval.scm = gh_cdr (pitch);
554                     return (YYSTATE == notes) ? NOTENAME_PITCH : TONICNAME_PITCH;
555                 } else if ((pitch = scm_hashq_get_handle (chordmodifier_tab_, sym))!= SCM_BOOL_F)
556                 {
557                     yylval.scm = gh_cdr (pitch);
558                     return CHORDMODIFIER_PITCH;
559                 }
560         }
561
562         yylval.scm = ly_str02scm (str.ch_C ());
563         return STRING;
564 }
565
566 bool
567 My_lily_lexer::note_state_b () const
568 {
569         return YY_START == notes;
570 }
571
572 bool
573 My_lily_lexer::chord_state_b () const
574 {
575         return YY_START == chords;
576 }
577
578 bool
579 My_lily_lexer::lyric_state_b () const
580 {
581         return YY_START == lyrics;
582 }
583
584 /*
585  urg, belong to String (_convert)
586  and should be generalised 
587  */
588 void
589 strip_leading_white (String&s)
590 {
591         int i=0;
592         for (;  i < s.length_i (); i++) 
593                 if (!isspace (s[i]))
594                         break;
595
596         s = s.nomid_str (0, i);
597 }
598
599 void
600 strip_trailing_white (String&s)
601 {
602         int i=s.length_i ();    
603         while (i--) 
604                 if (!isspace (s[i]))
605                         break;
606
607         s = s.left_str (i+1);
608 }
609
610
611
612 Lilypond_version oldest_version ("1.3.59");
613
614 void
615 print_lilypond_versions (ostream &os)
616 {
617   os << _f ("Oldest supported input version: %s", oldest_version.str ()) 
618     << endl;
619 }
620
621
622 bool
623 valid_version_b (String s)
624 {
625   Lilypond_version current ( MAJOR_VERSION "." MINOR_VERSION "." PATCH_LEVEL );
626   Lilypond_version ver (s);
627   if (! ((ver >= oldest_version) && (ver <= current)))
628         {       
629                 non_fatal_error (_f ("incorrect lilypond version: %s (%s, %s)", ver.str (), oldest_version.str (), current.str ()));
630                 non_fatal_error (_ ("Consider converting the input with the convert-ly script")); 
631                 return false;
632     }
633   return true;
634 }
635         
636
637 String
638 lyric_fudge (String s)
639 {
640   char  * chars  =s.copy_ch_p ();
641
642   for (char * p = chars; *p ; p++)
643     {
644       if (*p == '_' && (p == chars || *(p-1) != '\\'))
645         *p = ' ';
646     }
647   
648   s = String (chars);
649   delete[] chars;
650
651   int i =0;     
652   if ((i=s.index_i ("\\,")) != -1)   // change "\," to TeX's "\c "
653     {
654       * (s.ch_l () + i + 1) = 'c';
655       s = s.left_str (i+2) + " " + s.right_str (s.length_i ()-i-2);
656     }
657
658   return s;
659 }
660
661 /*
662 Convert "NUM/DEN" into a '(NUM . DEN) cons.
663 */
664 SCM
665 scan_fraction (String frac)
666 {
667         int i = frac.index_i ('/');
668         int l = frac.length_i ();
669         String left = frac.left_str (i);
670         String right = frac.right_str (l - i - 1);
671
672         int n = String_convert::dec2_i (left);
673         int d = String_convert::dec2_i (right);
674         return gh_cons (gh_int2scm (n), gh_int2scm (d));
675 }
676