Bison Grammar 分解重复的标记/表达式?

Bison Grammar breaks down on repeated tokens / expressions?

使用非常基本的 Bison/Flex 语法,我试图将标记/表达式拉入 C++ 对象以从中生成三个操作码(即内部表示)。我这样做是因为这个特定的解析器代表了一个更大的解析器的一个更小的子集。我的问题来自重复的表达式/标记。

例如:

10 + 55 将解析为 10 + 10。

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

55-HELLOWORLD / 100 将再次正常解析,大概是因为表达式的两边从来没有两个相同的标记。

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

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

我的假设是我在将 $1/$3 值加载到 class 对象然后将它们添加到解析器堆栈时正在做的事情就是问题所在。我已经尝试检查我生成的每个变量 + 指针的内存地址,它们看起来都像我期望的那样(即我没有覆盖同一个对象)。我已经尝试确保将值正确加载为它们的值标记,INT |和变量 |都将各自的变量正确加载到 classes.

问题似乎与表达式 OPERATION 表达式语句有关,当使用两个相同类型的值时,表达式是相同的。要使用较早的示例:

10 + 55 -> 表达式加表达式 -> $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 = ;
        cout<<x->toTOCStr()<<endl; 
    }; 
expression: 
    expression PLUS expression { 
        TOC *a1 = ;
        TOC *a2 = ;
        Operation op(a1, a2, OPS::ADD);
        TOC *t = &op;
        $$ = t;
    }
    |expression MINUS expression { 
        TOC *a1 = ;
        TOC *a2 = ;
        Operation op(a1, a2, OPS::SUBTRACT);
        TOC *t = &op;
        $$ = t;    
    }
    |expression MUL expression {
        TOC *a1 = ;
        TOC *a2 = ;
        Operation op(a1, a2, OPS::MULTIPLY);
        TOC *t = &op;
        $$ = t;
    }
    |expression DIV expression { 
        TOC *a1 = ;
        TOC *a2 = ;
        Operation op(a1, a2, OPS::DIVIDE);
        TOC *t = &op;
        $$ = t;
    }
    |LPAREN expression RPAREN { 
        TOC *t = ; 
        $$ =  t;
    }
    | INT { 
        Value<int> v = ;
        TOC *t = &v; 
        $$ =  t;
    }
    | FLOAT { 
        Value<float> v = ;
        TOC *t = &v;
        $$ = t; 
    }
    | VARIABLE {
        char* name = ;
        Variable v(name);
        TOC *t = &v;
        $$ = t;
    }
%%

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

以及我尝试加载的值(为清楚起见,合并为一个文件)。

Operation.h

enum OPS {
    SUBTRACT,
    ADD,
    MULTIPLY,
    DIVIDE,
    EXPONENT
};

class Operation : public TOC{

    OPS op;
    public:
        TOC* arg1;
        TOC* arg2;
        Operation(TOC* arg1_in, TOC* arg2_in, OPS operation){
            tt = TOC_TYPES::OPERATION_E;
            arg1 = arg1_in;
            arg2 = arg2_in;
            op = operation;
        };


        std::string toOPType(OPS e){
            switch (e){
                case SUBTRACT:
                    return "-";
                case ADD:
                    return "+";
                case MULTIPLY:
                    return "*";
                case DIVIDE:
                    return "/";
                case EXPONENT:
                    return "^";
                default:
                    return "[Operation Error!]";
            }
        }

        std::string toTOCStr(){
            return arg1->toTOCStr() + toOPType(op) + arg2->toTOCStr();
        }
};

Value.h

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

Variable.H

class Variable : public TOC {
    public:
        char *name;
        Variable(char* name_in){
            tt = TOC_TYPES::VARIABLE_E;
            name = name_in;
        }
        std::string toTOCStr(){
            std::string x = name;
            return x;
        }
};

TOC.h,以备不时之需

enum TOC_TYPES { 
    VARIABLE_E, 
    VALUE_E,
    OPERATION_E
};

class TOC{
    public:
        TOC_TYPES tt;   
        virtual std::string toTOCStr() = 0;
};

我的主文件只是加载一个文件并将 yyin 设置为其内容,然后调用 yyparse。我没有包括它,但如果需要的话可以(这不是很令人兴奋)。

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

这是一个问题示例:

    Operation op(a1, a2, OPS::ADD);
    TOC *t = &op;
    $$ = t;

t 是不必要的;您也可以写成 $$ = &op;。但这只是旁注。)

op 这里是一个 automatic 变量,它的生命周期在块退出时结束。在它的地址保存在 $$ 中后,这会立即发生。这使得产生式的语义价值成为悬空指针。

使用生命周期结束的变量地址是Undefined Behaviour,不过你大概能猜到是什么情况:下一次进入block时,栈在同一个地方,新的op 与旧地址具有相同的地址。 (不能保证会发生这种情况:未定义的行为是未定义的。但是这个特定的结果与您的观察结果一致。)

简而言之,熟悉 new 运算符:

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

别忘了在适当的时候delete