]> git.donarmstrong.com Git - lilypond.git/blob - lily/lexer.ll
Merge branch 'lilypond/translation' into staging
[lilypond.git] / lily / lexer.ll
1 %{ // -*- mode: c++; c-file-style: "linux" -*-
2 /*
3   This file is part of LilyPond, the GNU music typesetter.
4
5   Copyright (C) 1996--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
6                  Jan Nieuwenhuizen <janneke@gnu.org>
7
8   LilyPond is free software: you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation, either version 3 of the License, or
11   (at your option) any later version.
12
13   LilyPond is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /* Mode and indentation are at best a rough approximation based on TAB
23  * formatting (reasonable for compatibility with unspecific editor
24  * modes as Flex modes are hard to find) and need manual correction
25  * frequently.  Without a reasonably dependable way of formatting a
26  * Flex file sensibly, there is little point in trying to fix the
27  * inconsistent state of indentation.
28  */
29
30 /*
31   backup rules
32
33   after making a change to the lexer rules, run 
34       flex -b <this lexer file>
35   and make sure that 
36       lex.backup
37   contains no backup states, but only the reminder
38       Compressed tables always back up.
39  (don-t forget to rm lex.yy.cc :-)
40  */
41
42
43
44 #include <cstdio>
45 #include <cctype>
46 #include <cerrno>
47
48 /* Flex >= 2.5.29 fix; FlexLexer.h's multiple include bracing breaks
49    when building the actual lexer.  */
50
51 #define LEXER_CC
52
53 #include <iostream>
54 using namespace std;
55
56 #include "context-def.hh"
57 #include "duration.hh"
58 #include "international.hh"
59 #include "interval.hh"
60 #include "lily-guile.hh"
61 #include "lily-lexer.hh"
62 #include "lily-parser.hh"
63 #include "lilypond-version.hh"
64 #include "main.hh"
65 #include "music.hh"
66 #include "music-function.hh"
67 #include "parse-scm.hh"
68 #include "parser.hh"
69 #include "pitch.hh"
70 #include "source-file.hh"
71 #include "std-string.hh"
72 #include "string-convert.hh"
73 #include "version.hh"
74 #include "warn.hh"
75
76 /*
77 RH 7 fix (?)
78 */
79 #define isatty HORRIBLEKLUDGE
80
81 void strip_trailing_white (string&);
82 void strip_leading_white (string&);
83 string lyric_fudge (string s);
84 SCM lookup_markup_command (string s);
85 SCM lookup_markup_list_command (string s);
86 bool is_valid_version (string s);
87
88
89 #define start_quote()   \
90         yy_push_state (quote);\
91         yylval.string = new string
92
93 #define start_lyric_quote()     \
94         yy_push_state (lyric_quote);\
95         yylval.string = new string
96
97 #define yylval (*lexval_)
98
99 #define yylloc (*lexloc_)
100
101 #define YY_USER_ACTION  add_lexed_char (YYLeng ());
102
103
104 SCM scan_fraction (string);
105 SCM (* scm_parse_error_handler) (void *);
106
107
108
109 %}
110
111 %option c++
112 %option noyywrap
113 %option nodefault
114 %option debug
115 %option yyclass="Lily_lexer"
116 %option stack
117 %option never-interactive 
118 %option warn
119
120 %x extratoken
121 %x chords
122 %x figures
123 %x incl
124 %x lyrics
125 %x lyric_quote
126 %x longcomment
127 %x markup
128 %x notes
129 %x quote
130 %x sourcefileline
131 %x sourcefilename
132 %x version
133
134 A               [a-zA-Z\200-\377]
135 AA              {A}|_
136 N               [0-9]
137 AN              {AA}|{N}
138 ANY_CHAR        (.|\n)
139 PUNCT           [?!:'`]
140 ACCENT          \\[`'"^]
141 SPECIAL_CHAR            [&@]
142 NATIONAL        [\001-\006\021-\027\031\036]
143 TEX             {AA}|-|{PUNCT}|{ACCENT}|{NATIONAL}|{SPECIAL_CHAR}
144 DASHED_WORD             {A}({AN}|-)*
145 DASHED_KEY_WORD         \\{DASHED_WORD}
146
147
148
149 ALPHAWORD       {A}+
150 UNSIGNED        {N}+
151 E_UNSIGNED      \\{N}+
152 FRACTION        {N}+\/{N}+
153 INT             -?{UNSIGNED}
154 REAL            ({INT}\.{N}*)|(-?\.{N}+)
155 WHITE           [ \n\t\f\r]
156 HORIZONTALWHITE         [ \t]
157 BLACK           [^ \n\t\f\r]
158 RESTNAME        [rs]
159 NOTECOMMAND     \\{A}+
160 MARKUPCOMMAND   \\({A}|[-_])+
161 LYRICS          ({AA}|{TEX})[^0-9 \t\n\r\f]*
162 ESCAPED         [nt\\'"]
163 EXTENDER        __
164 HYPHEN          --
165 BOM_UTF8        \357\273\277
166
167 %%
168
169
170 <*>\r           {
171         // swallow and ignore carriage returns
172 }
173
174 <extratoken>{ANY_CHAR}  {
175   /* Generate a token without swallowing anything */
176
177   /* First unswallow the eaten character */
178   add_lexed_char (-YYLeng ());
179   yyless (0);
180
181   /* produce requested token */
182   int type = scm_to_int (scm_caar (extra_tokens_));
183   yylval.scm = scm_cdar (extra_tokens_);
184   extra_tokens_ = scm_cdr (extra_tokens_);
185   if (scm_is_null (extra_tokens_))
186     yy_pop_state ();
187
188   return type;
189 }
190
191 <extratoken><<EOF>>     {
192   /* Generate a token without swallowing anything */
193
194   /* produce requested token */
195   int type = scm_to_int (scm_caar (extra_tokens_));
196   yylval.scm = scm_cdar (extra_tokens_);
197   extra_tokens_ = scm_cdr (extra_tokens_);
198   if (scm_is_null (extra_tokens_))
199     yy_pop_state ();
200
201   return type;
202 }
203
204    /* Use the trailing context feature. Otherwise, the BOM will not be
205       found if the file starts with an identifier definition. */
206 <INITIAL,chords,lyrics,figures,notes>{BOM_UTF8}/.* {
207   if (this->lexloc_->line_number () != 1 || this->lexloc_->column_number () != 0)
208     {
209       LexerWarning (_ ("stray UTF-8 BOM encountered").c_str ());
210       // exit (1);
211     }
212   debug_output (_ ("Skipping UTF-8 BOM"));
213 }
214
215 <INITIAL,chords,figures,incl,lyrics,markup,notes>{
216   "%{"  {
217         yy_push_state (longcomment);
218   }
219   %[^{\n\r][^\n\r]*[\n\r]       {
220   }
221   %[^{\n\r]     { // backup rule
222   }
223   %[\n\r]       {
224   }
225   %[^{\n\r][^\n\r]*     {
226   }
227   {WHITE}+      {
228
229   }
230 }
231
232 <INITIAL,notes,figures,chords,markup>{
233         \"              {
234                 start_quote ();
235         }
236 }
237
238 <INITIAL,chords,lyrics,notes,figures>\\version{WHITE}*  {
239         yy_push_state (version);
240 }
241 <INITIAL,chords,lyrics,notes,figures>\\sourcefilename{WHITE}*   {
242         yy_push_state (sourcefilename);
243 }
244 <INITIAL,chords,lyrics,notes,figures>\\sourcefileline{WHITE}*   {
245         yy_push_state (sourcefileline);
246 }
247 <version>\"[^"]*\"     { /* got the version number */
248         string s (YYText () + 1);
249         s = s.substr (0, s.rfind ('\"'));
250
251         yy_pop_state ();
252
253         SCM top_scope = scm_car (scm_last_pair (scopes_));
254         scm_module_define (top_scope, ly_symbol2scm ("version-seen"), SCM_BOOL_T);
255
256         if (!is_valid_version (s))
257                 return INVALID;
258
259
260 }
261 <sourcefilename>\"[^"]*\"     {
262         string s (YYText () + 1);
263         s = s.substr (0, s.rfind ('\"'));
264
265         yy_pop_state ();
266         this->here_input().get_source_file ()->name_ = s;
267         message (_f ("Renaming input to: `%s'", s.c_str ()));
268         progress_indication ("\n");
269         scm_module_define (scm_car (scopes_),
270                      ly_symbol2scm ("input-file-name"),
271                      ly_string2scm (s));
272
273 }
274
275 <sourcefileline>{INT}   {
276         int i;
277         sscanf (YYText (), "%d", &i);
278
279         yy_pop_state ();
280         this->here_input ().get_source_file ()->set_line (here_input ().start (), i);
281 }
282
283 <version>{ANY_CHAR}     {
284         LexerError (_ ("quoted string expected after \\version").c_str ());
285         yy_pop_state ();
286 }
287 <sourcefilename>{ANY_CHAR}      {
288         LexerError (_ ("quoted string expected after \\sourcefilename").c_str ());
289         yy_pop_state ();
290 }
291 <sourcefileline>{ANY_CHAR}      {
292         LexerError (_ ("integer expected after \\sourcefileline").c_str ());
293         yy_pop_state ();
294 }
295 <longcomment>{
296         [^\%]*          {
297         }
298         \%*[^}%]*               {
299
300         }
301         "%"+"}"         {
302                 yy_pop_state ();
303         }
304 }
305
306
307 <INITIAL,chords,lyrics,notes,figures>\\maininput           {
308         if (!is_main_input_)
309         {
310                 start_main_input ();
311                 is_main_input_ = true;
312         }
313         else
314                 error (_ ("\\maininput not allowed outside init files"));
315 }
316
317 <INITIAL,chords,lyrics,figures,notes>\\include           {
318         yy_push_state (incl);
319 }
320 <incl>\"[^""]*\"   { /* got the include file name */
321         string s (YYText ()+1);
322         s = s.substr (0, s.rfind ('"'));
323
324         new_input (s, sources_);
325         yy_pop_state ();
326 }
327 <incl>\\{BLACK}*{WHITE}? { /* got the include identifier */
328         string s = YYText () + 1;
329         strip_trailing_white (s);
330         if (s.length () && (s[s.length () - 1] == ';'))
331           s = s.substr (0, s.length () - 1);
332
333         SCM sid = lookup_identifier (s);
334         if (scm_is_string (sid)) {
335                 new_input (ly_scm2string (sid), sources_);
336                 yy_pop_state ();
337         } else {
338             string msg (_f ("wrong or undefined identifier: `%s'", s ));
339
340             LexerError (msg.c_str ());
341             SCM err = scm_current_error_port ();
342             scm_puts ("This value was found in the table: ", err);
343             scm_display (sid, err);
344           }
345 }
346 <incl>(\$|#) { // scm for the filename
347         int n = 0;
348         Input hi = here_input();
349         hi.step_forward ();
350         SCM sval = ly_parse_scm (hi.start (), &n, hi,
351                 be_safe_global && is_main_input_, parser_);
352         sval = eval_scm (sval);
353
354         for (int i = 0; i < n; i++)
355         {
356                 yyinput ();
357         }
358         char_count_stack_.back () += n;
359
360         if (scm_is_string (sval)) {
361                 new_input (ly_scm2string (sval), sources_);
362                 yy_pop_state ();
363         } else {
364                 LexerError (_ ("string expected after \\include").c_str ());
365                 if (sval != SCM_UNDEFINED) {
366                         SCM err = scm_current_error_port ();
367                         scm_puts ("This value was found instead: ", err);
368                         scm_display (sval, err);
369                 }
370         }
371 }
372
373 <incl,version,sourcefilename>\"[^""]*   { // backup rule
374         error (_ ("end quote missing"));
375         exit (1);
376 }
377 <chords,notes,figures>{RESTNAME}        {
378         char const *s = YYText ();
379         yylval.scm = scm_from_locale_string (s);
380         return RESTNAME;
381 }
382 <chords,notes,figures>R         {
383         return MULTI_MEASURE_REST;
384 }
385 <INITIAL,chords,figures,lyrics,markup,notes>#   { //embedded scm
386         int n = 0;
387         Input hi = here_input();
388         hi.step_forward ();
389         SCM sval = ly_parse_scm (hi.start (), &n, hi,
390                 be_safe_global && is_main_input_, parser_);
391
392         if (sval == SCM_UNDEFINED)
393                 error_level_ = 1;
394
395         for (int i = 0; i < n; i++)
396         {
397                 yyinput ();
398         }
399         char_count_stack_.back () += n;
400
401         yylval.scm = sval;
402         return SCM_TOKEN;
403 }
404
405 <INITIAL,chords,figures,lyrics,markup,notes>\$  { //immediate scm
406         int n = 0;
407         Input hi = here_input();
408         hi.step_forward ();
409         SCM sval = ly_parse_scm (hi.start (), &n, hi,
410                 be_safe_global && is_main_input_, parser_);
411
412         for (int i = 0; i < n; i++)
413         {
414                 yyinput ();
415         }
416         char_count_stack_.back () += n;
417
418         sval = eval_scm (sval);
419                 
420         int token = scan_scm_id (sval);
421         if (!scm_is_eq (yylval.scm, SCM_UNSPECIFIED))
422           return token;
423 }
424
425 <INITIAL,notes,lyrics>{ 
426         \<\<    {
427                 return DOUBLE_ANGLE_OPEN;
428         }
429         \>\>    {
430                 return DOUBLE_ANGLE_CLOSE;
431         }
432 }
433
434 <INITIAL,notes>{
435         \<      {
436                 return ANGLE_OPEN;
437         }
438         \>      {
439                 return ANGLE_CLOSE;
440         }
441 }
442
443 <figures>{
444         _       {
445                 return FIGURE_SPACE;
446         }
447         \>              {
448                 return FIGURE_CLOSE;
449         }
450         \<      {
451                 return FIGURE_OPEN;
452         }
453 }
454
455 <notes,figures>{
456         {ALPHAWORD}     {
457                 return scan_bare_word (YYText ());
458         }
459
460         {NOTECOMMAND}   {
461                 return scan_escaped_word (YYText () + 1); 
462         }
463         {FRACTION}      {
464                 yylval.scm =  scan_fraction (YYText ());
465                 return FRACTION;
466         }
467         {UNSIGNED}/\/   | // backup rule
468         {UNSIGNED}              {
469                 yylval.scm = scm_c_read_string (YYText ());
470                 return UNSIGNED;
471         }
472         {E_UNSIGNED}    {
473                 yylval.i = String_convert::dec2int (string (YYText () +1));
474                 return E_UNSIGNED;
475         }
476 }
477
478 <quote,lyric_quote>{
479         \\{ESCAPED}     {
480                 *yylval.string += to_string (escaped_char (YYText ()[1]));
481         }
482         [^\\""]+        {
483                 *yylval.string += YYText ();
484         }
485         \"      {
486
487                 yy_pop_state ();
488
489                 /* yylval is union. Must remember STRING before setting SCM*/
490                 string *sp = yylval.string;
491                 yylval.scm = ly_string2scm (*sp);
492                 delete sp;
493                 return is_lyric_state () ? LYRICS_STRING : STRING;
494         }
495         .       {
496                 *yylval.string += YYText ();
497         }
498 }
499
500 <lyrics>{
501         \" {
502                 start_lyric_quote ();
503         }
504         {FRACTION}      {
505                 yylval.scm =  scan_fraction (YYText ());
506                 return FRACTION;
507         }
508         {UNSIGNED}/\/[^0-9] { // backup rule
509                 yylval.scm = scm_c_read_string (YYText ());
510                 return UNSIGNED;
511         }
512         {UNSIGNED}/\/   | // backup rule
513         {UNSIGNED}              {
514                 yylval.scm = scm_c_read_string (YYText ());
515                 return UNSIGNED;
516         }
517         {NOTECOMMAND}   {
518                 return scan_escaped_word (YYText () + 1);
519         }
520         {LYRICS} {
521                 /* ugr. This sux. */
522                 string s (YYText ()); 
523                 if (s == "__")
524                         return yylval.i = EXTENDER;
525                 if (s == "--")
526                         return yylval.i = HYPHEN;
527                 s = lyric_fudge (s);
528
529                 char c = s[s.length () - 1];
530                 if (c == '{' ||  c == '}') // brace open is for not confusing dumb tools.
531                         here_input ().warning (
532                                 _ ("Brace found at end of lyric.  Did you forget a space?"));
533                 yylval.scm = ly_string2scm (s);
534
535
536                 return LYRICS_STRING;
537         }
538         . {
539                 return YYText ()[0];
540         }
541 }
542 <chords>{
543         {ALPHAWORD}     {
544                 return scan_bare_word (YYText ());
545         }
546         {NOTECOMMAND}   {
547                 return scan_escaped_word (YYText () + 1);
548         }
549         {FRACTION}      {
550                 yylval.scm =  scan_fraction (YYText ());
551                 return FRACTION;
552         }
553         {UNSIGNED}/\/[^0-9] { // backup rule
554                 yylval.scm = scm_c_read_string (YYText ());
555                 return UNSIGNED;
556         }
557         {UNSIGNED}/\/   | // backup rule
558         {UNSIGNED}              {
559                 yylval.scm = scm_c_read_string (YYText ());
560                 return UNSIGNED;
561         }
562         -  {
563                 return CHORD_MINUS;
564         }
565         :  {
566                 return CHORD_COLON;
567         }
568         \/\+ {
569                 return CHORD_BASS;
570         }
571         \/  {
572                 return CHORD_SLASH;
573         }
574         \^  {
575                 return CHORD_CARET;
576         }
577         . {
578                 return YYText ()[0];
579         }
580 }
581
582
583 <markup>{
584         \\score {
585                 return SCORE;
586         }
587         {MARKUPCOMMAND} {
588                 string str (YYText () + 1);
589
590                 int token_type = MARKUP_FUNCTION;
591                 SCM s = lookup_markup_command (str);
592
593                 // lookup-markup-command returns a pair with the car
594                 // being the function to call, and the cdr being the
595                 // call signature specified to define-markup-command,
596                 // a list of predicates.
597
598                 if (!scm_is_pair (s)) {
599                   // If lookup-markup-command was not successful, we
600                   // try lookup-markup-list-command instead.
601                   // If this fails as well, we just scan and return
602                   // the escaped word.
603                   s = lookup_markup_list_command (str);
604                   if (scm_is_pair (s))
605                     token_type = MARKUP_LIST_FUNCTION;
606                   else
607                     return scan_escaped_word (str);
608                 }
609
610                 // If the list of predicates is, say,
611                 // (number? number? markup?), then tokens
612                 // EXPECT_MARKUP EXPECT_SCM EXPECT_SCM EXPECT_NO_MORE_ARGS
613                 // will be generated.  Note that we have to push them
614                 // in reverse order, so the first token pushed in the
615                 // loop will be EXPECT_NO_MORE_ARGS.
616
617                 yylval.scm = scm_car(s);
618
619                 // yylval now contains the function to call as token
620                 // value (for token type MARKUP_FUNCTION or
621                 // MARKUP_LIST_FUNCTION).
622
623                 push_extra_token(EXPECT_NO_MORE_ARGS);
624                 s = scm_cdr(s);
625                 for (; scm_is_pair(s); s = scm_cdr(s)) {
626                   SCM predicate = scm_car(s);
627
628                   if (predicate == ly_lily_module_constant ("markup-list?"))
629                     push_extra_token(EXPECT_MARKUP_LIST);
630                   else if (predicate == ly_lily_module_constant ("markup?"))
631                     push_extra_token(EXPECT_MARKUP);
632                   else
633                     push_extra_token(EXPECT_SCM, predicate);
634                 }
635                 return token_type;
636         }
637         [{}]    {
638                 return YYText ()[0];
639         }
640         [^$#{}\"\\ \t\n\r\f]+ {
641                 string s (YYText ()); 
642
643                 char c = s[s.length () - 1];
644                 /* brace open is for not confusing dumb tools.  */
645                 if (c == '{' ||  c == '}')
646                         here_input ().warning (
647                                 _ ("Brace found at end of markup.  Did you forget a space?"));
648                 yylval.scm = ly_string2scm (s);
649
650
651                 return STRING;
652         }
653         .  {
654                 return YYText()[0];
655         }
656 }
657
658 <longcomment><<EOF>> {
659                 LexerError (_ ("EOF found inside a comment").c_str ());
660                 is_main_input_ = false; // should be safe , can't have \include in --safe.
661                 if (!close_input ())
662                   yyterminate (); // can't move this, since it actually rets a YY_NULL
663         }
664
665 <<EOF>> { if (is_main_input_)
666         {
667                 /* 2 = init.ly + current file.
668                    > because we're before closing, but is_main_input_ should
669                    reflect after.
670                 */ 
671                 is_main_input_ = include_stack_.size () > 2;
672                 if (!close_input ())
673                 /* Returns YY_NULL */
674                         yyterminate ();
675         }
676         else if (!close_input ())
677                 /* Returns YY_NULL */
678                 yyterminate ();
679 }
680
681 <INITIAL>{
682         {DASHED_WORD}   {
683                 return scan_bare_word (YYText ());
684         }
685         {DASHED_KEY_WORD}       {
686                 return scan_escaped_word (YYText () + 1);
687         }
688 }
689
690 -{UNSIGNED}     | // backup rule
691 {REAL}          {
692         yylval.scm = scm_c_read_string (YYText ());
693         return REAL;
694 }
695 -\.     { // backup rule
696         yylval.scm = scm_from_double (0.0);
697         return REAL;
698 }
699
700 {UNSIGNED}      {
701         yylval.scm = scm_c_read_string (YYText ());
702         return UNSIGNED;
703 }
704
705
706 [{}]    {
707
708         return YYText ()[0];
709 }
710 [*:=]           {
711         char c = YYText ()[0];
712
713         return c;
714 }
715
716 <INITIAL,notes,figures>.        {
717         return YYText ()[0];
718 }
719
720 <INITIAL,lyrics,notes,figures>\\. {
721     char c = YYText ()[1];
722
723     switch (c) {
724     case '>':
725         return E_ANGLE_CLOSE;
726     case '<':
727         return E_ANGLE_OPEN;
728     case '!':
729         return E_EXCLAMATION;
730     case '(':
731         return E_OPEN;
732     case ')':
733         return E_CLOSE;
734     case '[':
735         return E_BRACKET_OPEN;
736     case '+':
737         return E_PLUS;
738     case ']':
739         return E_BRACKET_CLOSE;
740     case '~':
741         return E_TILDE;
742     case '\\':
743         return E_BACKSLASH;
744
745     default:
746         return E_CHAR;
747     }
748 }
749
750 <*>.            {
751         string msg = _f ("invalid character: `%c'", YYText ()[0]);
752         LexerError (msg.c_str ());
753         return YYText ()[0];
754 }
755
756 %%
757
758 /* Make the lexer generate a token of the given type as the next token. 
759  TODO: make it possible to define a value for the token as well */
760 void
761 Lily_lexer::push_extra_token (int token_type, SCM scm)
762 {
763         if (scm_is_null (extra_tokens_))
764         {
765                 if (YY_START != extratoken)
766                         hidden_state_ = YY_START;
767                 yy_push_state (extratoken);
768         }
769         extra_tokens_ = scm_acons (scm_from_int (token_type), scm, extra_tokens_);
770 }
771
772 void
773 Lily_lexer::push_chord_state (SCM tab)
774 {
775         pitchname_tab_stack_ = scm_cons (tab, pitchname_tab_stack_);
776         yy_push_state (chords);
777 }
778
779 void
780 Lily_lexer::push_figuredbass_state ()
781 {
782         yy_push_state (figures);
783 }
784
785 void
786 Lily_lexer::push_initial_state ()
787 {
788         yy_push_state (INITIAL);
789 }
790
791 void
792 Lily_lexer::push_lyric_state ()
793 {
794         yy_push_state (lyrics);
795 }
796
797 void
798 Lily_lexer::push_markup_state ()
799 {
800         yy_push_state (markup);
801 }
802
803 void
804 Lily_lexer::push_note_state (SCM tab)
805 {
806         pitchname_tab_stack_ = scm_cons (tab, pitchname_tab_stack_);
807         yy_push_state (notes);
808 }
809
810 void
811 Lily_lexer::pop_state ()
812 {
813         if (YYSTATE == notes || YYSTATE == chords)
814                 pitchname_tab_stack_ = scm_cdr (pitchname_tab_stack_);
815
816         yy_pop_state ();
817 }
818
819 int
820 Lily_lexer::identifier_type (SCM sid)
821 {
822         int k = try_special_identifiers (&yylval.scm , sid);
823         return k >= 0  ? k : SCM_IDENTIFIER;
824 }
825
826
827 int
828 Lily_lexer::scan_escaped_word (string str)
829 {
830         // use more SCM for this.
831
832 //      SCM sym = ly_symbol2scm (str.c_str ());
833
834         int i = lookup_keyword (str);
835         if (i == MARKUP && is_lyric_state ())
836                 return LYRIC_MARKUP;
837         if (i != -1)
838                 return i;
839
840         SCM sid = lookup_identifier (str);
841         if (sid != SCM_UNDEFINED)
842                 return scan_scm_id (sid);
843
844         string msg (_f ("unknown escaped string: `\\%s'", str));        
845         LexerError (msg.c_str ());
846
847         yylval.scm = ly_string2scm (str);
848
849         return STRING;
850 }
851
852 int
853 Lily_lexer::scan_scm_id (SCM sid)
854 {
855         if (is_music_function (sid))
856         {
857                 int funtype = SCM_FUNCTION;
858
859                 yylval.scm = get_music_function_transform (sid);
860
861                 SCM s = scm_object_property (yylval.scm, ly_symbol2scm ("music-function-signature"));
862                 SCM cs = scm_car (s);
863
864                 if (scm_is_pair (cs))
865                 {
866                         cs = SCM_CAR (cs);
867                 }
868
869                 if (scm_is_eq (cs, ly_lily_module_constant ("ly:music?")))
870                         funtype = MUSIC_FUNCTION;
871                 else if (scm_is_eq (cs, ly_lily_module_constant ("ly:event?")))
872                         funtype = EVENT_FUNCTION;
873                 else if (ly_is_procedure (cs))
874                         funtype = SCM_FUNCTION;
875                 else programming_error ("Bad syntax function predicate");
876
877                 push_extra_token (EXPECT_NO_MORE_ARGS);
878                 for (s = scm_cdr (s); scm_is_pair (s); s = scm_cdr (s))
879                 {
880                         SCM optional = SCM_UNDEFINED;
881                         cs = scm_car (s);
882
883                         if (scm_is_pair (cs))
884                         {
885                                 optional = SCM_CDR (cs);
886                                 cs = SCM_CAR (cs);
887                         }
888                         
889                         if (cs == Pitch_type_p_proc)
890                                 push_extra_token (EXPECT_PITCH);
891                         else if (cs == Duration_type_p_proc)
892                                 push_extra_token (EXPECT_DURATION);
893                         else if (ly_is_procedure (cs))
894                                 push_extra_token (EXPECT_SCM, cs);
895                         else
896                         {
897                                 programming_error ("Function parameter without type-checking predicate");
898                                 continue;
899                         }
900                         if (!scm_is_eq (optional, SCM_UNDEFINED))
901                                 push_extra_token (EXPECT_OPTIONAL, optional);
902                 }
903                 return funtype;
904         }
905         yylval.scm = sid;
906         return identifier_type (sid);
907 }
908
909 int
910 Lily_lexer::scan_bare_word (string str)
911 {
912         SCM sym = ly_symbol2scm (str.c_str ());
913         if ((YYSTATE == notes) || (YYSTATE == chords)) {
914                 SCM handle = SCM_BOOL_F;
915                 if (scm_is_pair (pitchname_tab_stack_))
916                         handle = scm_hashq_get_handle (scm_car (pitchname_tab_stack_), sym);
917                 
918                 if (scm_is_pair (handle)) {
919                         yylval.scm = scm_cdr (handle);
920                         if (unsmob_pitch (yylval.scm)) 
921                             return (YYSTATE == notes) ? NOTENAME_PITCH : TONICNAME_PITCH;
922                         else if (scm_is_symbol (yylval.scm))
923                             return DRUM_PITCH;
924                 }
925                 else if ((YYSTATE == chords)
926                         && (handle = scm_hashq_get_handle (chordmodifier_tab_, sym))!= SCM_BOOL_F)
927                 {
928                     yylval.scm = scm_cdr (handle);
929                     return CHORD_MODIFIER;
930                 }
931                 if ((chord_repetition_.repetition_symbol_ != SCM_EOL)
932                     && to_boolean (scm_equal_p (chord_repetition_.repetition_symbol_, sym)))
933                         return CHORD_REPETITION;
934         }
935
936         yylval.scm = ly_string2scm (str);
937         return STRING;
938 }
939
940 int
941 Lily_lexer::get_state () const
942 {
943         if (YY_START == extratoken)
944                 return hidden_state_;
945         else
946                 return YY_START;
947 }
948
949 bool
950 Lily_lexer::is_note_state () const
951 {
952         return get_state () == notes;
953 }
954
955 bool
956 Lily_lexer::is_chord_state () const
957 {
958         return get_state () == chords;
959 }
960
961 bool
962 Lily_lexer::is_lyric_state () const
963 {
964         return get_state () == lyrics;
965 }
966
967 bool
968 Lily_lexer::is_figure_state () const
969 {
970         return get_state () == figures;
971 }
972
973 SCM
974 Lily_lexer::eval_scm (SCM readerdata)
975 {
976         SCM sval = SCM_UNDEFINED;
977
978         if (!SCM_UNBNDP (readerdata))
979         {
980                 sval = ly_eval_scm (scm_car (readerdata),
981                                     *unsmob_input (scm_cdr (readerdata)),
982                                     be_safe_global && is_main_input_,
983                                     parser_);
984         }
985
986         if (SCM_UNBNDP (sval))
987         {
988                 error_level_ = 1;
989                 return SCM_UNSPECIFIED;
990         }
991         return sval;
992 }
993
994
995
996 /*
997  urg, belong to string (_convert)
998  and should be generalised 
999  */
1000 void
1001 strip_leading_white (string&s)
1002 {
1003         ssize i = 0;
1004         for (;  i < s.length (); i++)
1005                 if (!isspace (s[i]))
1006                         break;
1007
1008         s = s.substr (i);
1009 }
1010
1011 void
1012 strip_trailing_white (string&s)
1013 {
1014         ssize i = s.length ();  
1015         while (i--) 
1016                 if (!isspace (s[i]))
1017                         break;
1018
1019         s = s.substr (0, i + 1);
1020 }
1021
1022
1023
1024 Lilypond_version oldest_version ("2.7.38");
1025
1026
1027 bool
1028 is_valid_version (string s)
1029 {
1030   Lilypond_version current ( MAJOR_VERSION "." MINOR_VERSION "." PATCH_LEVEL );
1031   Lilypond_version ver (s);
1032   if (int (ver) < oldest_version)
1033         {       
1034                 non_fatal_error (_f ("file too old: %s (oldest supported: %s)", ver.to_string (), oldest_version.to_string ()));
1035                 non_fatal_error (_ ("consider updating the input with the convert-ly script"));
1036                 return false;
1037         }
1038
1039   if (ver > current)
1040         {
1041                 non_fatal_error (_f ("program too old: %s (file requires: %s)",  current.to_string (), ver.to_string ()));
1042                 return false;
1043         }
1044   return true;
1045 }
1046         
1047
1048 /*
1049   substitute _ and \,
1050 */
1051 string
1052 lyric_fudge (string s)
1053 {
1054   char *chars = string_copy (s);
1055
1056   for (char *p = chars; *p ; p++)
1057     {
1058       if (*p == '_' && (p == chars || *(p-1) != '\\'))
1059         *p = ' ';
1060     }
1061   
1062   s = string (chars);
1063   delete[] chars;
1064
1065   ssize i = 0;  
1066   if ((i = s.find ("\\,")) != NPOS)   // change "\," to TeX's "\c "
1067     {
1068       * (((char*)s.c_str ()) + i + 1) = 'c';
1069       s = s.substr (0, i + 2) + " " + s.substr (i - 2);
1070     }
1071
1072   return s;
1073 }
1074
1075 /*
1076 Convert "NUM/DEN" into a '(NUM . DEN) cons.
1077 */
1078 SCM
1079 scan_fraction (string frac)
1080 {
1081         ssize i = frac.find ('/');
1082         string left = frac.substr (0, i);
1083         string right = frac.substr (i + 1, (frac.length () - i + 1));
1084
1085         int n = String_convert::dec2int (left);
1086         int d = String_convert::dec2int (right);
1087         return scm_cons (scm_from_int (n), scm_from_int (d));
1088 }
1089
1090 SCM
1091 lookup_markup_command (string s)
1092 {
1093         SCM proc = ly_lily_module_constant ("lookup-markup-command");
1094         return scm_call_1 (proc, ly_string2scm (s));
1095 }
1096
1097 SCM
1098 lookup_markup_list_command (string s)
1099 {
1100         SCM proc = ly_lily_module_constant ("lookup-markup-list-command");
1101         return scm_call_1 (proc, ly_string2scm (s));
1102 }
1103
1104 /* Shut up lexer warnings.  */
1105 #if YY_STACK_USED
1106
1107 static void
1108 yy_push_state (int)
1109 {
1110 }
1111
1112 static void
1113 yy_pop_state ()
1114 {
1115 }
1116
1117 static int
1118 yy_top_state ()
1119 {
1120   return 0;
1121 }
1122
1123 static void
1124 silence_lexer_warnings ()
1125 {
1126    (void) yy_start_stack_ptr;
1127    (void) yy_start_stack_depth;
1128    (void) yy_start_stack;
1129    (void) yy_push_state;
1130    (void) yy_pop_state;
1131    (void) yy_top_state;
1132    (void) silence_lexer_warnings;
1133 }
1134 #endif