Boost.Spirit X3编译时间会随着递归规则而爆炸

时间:2017-10-26 09:35:11

标签: c++ boost-spirit boost-spirit-x3

以下程序需要10秒才能编译。当我将下面的parenProcess规则更改为'(' >> process >> ')'时,编译器会花费CPU,但似乎没有完成。 (我尝试制作一个较小的可重现程序 - 删除processparenProcess之间的规则,但编译时间不再爆炸。

如何在嵌入process时修复编译(时间)?

(其他一些小问题:有没有更好的方式来制定规则&x 39; x' xActual'?)

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include <iostream>
#include <string>
#include <vector>

namespace wccs_parser {

namespace x3 = boost::spirit::x3;
namespace ascii = x3::ascii;

using x3::long_;
using x3::ulong_;
using x3::lexeme;

//--- Ast structures

struct AstChannel {
    std::string label;
    bool complement;
};

struct AstAction {
    AstChannel channel;
    uint32_t weight;
};

struct AstRenaming {
    std::string from;
    std::string to;
};

struct AstNullProcess;
struct AstActionPrefixProcess;
struct AstChoiceProcess;
struct AstCompositionProcess;
struct AstRestrictionProcess;
struct AstRenamingProcess;
struct AstConstantProcess;

using AstAnyProcess = x3::variant<
    x3::forward_ast<AstNullProcess>,
    x3::forward_ast<AstActionPrefixProcess>,
    x3::forward_ast<AstChoiceProcess>,
    x3::forward_ast<AstCompositionProcess>,
    x3::forward_ast<AstRestrictionProcess>,
    x3::forward_ast<AstRenamingProcess>,
    x3::forward_ast<AstConstantProcess>
>;

struct AstNullProcess {};
struct AstActionPrefixProcess {
    AstAction action;
    AstAnyProcess subProcess;
};
struct AstChoiceProcess {
    std::vector<AstAnyProcess> subProcesses;
};
struct AstCompositionProcess {
    std::vector<AstAnyProcess> subProcesses;
};
struct AstRestrictionProcess {
    AstAnyProcess subProcess;
    std::vector<std::string> labels;
};
struct AstRenamingProcess {
    AstAnyProcess subProcess;
    std::vector<AstRenaming> renamings;
};
struct AstConstantProcess {
    std::string processName;
};

} // End namespace

BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstChannel, label, complement)
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstAction, channel, weight)
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstRenaming, from, to)
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstActionPrefixProcess, action, subProcess)
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstChoiceProcess, subProcesses)
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstCompositionProcess, subProcesses)
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstRestrictionProcess, subProcess, labels)
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstRenamingProcess, subProcess, renamings)
BOOST_FUSION_ADAPT_STRUCT(wccs_parser::AstConstantProcess, processName)

namespace wccs_parser {

//--- Rules

auto const constantName = x3::rule<struct constantRule, std::string> {"constantName"} =
    x3::lexeme[ascii::upper >> *(ascii::alnum)];

auto const label = x3::rule<struct labelRule, std::string> {"label"} =
    x3::lexeme[ascii::lower >> *(ascii::alnum)];

auto const channel = x3::rule<struct channelRule, AstChannel> {"channel"} = 
    label >> x3::matches['!'];

auto const action = x3::rule<struct actionRule, AstAction> {"action"} = 
    '<' >> channel >> ',' >> ulong_ >> '>'; 

auto renamingPair = x3::rule<struct renamingPairRule, AstRenaming> {"renamingPair"} =
    label > "=>" > label;

x3::rule<struct processRule, AstAnyProcess> process{"process"};

auto const nullProcess = x3::rule<struct nullProcessRule, AstNullProcess> {"nullProcess"} = '0' >> x3::attr(AstNullProcess());
auto const constant = x3::rule<struct constantRule, AstConstantProcess> {"constant"} = constantName;
///   HERE:
auto const parenProcess = '(' > nullProcess > ')';

auto const primitive = x3::rule<struct primitiveRule, AstAnyProcess> {"primitive"} =
      parenProcess
    | nullProcess
    | constant; 

auto const restrictionActual = x3::rule<struct restrictionActual, AstRestrictionProcess> {"restrictionActual"} =
    primitive >> '\\' >> '{' >> label % ',' >> '}';
auto const restriction = x3::rule<struct restrictionRule, AstAnyProcess> {"restriction"} =
      primitive >> !x3::lit('\\')
    | restrictionActual;

auto const renamingActual = x3::rule<struct renamingActualRule, AstRenamingProcess> {"renamingActual"} =
    restriction >> '[' >> renamingPair % ',' >> ']';
auto const renaming = x3::rule<struct renamingRule, AstAnyProcess> {"renaming"} =
      restriction >> !x3::lit('[')
    | renamingActual;

x3::rule<struct actionPrefixingRule, AstAnyProcess> actionPrefix{"actionPrefix"};
auto const actionPrefixActual = x3::rule<struct actionPrefixActualRule, AstActionPrefixProcess> {"actionPrefixActual"} =
    action > ('.' > actionPrefix);
auto const actionPrefix_def =
      actionPrefixActual
    | renaming;
BOOST_SPIRIT_DEFINE(actionPrefix)

auto const compositionActual = x3::rule<struct choiceActualrule, AstCompositionProcess> {"compositionActual"} =
    actionPrefix % '|'; 
auto const composition = x3::rule<struct compositionRule, AstAnyProcess> {"composition"} =
      actionPrefix >> !x3::lit('|')
    | compositionActual;

auto const choiceActual = x3::rule<struct choiceActualrule, AstChoiceProcess> {"choiceActual"} =
    composition % '+'; 
auto const choice = x3::rule<struct choiceRule, AstAnyProcess> {"choice"} =
      composition >> !x3::lit('+')
    | choiceActual;

auto const process_def = choice;
BOOST_SPIRIT_DEFINE(process)

auto const entry = x3::skip(ascii::space) [process];

} //End namespace

