Pregunta

Definición de interpolación de Wikipedia Solo estoy aprendiendo flex / bison y estoy escribiendo mi propio shell con él. Estoy tratando de encontrar una buena manera de hacer una interpolación variable. Mi enfoque inicial para esto fue tener un escaneo flexible para algo como ~ para mi directorio de inicio, o $ myVar, y luego establecer qué yyval.string a lo que se devuelve usando una función de búsqueda. Mi problema es que esto no me ayuda cuando el texto aparece como un token:

kbsh:/home/kbrandt% echo ~
/home/kbrandt
kbsh:/home/kbrandt% echo ~/foo
/home/kbrandt /foo
kbsh:/home/kbrandt%

La definición de lex que tengo para las variables:

\$[a-zA-Z/0-9_]+    {
    yylval.string=return_value(&variables, (yytext + sizeof(char)));;
    return(WORD);
}

Luego, en mi Gramática, tengo cosas como:

chdir_command:
    CD WORD { change_dir($2); }
    ;

¿Alguien sabe de una buena manera de manejar este tipo de cosas? ¿Estoy haciendo todo esto mal?

¿Fue útil?

Solución

La forma en que los shells 'tradicionales' manejan cosas como la sustitución de variables es difícil de manejar con lex / yacc. Lo que hacen es más como una expansión macro, donde DESPUÉS de expandir una variable, luego vuelven a tokenizar la entrada, sin expandir más variables. Entonces, por ejemplo, una entrada como '' xx $ {$ foo} '' donde 'foo' se define como 'bar' y 'bar' se define como '$ y' se expandirá a 'xx $ y', que se tratará como una sola palabra (y $ y NO se expandirá).

PUEDES lidiar con esto en flex, pero necesitas mucho código de soporte. Debe usar el material yy_buffer_state de flex para a veces redirigir la salida a un búfer desde el que luego volverá a escanear, y usar estados de inicio con cuidado para controlar cuándo las variables pueden y no pueden expandirse.

Probablemente sea más fácil usar un lexer muy simple que devuelva tokens como ALPHA (uno o más caracteres alfabéticos), NUMERIC (uno o más dígitos) o WHITESPACE (uno o más espacios o tabulaciones), y que el analizador los reúna apropiadamente, y terminas con reglas como:

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

como puede ver, esto se vuelve complejo bastante rápido.

name { $ = lookup($2); } | '

como puede ver, esto se vuelve complejo bastante rápido.

'{' word '}' { $ = lookup($3); } | '

como puede ver, esto se vuelve complejo bastante rápido.

'{' word ':' ....

como puede ver, esto se vuelve complejo bastante rápido.

Otros consejos

Parece generalmente correcto


No estoy seguro de qué está haciendo return_value , espero que strdup (3) sea el nombre de la variable, porque yytext es solo un buffer.

Si está preguntando sobre la división del trabajo entre lex y parse, estoy seguro de que es perfectamente razonable llevar el procesamiento de macros y la sustitución de parámetros al escáner y simplemente hacer que su gramática se ocupe de WORD s, listas, comandos, canalizaciones, redirecciones, etc. Después de todo, sería bastante razonable, aunque fuera de estilo y posiblemente derrotar el punto de su ejercicio, hacer todo con código.

Creo que hacer de cd o chdir un símbolo terminal y usarlo en una producción gramatical ... no es la mejor decisión de diseño. El hecho de que un comando esté integrado no significa que deba aparecer como una regla. Continúe y analice cd y chdir como cualquier otro comando. Verifique la semántica incorporada como una acción, no una producción.

Después de todo, ¿qué pasa si se redefine como un procedimiento de shell?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top