这个问题涉及两个问题(如accepted answer所示),这两个问题都出现在Boost Spirit X3的版本中,该版本随Boost 1.64一起提供,但这两个问题现已修复(或至少在编写时在编译时检测到)在编写时的开发分支(2017-04-30)。
我更新了mcve project以反映我使用开发分支而不是最新的升级版本所做的更改,希望它可以帮助其他遇到类似问题的人。
我正在努力学习如何将Spirit X3解析器分解为单独的可重用语法,正如example code(特别是rexpr_full和calc)以及来自CppCon 2015和{{3}的演示文稿所鼓励的那样}。
我有一个符号表(基本上将不同的类型映射到我支持的类型的枚举类),我想在几个解析器中重用它。我能找到的唯一符号表示例是罗马数字示例,它位于单个源文件中。
当我尝试以更结构化的示例的方式将符号表移动到它自己的cpp / h文件中时,如果我尝试解析不在符号表中的任何字符串,我的解析器将会出现段错误。如果符号表在与使用它的解析器相同的编译单元中定义,则会引发期望异常(这是我期望它做的)。
定义了BOOST_SPIRIT_X3_DEBUG后,我得到以下输出:
<FruitType>
<try>GrannySmith: Mammals</try>
<Identifier>
<try>GrannySmith: Mammals</try>
<success>: Mammals</success>
<attributes>[[
Process finished with exit code 11
我制作了一个小项目,展示了我想要实现的目标,可以在这里找到:BoostCon
我的问题:
fruit
解析器,但在我的完整项目中,我想在其他几个解析器中使用它。)以下是MCVE项目的代码:
的main.cpp
#include <iostream>
#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include "common.h"
#include "fruit.h"
namespace ast {
struct FruitType {
std::string identifier;
FRUIT fruit;
};
}
BOOST_FUSION_ADAPT_STRUCT(ast::FruitType, identifier, fruit);
namespace parser {
// Identifier
using identifier_type = x3::rule<class identifier, std::string>;
const auto identifier = identifier_type {"Identifier"};
const auto identifier_def = x3::raw[x3::lexeme[(x3::alpha | '_') >> *(x3::alnum | '_')]];
BOOST_SPIRIT_DEFINE(identifier);
// FruitType
struct fruit_type_class;
const auto fruit_type = x3::rule<fruit_type_class, ast::FruitType> {"FruitType"};
// Using the sequence operator creates a grammar which fails gracefully given invalid input.
// const auto fruit_type_def = identifier >> ':' >> make_fruit_grammar();
// Using the expectation operator causes EXC_BAD_ACCESS exception with invalid input.
// Instead, I would have expected an expectation failure exception.
// Indeed, an expectation failure exception is thrown when the fruit grammar is defined here in this compilation unit instead of in fruit.cpp.
const auto fruit_type_def = identifier > ':' > make_fruit_grammar();
BOOST_SPIRIT_DEFINE(fruit_type);
}
int main() {
std::string input = "GrannySmith: Mammals";
parser::iterator_type it = input.begin(), end = input.end();
const auto& grammar = parser::fruit_type;
auto result = ast::FruitType {};
bool successful_parse = boost::spirit::x3::phrase_parse(it, end, grammar, boost::spirit::x3::ascii::space, result);
if (successful_parse && it == end) {
std::cout << "Parsing succeeded!\n";
std::cout << result.identifier << " is a kind of " << to_string(result.fruit) << "!\n";
} else {
std::cout << "Parsing failed!\n";
}
return 0;
}
std::string to_string(FRUIT fruit) {
switch (fruit) {
case FRUIT::APPLES:
return "apple";
case FRUIT::ORANGES:
return "orange";
}
}
COMMON.H
#ifndef SPIRIT_FRUIT_COMMON_H
#define SPIRIT_FRUIT_COMMON_H
namespace x3 = boost::spirit::x3;
enum class FRUIT {
APPLES,
ORANGES
};
std::string to_string(FRUIT fruit);
namespace parser {
using iterator_type = std::string::const_iterator;
using context_type = x3::phrase_parse_context<x3::ascii::space_type>::type;
}
#endif //SPIRIT_FRUIT_COMMON_H
fruit.h
#ifndef SPIRIT_FRUIT_FRUIT_H
#define SPIRIT_FRUIT_FRUIT_H
#include <boost/spirit/home/x3.hpp>
#include "common.h"
namespace parser {
struct fruit_class;
using fruit_grammar = x3::rule<fruit_class, FRUIT>;
BOOST_SPIRIT_DECLARE(fruit_grammar)
fruit_grammar make_fruit_grammar();
}
#endif //SPIRIT_FRUIT_FRUIT_H
fruit.cpp
#include "fruit.h"
namespace parser {
struct fruit_symbol_table : x3::symbols<FRUIT> {
fruit_symbol_table() {
add
("Apples", FRUIT::APPLES)
("Oranges", FRUIT::ORANGES);
}
};
struct fruit_class;
const auto fruit = x3::rule<fruit_class, FRUIT> {"Fruit"};
const auto fruit_def = fruit_symbol_table {};
BOOST_SPIRIT_DEFINE(fruit);
BOOST_SPIRIT_INSTANTIATE(fruit_grammar, iterator_type, context_type);
fruit_grammar make_fruit_grammar() {
return fruit;
}
}
答案 0 :(得分:4)
在复制器上做得非常好。这让我想起了我的公关https://github.com/boostorg/spirit/pull/229(请参阅此处的分析Strange semantic behaviour of boost spirit x3 after splitting)。
问题是静态初始化命令Fiasco在规则初始化之前复制了规则的调试名称。
事实上,确实禁用调试信息会消除崩溃,并正确地引发期望失败。
开发分支¹也是如此,所以要么有另外类似的事情,要么我错过了一个地方。现在,知道你可以禁用调试输出。如果我找到了这个地点,我会发布更新。
我没有错过任何一个地方。 call_rule_definition
中有一个单独的问题,它使用实际的属性类型而不是已转换的属性类型参数化context_debug<>
辅助类:
#if defined(BOOST_SPIRIT_X3_DEBUG)
typedef typename make_attribute::type dbg_attribute_type;
context_debug<Iterator, dbg_attribute_type>
dbg(rule_name, first, last, dbg_attribute_type(attr_), ok_parse);
#endif
注释似乎表明此行为符合要求:它尝试在转换之前打印属性。但是,除非合成的属性类型与实际属性类型匹配,否则它完全不起作用。在这种情况下,它会使context_debug
对临时转换的属性进行悬空引用,从而导致Undefined Behaviour。
事实上,在工作案例中也存在未定义的行为。我只能假设在内联定义的情况下发生了很好的事情,使它看起来像预期的那样工作。
据我所知,这将是一个干净的解决方案,可以防止任何无根据的转换以及随之而来的临时转换:
#if defined(BOOST_SPIRIT_X3_DEBUG)
context_debug<Iterator, transform_attr>
dbg(rule_name, first, last, attr_, ok_parse);
#endif
我已为此创建了拉取请求:https://github.com/boostorg/spirit/pull/232
¹develop branch似乎未合并到1.64版本
中