/* This file contains the parser for the 'EM_table'. * Every row in the table is converted to a C-function. * The parser is a fairly simple program, for each row it prints a function- * header and the following actions are printed as C-statements. A CALL is just * copied, and an ASSEM_INSTR will be handed to the routine assemble(). * Assemble() will turn the assembly-string into a sequence of C-function calls. * How this should be done is expressed in the 'as_table'. * * There are however some complicating factors: * - A row in the 'EM_table' may contain CONDITION's. * The parser supports this by outptutting some "if" and "else"'s. * * - The user is allowed to use some abbreviation in the 'EM_table', (s)he * can use the DEF_C_INSTR's ( e.g. C_loe..). * One such DEF_C_INSTR must be expanded in 3 or more C-functions. * The solution is to copy the DEF_C_INSTR to a temporary file. Then * this file will be processed as many times as needed to generate all * the C-functions. * * - The assembler-instructions must be processed in blocks not one at a * time, so buffering is needed. * * - When generating object-code, the user can make use of the dist() * function. This function should return the number of bytes between * the current instruction and a labelled one, both forward and backward * references must be handled. * This requires the saving of the generated function, and * backpatching in a second phase. */ { #include #include "decl.h" #include "em.h" extern char yytext[]; extern int yylineno; extern int first_action, last_action, token; t_token tok, stok; int no_conversions = FALSE, library, segment = UNKNOWN; File *outfile; char *to_change; } %token C_INSTR, DEF_C_INSTR, CONDITION, ARROW, CALL, ASSEM_INSTR, DEFAULT, ERROR; %start table, table; %start def_row, def_row; %start c_table, c_table; %lexical lex_analyzer ; table : row* ; row : C_INSTR { set_outfile( yytext); header( yytext); set_segment(segment); } [ special | simple] { out( "}\n\n");} | DEF_C_INSTR { init_defaults( yytext);} [ Dspecial | Dsimple] { handle_defaults();} ; special : { out( "if( 0 ) ;\n"); } [ CONDITION { out( "else "); question( &yytext[0]);} simple { out( "}\n");} ]* DEFAULT { out( "else {\n");} simple { out( "}\n");} ; simple : ARROW { save_output();} actionlist { back_patch();} ; actionlist : { first_action = TRUE;} [ action { first_action = FALSE;} [ ';' action]* ]? '.' ; action : CALL { print_call( &yytext[0]);} | as_block { last_action = ( token == '.'); do_block_assemble();} ; as_block : { init_as_block();} ASSEM_INSTR { save_as( &yytext[0]);} more_as ; more_as : %if ( next_token() == ASSEM_INSTR) ';' ASSEM_INSTR { save_as( &yytext[0]);} more_as | ; /* Grammar rules for a default-instruction, just copy to a temporary file. */ Dspecial: CONDITION { out( " %s ", yytext);} Dsimple [ CONDITION { out( " %s ", yytext);} Dsimple ]* DEFAULT { out( " %s ", yytext);} Dsimple ; Dsimple : ARROW { out( "%s", yytext);} Dactionlist ; Dactionlist : [ Daction [ ';' { out( ";\n");} Daction ]* ]? '.' { out( ".\n");} ; Daction : CALL { out( "%s", yytext);} | ASSEM_INSTR { out( "\"%s\"", yytext);} ; def_row : { set_segment(segment);} [ special | simple] { out( "}\n\n");} ; /* This is a grammar to handle the -c flag ( only one entry has changed). * Skip all entries in the table until the one that has changed, then * use the grammar rules for the normal case above. */ c_table : c_row* ; c_row : %if ( to_change && strcmp( yytext, to_change) == 0) C_INSTR { set_outfile( yytext); header( yytext); set_segment(segment); } [ special | simple] { out( "}\n\n"); to_change = 0; } | C_INSTR [ c_special | c_simple] | %if ( to_change && strcmp( yytext, to_change) == 0) DEF_C_INSTR { init_defaults( yytext);} [ Dspecial | Dsimple] { handle_defaults(); to_change = 0; } | DEF_C_INSTR [ c_special | c_simple] ; c_special : CONDITION c_simple [ CONDITION c_simple ]* DEFAULT c_simple ; c_simple: ARROW c_actionlist ; c_actionlist : [ c_action [ ';' c_action]* ]? '.' ; c_action: CALL | ASSEM_INSTR ; { int saved = 0, token; int nerrors = 0; LLmessage( inserted_token) int inserted_token; { nerrors++; if ( inserted_token == 0) { fprint( STDERR, "EM_table : syntax error in line %d, >>", yylineno); print_token( LLsymb); fprint( STDERR, "<< will be deleted!!\n"); } else if ( inserted_token < 0) { fprint(STDERR,"EM_table : syntax error in line %d, garbage at end of table\n", yylineno); } else { fprint( STDERR, "EM_table : syntax error in line %d, >>", yylineno); print_token( inserted_token); fprint( STDERR, "<< will be inserted!!\n"); token = LLsymb; saved = 1; } } print_token( token) int token; { switch ( token) { case C_INSTR : fprint( STDERR, "C_INSTR %s", yytext); break; case ASSEM_INSTR : fprint( STDERR, "STRING %s", yytext); break; case CALL : fprint( STDERR, "CALL %s", yytext); break; case ARROW : fprint( STDERR, "==> "); break; case CONDITION: fprint( STDERR, "CONDITION %s", yytext); break; case DEFAULT : fprint( STDERR, "default "); break; case ERROR : fprint( STDERR, "unmatched %s", yytext); break; default : fprint( STDERR, " %c", token); break; } } /* The lexical analyzer. * It uses mylex() to get the tokens. * It supports the buffering of one token. * If the curent token is a C_INSTR it will set the global variable * 'C_instr_info' to point to the correct information ( arguments, header, * etc.). */ int lex_analyzer() { extern char *next; if ( saved) { saved = 0; } else { token = mylex(); *next = '\0'; } if ( token == C_INSTR) set_C_instr_info( yytext); return( token); } int next_token() { extern char *next; if ( !saved) { token = mylex(); *next = '\0'; saved = 1; } return( token); } /******************************************************************************/ /* Handle arguments : * -l : Library, generate one function per file. * -c : Change, only update the file. */ main( argc, argv) int argc; char **argv; { outfile = STDOUT; if ( argc > 1) { if ( strcmp( argv[1], "-l") == 0) library = TRUE; else if( strcmp( argv[1], "-c") == 0) { library = TRUE; to_change = argv[2]; c_table(); if (to_change) { fprint( STDERR, "No rule for %s\n", to_change); exit( 1); } exit(nerrors); } } else { library = FALSE; file_header(); } table(); exit(nerrors); } }