簡體   English   中英

boost :: spirit:qi :: rule或包含qi :: rule作為解析結果的結構

[英]boost::spirit: qi::rule or struct containing a qi::rule as parsing result

我想做的是在運行時從ABNF語法文件創建解析器。 我已經在qi :: grammar中實現了所有ABNF規則,就像這兩個規則一樣:

typedef /*qi::rule or struct containing qi::rule*/ parserRule

    [...] //all other ABNF rules according to RFC 5234

    rule =
            (
                    rulename[qi::_a = qi::_1] >>
                    definedAs >>
                    elements[qi::_b = qi::_1] >>
                    cNl
            )[qi::_val = px::bind(&AbnfParserFactory::fromRule, &factory, qi::_a, qi::_b)];


    rulelist =
            +(
                    rule[px::push_back(qi::_a, qi::_1)] |
                    (*cWsp >> cNl)
             ) >>
             eps[qi::_val = px::bind(&AbnfParserFactory::fromRulelist, &factory, qi::_a)];


qi::rule<Iterator, std::map<std::string, parserRule>(), qi::locals<std::vector<parserRule> > >  rulelist;
qi::rule<Iterator, parserRule(), qi::locals<std::string>, qi::locals<parserRule> >              rule;
[...] // all other ABNF rules

ParserFactory內部,根據閱讀的語法創建一個新的qi :: rule:

std::map<std::string, ReturnType> fromRulelist(std::vector<ReturnType> &rules)
{
    // return a map with <rulename, rule>
};

parserRule fromRule(std::string &name, parserRule &rule)
{
    //name the rule an return it
    rule.name(name);
    return rule;
};

問題是關於parserRule的類型。

如果我使用qi :: rule作為類型(通常像我通常想要的那樣),我將ParserFactoryfromRule分配的每個規則名稱(例如fromRule )。 我猜這是由Spirit內部工作的方式引起的( =運算符總是創建一個新的未命名規則。而=用於分配px :: bind函數結果)

但是,如果我嘗試將qi :: rule打包到一個結構中以避免發生此問題,那么我將不再能夠使用Spirit調試編譯我的代碼。 這是我嘗試過的:

typedef qi::rule<std::string::const_iterator, std::string()> FactoryRuleType;
struct parserRule
{
    FactoryRuleType mRule;
};

BOOST_FUSION_ADAPT_STRUCT(
 parserRule,
(FactoryRuleType, mRule)
)

[...] //rule definitions like above

debug(rule);
debug(rulelist);
[...] //debug all other rules

這會給我帶來大量的編譯錯誤(很長的路要在這里發布)。 我已經搜尋了幾天來試圖解決這個問題,但是沒有任何運氣。 我希望我提到了足夠的細節。

任何幫助表示贊賞。

編譯輸出摘錄:

