%{//-*-Fundamental-*- // midi-lexer.l /* yes, i know that midi is not really a (n evolving?) language, and that using lex/yacc to parse midi is overkill, as well as a grand example of misuse and asking for performance loss. it is, however, quite robust, simple, and very easy to extend incrementally. */ /* 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 "string-convert.hh" #include "mi2mu-global.hh" #include "mi2mu-proto.hh" #include "my-midi-lexer.hh" #include "midi-parser.hh" #define YY_USER_ACTION char_count_ += YYLeng(); // ugh %} %option c++ %option noyywrap %option nodefault %option debug %option yyclass="My_midi_lexer" %option stack %x data %x event %x i8 %x u8 %x int16 %x int32 %x meta_event %x track U8 [\x00-\xff] I8 {U8} INT16 {U8}{U8} BACKUP_INT16_0 {U8} INT32 {INT16}{INT16} BACKUP_INT32_0 {U8} BACKUP_INT32_1 {U8}{U8} BACKUP_INT32_2 {INT16}{U8} INT7_8UNSET [\x00-\x7f] INT7_8SET [\x80-\xff] VARINT {INT7_8SET}{0,3}{INT7_8UNSET} BACKUP_VARINT_0 {INT7_8SET} BACKUP_VARINT_1 {INT7_8SET}{INT7_8SET} BACKUP_VARINT_2 {INT7_8SET}{INT7_8SET}{INT7_8SET} HEADER MThd TRACK MTrk BACKUP_TOP_0 MT BACKUP_TOP_1 MTh BACKUP_TOP_2 MTr RUNNING_STATUS [\x00-\x5f] DATA_ENTRY [\x60-\x79] ALL_NOTES_OFF [\x7a-\x7f] NOTE_OFF [\x80-\x8f] NOTE_ON [\x90-\x9f] POLYPHONIC_AFTERTOUCH [\xa0-\xaf] CONTROLMODE_CHANGE [\xb0-\xbf] PROGRAM_CHANGE [\xc0-\xcf] CHANNEL_AFTERTOUCH [\xd0-\xdf] PITCHWHEEL_RANGE [\xe0-\xef] SYSEX_EVENT1 [\xf0] SYSEX_EVENT2 [\xf7] META_EVENT [\xff] SEQUENCE [\x00][\x02] YYTEXT [\x01] YYCOPYRIGHT [\x02] YYTRACK_NAME [\x03] YYINSTRUMENT_NAME [\x04] YYLYRIC [\x05] YYMARKER [\x06] YYCUE_POINT [\x07] END_OF_TRACK [\x2f][\x00] TEMPO [\x51][\x03] SMPTE_OFFSET [\x54][\x05] TIME [\x58][\x04] KEY [\x59][\x02] SSME [\0x7f][\x03] %% {HEADER} { LOGOUT(DEBUG_ver) << "lex: header" << endl; yy_push_state(int16); yy_push_state(int16); yy_push_state(int16); yy_push_state(int32); return HEADER; } {TRACK} { LOGOUT(DEBUG_ver) << "lex: track" << endl; yy_push_state(track); yy_push_state(int32); return TRACK; } {U8} { error(String("top level: header expected: ") + String_convert::bin2hex_str(String(*YYText()))); exit(1); } {BACKUP_TOP_0} { error(String("top level: header expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } {BACKUP_TOP_1} { error(String("top level: header expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } {BACKUP_TOP_2} { error(String("top level: header expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } {INT32} { // really signed? LOGOUT(DEBUG_ver) << "lex: int32" << endl; assert(YYLeng() == 4); String str((Byte const*)YYText(), YYLeng()); yylval.i = String_convert::bin2_i(str); yy_pop_state(); return INT32; } {BACKUP_INT32_0} { error(String("int32: int32 expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } {BACKUP_INT32_1} { error(String("int32: int32 expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } {BACKUP_INT32_2} { error(String("int32: int32 expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } {INT16} { // really signed? LOGOUT(DEBUG_ver) << "lex: int16" << endl; assert(YYLeng() == 2); String str((Byte const*)YYText(), YYLeng()); yylval.i = (short)String_convert::bin2_i(str); yy_pop_state(); return INT16; } {BACKUP_INT16_0} { error(String("int16: int16 expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } {I8} { LOGOUT(DEBUG_ver) << "lex: i8" << endl; assert(YYLeng() == 1); // yylval.byte = *(signed char*)YYText(); yylval.i = *(signed char*)YYText(); yy_pop_state(); return I8; } {U8} { LOGOUT(DEBUG_ver) << "lex: u8" << endl; assert(YYLeng() == 1); // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); yy_pop_state(); return U8; } {VARINT} { String str((Byte const*)YYText(), YYLeng()); yylval.i = My_midi_lexer::varint2_i(str); LOGOUT(DEBUG_ver) << String("lex: track: varint(") + String(yylval.i) + "): " + String_convert::bin2hex_str(str) << endl; yy_push_state(event); return VARINT; } {U8} { error(String("track: illegal byte: ") + String_convert::bin2hex_str(String(*YYText()))); exit(1); } {BACKUP_VARINT_0}{U8} { error(String("track: varint expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } {BACKUP_VARINT_1}{U8} { error(String("track: varint expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } {BACKUP_VARINT_2}{U8} { error(String("track: varint expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } {RUNNING_STATUS} { // yylval.byte = *(Byte*)YYText(); // yylval.i = *(Byte*)YYText(); yylval.i = running_status_i_; LOGOUT(DEBUG_ver) << String ("lex: running status: ") + String(yylval.i) << endl; /* 'running status' rather means 'missing status'. we'll put the running status data back, prepend (unput) the running status, and try again. */ yyless(0); unput(running_status_i_); return RUNNING_STATUS; } {DATA_ENTRY} { // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); LOGOUT(DEBUG_ver) << String ("lex: undefined data entry: ") + String(yylval.i) << endl; yy_pop_state(); yy_push_state(u8); return DATA_ENTRY; } {ALL_NOTES_OFF} { LOGOUT(DEBUG_ver) << "lex: all note off" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); LOGOUT(DEBUG_ver) << String ("lex: all notes off: ") + String(yylval.i) << endl; yy_pop_state(); yy_push_state(u8); yy_push_state(u8); return ALL_NOTES_OFF; } {NOTE_OFF} { LOGOUT(DEBUG_ver) << "lex: note off" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); running_status_i_ = yylval.i; yy_pop_state(); yy_push_state(u8); yy_push_state(u8); return NOTE_OFF; } {NOTE_ON} { LOGOUT(DEBUG_ver) << "lex: note on" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); running_status_i_ = yylval.i; yy_pop_state(); yy_push_state(u8); yy_push_state(u8); return NOTE_ON; } {POLYPHONIC_AFTERTOUCH} { LOGOUT(DEBUG_ver) << "lex: polyphonic aftertouch" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); running_status_i_ = yylval.i; yy_pop_state(); yy_push_state(u8); yy_push_state(u8); return POLYPHONIC_AFTERTOUCH; } {CONTROLMODE_CHANGE} { LOGOUT(DEBUG_ver) << "lex: controlmode change" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); running_status_i_ = yylval.i; yy_pop_state(); yy_push_state(u8); yy_push_state(u8); return CONTROLMODE_CHANGE; } {PROGRAM_CHANGE} { LOGOUT(DEBUG_ver) << "lex: program change" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); running_status_i_ = yylval.i; yy_pop_state(); yy_push_state(u8); return PROGRAM_CHANGE; } {CHANNEL_AFTERTOUCH} { LOGOUT(DEBUG_ver) << "lex: channel aftertouch" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); running_status_i_ = yylval.i; yy_pop_state(); yy_push_state(u8); yy_push_state(u8); return CHANNEL_AFTERTOUCH; } {PITCHWHEEL_RANGE} { LOGOUT(DEBUG_ver) << "lex: pitchwheel range" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); running_status_i_ = yylval.i; yy_pop_state(); yy_push_state(u8); yy_push_state(u8); return PITCHWHEEL_RANGE; } {SYSEX_EVENT1} { // len data LOGOUT(DEBUG_ver) << "lex: sysex1" << endl; yy_pop_state(); yy_push_state(data); return SYSEX_EVENT1; } {SYSEX_EVENT2} { // len data LOGOUT(DEBUG_ver) << "lex: sysex2" << endl; yy_pop_state(); // yy_push_state(u8); //? yy_push_state(data); return SYSEX_EVENT2; } {META_EVENT} { LOGOUT(DEBUG_ver) << "lex: meta" << endl; yy_push_state(meta_event); return META_EVENT; } {U8} { error(String("event: illegal byte: ") + String_convert::bin2hex_str(String(*YYText()))); exit(1); } {SEQUENCE} { // ssss sequence number LOGOUT(DEBUG_ver) << "lex: sequence" << endl; yy_pop_state(); yy_pop_state(); yy_push_state(int16); return SEQUENCE; } {YYTEXT} { // len data LOGOUT(DEBUG_ver) << "lex: text" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); yy_pop_state(); yy_pop_state(); yy_push_state(data); return YYTEXT; } {YYCOPYRIGHT} { LOGOUT(DEBUG_ver) << "lex: copyright" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); yy_pop_state(); yy_pop_state(); yy_push_state(data); return YYCOPYRIGHT; } {YYTRACK_NAME} { LOGOUT(DEBUG_ver) << "lex: track name" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); yy_pop_state(); yy_pop_state(); yy_push_state(data); return YYTRACK_NAME; } {YYINSTRUMENT_NAME} { LOGOUT(DEBUG_ver) << "lex: instrument name" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); yy_pop_state(); yy_pop_state(); yy_push_state(data); return YYINSTRUMENT_NAME; } {YYLYRIC} { LOGOUT(DEBUG_ver) << "lex: lyric" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); yy_pop_state(); yy_pop_state(); yy_push_state(data); return YYLYRIC; } {YYMARKER} { LOGOUT(DEBUG_ver) << "lex: marker" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); yy_pop_state(); yy_pop_state(); yy_push_state(data); return YYMARKER; } {YYCUE_POINT} { LOGOUT(DEBUG_ver) << "lex: cue point" << endl; // yylval.byte = *(Byte*)YYText(); yylval.i = *(Byte*)YYText(); yy_pop_state(); yy_pop_state(); yy_push_state(data); return YYCUE_POINT; } {TEMPO} { // tttttt usec LOGOUT(DEBUG_ver) << "lex: tempo" << endl; yy_pop_state(); yy_pop_state(); yy_push_state(u8); yy_push_state(u8); yy_push_state(u8); return TEMPO; } {SMPTE_OFFSET} { // hr mn se fr ff LOGOUT(DEBUG_ver) << "lex: smpte offset" << endl; yy_pop_state(); yy_pop_state(); yy_push_state(u8); yy_push_state(u8); yy_push_state(u8); yy_push_state(u8); yy_push_state(u8); return SMPTE_OFFSET; } {TIME} { // nn dd cc bb LOGOUT(DEBUG_ver) << "lex: time" << endl; yy_pop_state(); yy_pop_state(); yy_push_state(u8); yy_push_state(u8); yy_push_state(u8); yy_push_state(u8); return TIME; } {KEY} { // sf mi LOGOUT(DEBUG_ver) << "lex: key" << endl; yy_pop_state(); yy_pop_state(); yy_push_state(i8); yy_push_state(i8); return KEY; } {SSME} { // len data LOGOUT(DEBUG_ver) << "lex: smme" << endl; yy_pop_state(); yy_pop_state(); yy_push_state(data); return SSME; } {END_OF_TRACK} { LOGOUT(DEBUG_ver) << "lex: end of track" << endl; yy_pop_state(); yy_pop_state(); yy_pop_state(); return END_OF_TRACK; } {U8} { warning(String("meta_event: unimplemented event: ") + String_convert::bin2hex_str(String(*YYText())) // huh? // ,this->here_ch_C() ); yy_pop_state(); yy_pop_state(); yy_push_state(u8); yy_push_state(u8); return U8; } {VARINT} { LOGOUT(DEBUG_ver) << "lex: data" << endl; String str((Byte const*)YYText(), YYLeng()); int i = My_midi_lexer::varint2_i(str); String* str_p = new String; while (i--) *str_p += (char)yyinput(); yylval.str_p = str_p; yy_pop_state(); return DATA; } {U8} { error(String("data: illegal byte: ") + String_convert::bin2hex_str(String(*YYText()))); exit(1); } {BACKUP_VARINT_0}{U8} { error(String("data: varint expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } {BACKUP_VARINT_1}{U8} { error(String("data: varint expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } {BACKUP_VARINT_2}{U8} { error(String("data: varint expected: ") + String_convert::bin2hex_str(String(*(YYText())))); exit(1); } <> { // LOGOUT(NORMAL_ver) << "<>"; if (!close_i()) yyterminate(); // can't move this, since it actually rets a YY_NULL } %%