]> git.donarmstrong.com Git - lilypond.git/blob - lily/lexer.ll
release: 1.1.13
[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 PLET            \\\[
96 TELP            \\\]
97 EXTENDER        [_][_]
98
99 %%
100
101
102 <*>\r           {
103         // windows-suck-suck-suck
104 }
105
106 <INITIAL,chords,incl,lyrics,notes>{
107   "%{"  {
108         yy_push_state (longcomment);
109   }
110   %[^{\n].*\n   {
111   }
112   %[^{\n]       { // backup rule
113   }
114   %\n   {
115   }
116   %[^{\n].*     {
117   }
118   {WHITE}+      {
119
120   }
121 }
122
123 <longcomment>{
124         [^\%]*          {
125         }
126         \%*[^}%]*               {
127
128         }
129         "%"+"}"         {
130                 yy_pop_state ();
131         }
132         <<EOF>>         {
133                 LexerError (_ ("EOF found inside a comment").ch_C ());
134                 if (! close_input ()) 
135                   yyterminate (); // can't move this, since it actually rets a YY_NULL
136         }
137 }
138
139
140 <INITIAL,chords,lyrics,notes>\\maininput           {
141         if (!main_input_b_)
142         {
143                 start_main_input ();
144                 main_input_b_ = true;
145         }
146         else
147                 error ("\\maininput disallowed outside init files.");
148 }
149
150 <INITIAL,chords,lyrics,notes>\\include           {
151         yy_push_state (incl);
152 }
153 <incl>\"[^"]*\";?   { /* got the include file name */
154         String s (YYText ()+1);
155         s = s.left_str (s.index_last_i ('"'));
156         DOUT << "#include `" << s << "\'\n";
157         new_input (s,source_global_l);
158         yy_pop_state ();
159 }
160 <incl>\\{BLACK}*;?{WHITE} { /* got the include identifier */
161         String s = YYText () + 1;
162         strip_trailing_white (s);
163         if (s.length_i () && (s[s.length_i () - 1] == ';'))
164           s = s.left_str (s.length_i () - 1);
165         DOUT << "#include `\\" << s << "'\n";
166         Identifier * id = lookup_identifier (s);
167         if (id) 
168           {
169             String* s_l = id->access_content_String (false);
170             DOUT << "#include `" << *s_l << "\'\n";
171             new_input (*s_l, source_global_l);
172
173             yy_pop_state ();
174           }
175         else
176           {
177             String msg (_f ("undefined identifier: `%s\'", s ));        
178             LexerError (msg.ch_C ());
179           }
180 }
181 <incl>\"[^"]*   { // backup rule
182         cerr << _ ("missing end quote") << endl;
183         exit (1);
184 }
185 <notes>{RESTNAME}       {
186         const char *s = YYText ();
187         yylval.string = new String (s); 
188         DOUT << "rest:"<< yylval.string;
189         return RESTNAME;
190 }
191 <notes>R                {
192         return MEASURES;
193 }
194 <INITIAL,chords,lyrics,notes>\\\${BLACK}*{WHITE}        {
195         String s=YYText () + 2;
196         s=s.left_str (s.length_i () - 1);
197         return scan_escaped_word (s); 
198 }
199 <INITIAL,chords,lyrics,notes>\${BLACK}*{WHITE}          {
200         String s=YYText () + 1;
201         s=s.left_str (s.length_i () - 1);
202         return scan_bare_word (s);
203 }
204 <INITIAL,chords,lyrics,notes>\\\${BLACK}*               { // backup rule
205         cerr << _ ("white expected") << endl;
206         exit (1);
207 }
208 <INITIAL,chords,lyrics,notes>\${BLACK}*         { // backup rule
209         cerr << _ ("white expected") << endl;
210         exit (1);
211 }
212 <notes>{
213         {ALPHAWORD}     {
214                 return scan_bare_word (YYText ());
215         }
216
217         {NOTECOMMAND}   {
218                 return scan_escaped_word (YYText () + 1); 
219         }
220
221         {DIGIT}         {
222                 yylval.i = String_convert::dec2_i (String (YYText ()));
223                 return DIGIT;
224         }
225
226         {UNSIGNED}              {
227                 yylval.i = String_convert::dec2_i (String (YYText ()));
228                 return UNSIGNED;
229         }
230
231         \" {
232                 start_quote ();
233         }
234 }
235
236 \"              {
237         start_quote ();
238 }
239 <quote>{
240         \\{ESCAPED}     {
241                 *yylval.string += to_str (escaped_char(YYText()[1]));
242         }
243         [^\\"]+ {
244                 *yylval.string += YYText ();
245         }
246         \"      {
247                 DOUT << "quoted string: `" << *yylval.string << "'\n";
248                 yy_pop_state ();
249                 return STRING;
250         }
251         .       {
252                 *yylval.string += YYText ();
253         }
254 }
255
256 <lyrics>{
257         \" {
258                 start_quote ();
259         }
260         {UNSIGNED}              {
261                 yylval.i = String_convert::dec2_i (String (YYText ()));
262                 return UNSIGNED;
263         }
264         {NOTECOMMAND}   {
265                 return scan_escaped_word (YYText () + 1);
266         }
267         {LYRICS} {
268                 /* ugr. This sux. */
269                 String s (YYText ()); 
270                 if (s == "__")
271                         return yylval.i = EXTENDER;
272                 int i = 0;
273                 while ((i=s.index_i ("_")) != -1) // change word binding "_" to " "
274                         *(s.ch_l () + i) = ' ';
275                 if ((i=s.index_i ("\\,")) != -1)   // change "\," to TeX's "\c "
276                         {
277                         *(s.ch_l () + i + 1) = 'c';
278                         s = s.left_str (i+2) + " " + s.right_str (s.length_i ()-i-2);
279                         }
280                 yylval.string = new String (s);
281                 DOUT << "lyric : `" << s << "'\n";
282                 return STRING;
283         }
284         . {
285                 return yylval.c = YYText ()[0];
286         }
287 }
288 <chords>{
289         {ALPHAWORD}     {
290                 return scan_bare_word (YYText ());
291         }
292         {NOTECOMMAND}   {
293                 return scan_escaped_word (YYText () + 1);
294         }
295         {UNSIGNED}              {
296                 yylval.i = String_convert::dec2_i (String (YYText ()));
297                 return UNSIGNED;
298         }
299         . {
300                 return yylval.c = YYText ()[0];
301         }
302 }
303
304 <<EOF>> {
305         DOUT << "<<eof>>";
306
307         if (! close_input ()) { 
308           yyterminate (); // can't move this, since it actually rets a YY_NULL
309         }
310 }
311
312
313 {WORD}  {
314         return scan_bare_word (YYText ());
315 }
316 {KEYWORD}       {
317         return scan_escaped_word (YYText () + 1);
318 }
319 {REAL}          {
320         Real r;
321         int cnv=sscanf (YYText (), "%lf", &r);
322         assert (cnv == 1);
323         DOUT  << "REAL" << r<<'\n';
324         yylval.real = r;
325         return REAL;
326 }
327
328 {UNSIGNED}      {
329         yylval.i = String_convert::dec2_i (String (YYText ()));
330         return UNSIGNED;
331 }
332
333 [{}]    {
334
335         DOUT << "parens\n";
336         return YYText ()[0];
337 }
338 [*:=]           {
339         char c = YYText ()[0];
340         DOUT << "misc char" <<c<<"\n";
341         return c;
342 }
343
344 <lyrics,notes>{PLET}    {
345         return yylval.i = PLET;
346 }
347
348 <lyrics,notes>{TELP}    {
349         return yylval.i = TELP;
350 }
351
352 <INITIAL,notes>.        {
353         return yylval.c = YYText ()[0];
354 }
355
356 <INITIAL,lyrics,notes>\\. {
357     char c= YYText ()[1];
358     yylval.c = c;
359     switch (c) {
360     case '>':
361         return E_BIGGER;
362     case '<':
363         return E_SMALLER;
364     case '!':
365         return E_EXCLAMATION;
366     default:
367         return E_CHAR;
368     }
369 }
370
371 <*>.            {
372         String msg = _f ("illegal character: `%c\'", YYText ()[0]);
373         LexerError (msg.ch_C ());
374         return YYText ()[0];
375 }
376
377 %%
378
379 void
380 My_lily_lexer::push_note_state ()
381 {
382         yy_push_state (notes);
383 }
384
385 void
386 My_lily_lexer::push_chord_state ()
387 {
388         yy_push_state (chords);
389 }
390
391 void
392 My_lily_lexer::push_lyric_state ()
393 {
394         yy_push_state (lyrics);
395 }
396
397 void
398 My_lily_lexer::pop_state ()
399 {
400         yy_pop_state ();
401 }
402
403 int
404 My_lily_lexer::scan_escaped_word (String str)
405 {       
406         DOUT << "\\word: `" << str<<"'\n";
407         int l = lookup_keyword (str);
408         if (l != -1) {
409                 DOUT << "(keyword)\n";
410                 return l;
411         }
412         Identifier * id = lookup_identifier (str);
413         if (id) {
414                 DOUT << "(identifier)\n";
415                 yylval.id = id;
416                 return id->token_code_i_;
417         }
418         if ((YYSTATE != notes) && (YYSTATE != chords)) {
419                 if (notename_b (str)) {
420                         yylval.pitch = new Musical_pitch (lookup_notename (str));
421                         yylval.pitch->set_spot (Input (source_file_l (), 
422                           here_ch_C ()));
423                         return NOTENAME_PITCH;
424                 }
425         }
426         if (check_debug)
427                 print_declarations (true);
428         String msg (_f ("unknown escaped string: `\\%s\'", str));       
429         LexerError (msg.ch_C ());
430         DOUT << "(string)";
431         String *sp = new String (str);
432         yylval.string=sp;
433         return STRING;
434 }
435
436 int
437 My_lily_lexer::scan_bare_word (String str)
438 {
439         DOUT << "word: `" << str<< "'\n";       
440         if ((YYSTATE == notes) || (YYSTATE == chords)) {
441                 if (notename_b (str)) {
442                     DOUT << "(notename)\n";
443                     yylval.pitch = new Musical_pitch (lookup_notename (str));
444                     yylval.pitch->set_spot (Input (source_file_l (), 
445                       here_ch_C ()));
446                     return NOTENAME_PITCH;
447                 } else if (chordmodifier_b (str)) {
448                     DOUT << "(chordmodifier)\n";
449                     yylval.pitch = new Musical_pitch (lookup_chordmodifier (str));
450                     yylval.pitch->set_spot (Input (source_file_l (), 
451                       here_ch_C ()));
452                     return CHORDMODIFIER_PITCH;
453                 }
454         }
455
456         yylval.string=new String (str);
457         return STRING;
458 }
459
460 bool
461 My_lily_lexer::note_state_b () const
462 {
463         return YY_START == notes;
464 }
465
466 bool
467 My_lily_lexer::chord_state_b () const
468 {
469         return YY_START == chords;
470 }
471
472 bool
473 My_lily_lexer::lyric_state_b () const
474 {
475         return YY_START == lyrics;
476 }
477
478 /*
479  urg, belong to String(_convert)
480  and should be generalised 
481  */
482 void
483 strip_leading_white (String&s)
484 {
485         int i=0;
486         for (;  i < s.length_i (); i++) 
487                 if (!isspace (s[i]))
488                         break;
489
490         s = s.nomid_str (0, i);
491 }
492
493 void
494 strip_trailing_white (String&s)
495 {
496         int i=s.length_i ();    
497         while (i--) 
498                 if (!isspace (s[i]))
499                         break;
500
501         s = s.left_str (i+1);
502 }
503