/usr/include/boost/proto/operators.hpp:295:9: note:   template argument deduction/substitution failed:
/usr/include/boost/proto/operators.hpp: In substitution of ‘template<class Left, class Right> const typename boost::proto::detail::enable_binary<boost::proto::domainns_::deduce_domain, boost::proto::detail::not_a_grammar, boost::mpl::or_<boost::proto::is_extension<Arg>, boost::proto::is_extension<Right> >, boost::proto::tagns_::tag::shift_left, Left&, Right&>::type boost::proto::exprns_::operator<<(Left&, Right&) [with Left = std::basic_ostream<char>; Right = const boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >]’:
/usr/include/boost/spirit/home/support/attributes.hpp:1226:17:   required from ‘static void boost::spirit::traits::print_attribute_debug<Out, T, Enable>::call_impl3(Out&, const T_&, mpl_::false_) [with T_ = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Out = std::basic_ostream<char>; T = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Enable = void; mpl_::false_ = mpl_::bool_<false>]’
/usr/include/boost/spirit/home/support/attributes.hpp:1242:67:   required from ‘static void boost::spirit::traits::print_attribute_debug<Out, T, Enable>::call_impl2(Out&, const T_&, mpl_::false_) [with T_ = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Out = std::basic_ostream<char>; T = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Enable = void; mpl_::false_ = mpl_::bool_<false>]’
/usr/include/boost/spirit/home/support/attributes.hpp:1277:52:   required from ‘static void boost::spirit::traits::print_attribute_debug<Out, T, Enable>::call_impl(Out&, const T_&, mpl_::true_) [with T_ = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Out = std::basic_ostream<char>; T = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Enable = void; mpl_::true_ = mpl_::bool_<true>]’
/usr/include/boost/spirit/home/support/attributes.hpp:1283:52:   required from ‘static void boost::spirit::traits::print_attribute_debug<Out, T, Enable>::call(Out&, const T&) [with Out = std::basic_ostream<char>; T = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Enable = void]’
/usr/include/boost/spirit/home/support/attributes.hpp:1303:53:   required from ‘void boost::spirit::traits::print_attribute(Out&, const T&) [with Out = std::basic_ostream<char>; T = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >]’
/usr/include/boost/spirit/home/support/attributes.hpp:1196:57:   [ skipping 34 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/boost/function/function_template.hpp:722:7:   required from ‘boost::function4<R, T1, T2, T3, T4>::function4(Functor, typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type) [with Functor = boost::spirit::qi::debug_handler<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >, boost::spirit::unused_type, boost::spirit::qi::simple_trace>; R = bool; T0 = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T1 = const __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T2 = boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >&; T3 = const boost::spirit::unused_type&; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type = int]’
/usr/include/boost/function/function_template.hpp:1069:16:   required from ‘boost::function<R(T0, T1, T2, T3)>::function(Functor, typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type) [with Functor = boost::spirit::qi::debug_handler<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >, boost::spirit::unused_type, boost::spirit::qi::simple_trace>; R = bool; T0 = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T1 = const __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T2 = boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >&; T3 = const boost::spirit::unused_type&; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type = int]’
/usr/include/boost/function/function_template.hpp:1124:5:   required from ‘typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, boost::function<R(T0, T1, T2, T3)>&>::type boost::function<R(T0, T1, T2, T3)>::operator=(Functor) [with Functor = boost::spirit::qi::debug_handler<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >, boost::spirit::unused_type, boost::spirit::qi::simple_trace>; R = bool; T0 = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T1 = const __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T2 = boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >&; T3 = const boost::spirit::unused_type&; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, boost::function<R(T0, T1, T2, T3)>&>::type = boost::function<bool(__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&, boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >&, const boost::spirit::unused_type&)>&]’
/usr/include/boost/spirit/home/qi/nonterminal/debug_handler.hpp:122:13:   required from ‘void boost::spirit::qi::debug(boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>&) [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >; T1 = FactoryReturnType(); T2 = boost::spirit::unused_type; T3 = boost::spirit::unused_type; T4 = boost::spirit::unused_type]’
../src/AbnfReader.hpp:350:14:   required from ‘AbnfRules<Iterator>::AbnfRules() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >]’
../src/AbnfReader.cpp:27:12:   required from here
/usr/include/boost/proto/operators.hpp:295:9: error: no type named ‘type’ in ‘struct boost::proto::detail::enable_binary<boost::proto::domainns_::deduce_domain, boost::proto::detail::not_a_grammar, boost::mpl::or_<boost::proto::is_extension<std::basic_ostream<char> >, boost::proto::is_extension<const boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> > >, mpl_::bool_<false>, mpl_::bool_<false>, mpl_::bool_<false> >, boost::proto::tagns_::tag::shift_left, std::basic_ostream<char>&, const boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >&>’
make: *** [src/AbnfReader.o] Error 1

嗯,這是尷尬。 在嘗試像sehe建議的那樣編寫SSCCE時,我發現我的方法實際上一直都在起作用。 我在解決這個問題上遇到了很多麻煩...:C

這是我正在嘗試做的一個工作示例。 盡管它可以工作,但是這種行為仍然有些奇怪,這就是我認為它不能以這種方式工作的原因。

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
namespace ascii = boost::spirit::ascii;

typedef qi::rule<std::string::const_iterator, std::string()> parserRule;


namespace std
{
std::ostream& operator<<(std::ostream& stream, const parserRule& val);
}
std::ostream& std::operator<<(std::ostream& stream, const parserRule& val)
{
    stream << "RULE( " << val.name();
    stream << " )";
    return stream;
}

class Factory
{
public:
    Factory(){}
    parserRule createParser(std::string str)
    {
        parserRule r;
        r = qi::char_(str);
        r.name("FactoryRule");
        std::cout << r.name() << " now parses >" + str + "<" << std::end;
        return r;
    }
};

template <typename Iterator>
struct TestGrammar : qi::grammar<Iterator, parserRule()>
{

    TestGrammar() : TestGrammar::base_type(start, "Test grammar")
    {
        start = stringRule.alias();
        stringRule =    (
                ascii::char_("'") >>
                *(ascii::char_ - ascii::char_("'"))[qi::_a += qi::_1] >>
                ascii::char_("'")
        )[qi::_val = px::bind(&Factory::createParser, &factory, qi::_a)];

        start.name("Start");
        stringRule.name("StringRule");

        qi::debug(start);       // shows "RULE( unnamed-rule )"
        qi::debug(stringRule);  // shows "RULE( unnamed-rule )"
    }

