bishopnator bishopnator - 13 days ago 5
C++ Question

Using semantic action together with attribute propagation in spirit

I played a little with code at link and I have another question. I added semantic action to:

action = actions_ >> '(' >> parameters >> ')'[ /* semantic action placed here */];

so I can reuse the rule together with verification at multiple places. The problem is that then the spirit stops propagate my attribute type to the upper rules (which uses the
action
as parser). I read at link that operator
%=
should be used to enable it again (to have semantic actions and attribute propagation). But then I am getting compiler error that it is not possible to convert
boost::fuction::vector2<ast::actionid, ast::parameters>
to
ast::action
. Is there any macro in
fusion
to enable assignment in another direction? Or what should I do that the rule still exposes the same attribute as it is passed to the semantic action instead of having fusion vector there?

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 try to get an attribute from
start
rule. I get correctly parsed values in my semantic action (
ValidateAction
) but the attribute from
start
rule receive only uninitialized values (size of the output
std::vector
is 3 but values are uninitialized). I tried to replace the initialization of the rules with
%=
instead of simple
=
but then the mentioned compilation error pops.

Answer

There is BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT which is supposed to allow attribute compatibility rules to work inside semantic actions like they work during automation attribute propagation.

However, the superior solution is to specify the conversions you wish, when you wish them.

The most obvious approaches are

  • wrap the intermediate into a qi::rule<..., T()>

    Incidentally, I already solved your particular issue that way here boost spirit reporting semantic error in your previous question.

    Actually, I suppose you would like to have a stateful validator working on the fly, and you can use Attribute Traits to transform your intermediates to the desired AST (e.g. if you don't want to actually store the iterators in your AST)

  • wrap the sub-expression in a qi::transform_attribute<T>()[p] directive.

    Beware of a bug in some versions of Boost Spirit that requires you to explicitly deep-copy the subexpression in transform_attribute (use qi::copy(p))

Comments