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