]> git.donarmstrong.com Git - lilypond.git/blob - lily/lexer.ll
5aff58c66fa3170eeb718c38919479cd4dd61408
[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--2003 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 <errno.h>
28
29 /* Flex >= 2.5.29 fix; FlexLexer.h's multiple include bracing breaks
30    when building the actual lexer.  */
31 #define LEXER_CC
32
33 #include <iostream>
34 using namespace std;
35
36 #include "source-file.hh"
37 #include "parse-scm.hh"
38 #include "lily-guile.hh"
39 #include "string.hh"
40 #include "string-convert.hh"
41 #include "my-lily-lexer.hh"
42 #include "input-file-results.hh"
43 #include "interval.hh"
44 #include "lily-guile.hh"
45 #include "parser.hh"
46 #include "warn.hh"
47 #include "main.hh"
48 #include "version.hh"
49 #include "lilypond-input-version.hh"
50 #include "context-def.hh"
51 #include "identifier-smob.hh"
52
53 /*
54 RH 7 fix (?)
55 */
56 #define isatty HORRIBLEKLUDGE
57
58 void strip_trailing_white (String&);
59 void strip_leading_white (String&);
60 String lyric_fudge (String s);
61
62 SCM
63 lookup_markup_command (String s);
64
65 bool
66 valid_version_b (String s);
67
68
69
70 #define start_quote()   \
71         yy_push_state (quote);\
72         yylval.string = new String
73
74 #define yylval \
75         (*(YYSTYPE*)lexval)
76
77 #define YY_USER_ACTION  add_lexed_char (YYLeng ());
78 /*
79
80 LYRICS          ({AA}|{TEX})[^0-9 \t\n\f]*
81
82 */
83
84
85 SCM scan_fraction (String);
86 SCM (* scm_parse_error_handler) (void *);
87
88
89
90 %}
91
92 %option c++
93 %option noyywrap
94 %option nodefault
95 %option debug
96 %option yyclass="My_lily_lexer"
97 %option stack
98 %option never-interactive 
99 %option warn
100
101 %x renameinput
102 %x version
103 %x chords
104 %x incl
105 %x lyrics
106 %x notes
107 %x figures
108 %x quote
109 %x longcomment
110 %x markup 
111
112 A               [a-zA-Z]
113 AA              {A}|_
114 N               [0-9]
115 AN              {AA}|{N}
116 PUNCT           [?!:'`]
117 ACCENT          \\[`'"^]
118 NATIONAL        [\001-\006\021-\027\031\036\200-\377]
119 TEX             {AA}|-|{PUNCT}|{ACCENT}|{NATIONAL}
120 WORD            {A}{AN}*
121 ALPHAWORD       {A}+
122 DIGIT           {N}
123 UNSIGNED        {N}+
124 E_UNSIGNED      \\{N}+
125 FRACTION        {N}+\/{N}+
126 INT             -?{UNSIGNED}
127 REAL            ({INT}\.{N}*)|(-?\.{N}+)
128 KEYWORD         \\{WORD}
129 WHITE           [ \n\t\f\r]
130 HORIZONTALWHITE         [ \t]
131 BLACK           [^ \n\t\f\r]
132 RESTNAME        [rs]
133 NOTECOMMAND     \\{A}+
134 MARKUPCOMMAND   \\({A}|[-_])+
135 LYRICS          ({AA}|{TEX})[^0-9 \t\n\r\f]*
136 ESCAPED         [nt\\'"]
137 EXTENDER        __
138 HYPHEN          --
139 %%
140
141
142 <*>\r           {
143         // windows-suck-suck-suck
144 }
145
146 <INITIAL,chords,incl,markup,lyrics,notes,figures>{
147   "%{"  {
148         yy_push_state (longcomment);
149   }
150   %[^{\n\r].*[\n\r]     {
151   }
152   %[^{\n\r]     { // backup rule
153   }
154   %[\n\r]       {
155   }
156   %[^{\n\r].*   {
157   }
158   {WHITE}+      {
159
160   }
161 }
162
163 <INITIAL,chords,lyrics,notes,figures>\\version{WHITE}*  {
164         yy_push_state (version);
165 }
166 <INITIAL,chords,lyrics,notes,figures>\\renameinput{WHITE}*      {
167         yy_push_state (renameinput);
168 }
169 <version>\"[^"]*\"     { /* got the version number */
170         String s (YYText ()+1);
171         s = s.left_string (s.index_last ('\"'));
172
173         yy_pop_state();
174          if (!valid_version_b (s))
175                 return INVALID;
176 }
177 <renameinput>\"[^"]*\"     { /* got the version number */
178         String s (YYText ()+1);
179         s = s.left_string (s.index_last ('\"'));
180
181         yy_pop_state();
182         this->here_input().source_file_->name_ = s;
183         progress_indication (_f("\nRenamed input to `%s'", s.to_str0()));
184         scm_module_define (gh_car (scopes_),
185                      ly_symbol2scm ("input-file-name"),
186                      scm_makfrom0str (s.to_str0()));
187
188 }
189
190 <version>.      {
191         LexerError ("No quoted string found after \\version");
192         yy_pop_state ();
193 }
194 <renameinput>.  {
195         LexerError ("No quoted string found after \\renameinput");
196         yy_pop_state ();
197 }
198 <longcomment>{
199         [^\%]*          {
200         }
201         \%*[^}%]*               {
202
203         }
204         "%"+"}"         {
205                 yy_pop_state ();
206         }
207         <<EOF>>         {
208                 LexerError (_ ("EOF found inside a comment").to_str0 ());
209                 main_input_b_ = false;
210                 if (! close_input ()) 
211                   yyterminate (); // can't move this, since it actually rets a YY_NULL
212         }
213 }
214
215
216 <INITIAL,chords,lyrics,notes,figures>\\maininput           {
217         if (!main_input_b_)
218         {
219                 start_main_input ();
220                 main_input_b_ = true;
221         }
222         else
223                 error (_ ("\\maininput not allowed outside init files"));
224 }
225
226 <INITIAL,chords,lyrics,figures,notes>\\include           {
227         yy_push_state (incl);
228 }
229 <incl>\"[^"]*\";?   { /* got the include file name */
230         String s (YYText ()+1);
231         s = s.left_string (s.index_last ('"'));
232
233         new_input (s, &global_input_file->sources_ );
234         yy_pop_state ();
235 }
236 <incl>\\{BLACK}*;?{WHITE} { /* got the include identifier */
237         String s = YYText () + 1;
238         strip_trailing_white (s);
239         if (s.length () && (s[s.length () - 1] == ';'))
240           s = s.left_string (s.length () - 1);
241
242         SCM sid = lookup_identifier (s);
243         if (gh_string_p (sid)) {
244                 new_input (ly_scm2string (sid), &global_input_file->sources_);
245                 yy_pop_state ();
246         } else { 
247             String msg (_f ("wrong or undefined identifier: `%s'", s ));
248
249             LexerError (msg.to_str0 ());
250             SCM err = scm_current_error_port ();
251             scm_puts ("This value was found in the table: ", err);
252             scm_display (sid, err);
253           }
254 }
255 <incl>\"[^"]*   { // backup rule
256         error (_ ("Missing end quote"));
257         exit (1);
258 }
259 <chords,notes,figures>{RESTNAME}        {
260         const char *s = YYText ();
261         yylval.scm = scm_makfrom0str (s);
262         return RESTNAME;
263 }
264 <chords,notes,figures>R         {
265         return MULTI_MEASURE_REST;
266 }
267 <INITIAL,chords,lyrics,notes,figures>\\\${BLACK}*{WHITE}        {
268         String s=YYText () + 2;
269         s=s.left_string (s.length () - 1);
270         return scan_escaped_word (s); 
271 }
272 <INITIAL,chords,lyrics,notes,figures>\${BLACK}*{WHITE}          {
273         String s=YYText () + 1;
274         s=s.left_string (s.length () - 1);
275         return scan_bare_word (s);
276 }
277 <INITIAL,chords,lyrics,notes,figures>\\\${BLACK}*               { // backup rule
278         error (_("white expected"));
279         exit (1);
280 }
281 <INITIAL,chords,lyrics,notes,figures>\${BLACK}*         { // backup rule
282         error (_("white expected"));
283         exit (1);
284 }
285
286 <INITIAL,markup,chords,lyrics,notes,figures>#   { //embedded scm
287         //char const* s = YYText () + 1;
288         char const* s = here_str0 ();
289         int n = 0;
290         SCM sval = ly_parse_scm (s, &n, here_input (),
291                 safe_global_b && main_input_b_);
292
293         if (sval == SCM_UNDEFINED)
294         {
295                 sval = SCM_UNSPECIFIED;
296                 errorlevel_ = 1;
297         }
298
299         for (int i=0; i < n; i++)
300         {
301                 yyinput ();
302         }
303         char_count_stack_.top () += n;
304
305         if (unpack_identifier (sval) != SCM_UNDEFINED)
306         {
307                 yylval.scm = unpack_identifier(sval);
308                 return identifier_type (yylval.scm);
309         }
310                 
311         yylval.scm = sval;
312         return SCM_T;
313 }
314 <INITIAL,notes,lyrics>{ 
315         \<\<   {
316                 return LESSLESS;
317         }
318         \>\>   {
319                 return MOREMORE;
320         }
321 }
322 <figures>{
323         _       {
324                 return FIGURE_SPACE;
325         }
326         \>              {
327                 return FIGURE_CLOSE;
328         }
329         \<      {
330                 return FIGURE_OPEN;
331         }
332 }
333
334 <notes,figures>{
335         {ALPHAWORD}     {
336                 return scan_bare_word (YYText ());
337         }
338
339         {NOTECOMMAND}   {
340                 return scan_escaped_word (YYText () + 1); 
341         }
342         {FRACTION}      {
343                 yylval.scm =  scan_fraction (YYText ());
344                 return FRACTION;
345         }
346
347         {DIGIT}         {
348                 yylval.i = String_convert::dec2int (String (YYText ()));
349                 return DIGIT;
350         }
351         {UNSIGNED}              {
352                 yylval.i = String_convert::dec2int (String (YYText ()));
353                 return UNSIGNED;
354         }
355         {E_UNSIGNED}    {
356                 yylval.i = String_convert::dec2int (String (YYText () +1));
357                 return E_UNSIGNED;
358         }
359
360         \" {
361                 start_quote ();
362         }
363 }
364
365 \"              {
366         start_quote ();
367 }
368 <quote>{
369         \\{ESCAPED}     {
370                 *yylval.string += to_string (escaped_char (YYText ()[1]));
371         }
372         [^\\"]+ {
373                 *yylval.string += YYText ();
374         }
375         \"      {
376
377                 yy_pop_state ();
378
379                 /* yylval is union. Must remember STRING before setting SCM*/
380                 String *sp = yylval.string;
381                 yylval.scm = scm_makfrom0str (sp->to_str0 ());
382                 delete sp;
383                 return STRING;
384         }
385         .       {
386                 *yylval.string += YYText ();
387         }
388 }
389
390 <lyrics>{
391         \" {
392                 start_quote ();
393         }
394         {FRACTION}      {
395                 yylval.scm =  scan_fraction (YYText ());
396                 return FRACTION;
397         }
398         {UNSIGNED}              {
399                 yylval.i = String_convert::dec2int (String (YYText ()));
400                 return UNSIGNED;
401         }
402         {NOTECOMMAND}   {
403                 return scan_escaped_word (YYText () + 1);
404         }
405         {LYRICS} {
406                 /* ugr. This sux. */
407                 String s (YYText ()); 
408                 if (s == "__")
409                         return yylval.i = EXTENDER;
410                 if (s == "--")
411                         return yylval.i = HYPHEN;
412                 s = lyric_fudge (s);
413
414                 char c = s[s.length () - 1];
415                 if (c == '{' ||  c == '}') // brace open is for not confusing dumb tools.
416                         here_input ().warning (
417                                 _ ("Brace found at end of lyric.  Did you forget a space?"));
418                 yylval.scm = scm_makfrom0str (s.to_str0 ());
419
420
421                 return STRING;
422         }
423         . {
424                 return YYText ()[0];
425         }
426 }
427 <chords>{
428         {ALPHAWORD}     {
429                 return scan_bare_word (YYText ());
430         }
431         {NOTECOMMAND}   {
432                 return scan_escaped_word (YYText () + 1);
433         }
434         {FRACTION}      {
435                 yylval.scm =  scan_fraction (YYText ());
436                 return FRACTION;
437         }
438         {UNSIGNED}              {
439                 yylval.i = String_convert::dec2int (String (YYText ()));
440                 return UNSIGNED;
441         }
442         \" {
443                 start_quote ();
444         }
445         -  {
446                 return CHORD_MINUS;
447         }
448         :  {
449                 return CHORD_COLON;
450         }
451         \/\+ {
452                 return CHORD_BASS;
453         }
454         \/  {
455                 return CHORD_SLASH;
456         }
457         \^  {
458                 return CHORD_CARET;
459         }
460         . {
461                 return YYText ()[0];
462         }
463 }
464
465
466 <markup>{
467         \" {
468                 start_quote ();
469         }
470         \< {
471                 return '<';
472         }
473         \> {
474                 return '>';
475         }
476         {MARKUPCOMMAND} {
477                 String str (YYText() + 1);
478                 SCM s = lookup_markup_command (str);
479
480                 if (gh_pair_p (s) && gh_symbol_p (gh_cdr (s)) ) {
481                         yylval.scm = gh_car(s);
482                         SCM tag = gh_cdr(s);
483                         if (tag == ly_symbol2scm("markup0"))
484                                 return MARKUP_HEAD_MARKUP0;
485                         if (tag == ly_symbol2scm("empty"))
486                                 return MARKUP_HEAD_EMPTY;
487                         else if (tag == ly_symbol2scm ("markup0-markup1"))
488                                 return MARKUP_HEAD_MARKUP0_MARKUP1;
489                         else if (tag == ly_symbol2scm ("markup-list0"))
490                                 return MARKUP_HEAD_LIST0;
491                         else if (tag == ly_symbol2scm ("scheme0"))
492                                 return MARKUP_HEAD_SCM0;
493                         else if (tag == ly_symbol2scm ("scheme0-scheme1"))
494                                 return MARKUP_HEAD_SCM0_SCM1;
495                         else if (tag == ly_symbol2scm ("scheme0-markup1"))
496                                 return MARKUP_HEAD_SCM0_MARKUP1;
497                         else if (tag == ly_symbol2scm ("scheme0-scheme1-markup2"))
498                                 return MARKUP_HEAD_SCM0_SCM1_MARKUP2;
499                         else if (tag == ly_symbol2scm ("scheme0-scheme1-scheme2"))
500                                 return MARKUP_HEAD_SCM0_SCM1_SCM2;
501                         else {
502                                 programming_error ("No parser tag defined for this signature. Abort"); 
503                                 ly_display_scm (s);
504                                 assert(false);
505                         }
506                 } else
507                         return scan_escaped_word (str);
508         }
509         [{}]    {
510                 return YYText()[0];
511         }
512         [^#{}"\\ \t\n\r\f]+ {
513                 String s (YYText ()); 
514
515                 char c = s[s.length () - 1];
516                 if (c == '{' ||  c == '}') // brace open is for not confusing dumb tools.
517                         here_input ().warning (
518                                 _ ("Brace found at end of markup.  Did you forget a space?"));
519                 yylval.scm = scm_makfrom0str (s.to_str0 ());
520
521
522                 return STRING;
523         }
524         .  {
525                 return YYText()[0];
526         }
527 }
528
529 <<EOF>> {
530         main_input_b_ = false;
531         if (! close_input ()) { 
532           yyterminate (); // can't move this, since it actually rets a YY_NULL
533         }
534 }
535
536
537 {WORD}  {
538         return scan_bare_word (YYText ());
539 }
540 {KEYWORD}       {
541         return scan_escaped_word (YYText () + 1);
542 }
543 {REAL}          {
544         Real r;
545         int cnv=sscanf (YYText (), "%lf", &r);
546         assert (cnv == 1);
547
548         yylval.scm = gh_double2scm (r);
549         return REAL;
550 }
551
552 {UNSIGNED}      {
553         yylval.i = String_convert::dec2int (String (YYText ()));
554         return UNSIGNED;
555 }
556
557
558 [{}]    {
559
560         return YYText ()[0];
561 }
562 [*:=]           {
563         char c = YYText ()[0];
564
565         return c;
566 }
567
568 <INITIAL,notes,figures>.        {
569         return YYText ()[0];
570 }
571
572 <INITIAL,lyrics,notes,figures>\\. {
573     char c= YYText ()[1];
574
575     switch (c) {
576     case '>':
577         return E_BIGGER;
578     case '<':
579         return E_SMALLER;
580     case '!':
581         return E_EXCLAMATION;
582     case '(':
583         return E_OPEN;
584     case ')':
585         return E_CLOSE;
586     case '[':
587         return E_LEFTSQUARE;
588     case ']':
589         return E_RIGHTSQUARE;
590     case '~':
591         return E_TILDE;
592     case '\\':
593         return E_BACKSLASH;
594
595     default:
596         return E_CHAR;
597     }
598 }
599
600 <*>.            {
601         String msg = _f ("invalid character: `%c'", YYText ()[0]);
602         LexerError (msg.to_str0 ());
603         return YYText ()[0];
604 }
605
606 %%
607
608 void
609 My_lily_lexer::push_note_state (SCM tab)
610 {
611         pitchname_tab_stack_ = gh_cons (tab, pitchname_tab_stack_);
612         yy_push_state (notes);
613 }
614
615 void
616 My_lily_lexer::push_figuredbass_state()
617 {
618         yy_push_state (figures);
619 }
620 void
621 My_lily_lexer::push_chord_state (SCM tab)
622 {
623         pitchname_tab_stack_ = gh_cons (tab, pitchname_tab_stack_);
624         yy_push_state (chords);
625 }
626
627 void
628 My_lily_lexer::push_lyric_state ()
629 {
630         yy_push_state (lyrics);
631 }
632
633 void
634 My_lily_lexer::push_markup_state ()
635 {
636         yy_push_state (markup);
637 }
638
639 void
640 My_lily_lexer::pop_state ()
641 {
642         if (YYSTATE == notes || YYSTATE == chords)
643                 pitchname_tab_stack_ = gh_cdr (pitchname_tab_stack_);
644         yy_pop_state ();
645 }
646
647 int
648 My_lily_lexer::identifier_type(SCM sid)
649 {
650         int k = try_special_identifiers(&yylval.scm , sid);
651         return k >= 0  ? k : SCM_IDENTIFIER;
652 }
653
654
655 int
656 My_lily_lexer::scan_escaped_word (String str)
657 {
658         // use more SCM for this.
659
660 //      SCM sym = ly_symbol2scm (str.to_str0 ());
661
662         int l = lookup_keyword (str);
663         if (l != -1) {
664                 return l;
665         }
666         SCM sid = lookup_identifier (str);
667         if (sid != SCM_UNDEFINED)
668         {
669                 yylval.scm = sid;
670                 return identifier_type (sid);
671         }
672
673         String msg (_f ("unknown escaped string: `\\%s'", str));        
674         LexerError (msg.to_str0 ());
675
676         yylval.scm = scm_makfrom0str (str.to_str0 ());
677
678         return STRING;
679 }
680
681 int
682 My_lily_lexer::scan_bare_word (String str)
683 {
684         SCM sym = ly_symbol2scm (str.to_str0 ());
685         if ((YYSTATE == notes) || (YYSTATE == chords)) {
686                 SCM handle = SCM_BOOL_F;
687                 if (gh_pair_p (pitchname_tab_stack_))
688                         handle = scm_hashq_get_handle (gh_car (pitchname_tab_stack_), sym);
689                 
690                 if (gh_pair_p (handle)) {
691                         yylval.scm = ly_cdr (handle);
692                         if (unsmob_pitch (yylval.scm)) 
693                             return (YYSTATE == notes) ? NOTENAME_PITCH : TONICNAME_PITCH;
694                         else if (gh_symbol_p (yylval.scm))
695                             return DRUM_PITCH;
696                 }
697                 else if ((handle = scm_hashq_get_handle (chordmodifier_tab_, sym))!= SCM_BOOL_F)
698                 {
699                     yylval.scm = ly_cdr (handle);
700                     return CHORD_MODIFIER;
701                 }
702         }
703
704         yylval.scm = scm_makfrom0str (str.to_str0 ());
705         return STRING;
706 }
707
708 bool
709 My_lily_lexer::note_state_b () const
710 {
711         return YY_START == notes;
712 }
713
714 bool
715 My_lily_lexer::chord_state_b () const
716 {
717         return YY_START == chords;
718 }
719
720 bool
721 My_lily_lexer::lyric_state_b () const
722 {
723         return YY_START == lyrics;
724 }
725
726 bool
727 My_lily_lexer::figure_state_b () const
728 {
729         return YY_START == figures;
730 }
731
732 /*
733  urg, belong to String (_convert)
734  and should be generalised 
735  */
736 void
737 strip_leading_white (String&s)
738 {
739         int i=0;
740         for (;  i < s.length (); i++) 
741                 if (!isspace (s[i]))
742                         break;
743
744         s = s.nomid_string (0, i);
745 }
746
747 void
748 strip_trailing_white (String&s)
749 {
750         int i=s.length ();      
751         while (i--) 
752                 if (!isspace (s[i]))
753                         break;
754
755         s = s.left_string (i+1);
756 }
757
758
759
760 /* 1.9.0 == postfix articulations */ 
761 Lilypond_version oldest_version ("1.9.0");
762
763
764 bool
765 valid_version_b (String s)
766 {
767   Lilypond_version current ( MAJOR_VERSION "." MINOR_VERSION "." PATCH_LEVEL );
768   Lilypond_version ver (s);
769   if (! ((ver >= oldest_version) && (ver <= current)))
770         {       
771                 non_fatal_error (_f ("Incorrect lilypond version: %s (%s, %s)", ver.to_string (), oldest_version.to_string (), current.to_string ()));
772                 non_fatal_error (_ ("Consider updating the input with the convert-ly script")); 
773                 return false;
774     }
775   return true;
776 }
777         
778
779 /*
780   substittute _ adn \,
781 */
782 String
783 lyric_fudge (String s)
784 {
785   char  * chars  =s.get_copy_str0 ();
786
787   for (char * p = chars; *p ; p++)
788     {
789       if (*p == '_' && (p == chars || *(p-1) != '\\'))
790         *p = ' ';
791     }
792   
793   s = String (chars);
794   delete[] chars;
795
796   int i =0;     
797   if ((i=s.index ("\\,")) != -1)   // change "\," to TeX's "\c "
798     {
799       * (s.get_str0 () + i + 1) = 'c';
800       s = s.left_string (i+2) + " " + s.right_string (s.length ()-i-2);
801     }
802
803   return s;
804 }
805
806 /*
807 Convert "NUM/DEN" into a '(NUM . DEN) cons.
808 */
809 SCM
810 scan_fraction (String frac)
811 {
812         int i = frac.index ('/');
813         int l = frac.length ();
814         String left = frac.left_string (i);
815         String right = frac.right_string (l - i - 1);
816
817         int n = String_convert::dec2int (left);
818         int d = String_convert::dec2int (right);
819         return gh_cons (gh_int2scm (n), gh_int2scm (d));
820 }
821
822 // Breaks for flex 2.5.31
823 #if 0
824 /* avoid silly flex induced gcc warnings */
825 static void yy_push_state (int) {;}
826 static void yy_pop_state () {;}
827 static int yy_top_state () { return 0; }
828
829 static void
830 avoid_silly_flex_induced_gcc_warnings ()
831 {
832         (void)yy_start_stack_ptr;
833         (void)yy_start_stack_depth;
834         (void)yy_start_stack;
835         yy_push_state (0);
836         yy_pop_state ();
837         yy_top_state ();
838         avoid_silly_flex_induced_gcc_warnings ();
839 }
840 #endif
841
842 SCM
843 lookup_markup_command (String s)
844 {
845         static SCM proc ;
846         if (!proc)
847                 proc = scm_c_eval_string ("lookup-markup-command");
848
849         return scm_call_1 (proc, scm_makfrom0str (s.to_str0 ()));
850 }