在精神上使用语义动作和属性传播

Using semantic action together with attribute propagation in spirit

我在 玩了一点代码,我还有一个问题。我将语义动作添加到: action = actions_ >> '(' >> parameters >> ')'[ /* semantic action placed here */]; 所以我可以在多个地方重用规则和验证。问题是精神停止将我的属性类型传播到上层规则(使用 action 作为解析器)。我在 link 读到应该使用运算符 %= 再次启用它(以具有语义操作和属性传播)。但是后来我收到编译器错误,无法将 boost::fuction::vector2<ast::actionid, ast::parameters> 转换为 ast::actionfusion中是否有任何宏可以启用另一个方向的赋值?或者我应该怎么做才能使规则仍然公开与传递给语义操作相同的属性,而不是在那里使用融合向量?

示例代码:

#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,但值未初始化)。我试图用 %= 而不是简单的 = 替换规则的初始化,但随后出现了提到的编译错误。

BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT 应该允许属性兼容性规则在语义操作中起作用,就像它们在自动化属性传播期间起作用一样。

但是,更好的解决方案是在需要时指定所需的转换。

最明显的方法是

  • 把中间的包裹成qi::rule<..., T()>

    Incidentally, I already solved your particular issue that way here 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)

  • 将子表达式包装在 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))