%{ // -*-Fundamental-*-
+/*
+ lexer.l -- implement the Flex lexer
+
+ source file of the LilyPond music typesetter
+
+ (c) 1996,1997 Han-Wen Nienhuys <hanwen@stack.nl>
+*/
+
+
+/*
+ backup rules
+
+ after making a change to the lexer rules, run
+ flex -b <this lexer file>
+ 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 <stdio.h>
#include "string.hh"
#include "string-convert.hh"
-#include "lexer.hh"
+#include "my-lily-lexer.hh"
#include "varray.hh"
#include "parser.hh"
#include "debug.hh"
#include "input-score.hh"
#include "parseconstruct.hh"
#include "main.hh"
-#include "identparent.hh"
+#include "identifier.hh"
#define start_quote() \
yy_push_state(quote);\
yylval.string = new String
+#define yylval (*(YYSTYPE*)lexval_l)
+#define YY_USER_ACTION add_lexed_char(YYLeng());
%}
%option c++
%option noyywrap
%option nodefault
-%option yylineno
%option debug
-%option yyclass="My_flex_lexer"
+%option yyclass="My_lily_lexer"
%option stack
+%option never-interactive
+%option warn
-%x notes
%x incl
-%x quote
%x lyrics
-
+%x notes
+%x quote
+%x longcomment
A [a-zA-Z]
WORD {A}{AN}*
ALPHAWORD {A}+
INT -?{N}+
-REAL {INT}?(\.{N}*)?
-
-OPTSIGN !?
-PITCHMOD ['`]*{OPTSIGN}
-RESTNAME r|s|p
-NOTECOMMAND \\{WORD}
+REAL {INT}?(\.{N}+)?
+KEYWORD \\{WORD}
+WHITE [ \n\t\f]
+BLACK [^ \n\t\f]
+RESTNAME [rs]
+NOTECOMMAND \\{A}+
DOTS \.+
-LYRICS {TEX}+
-COMMENT [%#].*\n
+LYRICS ({AA}|{NATIONAL})[^0-9 \t\n\f]*
%%
-\$ {
- yy_push_state(notes);
-}
-\@ {
- yy_push_state(lyrics);
+<notes,incl,INITIAL,lyrics>{
+ "%{" {
+ yy_push_state(longcomment);
+ }
+ %[^{\n].*\n {
+ }
+ %[^{\n] { // backup rule
+ }
+ %\n {
+ }
+ %[^{\n].* {
+ }
+ {WHITE}+ {
+
+ }
}
-<notes>{RESTNAME} {
- const char *s = YYText();
- yylval.string = new String (s);
- mtor << "rest:"<< yylval.string;
- return RESTNAME;
-}
-<notes>{ALPHAWORD} {
- String str = YYText();
- mtor << "word: " << str<< eol;
- Identifier * id = lookup_identifier(str);
- if (id) {
- yylval.id = id;
- return id->token_code_i_;
+<longcomment>{
+ [^\%]* {
}
+ \%*[^}%]* {
- yylval.string=new String( str );
- return STRING;
-}
-<notes>{NOTECOMMAND} {
- String c = YYText() +1;
- mtor << "\\word: " << YYText()+1<<eol;
- int l = lookup_keyword(c);
- if (l != -1)
- return l;
- Identifier * id = lookup_identifier(c);
- if (id) {
- yylval.id = id;
- return id->token_code_i_;
}
- String *sp = new String( c);
- yylval.string=sp;
- return STRING;
-}
-
-<notes>{DOTS} {
- yylval.i = strlen(YYText());
- return DOTS;
-}
-<notes>{INT} {
- yylval.i = String_convert::dec2_i( String( YYText() ) );
- return INT;
+ "%"+"}" {
+ yy_pop_state();
+ }
}
-<notes>{COMMENT} {
+<longcomment><<EOF>> {
+ LexerError("EOF found inside a comment");
+ if (! close_input()) {
+ yyterminate(); // can't move this, since it actually rets a YY_NULL
+ }
}
-<notes>[ \t\n]+ {
-
+<notes,INITIAL,lyrics>
+\\include {
+ yy_push_state(incl);
}
-<notes>\$ {
+<incl>\"[^"]*\" { /* got the include file name */
+ String s (YYText()+1);
+ s = s.left_str(s.length_i()-1);
+ mtor << "#include `" << s << "\'\n";
+ new_input(s,source_l_g);
yy_pop_state();
}
-<notes>\" {
- start_quote();
+<incl>\"[^"]* { // backup rule
+ cerr << "missing end quote" << endl;
+ exit( 1 );
}
-<notes>. {
- return yylval.c = YYText()[0];
+<notes>{RESTNAME} {
+ const char *s = YYText();
+ yylval.string = new String (s);
+ mtor << "rest:"<< yylval.string;
+ return RESTNAME;
}
-
-
-\" {
- start_quote();
+<INITIAL,lyrics,notes>\\\${BLACK}*{WHITE} {
+ String s=YYText() + 2;
+ s=s.left_str(s.length_i() - 1);
+ return scan_escaped_word(s);
}
-<quote>[^"]+ {
- *yylval.string += YYText();
+<INITIAL,lyrics,notes>\${BLACK}*{WHITE} {
+ String s=YYText() + 1;
+ s=s.left_str(s.length_i() - 1);
+ return scan_bare_word(s);
}
-<quote>\" {
- mtor << "quoted string: `" << *yylval.string << "'\n";
- yy_pop_state();
- return STRING;
+<INITIAL,lyrics,notes>\\\${BLACK}* { // backup rule
+ cerr << "white expected" << endl;
+ exit( 1 );
}
-
-<lyrics>\" {
- start_quote();
-}
-<lyrics>{DOTS} {
- yylval.i = strlen(YYText());
- return DOTS;
+<INITIAL,lyrics,notes>\${BLACK}* { // backup rule
+ cerr << "white expected" << endl;
+ exit( 1 );
}
-<lyrics>{INT} {
- yylval.i = String_convert::dec2_i( String( YYText() ) );
- return INT;
-}
-<lyrics>{NOTECOMMAND} {
- String c = YYText() +1;
- mtor << "\\word: " << YYText()+1<<eol;
- int l = lookup_keyword(c);
- if (l != -1)
- return l;
+<notes>{
+ {ALPHAWORD}/\' {
+ post_quotes_b_ = true;
+ return scan_bare_word(YYText());
+ }
+ \'+ {
+ yylval.i = YYLeng();
+ if (post_quotes_b_) {
+ post_quotes_b_ = false;
+ return POST_QUOTES;
+ } else
+ return PRE_QUOTES;
+ }
+ {ALPHAWORD} {
+ return scan_bare_word(YYText());
-/* let's try passing tex's typesetting macros like \ss \alpha \c */
- String* str_p = new String(YYText());
- yylval.string=str_p;
- mtor << "\\word: `" << *str_p << "'\n";
- return STRING;
+ }
-/* and skip identifiers...
- Identifier * id = lookup_identifier(c);
- if (id) {
- yylval.id = id;
- return IDENTIFIER;
+ {NOTECOMMAND} {
+ return scan_escaped_word(YYText()+1);
}
- String *sp = new String( c);
- yylval.string=sp;
+ {DOTS} {
+ yylval.i = strlen(YYText());
+ return DOTS;
+ }
+ {INT} {
+ yylval.i = String_convert::dec2_i( String( YYText() ) );
+ return INT;
+ }
- return STRING;
-*/
-}
-<lyrics>{LYRICS} {
- /* ugr. This sux. */
- String s (YYText());
- 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);
- }
- yylval.string = new String(s);
- mtor << "lyric : `" << s << "'\n";
- return STRING;
-}
-<lyrics>\| {
- return YYText()[0];
+ \+\+ {
+ return CONCAT;
+ }
+ \" {
+ start_quote();
+ }
}
-<lyrics>{COMMENT} {
+\" {
+ start_quote();
}
-<lyrics>[{}] {
- return YYText()[0];
-}
-<lyrics>[()\[\]|/.^>_-] {
- return yylval.c = YYText()[0];
-}
-<lyrics>[ \t\n]+ {
-}
-<lyrics>@ {
- yy_pop_state();
+<quote>{
+ \\\\ {
+ *yylval.string += '\\';
+ }
+ \\\" {
+ *yylval.string +='\"';
+ }
+ [^"]+ {
+ *yylval.string += YYText();
+ }
+ \" {
+ mtor << "quoted string: `" << *yylval.string << "'\n";
+ yy_pop_state();
+ return STRING;
+ }
}
-<<EOF>> {
- mtor << "<<EOF>>";
+<lyrics>{
- if (! close_input())
- yyterminate(); // can't move this, since it actually rets a YY_NULL
+ \" {
+ start_quote();
+ }
+ {DOTS} {
+ yylval.i = strlen(YYText());
+ return DOTS;
+ }
+ {INT} {
+ yylval.i = String_convert::dec2_i( String( YYText() ) );
+ return INT;
+ }
+ {NOTECOMMAND} {
+ return scan_escaped_word(YYText()+1);
+ }
+ {LYRICS} {
+ /* ugr. This sux. */
+ String s (YYText());
+ 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);
+ }
+ yylval.string = new String(s);
+ mtor << "lyric : `" << s << "'\n";
+ return STRING;
+ }
+ . {
+ return yylval.c = YYText()[0];
+ }
}
+<<EOF>> {
+ mtor << "<<eof>>";
-include {
- yy_push_state(incl);
+ if (! close_input()) {
+ yyterminate(); // can't move this, since it actually rets a YY_NULL
+ }
}
-<incl>[ \t]* { /* eat the whitespace */ }
-<incl>\"[^"]*\"+ { /* got the include file name */
- String s (YYText()+1);
- s = s.left_str(s.length_i()-1);
- defined_ch_c_l = here_ch_c_l() - String( YYText() ).length_i() - 1;
- new_input(s);
- yy_pop_state();
+{WORD} {
+ return scan_bare_word(YYText());
}
-
-
-{WORD} {
- mtor << "word: " << YYText()<<eol;
- String c = YYText();
- int l = lookup_keyword(c);
- if (l != -1)
- return l;
- Identifier * id = lookup_identifier(c);
- if (id) {
- yylval.id = id;
- return IDENTIFIER;
- }
- String *sp = new String( c);
- mtor << "new id: " << *sp << eol;
- yylval.string=sp;
- return STRING;
+{KEYWORD} {
+ return scan_escaped_word(YYText()+1);
}
-
{REAL} {
Real r;
int cnv=sscanf (YYText(), "%lf", &r);
return REAL;
}
+{INT}\. { // backup rule
+ cerr << "int. encountered" << endl;
+ exit( 1 );
+}
+
[{}] {
mtor << "parens\n";
mtor << "misc char" <<c<<"\n";
return c;
}
-[ \t\n]+ {
-
+
+<INITIAL,notes>. {
+ return yylval.c = YYText()[0];
}
-{COMMENT} {
- //ignore
+<INITIAL,lyrics,notes>\\. {
+ 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;
+ }
}
-. {
- error( String( "illegal character: " ) + String( YYText()[0] ), here_ch_c_l() );
+
+<*>. {
+ LexerError( String( "illegal character: " ) +String( YYText()[0] ));
return YYText()[0];
}
%%
+void
+My_lily_lexer::push_note_state()
+{
+ yy_push_state(notes);
+}
+
+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)
+{
+ mtor << "\\word: `" << str<<"'\n";
+ int l = lookup_keyword(str);
+ if (l != -1) {
+ mtor << "(keyword)\n";
+ return l;
+ }
+ Identifier * id = lookup_identifier(str);
+ if (id) {
+ mtor << "(identifier)\n";
+ yylval.id = id;
+ return id->token_code_i_;
+ }
+ mtor << "(string)";
+ String *sp = new String( str);
+ yylval.string=sp;
+ return STRING;
+}
+
+int
+My_lily_lexer::scan_bare_word(String str)
+{
+ mtor << "word: `" << str<< "'\n";
+ if (YYSTATE == notes){
+ Melodic_req * mel_l = lookup_melodic_req_l(str);
+ if (mel_l) {
+ mtor << "(notename)\n";
+ yylval.melreq = mel_l;
+ return NOTENAME_ID;
+ }
+ }
+ if (YYSTATE != notes) {
+ // ugr. Should do this in note mode?
+ Identifier * id = lookup_identifier(str);
+ if (id) {
+ mtor << "(identifier)\n";
+ yylval.id = id;
+ return id->token_code_i_;
+ }
+ }
+ yylval.string=new String( str );
+ return STRING;
+}
+
+bool
+My_lily_lexer::note_state_b() const
+{
+ return YY_START == notes;
+}
+
+bool
+My_lily_lexer::lyric_state_b() const
+{
+ return YY_START == lyrics;
+}