Flex / LexおよびYacc / Bisonで変数を置換する方法
-
10-07-2019 - |
質問
ウィキペディアの補間定義 私はただフレックス/バイソンを学んでおり、それで自分のシェルを書いています。変数補間を行うための良い方法を見つけようとしています。これに対する私の最初のアプローチは、ホームディレクトリの〜や$ myVarのようなものをフレックススキャンし、ルックアップ関数を使用してyyval.stringを返すものに設定することでした。私の問題は、テキストが1つのトークンを表示するときにこれが役に立たないことです:
kbsh:/home/kbrandt% echo ~
/home/kbrandt
kbsh:/home/kbrandt% echo ~/foo
/home/kbrandt /foo
kbsh:/home/kbrandt%
変数のlex定義:
\$[a-zA-Z/0-9_]+ {
yylval.string=return_value(&variables, (yytext + sizeof(char)));;
return(WORD);
}
その後、私の文法には、次のようなものがあります:
chdir_command:
CD WORD { change_dir($2); }
;
この種のことを処理する良い方法を知っている人はいますか?私はこれについてすべて間違っていますか?
解決
「従来の」シェルが変数の置換などを処理する方法は、lex / yaccでは処理が困難です。それらが行うことは、変数を展開した後、さらに変数を展開することなく入力を再トークン化するマクロ展開のようなものです。たとえば、" xx $ {$ foo}"のような入力ここで、「foo」は「bar」として定義され、「bar」は「$ y」として定義されて「xx $ y」に展開され、1つの単語として扱われます(そして$ yは展開されません)。
フレックスでこれに対処できますが、多くのサポートコードが必要です。 flexのyy_buffer_stateを使用して、出力をバッファにリダイレクトしてから再スキャンし、開始状態を慎重に使用して変数を展開できる場合とできない場合を制御する必要があります。
おそらく、ALPHA(1つ以上のアルファベット文字)、NUMERIC(1つ以上の数字)、WHITESPACE(1つ以上のスペースまたはタブ)などのトークンを返す非常にシンプルなレクサーを使用し、パーサーにそれらをアセンブルさせます適切にすると、次のようなルールになります:
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: '
ご覧のとおり、これは非常に速く複雑になります。
name { $ = lookup($2); }
| '
ご覧のとおり、これは非常に速く複雑になります。
'{' word '}' { $ = lookup($3); }
| '
ご覧のとおり、これは非常に速く複雑になります。
'{' word ':' ....
ご覧のとおり、これは非常に速く複雑になります。
他のヒント
通常は問題ないように見えます
return_value
が何をしているのかわかりません。 yytext
は単なる strtext(3)
ですバッファ。
lexとparseの分業について質問している場合、マクロ処理とパラメーター置換をスキャナーにプッシュし、文法で WORD
を処理することは完全に合理的であると確信しています。 s、リスト、コマンド、パイプライン、リダイレクトなど。結局、コードを使用してすべてを実行することは、スタイルの外にあり、場合によっては演習のポイントを打ち負かしても、十分に合理的です。
cd
または chdir
を終端記号にして、それを文法制作で使用することは...最良の設計決定ではないと思います。コマンドがビルトインであるからといって、それがルールとして表示される必要があるわけではありません。他のコマンドと同様に、 cd
と chdir
を解析してください。プロダクションではなくアクションとして組み込みのセマンティクスを確認します。
結局、シェルプロシージャとして再定義された場合はどうなりますか?