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