Domanda

Dal manuale Bison:

  

In un semplice parser di comandi interattivo   dove ogni input è una riga, può   essere sufficiente per consentire a yyparse di   restituisce 1 in caso di errore e dispone del chiamante   ignora il resto della riga di input quando   ciò accade (e quindi chiama yyparse   di nuovo).

Questo è praticamente quello che voglio, ma ho difficoltà a mettermi al lavoro. Fondamentalmente, voglio rilevare ed errore in flex, e se viene rilevato un errore, Bison deve scartare l'intera linea. Quello che ho in questo momento, non funziona abbastanza bene perché i miei comandi vengono ancora eseguiti:

kbsh: ls '/home
Error: Unterminated Single Quote
admin  kbrandt  tempuser
syntax error
kbsh: 

Nel mio file Bison:

commands:
     /*Empty*/ { prompt(); } |
     command { prompt(); }
    ;

command:
    error {return 1; } |
    chdir_command |
    pwd_command |
    exit_command |
    WORD arg_list {
        execute_command($1, $2);
        //printf("%s, %s\n", $1, $2); 
    } |
    WORD { execute_command($1, NULL); }
    ;

E nel mio Flex:

'   {BEGIN inQuote; }

<inQuote>\n {printf("Error: Unterminated Single Quote\n"); BEGIN(0); return(ERROR);}
È stato utile?

Soluzione

Non credo che troverai una soluzione semplice per gestire questi tipi di errori di analisi nel lexer.

Vorrei mantenere il lexer (flex / lex) il più stupido possibile, dovrebbe semplicemente fornire un flusso di token di base (identificatori, parole chiave, ecc ...) e fare in modo che il parser (yacc / bison) esegua il rilevamento degli errori . In realtà è impostato esattamente per quello che vuoi, con una piccola ristrutturazione del tuo approccio ...

Nel lexer (parser.l), mantienilo semplice (nessuna gestione eol / newline), qualcosa del tipo (non è cosa completa):

}%

/* I don't recall if the backslashify is required below */
SINGLE_QUOTE_STRING \'.*\'
DOUBLE_QUOTE_STRING \".*\"

%%
{SINGLE_QUOTE_STRING} {
    yylval.charstr = copy_to_tmp_buffer(yytext);  // implies a %union
    return STRING;
}
{DOUBLE_QUOTE_STRING} {
    yylval.charstr = copy_to_tmp_buffer(yytext);  // implies a %union
    return STRING;
}
\n   return NEWLINE;

Quindi nel tuo file parser.y fai tutta la vera gestione (non è cosa completa):

command:
    error NEWLINE
        { yyclearin; yyerrorok; print_the_next_command_prompt(); }
    | chdir_command STRING NEWLINE
        { do_the_chdir($<charstr>2); print_the_next_command_prompt(); }
    | ... and so on ...

Ci sono due cose da notare qui:

  1. Lo spostamento di cose come NEWLINE sul lato yacc in modo da poter determinare quando l'utente ha terminato il comando, quindi è possibile cancellare le cose e ricominciare (supponendo che si abbia " int yywrap() {return 1;} " da qualche parte). Se provi a rilevarlo troppo presto in flex, quando sai di generare un errore?
  2. chdir non è un comando (a meno che non sia stato sottoposto a sub-ruling e tu non l'abbia semplicemente mostrato), ora ha chdir_command STRING (l'argomento di chdir). Questo fa sì che il parser possa capire cosa è andato storto, quindi puoi yyerror se quella directory non esiste, ecc ...

In questo modo dovresti ottenere qualcosa del genere (indovinando come potrebbe apparire chdir):

  
    

cd 'some_directory
    errore di sintassi
    cd 'directory_un_modulo'
    sei nel tizio della some_directory!

  

Ed è tutto gestito dalla grammatica yacc, non dal tokenizer.

Ho scoperto che mantenere la flessibilità il più semplice possibile ti dà la massima flessibilità. :)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top