Come eseguire la sostituzione di variabili con Flex / Lex e Yacc / Bison
-
10-07-2019 - |
Domanda
Definizione di interpolazione di Wikipedia Sto solo imparando flex / bison e sto scrivendo la mia shell con esso. Sto cercando di capire un buon modo per eseguire l'interpolazione variabile. Il mio approccio iniziale a questo era di avere la scansione flessibile per qualcosa come ~ per la mia directory home, o $ myVar, e quindi impostare ciò che yyval.string su ciò che viene restituito usando una funzione di ricerca. Il mio problema è che questo non mi aiuta quando il testo appare un token:
kbsh:/home/kbrandt% echo ~
/home/kbrandt
kbsh:/home/kbrandt% echo ~/foo
/home/kbrandt /foo
kbsh:/home/kbrandt%
La definizione lex che ho per le variabili:
\$[a-zA-Z/0-9_]+ {
yylval.string=return_value(&variables, (yytext + sizeof(char)));;
return(WORD);
}
Quindi nella mia grammatica, ho cose come:
chdir_command:
CD WORD { change_dir($2); }
;
Qualcuno sa di un buon modo di gestire questo genere di cose? Sto sbagliando tutto questo?
Soluzione
Il modo in cui le shell 'tradizionali' gestiscono cose come la sostituzione variabile è difficile da gestire con lex / yacc. Quello che fanno è più simile all'espansione macro, dove DOPO l'espansione di una variabile, ricodificano quindi l'input, senza espandere ulteriori variabili. Ad esempio, un input come " xx $ {$ foo} " dove 'foo' è definito come 'bar' e 'bar' è definito come '$ y' si espanderà in 'xx $ y' che sarà trattato come una singola parola (e $ y NON sarà espanso).
PUOI affrontarlo in modo flessibile, ma hai bisogno di molto codice di supporto. È necessario utilizzare le cose yy_buffer_state di flex per reindirizzare a volte l'output in un buffer da cui eseguire nuovamente la scansione e utilizzare attentamente gli stati iniziali per controllare quando le variabili possono e non possono essere espanse.
Probabilmente è più facile usare un lexer molto semplice che restituisce token come ALPHA (uno o più caratteri alfabetici), NUMERIC (una o più cifre) o WHITESPACE (uno o più spazi o tab) e fare assemblare il parser appropriatamente, e finisci con regole come:
simple_command: wordlist NEWLINE ;
wordlist: word | wordlist WHITESPACE word ;
word: word_frag
| word word_frag { $ = concat_string($1, $2); }
;
word_frag: single_quote_string
| double_quote_string
| variable
| ALPHA
| NUMERIC
...more options...
;
variable: '
come puoi vedere, questo diventa complesso abbastanza velocemente.
name { $ = lookup($2); }
| '
come puoi vedere, questo diventa complesso abbastanza velocemente.
'{' word '}' { $ = lookup($3); }
| '
come puoi vedere, questo diventa complesso abbastanza velocemente.
'{' word ':' ....
come puoi vedere, questo diventa complesso abbastanza velocemente.
Altri suggerimenti
Sembra generalmente OK
Non sono sicuro di cosa stia facendo return_value
, spero che strdup (3)
il nome della variabile, perché yytext
è solo un buffer.
Se stai chiedendo informazioni sulla divisione del lavoro tra lex e parse, sono sicuro che è perfettamente ragionevole inserire l'elaborazione macro e la sostituzione dei parametri nello scanner e avere solo il tuo affare grammaticale con WORD
s, liste, comandi, pipeline, reindirizzamenti, ecc. Dopotutto, sarebbe abbastanza ragionevole, anche se un po 'fuori moda e possibilmente sconfiggere il punto del tuo esercizio, fare tutto con il codice.
Penso che rendere cd
o chdir
un simbolo terminale e usarlo in una produzione grammaticale non sia ... la migliore decisione progettuale. Solo perché un comando è incorporato non significa che dovrebbe apparire come una regola. Vai avanti e analizza cd
e chdir
come qualsiasi altro comando. Controlla la semantica integrata come azione, non come produzione.
Dopo tutto, cosa succede se viene ridefinito come procedura di shell?