代码之家  ›  专栏  ›  技术社区  ›  Kyle Brandt

如何使用flex/lex和yacc/bison进行变量替换

  •  2
  • Kyle Brandt  · 技术社区  · 15 年前

    Wikipedia's Interpolation Definition 我只是在学习flex/bison,用它来写我自己的shell。我正试图找出一种做变量插值的好方法。我最初的方法是对home目录或$myvar进行flex扫描,然后使用查找函数将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); }
        ;
    

    有人知道处理这类事情的好方法吗?我这一切都错了吗?

    2 回复  |  直到 12 年前
        1
  •  4
  •   Chris Dodd    15 年前

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

    您可以在flex中处理这个问题,但是您需要大量的支持代码。您需要使用flex的yy_buffer_state工具,有时将输出重定向到一个缓冲区,然后重新扫描,并小心地使用start state s来控制变量何时可以扩展或不能扩展。

    它可能更容易使用一个非常简单的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 ':' ....
    

    正如你所看到的,这会很快变得复杂。

        2
  •  1
  •   DigitalRoss    15 年前

    看起来一般不错


    我不确定是什么 return_value 正在做,希望它会 strdup(3) 变量名,因为 yytext 只是一个缓冲区。

    如果您在询问lex和parse之间的分工,我相信将宏处理和参数替换推到scanner中并让您的语法处理是完全合理的。 WORD S、列表、命令、管道、重定向等。毕竟,它是合理的,尽管有点过时,而且可能会破坏您的练习点,但它可以用代码做任何事情。

    我真的认为 cd chdir 在语法产品中使用终端符号并不是最佳设计决策。仅仅因为命令是内置的,并不意味着它应该作为规则出现。继续分析 光盘 切迪尔 像其他命令一样。检查作为操作而不是生产的内置语义。

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