在 Bison 和 Flex 中使用变体
Using variant in Bison and Flex
我一直在将解析器从 c 重写为 c++,因此我尝试在我的代码中使用 variant。但是,我不确定如何将它与 flex 集成,而且我不断收到深奥的错误消息。
我的野牛文件看起来像
%require "3"
%language "c++"
%{
// declarations
%}
%define api.value.type {std::variant<double, std::string>}
%token COMMENT
%token <double> DOUBLE
%token <std::string> STRING
// grammar
我的词法分析器看起来像
%{
#include "y.tab.h"
%}
%option noyywrap
ID [a-zA-Z][a-zA-Z0-9_]*
%%
[ \t\n ]+ ;
\-?[0-9]+ |
\-?[0-9]+\. |
\-?[0-9]+\.[0-9]+ |
\-?\.[0-9]+ { yylval.emplace<double>(std::atof(yytext)); return DOUBLE;}
// other tokens
zA-Z][\.a-zA-Z0-9_]* { yylval.emplace<std::string>(yytext); return STRING;}
%%
我不确定我对 yylval 的用法,我正在尝试访问变体,就像我在 %union
.
中那样
我收到以下错误:
y.tab.h:125:18: error: ‘variant’ in namespace ‘std’ does not name a template type
typedef std::variant<double, std::string> semantic_type;
^~~~~~~
y.tab.h:197:27: error: ‘semantic_type’ does not name a type
const semantic_type& v);
^~~~~~~~~~~~~
y.tab.h:212:7: error: ‘semantic_type’ does not name a type
semantic_type value;
^~~~~~~~~~~~~
my_mdl.l: In function ‘int yylex()’:
my_mdl.l:16:3: error: ‘yylval’ was not declared in this scope
\-?\.[0-9]+ { yylval.emplace<double>(std::atof(yytext)); return DOUBLE;}
^~~~~~
my_mdl.l:16:3: note: suggested alternative: ‘yylex’
\-?\.[0-9]+ { yylval.emplace<double>(std::atof(yytext)); return DOUBLE;}
^~~~~~
yylex
my_mdl.l:16:18: error: expected primary-expression before ‘double’
\-?\.[0-9]+ { yylval.emplace<double>(std::atof(yytext)); return DOUBLE;}
^~~~~~
my_mdl.l:16:53: error: ‘DOUBLE’ was not declared in this scope
\-?\.[0-9]+ { yylval.emplace<double>(std::atof(yytext)); return DOUBLE;}
^~~~~~
my_mdl.l:18:10: error: ‘COMMENT’ was not declared in this scope
"//".* { return COMMENT;}
^~~~~~~
my_mdl.l:37:29: error: expected primary-expression before ‘>’ token
[a-zA-Z][\.a-zA-Z0-9_]* { yylval.emplace<std::string>(yytext); return STRING;}
^
my_mdl.l:37:47: error: ‘STRING’ was not declared in this scope
[a-zA-Z][\.a-zA-Z0-9_]* { yylval.emplace<std::string>(yytext); return STRING;}
^~~~~~
我的 .y
文件也有几百行错误,例如
my_mdl.y:88:79: error: no matching function for call to ‘MOVE::MOVE(<brace-enclosed initializer list>)’
p.add_command(Command{in_place_index<5>, MOVE( {{, , }}, )});
^
In file included from parsing/symt.h:7:0,
from my_mdl.y:10:
parsing/cmd.h:44:5: note: candidate: MOVE::MOVE(const Scalable<double, 3>&, const string&)
MOVE(const Scalable<double, 3> ¶ms, const std::string &scaleFactorName);
^~~~
MOVE
是定义为
的结构
struct MOVE {
MOVE(const Scalable<double, 3> ¶ms, const std::string &scaleFactorName);
Scalable<double, 3> params; // todo equationify
std::string scale_factor_name;
};
并且它是变体中的一种类型 (std::variant<MOVE, etc...> Command
)。奇怪的是,如果我写,这在我的代码中正常工作
p.add_command(Command{in_place_index<5>, MOVE{{{x, y, z}}, "asdfads"}});
您的程序包含的内容不足,无法给出准确的答案。请参阅有关准备 [mcse] 的 SO 帮助页面。但你似乎很可能得到错误
y.tab.h:125:18: error: ‘variant’ in namespace ‘std’ does not name a template type
因为你还没有安排 #include <variant>
在你的 flex 文件中。
typedef 本身来自 bison 生成的 header 文件中的生成代码,但是 bison 无法猜测它可能需要什么 #include
指令,所以它留给你插入它们.您必须确保在 #include
bison-generated header 之前定义了您的语义类型所需的所有类型。您可以在 flex 文件的序言块中插入适当的 #include
指令,或者您可以在 bison 文件中使用 %code requires
块。 (由于您使用的是 bison 3,后者可能是最佳解决方案。)
我不知道 SAVE
在您的 bison 文件中的错误是什么意思。我假设它是您拥有(或尚未定义)的宏,因此错误将是宏扩展的结果。
Bison 实际上提供了它的 自己的 自定义类型 'variant',这是 而不是 C++ std::variant
,并很好地支持它。
https://www.gnu.org/software/bison/manual/bison.html#C_002b_002b-Variants
Bison provides a variant based implementation of semantic values for C++. This alleviates all the limitations reported in the previous section, and in particular, object types can be used without pointers.
To enable variant-based semantic values, set the %define variable api.value.type to variant (see %define Summary). Then %union is ignored; instead of using the name of the fields of the %union to “type” the symbols, use genuine types.
For instance, instead of:
%union
{
int ival;
std::string* sval;
}
%token <ival> NUMBER;
%token <sval> STRING;
write:
%token <int> NUMBER;
%token <std::string> STRING;
STRING is no longer a pointer, which should fairly simplify the user actions in the grammar and in the scanner (in particular the memory management).
要启用它,您只需指定
%define api.value.type variant
然而这也会影响你的词法分析器。我使用 Ubuntu:
下的最新 flex/bison 二进制文件组合了一个小型演示 C++ flex/bison 框架
https://github.com/kfsone/flub
亮点是:
/* lexer */
{string} yylval->emplace<std::string>(yytext); return Token::STRING;
/* parser */
%language "C++"
%skeleton "lalr1.cc"
%require "3.8.2"
%define api.value.type variant
/*
%define api.value.automove // use with care
*/
%define parse.assert
%define parse.trace
%define parse.error detailed
%define parse.lac full
/* ... */
/*
NB: You now use the TYPE rather than union-member name
*/
%token <std::string> STRING
/*
// ditto for nterms, but I now prefer putting them by the production itself.
%nterm <string::string> string_literal
*/
%%
/* ... */
/* Declare the type of this non-terminal, to be done here in the
productions section, you need a trailing semicolon */
%nterm <std::string> using_file;
using_file
: "using" STRING
{ $$ = ; }
;
然后您可以使用更现代的 AST-building 方法,我发现有一个有用的方法是拥有一个我自己的包装器类型,它捕获一个值及其位置。
template<typename ValueType>
struct ParsedType
{
location mLoc; // or YourParserType::location_type
ValueType mValue;
};
using ParsedString = ParsedType<std::string>;
/* ... */
%nterm <ParsedString> using_file;
using_file
: "using" STRING
{ $$ = ParsedString(@2, ); }
我一直在将解析器从 c 重写为 c++,因此我尝试在我的代码中使用 variant。但是,我不确定如何将它与 flex 集成,而且我不断收到深奥的错误消息。
我的野牛文件看起来像
%require "3"
%language "c++"
%{
// declarations
%}
%define api.value.type {std::variant<double, std::string>}
%token COMMENT
%token <double> DOUBLE
%token <std::string> STRING
// grammar
我的词法分析器看起来像
%{
#include "y.tab.h"
%}
%option noyywrap
ID [a-zA-Z][a-zA-Z0-9_]*
%%
[ \t\n ]+ ;
\-?[0-9]+ |
\-?[0-9]+\. |
\-?[0-9]+\.[0-9]+ |
\-?\.[0-9]+ { yylval.emplace<double>(std::atof(yytext)); return DOUBLE;}
// other tokens
zA-Z][\.a-zA-Z0-9_]* { yylval.emplace<std::string>(yytext); return STRING;}
%%
我不确定我对 yylval 的用法,我正在尝试访问变体,就像我在 %union
.
我收到以下错误:
y.tab.h:125:18: error: ‘variant’ in namespace ‘std’ does not name a template type
typedef std::variant<double, std::string> semantic_type;
^~~~~~~
y.tab.h:197:27: error: ‘semantic_type’ does not name a type
const semantic_type& v);
^~~~~~~~~~~~~
y.tab.h:212:7: error: ‘semantic_type’ does not name a type
semantic_type value;
^~~~~~~~~~~~~
my_mdl.l: In function ‘int yylex()’:
my_mdl.l:16:3: error: ‘yylval’ was not declared in this scope
\-?\.[0-9]+ { yylval.emplace<double>(std::atof(yytext)); return DOUBLE;}
^~~~~~
my_mdl.l:16:3: note: suggested alternative: ‘yylex’
\-?\.[0-9]+ { yylval.emplace<double>(std::atof(yytext)); return DOUBLE;}
^~~~~~
yylex
my_mdl.l:16:18: error: expected primary-expression before ‘double’
\-?\.[0-9]+ { yylval.emplace<double>(std::atof(yytext)); return DOUBLE;}
^~~~~~
my_mdl.l:16:53: error: ‘DOUBLE’ was not declared in this scope
\-?\.[0-9]+ { yylval.emplace<double>(std::atof(yytext)); return DOUBLE;}
^~~~~~
my_mdl.l:18:10: error: ‘COMMENT’ was not declared in this scope
"//".* { return COMMENT;}
^~~~~~~
my_mdl.l:37:29: error: expected primary-expression before ‘>’ token
[a-zA-Z][\.a-zA-Z0-9_]* { yylval.emplace<std::string>(yytext); return STRING;}
^
my_mdl.l:37:47: error: ‘STRING’ was not declared in this scope
[a-zA-Z][\.a-zA-Z0-9_]* { yylval.emplace<std::string>(yytext); return STRING;}
^~~~~~
我的 .y
文件也有几百行错误,例如
my_mdl.y:88:79: error: no matching function for call to ‘MOVE::MOVE(<brace-enclosed initializer list>)’
p.add_command(Command{in_place_index<5>, MOVE( {{, , }}, )});
^
In file included from parsing/symt.h:7:0,
from my_mdl.y:10:
parsing/cmd.h:44:5: note: candidate: MOVE::MOVE(const Scalable<double, 3>&, const string&)
MOVE(const Scalable<double, 3> ¶ms, const std::string &scaleFactorName);
^~~~
MOVE
是定义为
struct MOVE {
MOVE(const Scalable<double, 3> ¶ms, const std::string &scaleFactorName);
Scalable<double, 3> params; // todo equationify
std::string scale_factor_name;
};
并且它是变体中的一种类型 (std::variant<MOVE, etc...> Command
)。奇怪的是,如果我写,这在我的代码中正常工作
p.add_command(Command{in_place_index<5>, MOVE{{{x, y, z}}, "asdfads"}});
您的程序包含的内容不足,无法给出准确的答案。请参阅有关准备 [mcse] 的 SO 帮助页面。但你似乎很可能得到错误
y.tab.h:125:18: error: ‘variant’ in namespace ‘std’ does not name a template type
因为你还没有安排 #include <variant>
在你的 flex 文件中。
typedef 本身来自 bison 生成的 header 文件中的生成代码,但是 bison 无法猜测它可能需要什么 #include
指令,所以它留给你插入它们.您必须确保在 #include
bison-generated header 之前定义了您的语义类型所需的所有类型。您可以在 flex 文件的序言块中插入适当的 #include
指令,或者您可以在 bison 文件中使用 %code requires
块。 (由于您使用的是 bison 3,后者可能是最佳解决方案。)
我不知道 SAVE
在您的 bison 文件中的错误是什么意思。我假设它是您拥有(或尚未定义)的宏,因此错误将是宏扩展的结果。
Bison 实际上提供了它的 自己的 自定义类型 'variant',这是 而不是 C++ std::variant
,并很好地支持它。
https://www.gnu.org/software/bison/manual/bison.html#C_002b_002b-Variants
Bison provides a variant based implementation of semantic values for C++. This alleviates all the limitations reported in the previous section, and in particular, object types can be used without pointers.
To enable variant-based semantic values, set the %define variable api.value.type to variant (see %define Summary). Then %union is ignored; instead of using the name of the fields of the %union to “type” the symbols, use genuine types.
For instance, instead of:
%union
{
int ival;
std::string* sval;
}
%token <ival> NUMBER;
%token <sval> STRING;
write:
%token <int> NUMBER;
%token <std::string> STRING;
STRING is no longer a pointer, which should fairly simplify the user actions in the grammar and in the scanner (in particular the memory management).
要启用它,您只需指定
%define api.value.type variant
然而这也会影响你的词法分析器。我使用 Ubuntu:
下的最新 flex/bison 二进制文件组合了一个小型演示 C++ flex/bison 框架https://github.com/kfsone/flub
亮点是:
/* lexer */
{string} yylval->emplace<std::string>(yytext); return Token::STRING;
/* parser */
%language "C++"
%skeleton "lalr1.cc"
%require "3.8.2"
%define api.value.type variant
/*
%define api.value.automove // use with care
*/
%define parse.assert
%define parse.trace
%define parse.error detailed
%define parse.lac full
/* ... */
/*
NB: You now use the TYPE rather than union-member name
*/
%token <std::string> STRING
/*
// ditto for nterms, but I now prefer putting them by the production itself.
%nterm <string::string> string_literal
*/
%%
/* ... */
/* Declare the type of this non-terminal, to be done here in the
productions section, you need a trailing semicolon */
%nterm <std::string> using_file;
using_file
: "using" STRING
{ $$ = ; }
;
然后您可以使用更现代的 AST-building 方法,我发现有一个有用的方法是拥有一个我自己的包装器类型,它捕获一个值及其位置。
template<typename ValueType>
struct ParsedType
{
location mLoc; // or YourParserType::location_type
ValueType mValue;
};
using ParsedString = ParsedType<std::string>;
/* ... */
%nterm <ParsedString> using_file;
using_file
: "using" STRING
{ $$ = ParsedString(@2, ); }