如何使用Flex / Lex和Yacc / Bison进行可变替换
-
10-07-2019 - |
题
维基百科的插值定义 我正在学习flex / bison,我正在编写自己的shell。我试图找出一种做变量插值的好方法。我最初的方法是对我的主目录或$ myVar进行flex扫描,然后使用查找功能设置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很难处理'传统'shell处理变量替换等问题的方法。他们所做的更像是宏扩展,在扩展变量之后,他们然后重新标记输入,而不扩展其他变量。例如,输入如<!>“xx $ {$ foo} <!>”;其中'foo'定义为'bar','bar'定义为'$ y'将扩展为'xx $ y',将被视为单个单词(并且$ y不会被展开)。
你可以在flex中处理这个问题,但是你需要很多支持代码。你需要使用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
终端符号并在语法制作中使用它是......不是最好的设计决定。仅仅因为命令是内置命令并不意味着它应该作为规则出现。继续并像任何其他命令一样解析<=>和<=>。将内置语义检查为操作,而不是生产。
毕竟,如果它被重新定义为shell程序呢?