Как выполнить замену переменных с помощью Flex/Lex и Yacc/Bison

Определение интерполяции из Википедии Я только изучаю flex/bison и пишу моя собственная оболочка с ним. Я пытаюсь найти хороший способ интерполяции переменных. Мой первоначальный подход к этому заключался в том, чтобы выполнить гибкое сканирование для чего-то вроде ~ для моего домашнего каталога или $myVar , а затем установить yyval.string на то, что возвращается с помощью функции поиска. Моя проблема в том, что это не помогает мне, когда в тексте появляется один токен:

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); }
    ;

Кто-нибудь знает хороший способ справиться с такими вещами? Я все делаю неправильно?


person Kyle Brandt    schedule 17.09.2009    source источник


Ответы (2)


То, как «традиционные» оболочки работают с такими вещами, как подстановка переменных, сложно реализовать с помощью lex/yacc. То, что они делают, больше похоже на макрорасширение, где ПОСЛЕ расширения переменной они повторно токенизируют ввод, не расширяя дополнительные переменные. Так, например, ввод типа «xx${$foo}», где «foo» определяется как «bar», а «bar» определяется как «$y», будет расширен до «xx$y», который будет рассматриваться как одно слово (и $y НЕ будет расширен).

Вы МОЖЕТЕ справиться с этим во flex, но вам нужно много вспомогательного кода. Вам нужно использовать материал yy_buffer_state flex, чтобы иногда перенаправлять вывод в буфер, из которого вы затем будете повторно сканировать, и тщательно использовать начальные состояния, чтобы контролировать, когда переменные могут и не могут быть расширены.

Вероятно, проще использовать очень простой лексер, который возвращает токены, такие как ALPHA (один или несколько буквенных символов), NUMERIC (одна или несколько цифр) или WHITESPACE (один или несколько пробелов или табуляций), и синтаксический анализатор собирает их соответствующим образом, и вы получаете такие правила, как:

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 ':' ....

как видите, это довольно быстро усложняется.

person Chris Dodd    schedule 21.09.2009

Выглядит в целом нормально


Я не уверен, что делает return_value, надеюсь, это будет strdup(3) имя переменной, потому что yytext — это просто буфер.

Если вы спрашиваете о разделении труда между lex и parse, я уверен, что вполне разумно запихнуть обработку макросов и подстановку параметров в сканер и просто иметь дело с грамматикой с WORD, списками, командами, конвейерами, перенаправлениями и т. д. В конце концов, было бы достаточно разумно, хотя и несколько не в стиле и, возможно, сбивающее с толку суть вашего упражнения, делать все с помощью кода.

Я действительно думаю, что делать cd или chdir конечным символом и использовать его в производстве грамматики - это... не лучшее дизайнерское решение. Тот факт, что команда является встроенной, не означает, что она должна появляться как правило. Идите вперед и проанализируйте cd и chdir, как и любую другую команду. Проверяйте встроенную семантику как действие, а не производство.

В конце концов, что, если его переопределить как процедуру оболочки?

person DigitalRoss    schedule 19.09.2009