%{ // -*-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 <ctype.h>
#include "string.hh"
#include "string-convert.hh"
-#include "notename.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 "identifier.hh"
+void strip_trailing_white(String&);
+void strip_leading_white(String&);
+
+
+#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 header
%x lyrics
-
+%x notes
+%x quote
+%x longcomment
A [a-zA-Z]
AA {A}|_
N [0-9]
AN {AA}|{N}
-PUNCT [?!,.:;]
-ACCENT [\\'"^]
+PUNCT [?!,.:;']
+ACCENT \\[`'"^]
NATIONAL [\241-\377]
TEX {AA}|-|{PUNCT}|{ACCENT}|{NATIONAL}
WORD {A}{AN}*
ALPHAWORD {A}+
INT -?{N}+
-REAL {INT}?(\.{N}*)?
-
-OPTSIGN !?
-PITCHMOD ['`]*{OPTSIGN}
-RESTNAME r|s|p
-NOTECOMMAND \\{WORD}
-NOTENAME [a-z]+
-UNOTENAME [A-Z][a-z]*
-DOTS \.+
-LYRICS {TEX}+
-COMMENT [%#].*\n
+REAL ({INT}\.{N}*)|(-?\.{N}+)
+KEYWORD \\{WORD}
+WHITE [ \n\t\f]
+HORIZONTALWHITE [ \t]
+BLACK [^ \n\t\f]
+RESTNAME [rs]
+NOTECOMMAND \\{A}+
+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>{UNOTENAME} {
- int *p=yylval.ii;
- return ret_notename(p, YYText(), -1);
+ }
}
-<notes>{NOTENAME} {
- int *p=yylval.ii;
- return ret_notename(p, YYText(), 0);
+<longcomment>{
+ [^\%]* {
+ }
+ \%*[^}%]* {
+
+ }
+ "%"+"}" {
+ yy_pop_state();
+ }
+ <<EOF>> {
+ LexerError("EOF found inside a comment");
+ if (! close_input())
+ yyterminate(); // can't move this, since it actually rets a YY_NULL
+ }
}
+<header>{
+ [\{\}] {
+ return YYText()[0];
+ }
+ ^{WORD} {
+ String s=YYText();
+ yylval.string = new String(s);
+ return FIELDNAME;
+ }
+ {HORIZONTALWHITE}+{BLACK}.*\n {
+ String s=YYText();
+ strip_leading_white(s);
+ strip_trailing_white(s);
+ yylval.string = new String(s);
+ return RECORDLINE;
+ }
+ {WHITE}* {
+ }
-<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 IDENTIFIER;
+ . {
+ return YYText()[0];
}
- String *sp = new String( c);
- yylval.string=sp;
- return STRING;
}
-<notes>{PITCHMOD} {
- const char *s = YYText();
- mtor << "pitchmod:"<< YYText()<<eol;
- yylval.string = new String (s);
- return PITCHMOD;
+
+<notes,INITIAL,lyrics>\\include {
+ yy_push_state(incl);
}
-<notes>{DOTS} {
- yylval.i = strlen(YYText());
- return DOTS;
+<incl>\"[^"]*\" { /* got the include file name */
+ String s (YYText()+1);
+ s = s.left_str(s.length_i()-1);
+ DOUT << "#include `" << s << "\'\n";
+ new_input(s,source_l_g);
+ yy_pop_state();
}
-<notes>{INT} {
- yylval.i = String_convert::dec2_i( String( YYText() ) );
- return INT;
+<incl>\"[^"]* { // backup rule
+ cerr << "missing end quote" << endl;
+ exit( 1 );
}
-<notes>{COMMENT} {
+<notes>{RESTNAME} {
+ const char *s = YYText();
+ yylval.string = new String (s);
+ DOUT << "rest:"<< yylval.string;
+ return RESTNAME;
}
-<notes>[ \t\n]+ {
-
+<INITIAL,lyrics,notes>\\\${BLACK}*{WHITE} {
+ String s=YYText() + 2;
+ s=s.left_str(s.length_i() - 1);
+ return scan_escaped_word(s);
}
-<notes>\$ {
- yy_pop_state();
+<INITIAL,lyrics,notes>\${BLACK}*{WHITE} {
+ String s=YYText() + 1;
+ s=s.left_str(s.length_i() - 1);
+ return scan_bare_word(s);
}
-<notes>\"[^"]*\" {
- String s (YYText()+1);
- s = s.left_str(s.length_i()-1);
- yylval.string = new String(s);
- return STRING;
+<INITIAL,lyrics,notes>\\\${BLACK}* { // backup rule
+ cerr << "white expected" << endl;
+ exit( 1 );
}
-<notes>. {
- return yylval.c = YYText()[0];
+<INITIAL,lyrics,notes>\${BLACK}* { // backup rule
+ cerr << "white expected" << endl;
+ exit( 1 );
}
+<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());
-\" {
- yy_push_state(quote);
-}
-<quote>[^"]* {
- yylval.string = new String (YYText());
-}
-<quote>\" {
- mtor << "quoted string\n";
- yy_pop_state();
- return STRING;
+ }
+
+ {NOTECOMMAND} {
+ return scan_escaped_word(YYText()+1);
+ }
+
+ {INT} {
+ yylval.i = String_convert::dec2_i( String( YYText() ) );
+ return INT;
+ }
+
+ \" {
+ start_quote();
+ }
}
-<lyrics>{DOTS} {
- yylval.i = strlen(YYText());
- return DOTS;
+\" {
+ start_quote();
}
-<lyrics>{INT} {
- yylval.i = String_convert::dec2_i( String( YYText() ) );
- return INT;
+<quote>{
+ \\\\ {
+ *yylval.string += '\\';
+ }
+ \\\" {
+ *yylval.string +='\"';
+ }
+ [^"]+ {
+ *yylval.string += YYText();
+ }
+ \" {
+ DOUT << "quoted string: `" << *yylval.string << "'\n";
+ yy_pop_state();
+ return STRING;
+ }
}
-<lyrics>{NOTECOMMAND} {
- String c = YYText() +1;
- mtor << "\\word: " << YYText()+1<<eol;
- int l = lookup_keyword(c);
- if (l != -1)
- return l;
-/* let's try passing tex's typesetting macros like \ss \alpha \c */
- String* str_p = new String(YYText());//huh?
- return STRING;
+<lyrics>{
-/* and skip identifiers...
- Identifier * id = lookup_identifier(c);
- if (id) {
- yylval.id = id;
- return IDENTIFIER;
+ \" {
+ start_quote();
+ }
+ {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);
+ DOUT << "lyric : `" << s << "'\n";
+ return STRING;
}
- String *sp = new String( c);
+ . {
+ return yylval.c = YYText()[0];
+ }
+}
- yylval.string=sp;
- return STRING;
-*/
+<<EOF>> {
+ DOUT << "<<eof>>";
+
+ if (! close_input()) {
+ yyterminate(); // can't move this, since it actually rets a YY_NULL
+ }
}
-<lyrics>\"[^"]*\" {
- String s (YYText()+1);
- s = s.left_str(s.length_i()-1);
- yylval.string = new String(s);
- return STRING;
+{WORD} {
+ return scan_bare_word(YYText());
}
-<lyrics>{LYRICS} {
- 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);
- return STRING;
+{KEYWORD} {
+ return scan_escaped_word(YYText()+1);
}
-<lyrics>\| {
- return YYText()[0];
+{REAL} {
+ Real r;
+ int cnv=sscanf (YYText(), "%lf", &r);
+ assert(cnv == 1);
+ DOUT << "REAL" << r<<'\n';
+ yylval.real = r;
+ return REAL;
}
-<lyrics>{COMMENT} {
+{INT} {
+ yylval.i = String_convert::dec2_i( String( YYText() ) );
+ return INT;
}
-<lyrics>[{}] {
+
+[{}] {
+
+ DOUT << "parens\n";
return YYText()[0];
}
-<lyrics>[()\[\]|/.^>_-] {
+[*:=] {
+ char c = YYText()[0];
+ DOUT << "misc char" <<c<<"\n";
+ return c;
+}
+
+<INITIAL,notes>. {
return yylval.c = YYText()[0];
}
-<lyrics>[ \t\n]+ {
+
+<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;
+ }
}
-<lyrics>@ {
- yy_pop_state();
+
+<*>. {
+ LexerError( String( "illegal character: " ) +String( YYText()[0] ));
+ return YYText()[0];
}
-<<EOF>> {
- mtor << "<<EOF>>";
+%%
- if (! close_input())
- yyterminate(); // can't move this, since it actually rets a YY_NULL
+void
+My_lily_lexer::push_note_state()
+{
+ yy_push_state(notes);
}
-
-include {
- yy_push_state(incl);
+void
+My_lily_lexer::push_lyric_state()
+{
+ yy_push_state(lyrics);
}
-<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();
+void
+My_lily_lexer::pop_state()
+{
+ yy_pop_state();
}
-
-{WORD} {
- mtor << "word: " << YYText()<<eol;
- String c = YYText();
- int l = lookup_keyword(c);
- if (l != -1)
+int
+My_lily_lexer::scan_escaped_word(String str)
+{
+ DOUT << "\\word: `" << str<<"'\n";
+ int l = lookup_keyword(str);
+ if (l != -1) {
+ DOUT << "(keyword)\n";
return l;
- Identifier * id = lookup_identifier(c);
- if (id) {
+ }
+ Identifier * id = lookup_identifier(str);
+ if (id) {
+ DOUT << "(identifier)\n";
yylval.id = id;
- return IDENTIFIER;
+ return id->token_code_i_;
}
- String *sp = new String( c);
- mtor << "new id: " << *sp << eol;
+ if ( YYSTATE != notes ) {
+ Melodic_req * mel_l = lookup_melodic_req_l(str);
+ if (mel_l) {
+ DOUT << "(notename)\n";
+ yylval.melreq = mel_l;
+ return NOTENAME_ID;
+ }
+ }
+ LexerError( "Unknown escaped string: `" + str + "'");
+ DOUT << "(string)";
+ String *sp = new String( str);
yylval.string=sp;
return STRING;
}
-{REAL} {
- Real r;
- int cnv=sscanf (YYText(), "%lf", &r);
- assert(cnv == 1);
- mtor << "REAL" << r<<'\n';
- yylval.real = r;
- return REAL;
-}
-
-[{}] {
+int
+My_lily_lexer::scan_bare_word(String str)
+{
+ DOUT << "word: `" << str<< "'\n";
+ if (YYSTATE == notes){
+ Melodic_req * mel_l = lookup_melodic_req_l(str);
+ if (mel_l) {
+ DOUT << "(notename)\n";
+ yylval.melreq = mel_l;
+ return NOTENAME_ID;
+ }
+ }
- mtor << "parens\n";
- return YYText()[0];
+ yylval.string=new String( str );
+ return STRING;
}
-[*:=] {
- char c = YYText()[0];
- mtor << "misc char" <<c<<"\n";
- return c;
+
+bool
+My_lily_lexer::note_state_b() const
+{
+ return YY_START == notes;
}
-[ \t\n]+ {
-
+
+bool
+My_lily_lexer::lyric_state_b() const
+{
+ return YY_START == lyrics;
}
-{COMMENT} {
- //ignore
+void
+My_lily_lexer::push_header_state()
+{
+ yy_push_state(header);
}
-. {
- error( String( "illegal character: " ) + String( YYText()[0] ), here_ch_c_l() );
- return YYText()[0];
+
+void strip_trailing_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_leading_white(String&s)
+{
+ int i=s.length_i();
+ while (i--)
+ if (!isspace(s[i]))
+ break;
-%%
+ s = s.left_str(i+1);
+}