使用与struct的联合的北美野牛简单的postfix计算器

时间:2018-03-12 17:27:29

标签: struct bison unions

我正在制作一个简单的计算器,可以打印后缀来学习野牛。我可以使后缀部分工作,但现在我需要对变量(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中有这个来处理&#34; =&#34;和变量

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

我得到并且错误 警告类型为非终结的空规则,不执行任何操作 我在这里找到的唯一答案是: Bison warning: Empty rule for typed nonterminal 它说要删除/ * empty * / part in:

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); }

野牛有效,但是当我尝试在visual studio中运行代码时,我会遇到很多错误:

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

那就像我得到的那样远。我找不到类似于我需要的简单例子。我只是想制作&#39; expr&#39;能够读取字符并打印出来。如果有人可以检查我的代码并推荐一些更改。真的很感激。

1 个答案:

答案 0 :(得分:1)

重要的是要真正理解你要求Bison做什么,以及当它表明你的指示是错误的时候会告诉你什么。

应用其他人的修复问题只有在他们犯了同样的错误时才会起作用。只是根据Google搜索进行随机更改既不是一种非常结构化的调试方式,也不是学习新工具。

现在,你说的是:

%type <e> expr line 

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

如果非终端产生一个值,它会在每个生产中产生一个值。要生成值,语义规则需要将值(正确的标记类型)分配给$$。为方便起见,如果没有语义操作且$1具有正确的标记类型,则Bison将提供默认规则$$ = $1。 (这句话不太正确,但它是一个有用的近似值。)许多人认为你不应该真正依赖这个默认值,但我认为只要你知道它正在发生就行了,已经证实前提条件是有效的。

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

line : /* empty */
  1. 没有语义操作,

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

  3. 因此,它会警告您某些情况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产生了一个值,但你实际上并没有做任何事情。提供这个价值。换句话说,解决方案就是 not 告诉Bison line产生一个值。 (相反,您可以在与line的每个作品相关联的语义规则中为line提供值,但如果您不打算每个人都使用该值,为什么会这样做?)

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

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

    $$引用expr本身,它被声明为具有类型标记e,即struct nPtr *。明星很重要:它表示语义值是指针。为了从指向结构的指针获取结构中的字段,您需要使用->运算符,就像这个简单的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->num,那么我们将尝试将值戳入未定义的地址。如果你很幸运,那将是段错误。否则,它可能会覆盖可执行文件中的任何内容。

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

    所以,这是$$.num部分。实际的陈述是

    $$.num = $1.num;
    

    现在让我们看一下右侧。 $1是指NUM,其标记类型为iValue,为intint不是结构,也没有命名字段。 $$.num = $1.num将被处理为:

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

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

    我希望能为您提供有关您需要做的事情的一些想法。如果有疑问,有各种全面工作examples in the Bison manual