【问题标题】:Basic PHP like interpreter using flex and bizon. Reduce/reduce and shift/reduce conflicts使用 flex 和 bizon 的基本 PHP 类解释器。减少/减少和转移/减少冲突
【发布时间】:2018-07-02 21:14:58
【问题描述】:

我正在编写类似 PHP 的解释器,但在 shift/reduce 和 reduce/reduce 冲突方面存在一些问题。 有人可以帮我理解 shift/reduce 和 reduce/reduce 冲突。

我必须编写和解释发送/回显非值并评估以“魔术”@ 字符开头的表达式,例如@if(cond) ... @end;。所以 "if" 必须被回显,而 @if(cond) 应该被解释

问题:scriptlang.y 包含 21 个 shift/reduce 冲突和 2 个 reduce/reduce 冲突。

%union {
    char* sval;
}
%token <sval> IDENTIFIER
%token <sval> RBRACKET
%token <sval> LBRACKET
%token <sval> KWSWITCH
%token <sval> KWIF
%token <sval> MAGICESC
%token MAGIC
%token ENDSTM

%type <sval> filechar

%start script

%%
script:
   commands
;
commands:   
    /* empty */
    | command 
    | commands command
;
command:
    filechar {
        analyser_echo($1,"filechar",analyser_canEcho);
    }
    | magic_command {}
;
filechar:
    IDENTIFIER
    | LBRACKET
    | RBRACKET
    | KWSWITCH
    | KWIF
    | MAGICESC
;
magic_command:
    MAGIC valuation
    | MAGIC alternative
;
valuation:
    LBRACKET IDENTIFIER RBRACKET {
        fprintf(yyout, "<val>");
    }
;
alternative:
    switch_alternative
    | if_alternative
;
switch_alternative:
    switch_block end_stm
;
switch_block:
    switch_stm
    | switch_stm commands
;
switch_stm:
    KWSWITCH LBRACKET IDENTIFIER RBRACKET {}
;
if_alternative:
    if_block end_stm
;
if_block:
    if_stm
    | if_stm commands
;
if_stm:
    KWIF LBRACKET IDENTIFIER RBRACKET {}
;
end_stm:
    ENDSTM
;
%%

flex 文件内容:

"@@" {
    yylval.sval = "@"; 
    return MAGICESC;
}
"@" {
    return MAGIC;
}
"(" {
    yylval.sval = yytext; 
    return LBRACKET;
}
")" {
    yylval.sval = yytext; 
    return RBRACKET;
}
"@end;" { 
    return ENDSTM;
}
"if" {
    yylval.sval = yytext; 
    return KWIF;
}
"switch" {
    yylval.sval = yytext; 
    return KWSWITCH;
}
[a-zA-Z][_a-zA-Z0-9]* {
    yylval.sval = yytext; 
    return IDENTIFIER;
}
\n|. {
    if(analyser_canEcho>0){
        ECHO;
    }
}
%%

【问题讨论】:

    标签: bison flex-lexer


    【解决方案1】:

    导致冲突的基本问题是您对commands 的处理。你的意图是将commands定义为零或多个commands,其写法如下:

    commands: %empty
            | commands command
    

    如果你要坚持至少有一个命令,你会这样写:

    commands: command
            | commands command
    

    混合这两种形式是行不通的,因为解析器不知道是从一个没有任何内容的commands 序列开始(%empty)还是一个command。您应该尝试准确理解为什么这会导致模棱两可;您会在此站点上找到许多类似问题的示例。例如,请参阅this question

    这会产生 21 次移位/减少冲突。减少/减少冲突是好奇产生的结果:

    switch_block: switch_stm commands
    if_block: if_stm commands
    

    ifswitch 语句是commands 序列中的单个command 元素; switchif 语句后面的任何内容将是 commands 中的下一个 command。将switch_block 定义为包含以下命令是完全模棱两可的:实际上,下一个command 可能仍然是switch_block 的一部分,或者它可能是command 之后的switch_block


    上面,我专门解决了您提出的问题:您的语法中的解析表冲突。您的语法和词汇规范还有其他各种问题,我强烈建议您学习您所获得的有关 bison/flex 的任何材料,和/或阅读bisonflex 手册。

    作为您阅读手册或其他材料的指南,我建议您至少关注两件事:

    1. 语义值的处理。句法关键字永远不需要将自己的表示形式作为语义值;事实上,句法关键字很少需要语义值。如果一个令牌确实要求它的语义值是它的表示,你需要记住yytext 是一个指向你不拥有的私有数据缓冲区的指针,并且将在没有警告的情况下对其进行修改。因此需要复制。

    2. 像您的 PHP 变体这样的嵌入式语言涉及两种不同的词法上下文。您有一个外部的、本质上未解释的上下文,以及一个包含在@@end; 之间的嵌入式上下文。 (F)lex 提供 开始条件 来帮助处理这种嵌入。该手册有一些示例,并且此站点周围还有更多示例。

    【讨论】:

    • 感谢您的回复,这有助于我通过在脚本规则中移动 /* empty */ 标签来解决源文件中的所有冲突,因为文件可能为空。 %start script %% script: /* empty */ commands ; commands: command | commands command ;
    • 也感谢您的建议,它将帮助我改进我的源代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多