【问题标题】:Bison simple postfix calculator using union with struct使用 union 和 struct 的 Bison 简单后缀计算器
【发布时间】:2018-03-13 07:46:09
【问题描述】:

我正在制作一个简单的计算器,可以打印后缀来学习野牛。我可以使后缀部分工作,但现在我需要对变量 (a-z) tex: a=3+2; 进行分配应该打印:a32+=;例如。我正在尝试修改我的工作后缀代码,以便也能够读取字符。 如果我理解正确,为了能够在 $$ 中放置不同的类型,我需要一个联合并制作节点,我应该使用结构,因为在我的情况下,'expr' 可以是 int 或 char。 这是我的解析器:

%code requires{
    struct nPtr{
        char *val;
        int num;
    };
}

%union {
    int iValue;
    char sIndex;
    struct nPtr *e;
};

%token PLUS MINUS STAR LPAREN RPAREN NEWLINE DIV ID NUMBER POW EQL
%type <iValue> NUMBER
%type <sIndex> ID
%type <e> expr line

%left PLUS MINUS
%left STAR DIV
%left POW
%left EQL

%%
line : /* empty */ 
     |line expr NEWLINE      { printf("=\n%d\n", $2.num); }
expr : LPAREN expr RPAREN     { $$.num = $2.num; }
     | expr PLUS expr        { $$.num = $1.num + $3.num; printf("+"); }
     | expr MINUS expr        { $$.num = $1.num - $3.num; printf("-"); }
     | expr STAR expr         { $$.num = $1.num * $3.num; printf("*"); }
     | expr DIV expr         { $$.num = $1.num / $3.num; printf("/");}
     | expr POW expr         { $$.num = pow($1.num, $3.num); printf("**");}
     | NUMBER                 { $$.num = $1.num; printf("%d", yylval); }
     | ID EQL expr           { printf("%c", yylval); }
     ;

%%

我在 lex 中有这个来处理“=”和变量

"="       { return EQL; }
[a-z]     { yylval.sIndex = strdup(yytext); return ID; }

我得到错误 警告键入的非终结符为空规则且无操作 我在这里找到的唯一答案是: Bison warning: Empty rule for typed nonterminal 它说只需删除 /* 空 */ 部分:

line: /* empty */
    | line expr NEWLINE { printf("=\n%d\n", $2.num); }

当我这样做时,我会收到 3 个新警告:

warning 3 nonterminal useless in grammar
warning 10 rules useless in grammar
fatal error: start symbol line does not derive any sentence

例如,我用谷歌搜索并找到了一些解决方案,但这些解决方案却给我带来了其他问题。 当我改变时:

line:line expr NEWLINE { printf("=\n%d\n", $2.num); }

line:expr NEWLINE { printf("=\n%d\n", $2.num); }

bison 可以工作,但是当我尝试在 Visual Studio 中运行代码时出现很多错误,例如:

left of '.e' must have struct/union type
'pow': too few arguments for call
'=': cannot convert from 'int' to 'YYSTYPE'

就我所知。我找不到与我的需求相似的简单示例。我只是想让'expr'能够读取一个字符并打印它。如果有人可以检查我的代码并推荐一些更改。真的很感激。

