我在link处玩了一些代码,我还有另外一个问题。我添加了语义动作:
action = actions_ >> '(' >> parameters >> ')'[ /* semantic action placed here */];
所以我可以在多个地方重复使用规则和验证。问题是然后精神停止将我的属性类型传播到上层规则(使用action
作为解析器)。我在link读到应该使用运算符%=
再次启用它(具有语义动作和属性传播)。但后来我收到编译错误,无法将boost::fuction::vector2<ast::actionid, ast::parameters>
转换为ast::action
。 fusion
中是否有任何宏可以在另一个方向上进行分配?或者我应该怎么做,规则仍然暴露相同的属性,因为它传递给语义动作而不是在那里有融合矢量?
示例代码:
#include "stdafx.h"
// boost
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi_iter_pos.hpp>
#include <boost/bind.hpp>
#include <boost/phoenix.hpp>
// std
#include <string>
#include <vector>
namespace bsqi = boost::spirit::qi;
namespace bsqi_coding = boost::spirit::standard_wide;
namespace bsqi_repos = boost::spirit::repository::qi;
//////////////////////////////////////////////////////////////////////////
enum class Action : uint8_t
{
eAction0 = 0,
eAction1,
eAction2
};
//////////////////////////////////////////////////////////////////////////
struct ActionSymbols : public boost::spirit::qi::symbols<wchar_t, Action>
{
ActionSymbols()
{
add
(L"action0", Action::eAction0)
(L"action1", Action::eAction1)
(L"action2", Action::eAction2)
;
}
} actionSymbols;
//////////////////////////////////////////////////////////////////////////
using ParameterValue = boost::variant<int, std::wstring>;
struct Parameter
{
std::wstring::const_iterator source; ///< position within the input where parameter begins
ParameterValue value; ///< type and value of the parameter
};
//////////////////////////////////////////////////////////////////////////
using Parameters = std::vector<Parameter>;
//////////////////////////////////////////////////////////////////////////
struct ActionParameters
{
Action action;
Parameters parameters;
};
//////////////////////////////////////////////////////////////////////////
BOOST_FUSION_ADAPT_STRUCT(Parameter, (std::wstring::const_iterator, source), (ParameterValue, value));
BOOST_FUSION_ADAPT_STRUCT(ActionParameters, (Action, action), (Parameters, parameters));
//////////////////////////////////////////////////////////////////////////
class SyntaxError : public std::runtime_error
{
public:
SyntaxError()
: std::runtime_error("Syntax error!")
{ }
};
//////////////////////////////////////////////////////////////////////////
template<typename IteratorT>
struct ScriptGrammar : bsqi::grammar<IteratorT, std::vector<ActionParameters>, bsqi_coding::space_type>
{
/// helper type to define all rules
template<typename T>
using RuleT = bsqi::rule<iterator_type, T, bsqi_coding::space_type>;
using result_type = std::vector<ActionParameters>;
explicit ScriptGrammar()
: base_type(start, "script")
{
// supported parameter types (int or quoted strings)
// note: iter_pos is used for saving the iterator for the parameter to enable generating more detailed error reports
parameter = bsqi_repos::iter_pos >> (bsqi::int_ | bsqi::lexeme[L'"' > *(bsqi_coding::char_ - L'"') > L'"']);
parameter.name("parameter");
// comma separator list of parameters (or no parameters)
parameters = -(parameter % L',');
parameters.name("parameters");
// action with parameters
action = (actionSymbols > L'(' > parameters > L')')[bsqi::_pass = boost::phoenix::bind(&ScriptGrammar::ValidateAction, this, bsqi::_1, bsqi::_2)];
action.name("action");
// action(..) [-> event(..) -> event(..) -> ..]
// eps = force to use this rule for parsing
// eoi = the rule must consume whole input
start = bsqi::eps > (action % L';') > L';' > bsqi::eoi;
}
private:
bool ValidateAction(Action action, const Parameters& parameters)
{
return true;
}
RuleT<Parameter> parameter;
RuleT<Parameters> parameters;
RuleT<ActionParameters> action;
RuleT<std::vector<ActionParameters>> start;
};
//////////////////////////////////////////////////////////////////////////
int _tmain(int argc, _TCHAR* argv[])
{
using ScriptParser = ScriptGrammar<std::wstring::const_iterator>;
ScriptParser parser;
auto input = std::wstring(L"\taction0(1, 2, 3); action1(\"str1\", \"str2\"); action2(\"strmix\", 0);\t\t");
auto it = input.begin();
ScriptParser::result_type output;
try
{
if(!phrase_parse(it, input.end(), parser, bsqi_coding::space, output))
throw SyntaxError();
}
catch(bsqi::expectation_failure<ScriptParser::iterator_type>& e)
{
std::cout << "Error! Expecting " << e.what_ << " here: \"" << std::string(e.first, e.last) << "\"";
}
catch(SyntaxError& e)
{
std::cout << e.what() << "\n";
}
return 0;
}
我尝试从start
规则获取属性。我在语义操作(ValidateAction
)中获得了正确解析的值,但start
规则中的属性仅接收未初始化的值(输出std::vector
的大小为3但值未初始化)。我试图用%=
而不是简单的=
替换规则的初始化,但随后会弹出提到的编译错误。
答案 0 :(得分:2)
BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
应该允许属性兼容性规则在语义操作中工作,就像它们在自动化属性传播期间一样。
然而,优越的解决方案是在您希望的时候指定您希望的转换。
最明显的方法是
将中间片包装成qi::rule<..., T()>
顺便说一下,我在上一期问题boost spirit reporting semantic error中已经解决了您的特定问题。
实际上,我想您希望有一个有状态的验证工具,您可以使用Attribute Traits将您的中间产品转换为所需的AST(例如,如果您不想实际存储你的AST中的迭代器
将子表达式包装在qi::transform_attribute<T>()[p]
directive中。
请注意某些Boost Spirit版本中的错误,要求您明确深度复制
transform_attribute
中的子表达式(使用qi::copy(p)
)