在 Bison 中访问一个产品的价值以及从另一个产品分配类型
Accessing The Value Of A Production In Bison As Well As Assigning Types From Another Production
我正在与 bison 一起工作,目前遇到了一个问题,并且对所有这些工作原理还比较陌生,我需要能够判断某个特定的作品是 > 或 + 还是 >= 但我不知道存储和检索此值的最佳方法是以下代码:
%union
{
char* text;
TYPE_INFO typeInfo;
};
N_ARITHLOGIC_EXPR : N_UN_OP N_EXPR
{
if(.type == FUNCTION){
yyerror("Arg 1 cannot be function");
}
$$.type = BOOL;
$$.numParams = NOT_APPLICABLE;
$$.returnType = NOT_APPLICABLE;
}
| N_BIN_OP N_EXPR N_EXPR
{
if(T_LT || T_GT || T_LE || T_GE || T_EQ || T_NE || T_NOT){
if(!((.type == INT && .type == INT) || (.type == STR && .type == STR))){
yyerror("Arg n must be integer or string");
}
else{
$$.type = BOOL;
$$.numParams = NOT_APPLICABLE;
$$.returnType = NOT_APPLICABLE;
}
}
else if(T_AND || T_OR){
if((.type == INT && .type == FUNCTION) || (.type == STR && .type == FUNCTION) || (.type == BOOL && .type == FUNCTION) || (.type == FUNCTION && .type == FUNCTION)){
yyerror("Arg n cannot be a function");
}
else{
$$.type = BOOL;
$$.numParams = NOT_APPLICABLE;
$$.returnType = NOT_APPLICABLE;
}
}
else if (T_ADD || T_SUB || T_MULT || T_DIV){
if(!(.type == INT && .type == INT)){
yyerror("Arg n must be integer");
}
else{
$$.type = INT;
$$.numParams = NOT_APPLICABLE;
$$.returnType = NOT_APPLICABLE;
}
}
}
;
if 语句现在显然不起作用,但我只需要能够查看它是有理运算符还是算术运算符等
另外,稍后我需要能够使用一个作品,所以说以下内容:
N_PROGN_OR_USERFUNCTCALL : N_FUNCT_NAME N_ACTUAL_PARAMS
{
}
| T_LPAREN N_LAMBDA_EXPR T_RPAREN N_ACTUAL_PARAMS
{
}
;
N_FUNCT_NAME : T_PROGN
{
//Change type of N_PROGN_OR_USERFUNCTCALL based off of the function return type of T_PROGN
}
根据 T_PROGN 的 return 类型,我需要能够更改 N_PROGN_OR_USERFUNCTCALL 的类型,最好的方法是什么?谢谢!
关键字(一般意义上,包括>
和<=
等运算符)最常见的样式是使每个关键字成为唯一的终结符。在某些情况下,这会导致语法中出现一定程度的重复,但它避免了扫描器和解析器之间的过度依赖。所以这有点平衡。
如果你想将两个具有不同语义的关键字合并到一个终端中,你可以通过使终端的语义类型成为 enum
(或道德等价物),并在词法分析器中设置它来实现.但您也可以在语法中组合两个关键字。
以下所有都有它们的用例,它们真的没有什么不同:
合并终端:
// Scanner patterns
"<=" yylval.op = OP_LE; return T_BINOP;
"<" yylval.op = OP_LT; return T_BINOP;
"+" yylval.op = OP_PLUS; return T_BINOP;
// etc.
// grammar
%token <opcode> T_BINOP
%%
expr: T_BIN_OP expr expr {
switch () {
case OP_LT: case OP_LE: case OP_EQ: ... {
if (check_compare(, )) {
$$ = (TypeInfo){ .type = BOOL,
.numParams = NOT_APPLICABLE,
.returnType = NOT_APPLICABLE };
else {
yyerror(...);
}
break
case OP_PLUS: case OP_MINUS: case OP_TIMES: ...
if (check_arith(, )) {
// ...
个别终端机:
// Scanner patterns
"<=" return OP_LE;
"<" return OP_LT;
"+" return OP_PLUS;
// etc.
// grammar
%token OP_LE "<=" OP_LT "<"
OP_PLUS "+"
...
%%
expr: "<=" expr expr {
if (check_compare(, )) {
$$ = (TypeInfo){ .type = BOOL,
.numParams = NOT_APPLICABLE,
.returnType = NOT_APPLICABLE };
else {
yyerror(...);
}
| "+" expr expr {
if (check_arith(, )) {
$$ = (TypeInfo){ .type = BOOL,
.numParams = NOT_APPLICABLE,
.returnType = NOT_APPLICABLE };
else {
yyerror(...);
}
// ...
或者,替代语法:
%token OP_LE "<=" OP_LT "<"
OP_PLUS "+" OP_TIMES "*"
...
%type <opcode> cmp_op arith_op ...
%%
cmp_op: "<=" { $$ = OP_LE; }
| "<" { $$ = OP_LT; }
// ...
arith_op: "+" { $$ = OP_PLUS; }
| "*" { $$ = OP_TIMES; }
// ...
expr: cmp_op expr expr {
if (check_compare(, )) {
$$ = (TypeInfo){ .type = BOOL,
.numParams = NOT_APPLICABLE,
.returnType = NOT_APPLICABLE };
else {
yyerror(...);
}
| arith_op expr expr {
if (check_arith(, )) {
$$ = (TypeInfo){ .type = BOOL,
.numParams = NOT_APPLICABLE,
.returnType = NOT_APPLICABLE };
else {
yyerror(...);
}
// ...
注意:上面的 None 实际上保存了仔细计算的操作码,也没有保存参数。但是,问题中的代码也没有。但是 none 版本之间存在显着差异。
老实说,我不确定你的第二个问题是什么意思。什么是“N_PROGN_OR_USERFUNCTCALL 的类型”?你是指你设置为TYPE_INFO的type
成员值的枚举吗?还是您的意思是您可能希望 N_PROGN_OR_USERFUNCTCALL 成为不止一种可能的语义类型?在后一种情况下,您需要重新考虑您的设计。 Bison/yacc 在这方面与 C 具有完全相同的语义;变量的类型就是变量的类型,您无法在运行时决定 x
是 int
还是 double
。非终结符在语法上等同于变量,每个非终结符都有一个预先声明的类型。如果您需要更多选择,您可以使用可区分联合(或 std::variant
,在 C++ 中),就像您可以使用底层实现语言一样。 (可区分联合是 struct
包含类型枚举和 union
不同类型的值。)
我正在与 bison 一起工作,目前遇到了一个问题,并且对所有这些工作原理还比较陌生,我需要能够判断某个特定的作品是 > 或 + 还是 >= 但我不知道存储和检索此值的最佳方法是以下代码:
%union
{
char* text;
TYPE_INFO typeInfo;
};
N_ARITHLOGIC_EXPR : N_UN_OP N_EXPR
{
if(.type == FUNCTION){
yyerror("Arg 1 cannot be function");
}
$$.type = BOOL;
$$.numParams = NOT_APPLICABLE;
$$.returnType = NOT_APPLICABLE;
}
| N_BIN_OP N_EXPR N_EXPR
{
if(T_LT || T_GT || T_LE || T_GE || T_EQ || T_NE || T_NOT){
if(!((.type == INT && .type == INT) || (.type == STR && .type == STR))){
yyerror("Arg n must be integer or string");
}
else{
$$.type = BOOL;
$$.numParams = NOT_APPLICABLE;
$$.returnType = NOT_APPLICABLE;
}
}
else if(T_AND || T_OR){
if((.type == INT && .type == FUNCTION) || (.type == STR && .type == FUNCTION) || (.type == BOOL && .type == FUNCTION) || (.type == FUNCTION && .type == FUNCTION)){
yyerror("Arg n cannot be a function");
}
else{
$$.type = BOOL;
$$.numParams = NOT_APPLICABLE;
$$.returnType = NOT_APPLICABLE;
}
}
else if (T_ADD || T_SUB || T_MULT || T_DIV){
if(!(.type == INT && .type == INT)){
yyerror("Arg n must be integer");
}
else{
$$.type = INT;
$$.numParams = NOT_APPLICABLE;
$$.returnType = NOT_APPLICABLE;
}
}
}
;
if 语句现在显然不起作用,但我只需要能够查看它是有理运算符还是算术运算符等
另外,稍后我需要能够使用一个作品,所以说以下内容:
N_PROGN_OR_USERFUNCTCALL : N_FUNCT_NAME N_ACTUAL_PARAMS
{
}
| T_LPAREN N_LAMBDA_EXPR T_RPAREN N_ACTUAL_PARAMS
{
}
;
N_FUNCT_NAME : T_PROGN
{
//Change type of N_PROGN_OR_USERFUNCTCALL based off of the function return type of T_PROGN
}
根据 T_PROGN 的 return 类型,我需要能够更改 N_PROGN_OR_USERFUNCTCALL 的类型,最好的方法是什么?谢谢!
关键字(一般意义上,包括>
和<=
等运算符)最常见的样式是使每个关键字成为唯一的终结符。在某些情况下,这会导致语法中出现一定程度的重复,但它避免了扫描器和解析器之间的过度依赖。所以这有点平衡。
如果你想将两个具有不同语义的关键字合并到一个终端中,你可以通过使终端的语义类型成为 enum
(或道德等价物),并在词法分析器中设置它来实现.但您也可以在语法中组合两个关键字。
以下所有都有它们的用例,它们真的没有什么不同:
合并终端:
// Scanner patterns
"<=" yylval.op = OP_LE; return T_BINOP;
"<" yylval.op = OP_LT; return T_BINOP;
"+" yylval.op = OP_PLUS; return T_BINOP;
// etc.
// grammar
%token <opcode> T_BINOP
%%
expr: T_BIN_OP expr expr {
switch () {
case OP_LT: case OP_LE: case OP_EQ: ... {
if (check_compare(, )) {
$$ = (TypeInfo){ .type = BOOL,
.numParams = NOT_APPLICABLE,
.returnType = NOT_APPLICABLE };
else {
yyerror(...);
}
break
case OP_PLUS: case OP_MINUS: case OP_TIMES: ...
if (check_arith(, )) {
// ...
个别终端机:
// Scanner patterns
"<=" return OP_LE;
"<" return OP_LT;
"+" return OP_PLUS;
// etc.
// grammar
%token OP_LE "<=" OP_LT "<"
OP_PLUS "+"
...
%%
expr: "<=" expr expr {
if (check_compare(, )) {
$$ = (TypeInfo){ .type = BOOL,
.numParams = NOT_APPLICABLE,
.returnType = NOT_APPLICABLE };
else {
yyerror(...);
}
| "+" expr expr {
if (check_arith(, )) {
$$ = (TypeInfo){ .type = BOOL,
.numParams = NOT_APPLICABLE,
.returnType = NOT_APPLICABLE };
else {
yyerror(...);
}
// ...
或者,替代语法:
%token OP_LE "<=" OP_LT "<"
OP_PLUS "+" OP_TIMES "*"
...
%type <opcode> cmp_op arith_op ...
%%
cmp_op: "<=" { $$ = OP_LE; }
| "<" { $$ = OP_LT; }
// ...
arith_op: "+" { $$ = OP_PLUS; }
| "*" { $$ = OP_TIMES; }
// ...
expr: cmp_op expr expr {
if (check_compare(, )) {
$$ = (TypeInfo){ .type = BOOL,
.numParams = NOT_APPLICABLE,
.returnType = NOT_APPLICABLE };
else {
yyerror(...);
}
| arith_op expr expr {
if (check_arith(, )) {
$$ = (TypeInfo){ .type = BOOL,
.numParams = NOT_APPLICABLE,
.returnType = NOT_APPLICABLE };
else {
yyerror(...);
}
// ...
注意:上面的 None 实际上保存了仔细计算的操作码,也没有保存参数。但是,问题中的代码也没有。但是 none 版本之间存在显着差异。
老实说,我不确定你的第二个问题是什么意思。什么是“N_PROGN_OR_USERFUNCTCALL 的类型”?你是指你设置为TYPE_INFO的type
成员值的枚举吗?还是您的意思是您可能希望 N_PROGN_OR_USERFUNCTCALL 成为不止一种可能的语义类型?在后一种情况下,您需要重新考虑您的设计。 Bison/yacc 在这方面与 C 具有完全相同的语义;变量的类型就是变量的类型,您无法在运行时决定 x
是 int
还是 double
。非终结符在语法上等同于变量,每个非终结符都有一个预先声明的类型。如果您需要更多选择,您可以使用可区分联合(或 std::variant
,在 C++ 中),就像您可以使用底层实现语言一样。 (可区分联合是 struct
包含类型枚举和 union
不同类型的值。)