]> git.donarmstrong.com Git - lilypond.git/blob - lily/lexer.ll
release: 1.3.118
[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
53
54 bool
55 valid_version_b (String s);
56
57
58
59 #define start_quote()   \
60         yy_push_state (quote);\
61         yylval.string = new String
62
63 #define yylval (*(YYSTYPE*)lexval_l)
64
65 #define YY_USER_ACTION  add_lexed_char (YYLeng ());
66 /*
67
68 LYRICS          ({AA}|{TEX})[^0-9 \t\n\f]*
69
70 */
71
72 %}
73
74 %option c++
75 %option noyywrap
76 %option nodefault
77 %option debug
78 %option yyclass="My_lily_lexer"
79 %option stack
80 %option never-interactive 
81 %option warn
82
83 %x version
84 %x chords
85 %x incl
86 %x lyrics
87 %x notes
88 %x quote
89 %x longcomment
90
91
92 A               [a-zA-Z]
93 AA              {A}|_
94 N               [0-9]
95 AN              {AA}|{N}
96 PUNCT           [?!:']
97 ACCENT          \\[`'"^]
98 NATIONAL  [\001-\006\021-\027\031\036\200-\377]
99 TEX             {AA}|-|{PUNCT}|{ACCENT}|{NATIONAL}
100 WORD            {A}{AN}*
101 ALPHAWORD       {A}+
102 DIGIT           {N}
103 UNSIGNED        {N}+
104 INT             -?{UNSIGNED}
105 REAL            ({INT}\.{N}*)|(-?\.{N}+)
106 KEYWORD         \\{WORD}
107 WHITE           [ \n\t\f\r]
108 HORIZONTALWHITE         [ \t]
109 BLACK           [^ \n\t\f\r]
110 RESTNAME        [rs]
111 NOTECOMMAND     \\{A}+
112 LYRICS          ({AA}|{TEX})[^0-9 \t\n\f]*
113 ESCAPED         [nt\\'"]
114 EXTENDER        __
115 HYPHEN          --
116 %%
117
118
119 <*>\r           {
120         // windows-suck-suck-suck
121 }
122
123 <INITIAL,chords,incl,lyrics,notes>{
124   "%{"  {
125         yy_push_state (longcomment);
126   }
127   %[^{\n].*\n   {
128   }
129   %[^{\n]       { // backup rule
130   }
131   %\n   {
132   }
133   %[^{\n].*     {
134   }
135   {WHITE}+      {
136
137   }
138 }
139
140 <INITIAL,chords,lyrics,notes>\\version{WHITE}*  {
141         yy_push_state (version);
142 }
143 <version>\"[^"]*\";?   { /* got the include file name */
144         String s (YYText ()+1);
145         s = s.left_str (s.index_last_i ('"'));
146         DEBUG_OUT << "#version `" << s << "'\n";
147         yy_pop_state ();
148         if (!valid_version_b (s))
149                 return INVALID;
150 }
151 <version>.      {
152         LexerError ("No quoted string found after \\version");
153         yy_pop_state ();
154 }
155 <longcomment>{
156         [^\%]*          {
157         }
158         \%*[^}%]*               {
159
160         }
161         "%"+"}"         {
162                 yy_pop_state ();
163         }
164         <<EOF>>         {
165                 LexerError (_ ("EOF found inside a comment").ch_C ());
166                 if (! close_input ()) 
167                   yyterminate (); // can't move this, since it actually rets a YY_NULL
168         }
169 }
170
171
172 <INITIAL,chords,lyrics,notes>\\maininput           {
173         if (!main_input_b_)
174         {
175                 start_main_input ();
176                 main_input_b_ = true;
177         }
178         else
179                 error (_ ("\\maininput disallowed outside init files"));
180 }
181
182 <INITIAL,chords,lyrics,notes>\\include           {
183         yy_push_state (incl);
184 }
185 <incl>\"[^"]*\";?   { /* got the include file name */
186         String s (YYText ()+1);
187         s = s.left_str (s.index_last_i ('"'));
188         DEBUG_OUT << "#include `" << s << "'\n";
189         new_input (s,source_global_l);
190         yy_pop_state ();
191 }
192 <incl>\\{BLACK}*;?{WHITE} { /* got the include identifier */
193         String s = YYText () + 1;
194         strip_trailing_white (s);
195         if (s.length_i () && (s[s.length_i () - 1] == ';'))
196           s = s.left_str (s.length_i () - 1);
197         DEBUG_OUT << "#include `\\" << s << "'\n";
198         SCM sid = lookup_identifier (s);
199         if (gh_string_p (sid)) {
200                 new_input (ly_scm2string (sid), source_global_l);
201                 yy_pop_state ();
202         } else { 
203             String msg (_f ("wrong or undefined identifier: `%s'", s ));        
204             LexerError (msg.ch_C ());
205           }
206 }
207 <incl>\"[^"]*   { // backup rule
208         cerr << _ ("Missing end quote") << endl;
209         exit (1);
210 }
211 <chords,notes>{RESTNAME}        {
212         const char *s = YYText ();
213         yylval.scm = ly_str02scm (s);
214         return RESTNAME;
215 }
216 <chords,notes>R         {
217         return MEASURES;
218 }
219 <INITIAL,chords,lyrics,notes>\\\${BLACK}*{WHITE}        {
220         String s=YYText () + 2;
221         s=s.left_str (s.length_i () - 1);
222         return scan_escaped_word (s); 
223 }
224 <INITIAL,chords,lyrics,notes>\${BLACK}*{WHITE}          {
225         String s=YYText () + 1;
226         s=s.left_str (s.length_i () - 1);
227         return scan_bare_word (s);
228 }
229 <INITIAL,chords,lyrics,notes>\\\${BLACK}*               { // backup rule
230         cerr << _ ("white expected") << endl;
231         exit (1);
232 }
233 <INITIAL,chords,lyrics,notes>\${BLACK}*         { // backup rule
234         cerr << _ ("white expected") << endl;
235         exit (1);
236 }
237 <INITIAL,chords,lyrics,notes>#  { //embedded scm
238         //char const* s = YYText () + 1;
239         char const* s = here_ch_C ();
240         int n = 0;
241         if (main_input_b_ && safe_global_b) {
242                 error (_ ("Can't evaluate Scheme in safe mode"));
243                 yylval.scm =  SCM_EOL;
244                 return SCM_T;
245         }
246         yylval.scm = ly_parse_scm (s, &n);
247         
248         for (int i=0; i < n; i++)
249         {
250                 yyinput ();
251         }
252         char_count_stack_.top () += n;
253
254         return SCM_T;
255 }
256 <notes>{
257         {ALPHAWORD}     {
258                 return scan_bare_word (YYText ());
259         }
260
261         {NOTECOMMAND}   {
262                 return scan_escaped_word (YYText () + 1); 
263         }
264
265         {DIGIT}         {
266                 yylval.i = String_convert::dec2_i (String (YYText ()));
267                 return DIGIT;
268         }
269         {UNSIGNED}              {
270                 yylval.i = String_convert::dec2_i (String (YYText ()));
271                 return UNSIGNED;
272         }
273
274         \" {
275                 start_quote ();
276         }
277 }
278
279 \"              {
280         start_quote ();
281 }
282 <quote>{
283         \\{ESCAPED}     {
284                 *yylval.string += to_str (escaped_char(YYText()[1]));
285         }
286         [^\\"]+ {
287                 *yylval.string += YYText ();
288         }
289         \"      {
290                 DEBUG_OUT << "quoted string: `" << *yylval.string << "'\n";
291                 yy_pop_state ();
292
293                 /* yylval is union. Must remember STRING before setting SCM*/
294                 String *sp = yylval.string;
295                 yylval.scm = ly_str02scm  (sp->ch_C ());
296                 delete sp;
297                 return STRING;
298         }
299         .       {
300                 *yylval.string += YYText ();
301         }
302 }
303
304 <lyrics>{
305         \" {
306                 start_quote ();
307         }
308         {UNSIGNED}              {
309                 yylval.i = String_convert::dec2_i (String (YYText ()));
310                 return UNSIGNED;
311         }
312         {NOTECOMMAND}   {
313                 return scan_escaped_word (YYText () + 1);
314         }
315         {LYRICS} {
316                 /* ugr. This sux. */
317                 String s (YYText ()); 
318                 if (s == "__")
319                         return yylval.i = EXTENDER;
320                 if (s == "--")
321                         return yylval.i = HYPHEN;
322                 int i = 0;
323                 while ((i=s.index_i ("_")) != -1) // change word binding "_" to " "
324                         *(s.ch_l () + i) = ' ';
325                 if ((i=s.index_i ("\\,")) != -1)   // change "\," to TeX's "\c "
326                         {
327                         *(s.ch_l () + i + 1) = 'c';
328                         s = s.left_str (i+2) + " " + s.right_str (s.length_i ()-i-2);
329                         }
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                 DEBUG_OUT << "lyric : `" << s << "'\n";
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         DEBUG_OUT << "<<eof>>";
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         DEBUG_OUT  << "REAL" << r<<'\n';
395         yylval.real = r;
396         return REAL;
397 }
398
399 {UNSIGNED}      {
400         yylval.i = String_convert::dec2_i (String (YYText ()));
401         return UNSIGNED;
402 }
403
404 [{}]    {
405         DEBUG_OUT << "parens\n";
406         return YYText ()[0];
407 }
408 [*:=]           {
409         char c = YYText ()[0];
410         DEBUG_OUT << "misc char" <<c<<"\n";
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     default:
429         return E_CHAR;
430     }
431 }
432
433 <*>.            {
434         String msg = _f ("invalid character: `%c'", YYText ()[0]);
435         LexerError (msg.ch_C ());
436         return YYText ()[0];
437 }
438
439 %%
440
441 void
442 My_lily_lexer::push_note_state ()
443 {
444         yy_push_state (notes);
445 }
446
447 void
448 My_lily_lexer::push_chord_state ()
449 {
450         yy_push_state (chords);
451 }
452
453 void
454 My_lily_lexer::push_lyric_state ()
455 {
456         yy_push_state (lyrics);
457 }
458
459 void
460 My_lily_lexer::pop_state ()
461 {
462         yy_pop_state ();
463 }
464
465 int
466 My_lily_lexer::scan_escaped_word (String str)
467 {
468         // use more SCM for this.
469
470         SCM sym = ly_symbol2scm (str.ch_C());
471
472         int l = lookup_keyword (str);
473         if (l != -1) {
474                 return l;
475         }
476         SCM sid = lookup_identifier (str);
477         if (gh_string_p (sid)) {
478                 yylval.scm = sid; 
479                 return STRING_IDENTIFIER;
480         } else if (gh_number_p (sid)) {
481                 yylval.scm = sid;
482                 return NUMBER_IDENTIFIER;
483         } else if (unsmob_translator_def (sid)) {
484                 yylval.scm = sid;
485                 return TRANSLATOR_IDENTIFIER;
486         } else if (unsmob_score (sid)) {
487                 yylval.scm =sid;
488                 return SCORE_IDENTIFIER;
489         } else if (Music * mus =unsmob_music (sid)) {
490                 yylval.scm = sid;
491                 
492                 return dynamic_cast<Request*> (mus) ? REQUEST_IDENTIFIER : MUSIC_IDENTIFIER;
493         } else if (unsmob_duration (sid)) {
494                 yylval.scm = sid;
495                 return DURATION_IDENTIFIER;
496         } else if (unsmob_music_output_def (sid)) {
497                 yylval.scm = sid;
498                 return MUSIC_OUTPUT_DEF_IDENTIFIER;
499         }
500
501         if (sid != SCM_UNDEFINED) {
502                 yylval.scm = sid;
503                 return SCM_IDENTIFIER;
504         }
505
506         if ((YYSTATE != notes) && (YYSTATE != chords)) {
507                 SCM pitch = scm_hashq_get_handle (pitchname_tab_, sym);
508                 
509                 if (gh_pair_p (pitch))
510                 {
511                         yylval.scm = gh_cdr (pitch);
512                         return NOTENAME_PITCH;
513                 }
514         }
515         String msg (_f ("unknown escaped string: `\\%s'", str));        
516         LexerError (msg.ch_C ());
517
518         yylval.scm = ly_str02scm(str.ch_C());
519
520         return STRING;
521 }
522
523 int
524 My_lily_lexer::scan_bare_word (String str)
525 {
526         SCM sym = ly_symbol2scm (str.ch_C ());
527         if ((YYSTATE == notes) || (YYSTATE == chords)) {
528                 SCM pitch = scm_hashq_get_handle (pitchname_tab_, sym);
529                 if (gh_pair_p (pitch)) {
530                     yylval.scm = gh_cdr (pitch);
531                     return (YYSTATE == notes) ? NOTENAME_PITCH : TONICNAME_PITCH;
532                 } else if ((pitch = scm_hashq_get_handle (chordmodifier_tab_, sym))!= SCM_BOOL_F)
533                 {
534                     yylval.scm = gh_cdr (pitch);
535                     return CHORDMODIFIER_PITCH;
536                 }
537         }
538
539         yylval.scm = ly_str02scm (str.ch_C());
540         return STRING;
541 }
542
543 bool
544 My_lily_lexer::note_state_b () const
545 {
546         return YY_START == notes;
547 }
548
549 bool
550 My_lily_lexer::chord_state_b () const
551 {
552         return YY_START == chords;
553 }
554
555 bool
556 My_lily_lexer::lyric_state_b () const
557 {
558         return YY_START == lyrics;
559 }
560
561 /*
562  urg, belong to String(_convert)
563  and should be generalised 
564  */
565 void
566 strip_leading_white (String&s)
567 {
568         int i=0;
569         for (;  i < s.length_i (); i++) 
570                 if (!isspace (s[i]))
571                         break;
572
573         s = s.nomid_str (0, i);
574 }
575
576 void
577 strip_trailing_white (String&s)
578 {
579         int i=s.length_i ();    
580         while (i--) 
581                 if (!isspace (s[i]))
582                         break;
583
584         s = s.left_str (i+1);
585 }
586
587
588
589
590 bool
591 valid_version_b (String s)
592 {
593   Lilypond_version current ( MAJOR_VERSION "." MINOR_VERSION "." PATCH_LEVEL );
594   Lilypond_version ver (s);
595   if (!((ver >= oldest_version) && (ver <= current)))
596         {       
597                 non_fatal_error (_f ("incorrect lilypond version: %s (%s, %s)", ver.str (), oldest_version.str (), current.str ()));
598                 non_fatal_error (_("Consider converting the input with the convert-ly script")); 
599                 return false;
600     }
601   return true;
602 }
603