使用非常基本的 Bison/Flex 语法,我试图将标记/表达式拉入 C++ 对象以从中生成三个操作码(即内部表示)。我这样做是因为这个特定的解析器代表了一个更大的解析器的一个更小的子集。我的问题来自重复的表达式/标记。
例如:
10 + 55 将解析为 10 + 10。
10 + VARIABLLENAME 可以很好地解析,因为 INT 和 VARIABLE 是不同的标记。
55-HELLOWORLD/100 将再次正常解析,大概是因为表达式的两边从来没有两个相同的标记。
55-HELLOWORLD - 100 段故障。重复操作标记(即 -、+、/等会导致解析器崩溃)。
TLDR:当重复值类型(即 INT、FLOAT、VARIABLE)时,相同的标记会返回两次。重复操作时,解析器会出现错误。
我的假设是我在将 $1/$3 值加载到类对象中然后将它们添加到解析器堆栈时出现问题。我已经尝试检查我生成的每个变量 + 指针的内存地址,它们看起来都像我期望的那样(即我没有覆盖同一个对象)。我已经尝试确保将值正确加载为它们的值标记,INT |和变量 |两者都将各自的变量正确加载到类中。
问题似乎是针对表达式 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);
}
以及我尝试加载的值(为清楚起见,合并为一个文件)。
操作.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();
}
};
值.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* 中,然后我可以向下迭代以在每个级别生成三个操作代码。然而,这个打破重复标记和操作的错误真的让我很困惑。
最佳答案
这是问题的一个例子:
Operation op(a1, a2, OPS::ADD);
TOC *t = &op;
$$ = t;
(t
是不必要的;您也可以编写 $$ = &op;
。但这只是旁注。)
op
这里是一个 automatic 变量,它的生命周期在 block 退出时结束。在它的地址保存在 $$
中后,这会立即发生。这使得产生式的语义值成为悬空指针。
使用生命周期结束的变量的地址是未定义的行为,但你大概可以猜到发生了什么:下一次进入该 block 时,堆栈在同一个地方并且新的op
与旧地址具有相同的地址。 (不能保证会发生这种情况:未定义的行为在定义上是未定义的。但这个特定结果与您的观察结果一致。)
简而言之,熟悉 new
运算符:
$$ = new Operation(a1, a2, OPS::ADD);
并且不要忘记在适当的时候删除
它。
关于c++ - Bison Grammar 分解重复的标记/表达式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54523589/