int main() {
    std::string str("0 + (0)");
    wccs_parser::AstAnyProcess root;
    auto iter = str.begin();
    auto end = str.end();
    bool r = parse(iter, end, wccs_parser::entry, root);

    if (r) {
        std::cout << str << std::endl << std::endl << " Parses OK: " << std::endl;
    }
    else {
        std::cout << "Parsing failed\n";
    }

    if (iter != end) std::cout << "Partial match" << std::endl;
    return 0;
}

1 个答案:

答案 0 :(得分:1)

这是一个已知问题。邮件列表上的CppEvans(?)声称在分支机构上有一个解决方法,但是这个分支远远落后,而且这些变化非常具有侵入性,所以我无法审核它/担保它。

因此,正确的追索权是在邮件列表上发布,以便让主要开发人员参与其中,并提高对此停止问题的认识。

无论如何,在不改变代码行为的情况下,您可以使用简写:

template <typename T> auto rule = [](const char* name = typeid(T).name()) {
    struct _{};
    return x3::rule<_, T> {name};
};

template <typename T> auto as = [](auto p) { return rule<T>() = p; };

这样可以更方便地编写重复的Ast强制:

auto constantName = as<std::string>(x3::lexeme[ascii::upper >> *(ascii::alnum)]);
auto label        = as<std::string>(x3::lexeme[ascii::lower >> *(ascii::alnum)]);

auto channel      = as<AstChannel>(label >> x3::matches['!']);

auto action       = as<AstAction>('<' >> channel >> ',' >> x3::ulong_ >> '>');

auto renamingPair = as<AstRenaming>(label > "=>" > label);

auto nullProcess  = as<AstNullProcess>(x3::omit['0']);
auto constant     = as<AstConstantProcess>(constantName);

auto parenProcess = '(' > nullProcess > ')';

auto primitive = rule<AstAnyProcess> ("primitive")
    = parenProcess
    | nullProcess
    | constant; 

auto restrictionActual = as<AstRestrictionProcess>(primitive >> '\\' >> '{' >> label % ',' >> '}');
auto restriction = rule<AstAnyProcess> ("restriction")
    = primitive >> !x3::lit('\\')
    | restrictionActual
    ;

auto renamingActual = as<AstRenamingProcess>(restriction >> '[' >> renamingPair % ',' >> ']');
auto renaming = rule<AstAnyProcess> ("renaming")
    = restriction >> !x3::lit('[')
    | renamingActual
    ;

auto actionPrefixActual = as<AstActionPrefixProcess>(action > ('.' > actionPrefix));
auto actionPrefix_def = actionPrefixActual | renaming;

auto compositionActual = as<AstCompositionProcess>(actionPrefix % '|'); 
auto composition = rule<AstAnyProcess> ("composition")
    = actionPrefix >> !x3::lit('|')
    | compositionActual
    ;

auto choiceActual = as<AstChoiceProcess>(composition % '+'); 
auto choice = rule<AstAnyProcess> ("choice")
    = composition >> !x3::lit('+')
    | choiceActual
    ;

auto process_def = choice;
BOOST_SPIRIT_DEFINE(actionPrefix, process)

auto const entry = x3::skip(ascii::space) [process];

程序仍以相同的输出运行。