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