Bison语法在重复标记/表达式上出现故障? 使用一个非常基本的BISO/Flex语法,我试图把令牌/表达式拉到C++对象中,以从(即内部表示)生成三个OP代码。我这样做是因为这个特定的解析器代表了一个较大解析器的较小子集。我的问题来自重复的表达式/标记

Bison语法在重复标记/表达式上出现故障? 使用一个非常基本的BISO/Flex语法,我试图把令牌/表达式拉到C++对象中,以从(即内部表示)生成三个OP代码。我这样做是因为这个特定的解析器代表了一个较大解析器的较小子集。我的问题来自重复的表达式/标记,c++,compiler-construction,bison,flex-lexer,lex,C++,Compiler Construction,Bison,Flex Lexer,Lex,例如: 10+55将解析为10+10 10+VARIABLLENAME将很好地解析,因为INT和VARIABLE是不同的标记 55-HELLOWORLD/100将再次解析fine,这可能是因为表达式的两边都没有两个相同的标记 55-HELLOWORLD-100 Seg故障排除。重复操作标记(即-、+、/等会导致解析器崩溃) TLDR:当重复值类型(即INT、FLOAT、VARIABLE)时,相同的令牌返回两次。重复操作时,解析器seg会出错。 我的假设是,当将$1/$3值加载到类对象中,然后将它

例如:

10+55将解析为10+10

10+VARIABLLENAME将很好地解析,因为INT和VARIABLE是不同的标记

55-HELLOWORLD/100将再次解析fine,这可能是因为表达式的两边都没有两个相同的标记

55-HELLOWORLD-100 Seg故障排除。重复操作标记(即-、+、/等会导致解析器崩溃)

TLDR:当重复值类型(即INT、FLOAT、VARIABLE)时,相同的令牌返回两次。重复操作时,解析器seg会出错。

我的假设是,当将$1/$3值加载到类对象中,然后将它们添加到解析器堆栈中时,问题就出现了。我已经试着检查我生成的每个变量+指针的内存地址,它们看起来都和我预期的一样(即,我没有重写同一个对象)。我已经尝试确保值作为值标记正确加载,INT |和VARIABLE |都将各自的变量正确加载到类中

问题似乎是针对表达式操作表达式语句,当使用两个相同类型的值时,表达式是相同的。要使用前面的示例,请执行以下操作:

10+55->expression PLUS expression->$1=10,$3=10

当变量加载为INT时,两个变量是否都如预期的那样

这是我各自的parser.y,以及我试图加载值的对象

%{
  #include <cstdio>
  #include <iostream>
  #include "TOC/Operation.h"
  #include "TOC/Value.h"
  #include "TOC/Variable.h"
  #include "TOC.h"

  using namespace std;

  extern int yylex();
  extern int yyparse();
  extern FILE *yyin;

  void yyerror(const char *s);
%}

%code requires {
    // This is required to force bison to include TOC before the preprocessing of union types and YYTYPE.
    #include "TOC.h"
}

%union {
  int ival;
  float fval;
  char *vval;
  TOC * toc_T;
}

%token <ival> INT
%token <fval> FLOAT
%token <vval> VARIABLE

%token ENDL PLUS MINUS MUL DIV LPAREN RPAREN

%type <toc_T> expression1
%type <toc_T> expression

%right PLUS MINUS
%right MUL DIV

%start start
%%
start:
        expressions;
expressions:
    expressions expression1 ENDL
    | expression1 ENDL;
expression1:
    expression { 
        TOC* x = $1;
        cout<<x->toTOCStr()<<endl; 
    }; 
expression: 
    expression PLUS expression { 
        TOC *a1 = $1;
        TOC *a2 = $3;
        Operation op(a1, a2, OPS::ADD);
        TOC *t = &op;
        $$ = t;
    }
    |expression MINUS expression { 
        TOC *a1 = $1;
        TOC *a2 = $3;
        Operation op(a1, a2, OPS::SUBTRACT);
        TOC *t = &op;
        $$ = t;    
    }
    |expression MUL expression {
        TOC *a1 = $1;
        TOC *a2 = $3;
        Operation op(a1, a2, OPS::MULTIPLY);
        TOC *t = &op;
        $$ = t;
    }
    |expression DIV expression { 
        TOC *a1 = $1;
        TOC *a2 = $3;
        Operation op(a1, a2, OPS::DIVIDE);
        TOC *t = &op;
        $$ = t;
    }
    |LPAREN expression RPAREN { 
        TOC *t = $2; 
        $$ =  t;
    }
    | INT { 
        Value<int> v = $1;
        TOC *t = &v; 
        $$ =  t;
    }
    | FLOAT { 
        Value<float> v = $1;
        TOC *t = &v;
        $$ = t; 
    }
    | VARIABLE {
        char* name = $1;
        Variable v(name);
        TOC *t = &v;
        $$ = t;
    }
%%

void yyerror(const char *s) {
  cout << "Parser Error:  Message: " << s << endl;
  exit(-1);
}

template <class T> class Value : public TOC {
    public:
        T argument;
        Value(T arg){
            tt = TOC_TYPES::VALUE_E;
            argument = arg;
        }

        std::string toTOCStr(){
            std::string x = std::to_string(argument);
            return x;
        }
};
TOC.h,如果需要的话

enum TOC_TYPES { 
    VARIABLE_E, 
    VALUE_E,
    OPERATION_E
};

class TOC{
    public:
        TOC_TYPES tt;   
        virtual std::string toTOCStr() = 0;
};
在调用yyparse之前,我的主文件只是加载一个文件并将yyin设置为其内容。我没有把它包括在内,但如果需要的话可以(这不是很令人兴奋)


理想情况下,我希望将我的整个RD解析树加载到一个TOC*中,然后我可以遍历它,在每个级别生成三个操作代码。然而,这个打破重复标记和操作的错误真的让我难堪

下面是一个问题示例:

    Operation op(a1, a2, OPS::ADD);
    TOC *t = &op;
    $$ = t;
t
是不必要的;您也可以编写
$$=&op;
。但这只是一个旁注。)

op
这是一个自动变量,其生存期在块退出时结束。这会在其地址保存在
$$
中后立即发生。这使得产品的语义值成为一个悬而未决的指针

使用生命周期已结束的变量的地址是未定义的行为,但您可能可以猜测发生了什么:下一次输入块时,堆栈位于同一位置,新的
op
与旧的地址相同。(无法保证会发生这种情况:未定义的行为是未定义的。但这一特定结果与您的观察结果一致。)

简言之,使用
新的
操作符:

$$ = new Operation(a1, a2, OPS::ADD);

<> >不要忘记<代码>删除>代码>在适当的时候。

C++中,为了方便起见,您可能希望使用<代码> STD::UnQuyJPPT和/或<代码> STD::SyrdYPPTR 。(我还没有尝试过这些和bison。)@torek:bison的最新版本包括了一个功能,它添加了对
std::move
的调用,使得使用
std::unique_ptr
成为可能,而杂波更少。不过,我还没有玩过。
    Operation op(a1, a2, OPS::ADD);
    TOC *t = &op;
    $$ = t;
$$ = new Operation(a1, a2, OPS::ADD);