Как сделать переменную замену с Flex / Lex и Yacc / Bison - PullRequest
2 голосов
/ 17 сентября 2009

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

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

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

Ответы [ 2 ]

4 голосов
/ 21 сентября 2009

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

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

Возможно, проще использовать очень простой лексер, который возвращает токены, такие как 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 ':' ....

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

1 голос
/ 19 сентября 2009

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


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

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

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

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

...