Bison C++ 中间规则值因变体而丢失
Bison C++ mid-rule value lost with variants
我正在使用带有 lalr1.cc
框架的 Bison 来生成 C++ 解析器和 api.value.type variant
。我尝试对 return 值使用中间规则操作,该值将在进一步的语义操作中使用,但看起来堆栈上的值变为零。下面是一个例子。
parser.y:
%require "3.0"
%skeleton "lalr1.cc"
%defines
%define api.value.type variant
%code {
#include <iostream>
#include "parser.yy.hpp"
extern int yylex(yy::parser::semantic_type * yylval);
}
%token <int> NUM
%%
expr: NUM | expr { $<int>$ = 42; } '+' NUM { std::cout << $<int>2 << std::endl; };
%%
void yy::parser::error(const std::string & message){
std::cerr << message << std::endl;
}
int main(){
yy::parser p;
return p.parse();
}
lexer.flex:
%option noyywrap
%option nounput
%option noinput
%{
#include "parser.yy.hpp"
typedef yy::parser::token token;
#define YY_DECL int yylex(yy::parser::semantic_type * yylval)
%}
%%
[ \n\t]+
[0-9]+ {
yylval->build(atoi(yytext));
return token::NUM;
}
. {
return yytext[0];
}
编译:
bison -o parser.yy.cpp parser.y
flex -o lexer.c lexer.flex
g++ parser.yy.cpp lexer.c -O2 -Wall -o parser
像 2+2
这样的简单输入应该打印值 42
,但出现 0
。当 variant type 更改为 %union
时,打印的值是应该的。作为解决方法,我一直在使用带有 $<type>-n
的标记操作来从堆栈中获取更深层次的值,但这种方法会降低易读性和可维护性。
我在生成的源代码中读到,当使用变体类型时,默认操作 { $$ = }
不会执行。它是这种行为的一个例子还是一个错误?
我投给 "bug"。它与没有默认操作无关。
我跟踪执行直到它试图将 MRA 的值压入堆栈。但是,它没有获得 MRA 的正确类型,结果是对 yypush_
的调用什么也不做,这显然不是所需的操作。
如果我是你,我会报告问题。
这确实是 Bison 3.0.5 中的一个错误,但使用 $<foo>1
本质上也是错误的(但别无选择!)。
考虑:
// 1.y
%nterm <int> exp
%%
exp: { $<int>$ = 42; } { $$ = $<int>1; }
它被翻译成这样:
// 2.y
%nterm <int> exp
%%
@1: %empty { $<int>$ = 42; }
exp: @1 { $$ = $<int>1; }
这里的错误是我们没有告诉Bison@1
的语义值的类型是什么,所以它无法正确处理这个语义值(通常,没有%printer
并且不会应用 %destructor
)。更糟糕的是:在这里,因为它不知道这里实际存储了什么值,所以无法正确操作它:它不知道如何将它从局部变量 $$
复制到堆栈,这就是为什么值丢失了。 (在 C 中,语义值的位被盲目复制,所以它有效。在具有 Bison 变体的 C++ 中,我们使用与当前类型对应的精确 assignment/copy 运算符,这需要知道类型是什么。)
显然更好的翻译应该是给 @1
一个类型:
// 3.y
%nterm <int> exp @1
%%
@1: %empty { $$ = 42; }
exp: @1 { $$ = ; }
这实际上是任何人都会写的。
感谢您的提问,Bison 3.1 现在具有 typed midrule actions:
// 4.y
%nterm <int> exp
%%
exp: <int>{ $$ = 42; } { $$ = ; }
脱糖,4.y 生成与 3.y 完全相同。
有关详细信息,请参阅 http://lists.gnu.org/archive/html/bug-bison/2017-06/msg00000.html and http://lists.gnu.org/archive/html/bug-bison/2018-06/msg00001.html。
我正在使用带有 lalr1.cc
框架的 Bison 来生成 C++ 解析器和 api.value.type variant
。我尝试对 return 值使用中间规则操作,该值将在进一步的语义操作中使用,但看起来堆栈上的值变为零。下面是一个例子。
parser.y:
%require "3.0"
%skeleton "lalr1.cc"
%defines
%define api.value.type variant
%code {
#include <iostream>
#include "parser.yy.hpp"
extern int yylex(yy::parser::semantic_type * yylval);
}
%token <int> NUM
%%
expr: NUM | expr { $<int>$ = 42; } '+' NUM { std::cout << $<int>2 << std::endl; };
%%
void yy::parser::error(const std::string & message){
std::cerr << message << std::endl;
}
int main(){
yy::parser p;
return p.parse();
}
lexer.flex:
%option noyywrap
%option nounput
%option noinput
%{
#include "parser.yy.hpp"
typedef yy::parser::token token;
#define YY_DECL int yylex(yy::parser::semantic_type * yylval)
%}
%%
[ \n\t]+
[0-9]+ {
yylval->build(atoi(yytext));
return token::NUM;
}
. {
return yytext[0];
}
编译:
bison -o parser.yy.cpp parser.y
flex -o lexer.c lexer.flex
g++ parser.yy.cpp lexer.c -O2 -Wall -o parser
像 2+2
这样的简单输入应该打印值 42
,但出现 0
。当 variant type 更改为 %union
时,打印的值是应该的。作为解决方法,我一直在使用带有 $<type>-n
的标记操作来从堆栈中获取更深层次的值,但这种方法会降低易读性和可维护性。
我在生成的源代码中读到,当使用变体类型时,默认操作 { $$ = }
不会执行。它是这种行为的一个例子还是一个错误?
我投给 "bug"。它与没有默认操作无关。
我跟踪执行直到它试图将 MRA 的值压入堆栈。但是,它没有获得 MRA 的正确类型,结果是对 yypush_
的调用什么也不做,这显然不是所需的操作。
如果我是你,我会报告问题。
这确实是 Bison 3.0.5 中的一个错误,但使用 $<foo>1
本质上也是错误的(但别无选择!)。
考虑:
// 1.y
%nterm <int> exp
%%
exp: { $<int>$ = 42; } { $$ = $<int>1; }
它被翻译成这样:
// 2.y
%nterm <int> exp
%%
@1: %empty { $<int>$ = 42; }
exp: @1 { $$ = $<int>1; }
这里的错误是我们没有告诉Bison@1
的语义值的类型是什么,所以它无法正确处理这个语义值(通常,没有%printer
并且不会应用 %destructor
)。更糟糕的是:在这里,因为它不知道这里实际存储了什么值,所以无法正确操作它:它不知道如何将它从局部变量 $$
复制到堆栈,这就是为什么值丢失了。 (在 C 中,语义值的位被盲目复制,所以它有效。在具有 Bison 变体的 C++ 中,我们使用与当前类型对应的精确 assignment/copy 运算符,这需要知道类型是什么。)
显然更好的翻译应该是给 @1
一个类型:
// 3.y
%nterm <int> exp @1
%%
@1: %empty { $$ = 42; }
exp: @1 { $$ = ; }
这实际上是任何人都会写的。
感谢您的提问,Bison 3.1 现在具有 typed midrule actions:
// 4.y
%nterm <int> exp
%%
exp: <int>{ $$ = 42; } { $$ = ; }
脱糖,4.y 生成与 3.y 完全相同。
有关详细信息,请参阅 http://lists.gnu.org/archive/html/bug-bison/2017-06/msg00000.html and http://lists.gnu.org/archive/html/bug-bison/2018-06/msg00001.html。