]> git.donarmstrong.com Git - lilypond.git/blob - lily/lexer.ll
release: 1.1.56
[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 HYPHEN          --
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 <chords,notes>{RESTNAME}        {
184         const char *s = YYText ();
185         yylval.string = new String (s); 
186         DOUT << "rest:"<< yylval.string;
187         return RESTNAME;
188 }
189 <chords,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                 if (s == "--")
271                         return yylval.i = HYPHEN;
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                 start_quote ();
301         }
302         . {
303                 return yylval.c = YYText ()[0];
304         }
305 }
306
307 <<EOF>> {
308         DOUT << "<<eof>>";
309
310         if (! close_input ()) { 
311           yyterminate (); // can't move this, since it actually rets a YY_NULL
312         }
313 }
314
315
316 {WORD}  {
317         return scan_bare_word (YYText ());
318 }
319 {KEYWORD}       {
320         return scan_escaped_word (YYText () + 1);
321 }
322 {REAL}          {
323         Real r;
324         int cnv=sscanf (YYText (), "%lf", &r);
325         assert (cnv == 1);
326         DOUT  << "REAL" << r<<'\n';
327         yylval.real = r;
328         return REAL;
329 }
330
331 {UNSIGNED}      {
332         yylval.i = String_convert::dec2_i (String (YYText ()));
333         return UNSIGNED;
334 }
335
336 [{}]    {
337
338         DOUT << "parens\n";
339         return YYText ()[0];
340 }
341 [*:=]           {
342         char c = YYText ()[0];
343         DOUT << "misc char" <<c<<"\n";
344         return c;
345 }
346
347 <INITIAL,notes>.        {
348         return yylval.c = YYText ()[0];
349 }
350
351 <INITIAL,lyrics,notes>\\. {
352     char c= YYText ()[1];
353     yylval.c = c;
354     switch (c) {
355     case '>':
356         return E_BIGGER;
357     case '<':
358         return E_SMALLER;
359     case '!':
360         return E_EXCLAMATION;
361     default:
362         return E_CHAR;
363     }
364 }
365
366 <*>.            {
367         String msg = _f ("invalid character: `%c\'", YYText ()[0]);
368         LexerError (msg.ch_C ());
369         return YYText ()[0];
370 }
371
372 %%
373
374 void
375 My_lily_lexer::push_note_state ()
376 {
377         yy_push_state (notes);
378 }
379
380 void
381 My_lily_lexer::push_chord_state ()
382 {
383         yy_push_state (chords);
384 }
385
386 void
387 My_lily_lexer::push_lyric_state ()
388 {
389         yy_push_state (lyrics);
390 }
391
392 void
393 My_lily_lexer::pop_state ()
394 {
395         yy_pop_state ();
396 }
397
398 int
399 My_lily_lexer::scan_escaped_word (String str)
400 {       
401         DOUT << "\\word: `" << str<<"'\n";
402         int l = lookup_keyword (str);
403         if (l != -1) {
404                 DOUT << "(keyword)\n";
405                 return l;
406         }
407         Identifier * id = lookup_identifier (str);
408         if (id) {
409                 DOUT << "(identifier)\n";
410                 yylval.id = id;
411                 return id->token_code_i_;
412         }
413         if ((YYSTATE != notes) && (YYSTATE != chords)) {
414                 if (notename_b (str)) {
415                         yylval.pitch = new Musical_pitch (lookup_notename (str));
416                         yylval.pitch->set_spot (Input (source_file_l (), 
417                           here_ch_C ()));
418                         return NOTENAME_PITCH;
419                 }
420         }
421         if (check_debug)
422                 print_declarations (true);
423         String msg (_f ("unknown escaped string: `\\%s\'", str));       
424         LexerError (msg.ch_C ());
425         DOUT << "(string)";
426         String *sp = new String (str);
427         yylval.string=sp;
428         return STRING;
429 }
430
431 int
432 My_lily_lexer::scan_bare_word (String str)
433 {
434         DOUT << "word: `" << str<< "'\n";       
435         if ((YYSTATE == notes) || (YYSTATE == chords)) {
436                 if (notename_b (str)) {
437                     DOUT << "(notename)\n";
438                     yylval.pitch = new Musical_pitch (lookup_notename (str));
439                     yylval.pitch->set_spot (Input (source_file_l (), 
440                       here_ch_C ()));
441                     return (YYSTATE == notes) ? NOTENAME_PITCH : TONICNAME_PITCH;
442                 } else if (chordmodifier_b (str)) {
443                     DOUT << "(chordmodifier)\n";
444                     yylval.pitch = new Musical_pitch (lookup_chordmodifier (str));
445                     yylval.pitch->set_spot (Input (source_file_l (), 
446                       here_ch_C ()));
447                     return CHORDMODIFIER_PITCH;
448                 }
449         }
450
451         yylval.string=new String (str);
452         return STRING;
453 }
454
455 bool
456 My_lily_lexer::note_state_b () const
457 {
458         return YY_START == notes;
459 }
460
461 bool
462 My_lily_lexer::chord_state_b () const
463 {
464         return YY_START == chords;
465 }
466
467 bool
468 My_lily_lexer::lyric_state_b () const
469 {
470         return YY_START == lyrics;
471 }
472
473 /*
474  urg, belong to String(_convert)
475  and should be generalised 
476  */
477 void
478 strip_leading_white (String&s)
479 {
480         int i=0;
481         for (;  i < s.length_i (); i++) 
482                 if (!isspace (s[i]))
483                         break;
484
485         s = s.nomid_str (0, i);
486 }
487
488 void
489 strip_trailing_white (String&s)
490 {
491         int i=s.length_i ();    
492         while (i--) 
493                 if (!isspace (s[i]))
494                         break;
495
496         s = s.left_str (i+1);
497 }
498