Как сделать переменную замену с Flex / Lex и Yacc / Bison

StackOverflow https://stackoverflow.com/questions/1440147

Вопрос

Определение интерполяции из Википедии Я только учусь флексу / бизону и пишу свою собственную оболочку. Я пытаюсь найти хороший способ сделать переменную интерполяцию. Мой первоначальный подход к этому состоял в том, чтобы выполнить 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); }
    ;

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

Это было полезно?

Решение

То, как «традиционные» оболочки имеют дело с такими вещами, как подстановка переменных, трудно обработать с помощью 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 ':' ....

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

Другие советы

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

<Ч>

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top