【问题讨论】:

    标签: struct bison unions


    【解决方案1】:

    重要的是要真正了解您要求 Bison 做什么,以及当它暗示您的指示有误时它会告诉您什么。

    仅当他们犯了与您相同的错误时,从其他人的问题中应用修复程序才有效。仅仅根据 Google 搜索进行随机更改既不是一种非常结构化的调试方式,也不是学习新工具的方式。

    现在,你说的是:

    %type <e> expr line 
    

    这意味着exprline产生一个值,其联合标记为e。 (标记 e 是指向 struct nPtr 的指针。我怀疑这也是你想要的,但我们将从 Bison 警告开始。)

    如果一个非终结符产生一个值,它会在每个产生式中产生一个值。为了产生一个值,语义规则需要为$$ 分配一个值(正确的标签类型)。为方便起见,如果没有语义操作并且$1 具有正确的标签类型,则 Bison 将提供默认规则 $$ = $1。 (这句话不太正确,但它是一个有用的近似值。)很多人认为你实际上不应该依赖这个默认值,但我认为只要你知道它正在发生,并且验证了前提条件是有效的,就可以了.

    在您的情况下,您有两个 line 的产品。他们都没有为$$ 赋值。这可能表明您实际上并不打算让 line 具有语义值,如果我们仔细观察,我们会发现您从未尝试使用任何语义值非终端 line 的实例。据我所知,Bison 不会尝试进行如此详细的代码检查,但它能够注意到产生式:

    line : /* empty */
    
    1. 没有语义动作,并且

    2. 右侧没有任何符号,因此无法构造默认操作。

    因此,它会警告您在某些情况下line 不会设置其值。如果您的 C 编译器注意到您使用的变量从未分配过值,则可以将此视为相同的警告。

    在这种情况下,正如我所说,Bison 没有注意到您从未使用过 line 的值。它只是假设如果您不打算以某种方式提供值,您就不会声明值的类型。您确实声明了类型,并且您从不提供值。所以你对 Bison 的指示并不清楚,对吧?

    现在,解决方案不是删除生产。生产没有问题,如果你删除它,那么line 只剩下一个生产:

    line : line expr NEWLINE
    

    这是一个递归产生式,只有当line 有其他不递归的产生式时,递归才能终止。那将是您刚刚删除的产品line: %empty。所以现在 Bison 可以看到非终端 line无用。它是无用的,因为它永远无法在没有非终结符的情况下派生任何字符串。

    但这是一个大问题,因为这是您的语法应该识别的非终结符。如果line 无法派生任何字符串,那么您的语法将无法识别任何内容。此外,因为line 是无用的,并且expr 仅由line 中的产生式使用(递归使用除外),expr 本身永远无法实际使用。所以它也变得毫无用处。 (你可以认为这相当于你的 C 编译器抱怨你有无法访问的代码。)

    然后您尝试通过为line 制定(唯一剩下的)非递归规则来解决问题。现在你有:

    line : expr NEWLINE
    

    这很好,它使line 再次有用。但它无法识别您最初设定要识别的语言,因为它现在只能识别一行。 (也就是说,一个expr 后跟一个NEWLINE,正如制作所说的那样。)

    我希望你现在已经发现你最初的问题不是语法,它总是正确的,而是你告诉 Bison line 产生了一个值,但你实际上并没有做任何事情来提供它价值。换句话说,解决方案是告诉 Bison line 产生一个值。 (相反,您可以在与 line 的每个产生式关联的语义规则中为 line 提供一个值,但如果您不打算让每个人都使用该值,为什么要这样做?)

    现在,让我们回到实际的类型声明,看看 C 编译器为什么会报错。我们可以从一个简单的开始:

    expr : NUM { $$.num = $1.num; }
    

    $$ 指的是expr 本身,它被声明为具有类型标记e,即struct nPtr *。星号很重要:它表示语义值是一个指针。为了从指向结构的指针获取结构中的字段,您需要使用-&gt; 运算符,就像这个简单的 C 示例一样:

    struct nPtr the_value;        /* The actual struct */
    struct nPtr *p = &the_value;  /* A pointer to the struct */
    p.num = 3;                    /* Error: p is not a struct */
    p->num = 3;                   /* Fine. p points to a struct */
                                  /* which has a field called num */
    (*p).num = 3;                 /* Also fine, means exactly the same thing. */
                                  /* But don't write this. p->num is much */
                                  /* more readable. */
    

    同样值得注意的是,在 C 示例中,我们必须确保 p 已初始化为某个实际内存的地址。如果我们没有这样做并且我们试图将某些东西分配给p-&gt;num,那么我们将试图将一个值插入一个未定义的地址。如果你很幸运,那将是段错误。否则,它可能会覆盖可执行文件中的任何内容。

    如果其中任何一个不明显,请返回您的 C 教科书并确保您了解 C 指针的工作原理,然后再尝试使用它们。

    所以,这就是$$.num 部分。实际的说法是

    $$.num = $1.num;
    

    现在让我们看看右边。 $1 指的是具有标签类型iValueNUM,即intint 不是结构,它没有命名字段。 $$.num = $1.num 将被处理为:

    int i = <some value>;
    $$.num = i.num;
    

    这完全没有意义。在这里,编译器将再次抱怨iValue 不是结构,但出于不同的原因:在左侧,e 是指向结构(不是结构)的指针;右侧的iValue 是一个整数(也不是结构)。

    我希望这能给你一些关于你需要做什么的想法。如果有疑问,有各种完整的工作examples in the Bison manual

    【讨论】:

      猜你喜欢
      • 2023-03-17
      • 1970-01-01
      • 1970-01-01
      • 2013-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多