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

时间:2016-11-21 15:11:02

标签: c++ boost-spirit-qi

我在link处玩了一些代码,我还有另外一个问题。我添加了语义动作: 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但值未初始化)。我试图用%=而不是简单的=替换规则的初始化,但随后会弹出提到的编译错误。

1 个答案:

答案 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)