用一个非常基本的野牛/ Flex的语法,我试图拉令牌/表达式转化为C ++对象,以产生从(即内部表示)三个运码。我这样做,因为这个特殊的解析器代表一个更大的解析器的一个较小的子集。我的问题带有一再表示/令牌。
例如:
10 + 55将解析为10 + 10。
10 + VARIABLLENAME将解析细,为INT和VARIABLE是不同的令牌。
55 HELLOWORLD / 100将再次解析细,大概是因为从未有两个表达的同样任一侧。
55 HELLOWORLD - 100个赛格故障了。重复操作的令牌(即 - ,+,/等导致解析器崩溃)。
TLDR:当重复值类型(即INT,FLOAT,变量),同一令牌返回两次。当重复操作,解析器赛格故障。
我的假设是什么装载1 / $ 3个值时为类对象,然后将它们添加到分析器栈我做的问题。我已经尝试检查每个可变+指针I生成的存储器地址,并且它们都似乎是作为我期望(即IM不overriting相同的对象)。我试过,确保值正确加载它们的价值代币,INT |和可变|既正确加载各自瓦尔成类。
这个问题似乎使用两个相同类型的值的表达式是相同的时被精确定位到表达式运算表达式语句。要使用前面的例子:
10 + 55 - >表达PLUS表达 - > $ 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;
}
};
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之前。我没有将它,但是如果needsbe(这不是很令人兴奋)。
理想情况下,我想我的整个RD解析树加载到TOC *,我可以再重复上下贯通,产生在每个级别三个运代码。此错误重复破令牌和操作真的绊倒了我但是。
这里的问题的一个例子:
Operation op(a1, a2, OPS::ADD);
TOC *t = &op;
$$ = t;
(t
是不必要的,你也可以同样有书面$$ = &op;
但是,这只是一个侧面说明。)
op
这里是一个自动可变的,其寿命当块退出结束。而且,它的地址保存在$$
后立即发生。这使得生产的语义值悬空指针。
使用它的使用寿命结束后是未定义行为变量的地址,但你可能已经猜到发生了什么事:进入语句块接下来的时间,堆栈是在同一个地方,新op
具有相同的地址与旧。 (有没有保证会发生:未定义的行为被定义不确定,但这个特殊的结果与你的观察是一致的。)
总之,让舒适与new
操作:
$$ = new Operation(a1, a2, OPS::ADD);
而且不要忘了在适当的时刻到delete
它。