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