Using semantic action along with spreading attributes in the spirit
I have been playing around a bit with the code on the link and I have another question. I added a semantic action:
action = actions_ >> '(' >> parameters >> ')'[ /* semantic action placed here */];
so I can reuse the rule along with validation in multiple places. The problem is then spirit stops propagating my attribute type to the top rules (which it uses action
as a parser). I read in the link that the operator %=
must be used to re-enable it (have semantic actions and attribute propagation). But then I get a compiler error that cannot be converted boost::fuction::vector2<ast::actionid, ast::parameters>
to ast::action
. Is there a macro infusion
to enable assignment in the other direction? Or what should I do that the rule still exposes the same attribute that is passed to the semantic action, and not where the merge file is?
Sample code:
#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;
}
I am trying to get an attribute from a rule start
. I am evaluating the values ββcorrectly in semantic action ( ValidateAction
), but the attribute from the rule start
only gets uninitialized values ββ(output size std::vector
is 3, but the values ββare not initialized). I tried to replace the rule initialization %=
instead of the simple one =
, but then the mentioned compilation error appears.
There is BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
one that should allow attribute compatibility rules to work within semantic actions as they work during automation attribute propagation.
An excellent solution, however, is to specify the conversions you want whenever you want.
The most obvious approaches are
-
wrap the intermediate link in
qi::rule<..., T()>
By the way, I have already solved your specific problem in this way, raise the level of semantic error expressing spirit in your previous question.
In fact, I suppose you would like to have a workable validator on the fly, and you can use Attribute Properties to convert your intermediates to the AST you want (for example, if you don't want to store iterators in your AST)
-
complete the subexpression in the directive
qi::transform_attribute<T>()[p]
.Beware of bugs in some versions of Boost Spirit that require explicitly deep copying of the subexpression in
transform_attribute
(useqi::copy(p)
)