%{ // -*-Fundamental-*- /* lexer.l -- implement the Flex lexer source file of the LilyPond music typesetter (c) 1996--1999 Han-Wen Nienhuys Jan Nieuwenhuizen */ /* backup rules after making a change to the lexer rules, run flex -b and make sure that lex.backup contains no backup states, but only the reminder Compressed tables always back up. (don-t forget to rm lex.yy.cc :-) */ #include #include #include "lily-guile.hh" #include "string.hh" #include "string-convert.hh" #include "my-lily-lexer.hh" #include "array.hh" #include "interval.hh" #include "lily-guile.hh" #include "parser.hh" #include "debug.hh" #include "main.hh" #include "musical-request.hh" #include "identifier.hh" #include "mudela-version.hh" #include "version.hh" void strip_trailing_white (String&); void strip_leading_white (String&); bool valid_version_b (String s); #define start_quote() \ yy_push_state (quote);\ yylval.string = new String #define yylval (*(YYSTYPE*)lexval_l) #define YY_USER_ACTION add_lexed_char (YYLeng ()); /* LYRICS ({AA}|{TEX})[^0-9 \t\n\f]* */ %} %option c++ %option noyywrap %option nodefault %option debug %option yyclass="My_lily_lexer" %option stack %option never-interactive %option warn %x version %x chords %x incl %x lyrics %x notes %x quote %x longcomment A [a-zA-Z] AA {A}|_ N [0-9] AN {AA}|{N} PUNCT [?!:'] ACCENT \\[`'"^] NATIONAL [\001-\006\021-\027\031\036\200-\377] TEX {AA}|-|{PUNCT}|{ACCENT}|{NATIONAL} WORD {A}{AN}* ALPHAWORD {A}+ DIGIT {N} UNSIGNED {N}+ INT -?{UNSIGNED} REAL ({INT}\.{N}*)|(-?\.{N}+) KEYWORD \\{WORD} WHITE [ \n\t\f\r] HORIZONTALWHITE [ \t] BLACK [^ \n\t\f\r] RESTNAME [rs] NOTECOMMAND \\{A}+ LYRICS ({AA}|{TEX})[^0-9 \t\n\f]* ESCAPED [nt\\'"] EXTENDER __ HYPHEN -- %% <*>\r { // windows-suck-suck-suck } { "%{" { yy_push_state (longcomment); } %[^{\n].*\n { } %[^{\n] { // backup rule } %\n { } %[^{\n].* { } {WHITE}+ { } } \\version{WHITE}* { yy_push_state (version); } \"[^"]*\";? { /* got the include file name */ String s (YYText ()+1); s = s.left_str (s.index_last_i ('"')); DEBUG_OUT << "#version `" << s << "'\n"; if (!valid_version_b (s)) return INVALID; yy_pop_state (); } . { LexerError ("No quoted string found after \\version"); yy_pop_state (); } { [^\%]* { } \%*[^}%]* { } "%"+"}" { yy_pop_state (); } <> { LexerError (_ ("EOF found inside a comment").ch_C ()); if (! close_input ()) yyterminate (); // can't move this, since it actually rets a YY_NULL } } \\maininput { if (!main_input_b_) { start_main_input (); main_input_b_ = true; } else error (_ ("\\maininput disallowed outside init files")); } \\include { yy_push_state (incl); } \"[^"]*\";? { /* got the include file name */ String s (YYText ()+1); s = s.left_str (s.index_last_i ('"')); DEBUG_OUT << "#include `" << s << "'\n"; new_input (s,source_global_l); yy_pop_state (); } \\{BLACK}*;?{WHITE} { /* got the include identifier */ String s = YYText () + 1; strip_trailing_white (s); if (s.length_i () && (s[s.length_i () - 1] == ';')) s = s.left_str (s.length_i () - 1); DEBUG_OUT << "#include `\\" << s << "'\n"; Identifier * id = lookup_identifier (s); if (id) { String* s_l = id->access_content_String (false); DEBUG_OUT << "#include `" << *s_l << "'\n"; new_input (*s_l, source_global_l); yy_pop_state (); } else { String msg (_f ("undefined identifier: `%s'", s )); LexerError (msg.ch_C ()); } } \"[^"]* { // backup rule cerr << _ ("Missing end quote") << endl; exit (1); } {RESTNAME} { const char *s = YYText (); yylval.scm = ly_str02scm (s); return RESTNAME; } R { return MEASURES; } \\\${BLACK}*{WHITE} { String s=YYText () + 2; s=s.left_str (s.length_i () - 1); return scan_escaped_word (s); } \${BLACK}*{WHITE} { String s=YYText () + 1; s=s.left_str (s.length_i () - 1); return scan_bare_word (s); } \\\${BLACK}* { // backup rule cerr << _ ("white expected") << endl; exit (1); } \${BLACK}* { // backup rule cerr << _ ("white expected") << endl; exit (1); } # { //embedded scm //char const* s = YYText () + 1; char const* s = here_ch_C (); int n = 0; if (main_input_b_ && safe_global_b) { error (_ ("Can't evaluate Scheme in safe mode")); return SCM_EOL; } yylval.scm = ly_parse_scm (s, &n); DEBUG_OUT << "Scheme: "; if (flower_dstream) ly_display_scm (yylval.scm); for (int i=0; i < n; i++) { yyinput (); } char_count_stack_.top () += n; return SCM_T; } { {ALPHAWORD} { return scan_bare_word (YYText ()); } {NOTECOMMAND} { return scan_escaped_word (YYText () + 1); } {DIGIT} { yylval.i = String_convert::dec2_i (String (YYText ())); return DIGIT; } {UNSIGNED} { yylval.i = String_convert::dec2_i (String (YYText ())); return UNSIGNED; } \" { start_quote (); } } \" { start_quote (); } { \\{ESCAPED} { *yylval.string += to_str (escaped_char(YYText()[1])); } [^\\"]+ { *yylval.string += YYText (); } \" { DEBUG_OUT << "quoted string: `" << *yylval.string << "'\n"; yy_pop_state (); /* yylval is union. Must remember STRING before setting SCM*/ String *sp = yylval.string; yylval.scm = ly_str02scm (sp->ch_C ()); delete sp; return STRING; } . { *yylval.string += YYText (); } } { \" { start_quote (); } {UNSIGNED} { yylval.i = String_convert::dec2_i (String (YYText ())); return UNSIGNED; } {NOTECOMMAND} { return scan_escaped_word (YYText () + 1); } {LYRICS} { /* ugr. This sux. */ String s (YYText ()); if (s == "__") return yylval.i = EXTENDER; if (s == "--") return yylval.i = HYPHEN; int i = 0; while ((i=s.index_i ("_")) != -1) // change word binding "_" to " " *(s.ch_l () + i) = ' '; if ((i=s.index_i ("\\,")) != -1) // change "\," to TeX's "\c " { *(s.ch_l () + i + 1) = 'c'; s = s.left_str (i+2) + " " + s.right_str (s.length_i ()-i-2); } char c = s[s.length_i () - 1]; if (c == '{' && c == '}') // brace open is for not confusing dumb tools. here_input ().warning ( "Brace found at end of lyric. Did you forget a space?"); yylval.scm = ly_str02scm (s.ch_C ()); DEBUG_OUT << "lyric : `" << s << "'\n"; return STRING; } . { return yylval.c = YYText ()[0]; } } { {ALPHAWORD} { return scan_bare_word (YYText ()); } {NOTECOMMAND} { return scan_escaped_word (YYText () + 1); } {UNSIGNED} { yylval.i = String_convert::dec2_i (String (YYText ())); return UNSIGNED; } \" { start_quote (); } - { return CHORD_MINUS; } \^ { return CHORD_CARET; } . { return yylval.c = YYText ()[0]; } } <> { DEBUG_OUT << "<>"; if (! close_input ()) { yyterminate (); // can't move this, since it actually rets a YY_NULL } } {WORD} { return scan_bare_word (YYText ()); } {KEYWORD} { return scan_escaped_word (YYText () + 1); } {REAL} { Real r; int cnv=sscanf (YYText (), "%lf", &r); assert (cnv == 1); DEBUG_OUT << "REAL" << r<<'\n'; yylval.real = r; return REAL; } {UNSIGNED} { yylval.i = String_convert::dec2_i (String (YYText ())); return UNSIGNED; } [{}] { DEBUG_OUT << "parens\n"; return YYText ()[0]; } [*:=] { char c = YYText ()[0]; DEBUG_OUT << "misc char" <. { return yylval.c = YYText ()[0]; } \\. { char c= YYText ()[1]; yylval.c = c; switch (c) { case '>': return E_BIGGER; case '<': return E_SMALLER; case '!': return E_EXCLAMATION; default: return E_CHAR; } } <*>. { String msg = _f ("invalid character: `%c'", YYText ()[0]); LexerError (msg.ch_C ()); return YYText ()[0]; } %% void My_lily_lexer::push_note_state () { yy_push_state (notes); } void My_lily_lexer::push_chord_state () { yy_push_state (chords); } void My_lily_lexer::push_lyric_state () { yy_push_state (lyrics); } void My_lily_lexer::pop_state () { yy_pop_state (); } int My_lily_lexer::scan_escaped_word (String str) { DEBUG_OUT << "\\word: `" << str<<"'\n"; int l = lookup_keyword (str); if (l != -1) { DEBUG_OUT << "(keyword)\n"; return l; } Identifier * id = lookup_identifier (str); if (id) { DEBUG_OUT << "(identifier)\n"; yylval.id = id; return id->token_code_i_; } if ((YYSTATE != notes) && (YYSTATE != chords)) { if (notename_b (str)) { yylval.pitch = new Musical_pitch (lookup_notename (str)); yylval.pitch->set_spot (Input (source_file_l (), here_ch_C ())); return NOTENAME_PITCH; } } if (flower_dstream) print_declarations (true); String msg (_f ("unknown escaped string: `\\%s'", str)); LexerError (msg.ch_C ()); DEBUG_OUT << "(string)"; yylval.scm = ly_str02scm(str.ch_C()); return STRING; } int My_lily_lexer::scan_bare_word (String str) { DEBUG_OUT << "word: `" << str<< "'\n"; if ((YYSTATE == notes) || (YYSTATE == chords)) { if (notename_b (str)) { DEBUG_OUT << "(notename)\n"; yylval.pitch = new Musical_pitch (lookup_notename (str)); yylval.pitch->set_spot (Input (source_file_l (), here_ch_C ())); return (YYSTATE == notes) ? NOTENAME_PITCH : TONICNAME_PITCH; } else if (chordmodifier_b (str)) { DEBUG_OUT << "(chordmodifier)\n"; yylval.pitch = new Musical_pitch (lookup_chordmodifier (str)); yylval.pitch->set_spot (Input (source_file_l (), here_ch_C ())); return CHORDMODIFIER_PITCH; } } yylval.scm = ly_str02scm (str.ch_C()); return STRING; } bool My_lily_lexer::note_state_b () const { return YY_START == notes; } bool My_lily_lexer::chord_state_b () const { return YY_START == chords; } bool My_lily_lexer::lyric_state_b () const { return YY_START == lyrics; } /* urg, belong to String(_convert) and should be generalised */ void strip_leading_white (String&s) { int i=0; for (; i < s.length_i (); i++) if (!isspace (s[i])) break; s = s.nomid_str (0, i); } void strip_trailing_white (String&s) { int i=s.length_i (); while (i--) if (!isspace (s[i])) break; s = s.left_str (i+1); } bool valid_version_b (String s) { Mudela_version current ( MAJOR_VERSION "." MINOR_VERSION "." PATCH_LEVEL ); Mudela_version ver (s); if (!((ver >= oldest_version) && (ver <= current))) { error (_f ("incorrect mudela version: %s (%s, %s)", ver.str (), oldest_version.str (), current.str ())); if (!version_ignore_global_b) return false; } return true; }