如何使用Flex/Lex和Yacc/Bison进行变量替换

如何使用Flex/Lex和Yacc/Bison进行变量替换,c,bison,yacc,lex,flex-lexer,C,Bison,Yacc,Lex,Flex Lexer,我正在学习flex/bison,我正在用它编写自己的shell。我正试图找出一个很好的方法来做变量插值。我最初的方法是让flex扫描像~ for My home directory或$myVar这样的内容,然后将yyval.string设置为使用查找函数返回的内容。我的问题是,当文本显示为一个标记时,这对我没有帮助: kbsh:/home/kbrandt% echo ~ /home/kbrandt kbsh:/home/kbrandt% echo ~/foo /home/kbrandt /foo

我正在学习flex/bison,我正在用它编写自己的shell。我正试图找出一个很好的方法来做变量插值。我最初的方法是让flex扫描像~ for My home directory或$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); }
    ;
有人知道处理这类事情的好方法吗?我这样做完全错了吗?

看起来总体上还行
我不确定
return\u value
在做什么,希望它将
strdup(3)
变量名,因为
yytext
只是一个缓冲区

如果您想了解lex和parse之间的分工,我相信将宏处理和参数替换推到扫描仪中,让您的语法处理
WORD
s、列表、命令、管道、重定向等是完全合理的。毕竟,这是合理的,尽管有点过时,而且可能会挫败你练习的重点,用代码做每件事

我确实认为,将
cd
chdir
作为一个终端符号,并在语法产品中使用它……不是最好的设计决策。命令是内置的并不意味着它应该作为规则出现。继续,像其他命令一样解析
cd
chdir
。检查作为操作而不是产品的内置语义


毕竟,如果它被重新定义为一个shell过程呢?

传统的shell处理变量替换之类的事情的方式很难用lex/yacc处理。它们所做的更像是宏扩展,在扩展变量后,它们会重新标记输入,而不会扩展更多的变量。例如,像“xx${$foo}”这样的输入,其中“foo”被定义为“bar”,而“bar”被定义为“$y”,将扩展为“xx$y”,该输入将被视为单个单词(并且$y不会被扩展)

您可以在flex中处理这个问题,但需要大量支持代码。您需要使用flex的yy_buffer_state东西,有时将输出重定向到一个缓冲区,然后从中重新扫描,并小心地使用开始状态来控制变量何时可以扩展和何时不能扩展

使用一个非常简单的lexer可能更容易,它返回像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 ':' ....
如你所见,这很快就会变得复杂