我使用基本的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 *中,然后可以向下迭代以在每个级别生成三个操作码。但是,打破重复标记和操作的错误确实让我感到困扰。
答案 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
。