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