【发布时间】:2021-05-20 22:04:15
【问题描述】:
我有两种我认为是合法的方法来在我的 ast 中定义一个代表字符串的类,如下所示:
struct white : std::string {};
或
struct white {std::string text;};
我在多个地方使用它来表示我想在我的 ast 中捕获的文字、标识符、cmets 甚至空白。
在某些地方我必须使用第一种形式,而在其他地方我必须使用第二种形式,否则我会收到带有可怕错误消息的编译错误:
...
no matching function for call to ??minimal::ast::rules::rules(__gnu_cxx::__normal_iterator<minimal::ast::gap_item*,
std::vector<minimal::ast::gap_item> >&,
__gnu_cxx::__normal_iterator<minimal::ast::gap_item*, std::vector<minimal::ast::gap_item> >&)??
...
嵌入了几页错误输出,不知道可能有错误的源代码行。
我应该如何解释这种类型的错误信息? 为什么我必须在不同的地方使用不同的形式/风格的课程?
这个最小的例子说明了问题(当#defines被[un]注释时)
//{{{ Notes
/*
Purpose: Demonstrate problem causing error messages like:
no matching function for call to ??minimal::ast::rule::rule(std::_List_iterator<minimal::ast::gap_item>&,
std::_List_iterator<minimal::ast::gap_item>&)??
no matching function for call to ??minimal::ast::rules::rules(__gnu_cxx::__normal_iterator<minimal::ast::gap_item*,
std::vector<minimal::ast::gap_item> >&,
__gnu_cxx::__normal_iterator<minimal::ast::gap_item*, std::vector<minimal::ast::gap_item> >&)??
There are two aspects to this problem:
1) The error message is enormous (sometimes larger than my terminal scroll buffer) and is undecipherable (to me).
2) I do not understand what is really causing the problem, especially where, for similar requirements,
one construct works in one place, but another construct is needed elsewhere.
The problem appears with a subtle change in coding style for the ast:
#ifndef WITH_INSTANCED_STRING_WHITE
struct white : string {};
#else
struct white {string text;};
#endif
For this case, the problem occurs when WITH_INSTANCED_STRING_WHITE is undefined.
However, the opposite happens for WITH_INSTANCED_STRING_LITERAL or WITH_INSTANCED_STRING_C_IDENTIFIER.
Context: Attempting to parse "yacc" as a precursor to morphing yacc to x3.
There is important information in the c-style comments so gaps (comments or white) are not skipped.
Notation: Nested pairs of "//{{{" and "//}}}" denote folds which some editors can hide or fold into one line
Compile: g++ src/rgw29_minimal.cpp -o bin/rgw29_minimal
*/
//}}}
//{{{ rgw29_yacc.hpp
//{{{ Define
#define WITH_INSTANCED_STRING_WHITE
//#define WITH_INSTANCED_STRING_LITERAL
//#define WITH_INSTANCED_STRING_C_IDENTIFIER
//}}}
//{{{ include
#include <string>
#include <boost/cstdlib.hpp> // for boost::exit_success
#include <boost/filesystem.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp> // for x3::variant<...>
//}}}
//{{{ namespace and globals
namespace x3 = boost::spirit::x3;
using iterator_type = std::string::const_iterator;
using context_type = x3::phrase_parse_context<x3::ascii::space_type>::type;
//}}}
//}}}
//{{{ data/rgw29/yacc_grammar.yacc
//https://pubs.opengroup.org/onlinepubs/009696799/utilities/yacc.html
#include <string>
namespace minimal {
std::string bad_data = R"(
/* This is to demonstrate successful compilation, but a parse failure. */
spec : defs MARK rules tail
;
)";
std::string yacc_data = R"(
%start spec
%%
spec : defs MARK rules tail
;
)";
}
//}}}
//{{{ rgw29_yacc_ast.hpp
//{{{ include
#include <iostream>
#include <boost/fusion/include/io.hpp> // for boost::fusion::tuple_open/close/delimiter
#include <boost/fusion/include/std_pair.hpp> // for usage inside BOOST_FUSION_ADAPT_STRUCT
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/optional/optional_io.hpp> // for ostream<< boost::optional<>
//}}}
//{{{ ast yacc
namespace minimal { namespace ast
{
using namespace std;
enum yacc_token
{ lcomment
, rcomment
, semi
, colon
, mark
};
inline ostream& operator<<(ostream& os, yacc_token const& a)
{
//os << "yacc_token: ";
switch (a)
{
case lcomment : os << "/*"; break;
case rcomment : os << "*/"; break;
case semi : os << ";"; break;
case colon : os << ":"; break;
case mark : os << "%%"; break;
default : BOOST_ASSERT(0);
}
return os;
}
#ifndef WITH_INSTANCED_STRING_WHITE
struct white
: string
{
};
#else
struct white
{
string text;
};
#endif
struct comment
{
yacc_token open;
string text;
yacc_token close;
};
struct gap_item
: x3::variant
< comment
, white
>
{
using base_type::base_type;
using base_type::operator=;
};
struct gap
: vector<gap_item>
{
};
#ifndef WITH_INSTANCED_STRING_LITERAL
struct literal
: string
{
};
#else
struct literal
{
string text;
};
#endif
#ifndef WITH_INSTANCED_STRING_C_IDENTIFIER
struct c_identifier
: string
{
};
#else
struct c_identifier
{
string text;
};
#endif
struct rule_item
: x3::variant
< yacc_token
, c_identifier
, gap
, literal
//, string
>
{
using base_type::base_type;
using base_type::operator=;
};
struct rule
: vector<rule_item>
{
};
struct rules
: vector<rule>
{
};
struct def_item
: x3::variant
< gap
, string
>
{
using base_type::base_type;
using base_type::operator=;
};
struct defs
: vector<def_item>
{
};
struct spec_item
: x3::variant
< defs
, rules
, yacc_token
, gap
>
{
using base_type::base_type;
using base_type::operator=;
};
struct spec
{
defs defs_;
yacc_token mark_;
gap gap1;
rules rules_;
};
using yacc_ast_type = spec;
}}
//}}}
//{{{ fusion adapt
#ifdef WITH_INSTANCED_STRING_WHITE
BOOST_FUSION_ADAPT_STRUCT(minimal::ast::white, text)
#endif
#ifdef WITH_INSTANCED_STRING_LITERAL
BOOST_FUSION_ADAPT_STRUCT(minimal::ast::literal, text)
#endif
#ifdef WITH_INSTANCED_STRING_C_IDENTIFIER
BOOST_FUSION_ADAPT_STRUCT(minimal::ast::c_identifier, text)
#endif
BOOST_FUSION_ADAPT_STRUCT(minimal::ast::comment, open, text, close)
BOOST_FUSION_ADAPT_STRUCT(minimal::ast::spec, defs_, mark_, gap1, rules_)
//}}}
//}}}
//{{{ rgw29_yacc_grammar.hpp
//{{{ alias
namespace minimal { namespace parser
{
using x3::char_;
using x3::lexeme;
using x3::no_skip;
using x3::alnum;
using x3::alpha;
using x3::print;
using x3::ascii::string;
using x3::ascii::space;
}}
//}}}
//{{{ parse rule type
namespace minimal { namespace parser
{
struct comment_class;
using comment_type = x3::rule<comment_class, ast::comment>;
BOOST_SPIRIT_DECLARE(comment_type);
struct white_class;
using white_type = x3::rule<white_class, ast::white>;
BOOST_SPIRIT_DECLARE(white_type);
struct gap_class;
using gap_type = x3::rule<gap_class, ast::gap>;
BOOST_SPIRIT_DECLARE(gap_type);
struct defs_class;
using defs_type = x3::rule<defs_class, ast::defs>;
BOOST_SPIRIT_DECLARE(defs_type);
struct c_identifier_class;
using c_identifier_type = x3::rule<c_identifier_class, ast::c_identifier>;
BOOST_SPIRIT_DECLARE(c_identifier_type);
struct literal_class;
using literal_type = x3::rule<literal_class, ast::literal>;
BOOST_SPIRIT_DECLARE(literal_type);
struct rules_class;
using rules_type = x3::rule<rules_class, ast::rules>;
BOOST_SPIRIT_DECLARE(rules_type);
struct rule_class;
using rule_type = x3::rule<rule_class, ast::rule>;
BOOST_SPIRIT_DECLARE(rule_type);
struct spec_class;
using spec_type = x3::rule<spec_class, ast::spec>;
BOOST_SPIRIT_DECLARE(spec_type);
struct inner_class;
using inner_rule_type = x3::rule<inner_class, ast::yacc_ast_type>;
BOOST_SPIRIT_DECLARE(inner_rule_type);
struct start_class;
using start_rule_type = x3::rule<start_class, ast::yacc_ast_type>;
BOOST_SPIRIT_DECLARE(start_rule_type);
}}
//}}}
//}}}
//{{{ rgw29_yacc_grammar.cpp
//{{{ Token
namespace minimal { namespace parser
{
x3::symbols<ast::yacc_token> lcomment;
x3::symbols<ast::yacc_token> rcomment;
x3::symbols<ast::yacc_token> semi;
x3::symbols<ast::yacc_token> colon;
x3::symbols<ast::yacc_token> mark;
}}
//}}}
//{{{ add_keywords
namespace minimal { namespace parser
{
void add_keywords()
{
static bool once = false;
if (once)
return;
once = true;
lcomment.add ("/*", ast::lcomment);
rcomment.add ("*/", ast::rcomment);
semi.add (";", ast::semi);
colon.add (":", ast::colon);
mark.add ("%%", ast::mark);
}
}}
//}}}
//{{{ rule id
namespace minimal { namespace parser
{
c_identifier_type const c_identifier = "c_identifier";
literal_type const literal = "literal";
comment_type const comment = "comment";
white_type const white = "white";
gap_type const gap = "gap";
defs_type const defs = "defs";
rules_type const rules = "rules";
spec_type const spec = "spec";
rule_type const rule = "rule";
inner_rule_type const inner_rule = "inner_rule";
start_rule_type const start_rule = "start_rule";
}}
//}}}
//{{{ rule definition (cut down for this minimal example)
namespace minimal { namespace parser
{
auto const comment_def = lcomment >> *(char_ - rcomment) >> rcomment; // /* ... */
auto const white_def = +space; // single space as string
auto const gap_def = +(white | comment); // spaces/comments
auto const c_identifier_def = (alpha | char_("_.")) >> *(alnum | char_("_.")); // with non-initial number
auto const literal_def = char_("'") >> -char_("\\") >> (print - '\'') >> char_("'"); // 'c' or '\n'
auto const spec_def = defs >> mark >> gap >> rules; // without tail
auto const defs_def = +(gap | +(char_ -lcomment -rcomment -mark)); // skip over all defs
auto const rules_def = +rule;
auto const rule_def = c_identifier > gap > colon > gap
> *(gap | literal | +(char_ -lcomment -rcomment -semi -literal)) // catch_all to next semi
> semi > gap;
auto const inner_rule_def = no_skip[spec];
auto const start_rule_def = inner_rule_def;
}}
//}}}
//{{{ tie rule to definition
namespace minimal { namespace parser
{
BOOST_SPIRIT_DEFINE(comment, white, gap, c_identifier, literal)
BOOST_SPIRIT_DEFINE(defs, rule, rules, spec);
BOOST_SPIRIT_DEFINE(inner_rule, start_rule);
}}
//}}}
//{{{ start_rule()
namespace minimal
{
parser::start_rule_type const& start_rule(){parser::add_keywords(); return parser::start_rule;}
}
//}}}
//{{{ parse_without_error_handler_and_positions(...)
namespace minimal { namespace parser
{
bool
parse_without_error_handler_and_positions
( iterator_type& iter
, iterator_type end
, ast::yacc_ast_type& ast
)
{
auto const parser_with_error_handler_and_positions = minimal::start_rule();
return phrase_parse(iter, end, parser_with_error_handler_and_positions, space, ast);
}
}}
//}}}
//}}}
//{{{ rgw29_yacc_trial.cpp
//{{{ yacc implementation
namespace minimal
{
//{{{ parse_yacc_string (not a lambda)
bool
parse_yacc_string(std::string const& source)
{
iterator_type iter (source.begin());
iterator_type const end (source.end());
minimal::ast::yacc_ast_type ast;
bool success = parser::parse_without_error_handler_and_positions(iter, end, ast);
if (success && iter==end)
{
std::cout << " parse succeeded" << std::endl;
}
else
{
std::cout << " parse failed (as expected)" << std::endl;
}
return success;
};
//}}}
}
//}}}
//}}}
//{{{ rgw29_yacc.cpp
//{{{ main
int
main(int argc, char* argv[], char* envp[])
{
using namespace std;
using namespace minimal;
parse_yacc_string(yacc_data);
parse_yacc_string(bad_data);
cout << "... successfully completed " << argv[0] << endl;
return boost::exit_success;
}
//}}}
//}}}
****** 编辑 2021-05-30 ******
我认为 X3 中存在与多个嵌套容器和变体相关的错误。但这是开发人员需要解决的问题。
我有一个更简单的例子:
// Purpose: Demonstrate problem with lower level detail (gap_item) where wrapped rule (gap) should do.
#define BOOST_SPIRIT_X3_DEBUG // Provide some meaningful reassuring output.
//#define WITHOUT_WHITE // All ok until white added.
#define WITH_GAP_ITEM_IN_VARIANT // Unnacceptable workaround
#include <string>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace x3 = boost::spirit::x3;
namespace minimal { namespace ast
{
struct white : std::string {using std::string::string;};
struct braced {char open; std::string text; char close;};
#ifdef WITHOUT_WHITE
struct gap_item : x3::variant<char,braced> {using base_type::operator=;};
#else // WITHOUT_WHITE
struct gap_item : x3::variant<white,braced> {using base_type::operator=;};
#endif // WITHOUT_WHITE
struct gap : std::vector<gap_item> {};
#ifndef WITH_GAP_ITEM_IN_VARIANT
struct start_item : x3::variant<gap,std::string> {using base_type::operator=;};
#else // WITH_GAP_ITEM_IN_VARIANT
struct start_item : x3::variant<gap,std::string,gap_item,char> {using base_type::base_type; using base_type::operator=;};
#endif // WITH_GAP_ITEM_IN_VARIANT
struct start : std::vector<start_item> {using std::vector<start_item>::vector;};
}}
BOOST_FUSION_ADAPT_STRUCT(minimal::ast::braced, open, text, close)
namespace minimal { namespace parser
{
using x3::char_;
using x3::space;
using x3::raw;
x3::rule<struct white, ast::white> const white = "white";
x3::rule<struct braced, ast::braced> const braced = "braced";
x3::rule<struct gap, ast::gap> const gap = "gap";
x3::rule<struct start, ast::start> const start = "start";
auto const white_def = raw[+space];
auto const braced_def = char_('{') >> *(char_ -'}') >> char_('}'); // { ... }
#ifdef WITHOUT_WHITE
auto const gap_def = +(space | braced); // spaces etc
#else // WITHOUT_WHITE
auto const gap_def = +(white | braced); // spaces etc
#endif // WITHOUT_WHITE
auto const start_def = +(gap | +(char_ -'{' -space)); // gap=container or string
BOOST_SPIRIT_DEFINE(white, gap, braced, start);
}}
int
main()
{
char const* iter = "? {;};", * const end = iter + std::strlen(iter);
minimal::ast::start ast;
return !parse(iter, end, minimal::parser::start, ast) || iter!=end;
}
//}}}
我开始时没有 white 用于集体空白,而不是单个 char。
为了便于测试/演示,我包含了两个定义。
#define WITHOUT_WHITE // All ok until white added.
//#define WITH_GAP_ITEM_IN_VARIANT // Unnacceptable workaround
没有white,一切都很好。
当我添加white 通过评论//#define WITHOUT_WHITE 时,我收到错误消息,基本上是告诉我将char 和gap_item 添加到我的start_item 变体中。好的,现在代码编译并运行。但错误是 gap 被忽略/绕过,并且与 ast 中的间隙相关的操作(例如打印)没有执行。
谁能建议一种不同的方式来构建 AST 或语法,以使 X3 完成它应该做的事情?
****** 编辑 2021-05-31 ******
我找到了两种解决方法,它们一致和可能可扩展到一个真实的 X3 完整应用程序(但我还没有将这些变通方法中的任何一个传播回 X3 应用程序——这将需要一些时间)。对于这个最小的示例,单独使用任何一种解决方法都可以,并且应用两者都可以。
- 用单个元素结构替换任何“无偿继承”的每个实例。这会导致生成单个元素元组,尽管这应该是受支持的,但可能存在潜伏错误的极端情况。
// Purpose: Working example using single element *** TUPLE *** consistently, instead of class inheritance
#define BOOST_SPIRIT_X3_DEBUG // Provide some meaningful reassuring output.
#include <string>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace x3 = boost::spirit::x3;
namespace ast {
struct white {std::string value;}; // *** TUPLE ***
struct braced {char open; std::string text; char close;};
struct gap_item : x3::variant<white,braced> {using base_type::operator=;};
struct gap {std::vector<gap_item> value;}; // *** TUPLE ***
struct start_item : x3::variant<gap,std::string> {using base_type::operator=;};
struct start {std::vector<start_item> value;}; // *** TUPLE ***
}
BOOST_FUSION_ADAPT_STRUCT(ast::white, value) // *** TUPLE ***
BOOST_FUSION_ADAPT_STRUCT(ast::gap, value) // *** TUPLE ***
BOOST_FUSION_ADAPT_STRUCT(ast::start, value) // *** TUPLE ***
BOOST_FUSION_ADAPT_STRUCT(ast::braced, open, text, close)
namespace parser {
using x3::char_; using x3::space; using x3::raw; using x3::graph;
x3::rule<struct white, ast::white> const white = "white";
x3::rule<struct braced, ast::braced> const braced = "braced";
x3::rule<struct gap, ast::gap> const gap = "gap";
x3::rule<struct start, ast::start> const start = "start";
static auto const white_def = raw[+space];
static auto const braced_def = char_('{') >> *~char_('}') >> char_('}'); // { ... }
static auto const gap_def = +(white | braced); // spaces etc
static auto const start_def = +(gap | +(graph -'{')); // gap or body
BOOST_SPIRIT_DEFINE(white, braced, gap, start);
}
int main()
{
char const* iter = "? {;};", * const end = iter + std::strlen(iter);
ast::start ast;
return !parse(iter, end, parser::start, ast) || iter!=end;
}
- 对于存在变体容器的每种情况,在该变体的语法中添加一个额外的规则。
// Purpose: Working example when an *** EXTRA RULE *** (start_item) is used to break synthesized nested containers
#define BOOST_SPIRIT_X3_DEBUG // Provide some meaningful reassuring output.
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace x3 = boost::spirit::x3;
namespace ast {
struct white : std::string {using std::string::string;};
struct braced {char open; std::string text; char close;};
struct gap_item : x3::variant<white,braced> {using base_type::operator=;};
struct gap : std::vector<gap_item> {};
struct start_item : x3::variant<gap,std::string> {using base_type::operator=;};
struct start : std::vector<start_item> {using std::vector<start_item>::vector;};
}
BOOST_FUSION_ADAPT_STRUCT(ast::braced, open, text, close)
namespace parser {
using x3::char_; using x3::space; using x3::raw; using x3::graph;
x3::rule<struct white, ast::white> const white = "white";
x3::rule<struct braced, ast::braced> const braced = "braced";
x3::rule<struct gap_item, ast::gap_item> const gap_item = "gap_item"; // *** EXTRA RULE ***
x3::rule<struct gap, ast::gap> const gap = "gap";
x3::rule<struct start_item, ast::start_item> const start_item = "start_item"; // *** EXTRA RULE ***
x3::rule<struct start, ast::start> const start = "start";
auto const white_def = raw[+space];
auto const braced_def = char_('{') >> *~char_('}') >> char_('}'); // { ... }
auto const gap_item_def = white | braced; // white etc // *** EXTRA RULE ***
auto const gap_def = +gap_item; // spaces etc
auto const start_item_def = gap | +(char_ - '{' - space); // gap or body // *** EXTRA RULE ***
auto const start_def = +start_item; // multiple gaps or bodies
BOOST_SPIRIT_DEFINE(white, gap, braced, start_item, start);
}
int main() {
char const* iter = "? {;};", * const end = iter + std::strlen(iter);
ast::start ast;
return !parse(iter, end, parser::start, ast) || iter!=end;
}
更改分别用// *** TUPLE *** 或// *** EXTRA RULE *** 表示。
我个人更喜欢对 AST 的更改(它只是定义类的一种不同方式)而不是对解析器的更改(这会增加额外的混乱并破坏 X3 的精神 [双关语!])。
请各位专家帮助我和其他人确定哪个更好。
【问题讨论】:
-
哇。我不会因为你没有在这个问题上付出足够的努力而责备你,但是......这不是最小的。它是独立的,但它可能与最小化相反。
标签: c++ string boost-spirit