    qi::rule<Iterator, parserRule() > start;
    qi::rule<Iterator, parserRule(), qi::locals<std::string> > stringRule;
    Factory factory;
};

int main()
{
    typedef std::string::const_iterator iterator_type;
    typedef TestGrammar<iterator_type> TGrammar;


    TGrammar test_parser;

    std::string test = "parse THIS!";
    std::string input = "'"+test+"'";
    parserRule result;

    std::string::const_iterator iter = input.begin();
    std::string::const_iterator end = input.end();

    bool r = parse(iter, end, test_parser, result);

    if (r && iter == end)
    {
        std::cout << "-------------------------\n";
        std::cout << "1st Parsing succeeded\n";
        std::cout << "-------------------------\n";
    }
    else
    {
        std::string rest(iter, end);
        std::cout << "-------------------------\n";
        std::cout << "1st Parsing failed\n";
        std::cout << "stopped at: \"" << rest << "\"\n";
        std::cout << "-------------------------\n";
    }

    iterator_type first(test.begin()), last(test.end());

    qi::debug(result); //shows correct rule name
    r = qi::phrase_parse(first, last, result, boost::spirit::ascii::space);

    if (r && iter == end)
    {
        std::cout << "-------------------------\n";
        std::cout << "2nd Parsing succeeded\n";
        std::cout << "-------------------------\n";
    }
    else
    {
        std::string rest(first, last);
        std::cout << "-------------------------\n";
        std::cout << "2nd Parsing failed\n";
        std::cout << "stopped at: \"" << rest << "\"\n";
        std::cout << "-------------------------\n";
    }
}

這是輸出:

FactoryRule now parses >parse THIS!<
<Start>
  <try>'parse THIS!'</try>
  <StringRule>
    <try>'parse THIS!'</try>
    <success></success>
    <attributes>[[RULE( unnamed-rule )]]</attributes><locals>(parse THIS!)</locals>
  </StringRule>
  <success></success>
  <attributes>[[RULE( unnamed-rule )]]</attributes>
</Start>
-------------------------
1st Parsing succeeded
-------------------------
<FactoryRule>
  <try>parse THIS!</try>
  <success>arse THIS!</success>
  <attributes>[[p]]</attributes>
</FactoryRule>
-------------------------
2nd Parsing succeeded
-------------------------

如您所見,調試規則start並將stringRule始終顯示為“ RULE(unnamed-rule)”作為屬性。 這使我相信,由於分配px::bind結果時使用= -operator導致分配的規則名稱丟失。 由於調試result規則會顯示正確的名稱,因此我假設“ unnamed-rule”輸出僅未顯示正確的FINAL綜合屬性,而是當前的“空”屬性。

BME SHAME ON ME在看到“未命名規則”時立即感到恐慌,並且花了很多天試圖解決這個問題,甚至在這里要求通過調試result規則來實際測試整個result

仍然感謝您嘗試幫助我。 我希望這至少對其他人有用。

僅憑經驗,這里有兩個問題可以解決,而無需進一步的代碼:

  1. 單元素適應的結構帶來問​​題,請參閱:[鏈接即將來臨]

  2. 調試信息需要調試打印特性的特殊化(回溯到ostream流)。 顯然,對於qi::rule尚未這樣做。 因此,要么添加它。

    也許,也許您可​​以使包含的結構iostream-able能夠避免它(但是我擔心融合適應可能會優先考慮。值得一試)


問:是的,您是正確的。 我必須在運行時動態地編寫語法。 為了確保這是可能的,我已經寫了很多測試片段,它們按預期工作。

  • 直言不諱:精神不是實現這一目標的工具。 Spirit是一種工具,可以從作為表達式模板的“ praser表達式”生成靜態編譯的語法。 整個目的是在編譯時使用經過完全優化的靜態多態性。 我建議也許創建一組運行時多態解析器組件(您可以依次使用Spirit來實現),並用它們組成動態語法。

    某種可能的中間立場是將qi::lazy與一組預定義的不可變“原子”規則一起使用(這樣就不會即時生成),然后使用這些“原子”規則通過引用來構成解析器。

  • 基本上,只要您嘗試動態組成表達式模板 (而是准備好使用類型擦除的非終結符),就應該沒問題。

    如果您需要更多,您將很快進入UB-land。 boost::proto::deep_copy ,您可以使用boost::proto::deep_copy來爭取出路,但這樣做有局限性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM