]> git.donarmstrong.com Git - lilypond.git/blob - lily/lexer.ll
dc409d288a4b5981a5ba2468a644e105f3c30618
[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,1997 Han-Wen Nienhuys <hanwen@cs.uu.nl>
8 */
9
10
11 /*
12   backup rules
13
14   after making a change to the lexer rules, run 
15       flex -b <this lexer file>
16   and make sure that 
17       lex.backup
18   contains no backup states, but only the reminder
19       Compressed tables always back up.
20   (don-t forget to rm lex.yy.cc :-)
21  */
22
23
24 #include <stdio.h>
25 #include <ctype.h>
26
27 #include "string.hh"
28 #include "string-convert.hh"
29 #include "my-lily-lexer.hh"
30 #include "array.hh"
31 #include "interval.hh"
32 #include "parser.hh"
33 #include "debug.hh"
34 #include "parseconstruct.hh"
35 #include "main.hh"
36 #include "musical-request.hh"
37 #include "identifier.hh"
38 void strip_trailing_white (String&);
39 void strip_leading_white (String&);
40
41 #define start_quote()   \
42         yy_push_state (quote);\
43         yylval.string = new String
44
45 #define yylval (*(YYSTYPE*)lexval_l)
46
47 #define YY_USER_ACTION  add_lexed_char (YYLeng ());
48 /*
49
50 LYRICS          ({AA}|{TEX})[^0-9 \t\n\f]*
51
52 */
53
54 %}
55
56 %option c++
57 %option noyywrap
58 %option nodefault
59 %option debug
60 %option yyclass="My_lily_lexer"
61 %option stack
62 %option never-interactive 
63 %option warn
64
65 %x chords
66 %x incl
67 %x lyrics
68 %x notes
69 %x quote
70 %x longcomment
71
72
73 A               [a-zA-Z]
74 AA              {A}|_
75 N               [0-9]
76 AN              {AA}|{N}
77 PUNCT           [?!:']
78 ACCENT          \\[`'"^]
79 NATIONAL  [\001-\006\021-\027\031\036\200-\377]
80 TEX             {AA}|-|{PUNCT}|{ACCENT}|{NATIONAL}
81 WORD            {A}{AN}*
82 ALPHAWORD       {A}+
83 DIGIT           {N}
84 UNSIGNED        {N}+
85 INT             -?{UNSIGNED}
86 REAL            ({INT}\.{N}*)|(-?\.{N}+)
87 KEYWORD         \\{WORD}
88 WHITE           [ \n\t\f\r]
89 HORIZONTALWHITE         [ \t]
90 BLACK           [^ \n\t\f\r]
91 RESTNAME        [rs]
92 NOTECOMMAND     \\{A}+
93 LYRICS          ({AA}|{TEX})[^0-9 \t\n\f]*
94 ESCAPED         [nt\\'"]
95 EXTENDER        [_][_]
96
97 %%
98
99
100 <*>\r           {
101         // windows-suck-suck-suck
102 }
103
104 <INITIAL,chords,incl,lyrics,notes>{
105   "%{"  {
106         yy_push_state (longcomment);
107   }
108   %[^{\n].*\n   {
109   }
110   %[^{\n]       { // backup rule
111   }
112   %\n   {
113   }
114   %[^{\n].*     {
115   }
116   {WHITE}+      {
117
118   }
119 }
120
121 <longcomment>{
122         [^\%]*          {
123         }
124         \%*[^}%]*               {
125
126         }
127         "%"+"}"         {
128                 yy_pop_state ();
129         }
130         <<EOF>>         {
131                 LexerError (_ ("EOF found inside a comment").ch_C ());
132                 if (! close_input ()) 
133                   yyterminate (); // can't move this, since it actually rets a YY_NULL
134         }
135 }
136
137
138 <INITIAL,chords,lyrics,notes>\\maininput           {
139         if (!main_input_b_)
140         {
141                 start_main_input ();
142                 main_input_b_ = true;
143         }
144         else
145                 error ("\\maininput disallowed outside init files.");
146 }
147
148 <INITIAL,chords,lyrics,notes>\\include           {
149         yy_push_state (incl);
150 }
151 <incl>\"[^"]*\";?   { /* got the include file name */
152         String s (YYText ()+1);
153         s = s.left_str (s.index_last_i ('"'));
154         DOUT << "#include `" << s << "\'\n";
155         new_input (s,source_global_l);
156         yy_pop_state ();
157 }
158 <incl>\\{BLACK}*;?{WHITE} { /* got the include identifier */
159         String s = YYText () + 1;
160         strip_trailing_white (s);
161         if (s.length_i () && (s[s.length_i () - 1] == ';'))
162           s = s.left_str (s.length_i () - 1);
163         DOUT << "#include `\\" << s << "'\n";
164         Identifier * id = lookup_identifier (s);
165         if (id) 
166           {
167             String* s_l = id->access_content_String (false);
168             DOUT << "#include `" << *s_l << "\'\n";
169             new_input (*s_l, source_global_l);
170
171             yy_pop_state ();
172           }
173         else
174           {
175             String msg (_f ("undefined identifier: `%s\'", s ));        
176             LexerError (msg.ch_C ());
177           }
178 }
179 <incl>\"[^"]*   { // backup rule
180         cerr << _ ("missing end quote") << endl;
181         exit (1);
182 }
183 <notes>{RESTNAME}       {
184         const char *s = YYText ();
185         yylval.string = new String (s); 
186         DOUT << "rest:"<< yylval.string;
187         return RESTNAME;
188 }
189 <notes>R                {
190         return MEASURES;
191 }
192 <INITIAL,chords,lyrics,notes>\\\${BLACK}*{WHITE}        {
193         String s=YYText () + 2;
194         s=s.left_str (s.length_i () - 1);
195         return scan_escaped_word (s); 
196 }
197 <INITIAL,chords,lyrics,notes>\${BLACK}*{WHITE}          {
198         String s=YYText () + 1;
199         s=s.left_str (s.length_i () - 1);
200         return scan_bare_word (s);
201 }
202 <INITIAL,chords,lyrics,notes>\\\${BLACK}*               { // backup rule
203         cerr << _ ("white expected") << endl;
204         exit (1);
205 }
206 <INITIAL,chords,lyrics,notes>\${BLACK}*         { // backup rule
207         cerr << _ ("white expected") << endl;
208         exit (1);
209 }
210 <notes>{
211         {ALPHAWORD}     {
212                 return scan_bare_word (YYText ());
213         }
214
215         {NOTECOMMAND}   {
216                 return scan_escaped_word (YYText () + 1); 
217         }
218
219         {DIGIT}         {
220                 yylval.i = String_convert::dec2_i (String (YYText ()));
221                 return DIGIT;
222         }
223
224         {UNSIGNED}              {
225                 yylval.i = String_convert::dec2_i (String (YYText ()));
226                 return UNSIGNED;
227         }
228
229         \" {
230                 start_quote ();
231         }
232 }
233
234 \"              {
235         start_quote ();
236 }
237 <quote>{
238         \\{ESCAPED}     {
239                 *yylval.string += to_str (escaped_char(YYText()[1]));
240         }
241         [^\\"]+ {
242                 *yylval.string += YYText ();
243         }
244         \"      {
245                 DOUT << "quoted string: `" << *yylval.string << "'\n";
246                 yy_pop_state ();
247                 return STRING;
248         }
249         .       {
250                 *yylval.string += YYText ();
251         }
252 }
253
254 <lyrics>{
255         \" {
256                 start_quote ();
257         }
258         {UNSIGNED}              {
259                 yylval.i = String_convert::dec2_i (String (YYText ()));
260                 return UNSIGNED;
261         }
262         {NOTECOMMAND}   {
263                 return scan_escaped_word (YYText () + 1);
264         }
265         {LYRICS} {
266                 /* ugr. This sux. */
267                 String s (YYText ()); 
268                 if (s == "__")
269                         return yylval.i = EXTENDER;
270                 int i = 0;
271                 while ((i=s.index_i ("_")) != -1) // change word binding "_" to " "
272                         *(s.ch_l () + i) = ' ';
273                 if ((i=s.index_i ("\\,")) != -1)   // change "\," to TeX's "\c "
274                         {
275                         *(s.ch_l () + i + 1) = 'c';
276                         s = s.left_str (i+2) + " " + s.right_str (s.length_i ()-i-2);
277                         }
278                 yylval.string = new String (s);
279                 DOUT << "lyric : `" << s << "'\n";
280                 return STRING;
281         }
282         . {
283                 return yylval.c = YYText ()[0];
284         }
285 }
286 <chords>{
287         {ALPHAWORD}     {
288                 return scan_bare_word (YYText ());
289         }
290         {NOTECOMMAND}   {
291                 return scan_escaped_word (YYText () + 1);
292         }
293         {UNSIGNED}              {
294                 yylval.i = String_convert::dec2_i (String (YYText ()));
295                 return UNSIGNED;
296         }
297         . {
298                 return yylval.c = YYText ()[0];
299         }
300 }
301
302 <<EOF>> {
303         DOUT << "<<eof>>";
304
305         if (! close_input ()) { 
306           yyterminate (); // can't move this, since it actually rets a YY_NULL
307         }
308 }
309
310
311 {WORD}  {
312         return scan_bare_word (YYText ());
313 }
314 {KEYWORD}       {
315         return scan_escaped_word (YYText () + 1);
316 }
317 {REAL}          {
318         Real r;
319         int cnv=sscanf (YYText (), "%lf", &r);
320         assert (cnv == 1);
321         DOUT  << "REAL" << r<<'\n';
322         yylval.real = r;
323         return REAL;
324 }
325
326 {UNSIGNED}      {
327         yylval.i = String_convert::dec2_i (String (YYText ()));
328         return UNSIGNED;
329 }
330
331 [{}]    {
332
333         DOUT << "parens\n";
334         return YYText ()[0];
335 }
336 [*:=]           {
337         char c = YYText ()[0];
338         DOUT << "misc char" <<c<<"\n";
339         return c;
340 }
341
342 <INITIAL,notes>.        {
343         return yylval.c = YYText ()[0];
344 }
345
346 <INITIAL,lyrics,notes>\\. {
347     char c= YYText ()[1];
348     yylval.c = c;
349     switch (c) {
350     case '>':
351         return E_BIGGER;
352     case '<':
353         return E_SMALLER;
354     case '!':
355         return E_EXCLAMATION;
356     default:
357         return E_CHAR;
358     }
359 }
360
361 <*>.            {
362         String msg = _f ("illegal character: `%c\'", YYText ()[0]);
363         LexerError (msg.ch_C ());
364         return YYText ()[0];
365 }
366
367 %%
368
369 void
370 My_lily_lexer::push_note_state ()
371 {
372         yy_push_state (notes);
373 }
374
375 void
376 My_lily_lexer::push_chord_state ()
377 {
378         yy_push_state (chords);
379 }
380
381 void
382 My_lily_lexer::push_lyric_state ()
383 {
384         yy_push_state (lyrics);
385 }
386
387 void
388 My_lily_lexer::pop_state ()
389 {
390         yy_pop_state ();
391 }
392
393 int
394 My_lily_lexer::scan_escaped_word (String str)
395 {       
396         DOUT << "\\word: `" << str<<"'\n";
397         int l = lookup_keyword (str);
398         if (l != -1) {
399                 DOUT << "(keyword)\n";
400                 return l;
401         }
402         Identifier * id = lookup_identifier (str);
403         if (id) {
404                 DOUT << "(identifier)\n";
405                 yylval.id = id;
406                 return id->token_code_i_;
407         }
408         if ((YYSTATE != notes) && (YYSTATE != chords)) {
409                 if (notename_b (str)) {
410                         yylval.pitch = new Musical_pitch (lookup_notename (str));
411                         yylval.pitch->set_spot (Input (source_file_l (), 
412                           here_ch_C ()));
413                         return NOTENAME_PITCH;
414                 }
415         }
416         if (check_debug)
417                 print_declarations (true);
418         String msg (_f ("unknown escaped string: `\\%s\'", str));       
419         LexerError (msg.ch_C ());
420         DOUT << "(string)";
421         String *sp = new String (str);
422         yylval.string=sp;
423         return STRING;
424 }
425
426 int
427 My_lily_lexer::scan_bare_word (String str)
428 {
429         DOUT << "word: `" << str<< "'\n";       
430         if ((YYSTATE == notes) || (YYSTATE == chords)) {
431                 if (notename_b (str)) {
432                     DOUT << "(notename)\n";
433                     yylval.pitch = new Musical_pitch (lookup_notename (str));
434                     yylval.pitch->set_spot (Input (source_file_l (), 
435                       here_ch_C ()));
436                     return (YYSTATE == notes) ? NOTENAME_PITCH : TONICNAME_PITCH;
437                 } else if (chordmodifier_b (str)) {
438                     DOUT << "(chordmodifier)\n";
439                     yylval.pitch = new Musical_pitch (lookup_chordmodifier (str));
440                     yylval.pitch->set_spot (Input (source_file_l (), 
441                       here_ch_C ()));
442                     return CHORDMODIFIER_PITCH;
443                 }
444         }
445
446         yylval.string=new String (str);
447         return STRING;
448 }
449
450 bool
451 My_lily_lexer::note_state_b () const
452 {
453         return YY_START == notes;
454 }
455
456 bool
457 My_lily_lexer::chord_state_b () const
458 {
459         return YY_START == chords;
460 }
461
462 bool
463 My_lily_lexer::lyric_state_b () const
464 {
465         return YY_START == lyrics;
466 }
467
468 /*
469  urg, belong to String(_convert)
470  and should be generalised 
471  */
472 void
473 strip_leading_white (String&s)
474 {
475         int i=0;
476         for (;  i < s.length_i (); i++) 
477                 if (!isspace (s[i]))
478                         break;
479
480         s = s.nomid_str (0, i);
481 }
482
483 void
484 strip_trailing_white (String&s)
485 {
486         int i=s.length_i ();    
487         while (i--) 
488                 if (!isspace (s[i]))
489                         break;
490
491         s = s.left_str (i+1);
492 }
493