野牛语法会破坏重复的记号/表达式吗?

时间:2019-02-04 20:07:00

标签: c++ compiler-construction bison flex-lexer lex

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

例如:

10 + 55将解析为10 + 10。

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

55-HELLOWORLD / 100将再次解析良好,大概是因为表达式的两边永远不会有两个相同的标记。

55-HELLOWORLD-出现100段故障。重复操作令牌(即-,+,/等会导致解析器崩溃)。

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

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

当使用两个相同类型的值的表达式相同时,问题似乎恰好指向表达式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 = $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);
}

以及我要加载的值(为了清楚起见,连接为一个文件)。

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

变量。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;
};

在调用yyparse之前,我的主文件只是加载到文件中并将yyin设置为其内容。我没有包括它,但是如果需要可以(不是很令人兴奋)。

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

1 个答案:

答案 0 :(得分:2)

这是问题的一个例子:

   if (!pinDropAnimationController.isAnimating) {
          if (mapController.isCameraMoving) {
            pinDropAnimationTween.animate(
                new CurvedAnimation(
                    parent: pinDropAnimationController, curve: Curves.linear));
            pinDropAnimationController.forward();
          } else {
            pinDropAnimationTween.animate(
                new CurvedAnimation(
                    parent: pinDropAnimationController, curve: Curves.elasticIn));
            pinDropAnimationController.reverse();
          }
         }
   }

({ <NavigationLinkWrapper> <NavigationLink> <NavLink exact strict to={`/home/${data.id}`} activeClassName='navigation-link--active'>Lingerie</NavLink> </NavigationLink> </NavigationLinkWrapper> 是不必要的;您也可以写 Operation op(a1, a2, OPS::ADD); TOC *t = &op; $$ = t; 。但这只是一个旁注。)

t是一个 automatic 变量,其生存期在退出该块时结束。这会在其地址保存在$$ = &op;中后立即发生。这使生产的语义值成为悬空的指针。

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

简而言之,请熟悉$$运算符:

op

也不要忘记在适当的时候new