创建用于检索字符串向量的语法

时间:2015-11-28 18:28:44

标签: c++ parsing boost boost-spirit text-parsing

我有一个包含表单数据的文件:

fractal mand1 {
    ;lkkj;kj;
}

fractal mand2 {
    if (...) {
        blablah;
    }
}

fractal julia1 {
    a = ss;
}

我想提取数据容器的名称,因此我想检索包含特定案例mand1mand2julia1的数据。

我已阅读有关parsing a number list into a vector的示例,但我想将语法保存在单独的文件中。

我创建了一个表示语法的结构,然后我使用它来解析包含数据的字符串。我希望输出像

mand1
mand2
julia1

相反,我获得了

mand1 {
        ;lkkj;kj;
    }

    fractal mand2 {
        if (...) {
            blablah;
        }
    }

    fractal julia1 {
        a = ss;
    }

我的解析器识别第一个fractal术语,但然后它将文件的其余部分解析为单个字符串项,而不是根据需要解析它。

我做错了什么?

#include <boost/spirit/include/qi.hpp>
#include <string>
#include <vector>
#include <iostream>

using boost::spirit::ascii::space;
using boost::spirit::ascii::space_type;
using boost::spirit::qi::phrase_parse;
using boost::spirit::qi::lit;
using boost::spirit::qi::lexeme;
using boost::spirit::qi::skip;
using boost::spirit::ascii::char_;
using boost::spirit::ascii::no_case;
using boost::spirit::qi::rule;

typedef std::string::const_iterator sit;

template <typename Iterator>
struct FractalListParser : boost::spirit::qi::grammar<Iterator, std::vector<std::string>(), boost::spirit::ascii::space_type> {
    FractalListParser() : FractalListParser::base_type(start)   {

        no_quoted_string %= *(lexeme[+(char_ - '"')]);
        start %= *(no_case[lit("fractal")] >> no_quoted_string >> '{' >> *(skip[*(char_)]) >> '}');
    }

    rule<Iterator, std::string(), space_type> no_quoted_string;
    rule<Iterator, std::vector<std::string>(), space_type> start;
};

int main() {

    const std::string fractalListFile(R"(
    fractal mand1 {
        ;lkkj;kj;
    }

    fractal mand2 {
        if (...) {
            blablah;
        }
    }

    fractal julia1 {
        a = ss;
    }
    )");

    std::cout << "Read Test:" << std::endl;
    FractalListParser<sit> parser;
    std::vector<std::string> data;
    bool r = phrase_parse(fractalListFile.begin(), fractalListFile.end(), parser, space, data);
    for (auto& i : data) std::cout << i << std::endl;
    return 0;
}

1 个答案:

答案 0 :(得分:3)

如果您使用错误处理,您将发现解析失败,并且没有任何有效解析:

<强> Live On Coliru

输出:

Read Test:
Parse success:
----
mand1 {
    ;lkkj;kj;
}

fractal mand2 {
    if (...) {
        blablah;
    }
}

fractal julia1 {
    a = ss;
}

Remaining unparsed input: 'fractal mand1 {
    ;lkkj;kj;
}

fractal mand2 {
    if (...) {
        blablah;
    }
}

fractal julia1 {
    a = ss;
}
'

问题是什么?

  1. 你可能想忽略&#34; body&#34; ({}之间)。因此,我想您实际上想要omit属性:

         >> '{' >> *(omit[*(char_)]) >> '}'
    

    而不是skip(*char_)

  2. 表达式*char_是贪婪的,并且总是匹配到输入的结尾......你可能想限制字符集:

    • 在&#34;名称&#34; *~char_("\"{")以避免&#34;进食&#34;所有的身体也是如此。为避免匹配空格,请使用graph(例如+graph - '"')。如果你想解析&#34;标识符&#34;明确例如。

      alpha > *(alnum | char_('_'))
      
    • 身体*~char_('}')*(char_ - '}')中的
    • (后者效率较低)。

  3. 可选量词的嵌套效率不高:

    *(omit[*(char_)])
    

    最坏情况运行时间会非常慢(因为*char_可能为空,*(omit[*(char_)])也可能为空)。说出你的意思:

    omit[*char_]
    
  4. 获得词汇的最简单方法是从规则声明中删除船长(另请参阅Boost spirit skipper issues

  5. 程序逻辑:

    1. 由于您的示例包含嵌套块(例如mand2),因此您需要递归处理块,以避免调用外部块的第一个}

      block = '{' >> -block % (+~char_("{}")) >> '}';
      
    2. 松散的提示:

      1. 使用BOOST_SPIRIT_DEBUG找出拒绝/匹配解析的位置。例如。在重构规则之后:

        我们得到了输出(On Coliru):

        Read Test:
        <start>
        <try>fractal mand1 {\n    </try>
        <no_quoted_string>
            <try>mand1 {\n    ;lkkj;kj</try>
            <success> {\n    ;lkkj;kj;\n}\n\n</success>
            <attributes>[[m, a, n, d, 1]]</attributes>
        </no_quoted_string>
        <body>
            <try>{\n    ;lkkj;kj;\n}\n\nf</try>
            <fail/>
        </body>
        <success>fractal mand1 {\n    </success>
        <attributes>[[]]</attributes>
        </start>
        Parse success:
        Remaining unparsed input: 'fractal mand1 {
            ;lkkj;kj;
        }
        
        fractal mand2 {
            if (...) {
                blablah;
            }
        }
        
        fractal julia1 {
            a = ss;
        }
        '
        

        这个输出帮助我发现我实际上忘记了身体规则中的- '}'部分......:)

      2. 当该规则定义中没有涉及语义操作时,%=不需要fractal

      3. 您可能希望确保fractalset multi { .... }实际上是一个单独的词,因此您不匹配//#define BOOST_SPIRIT_DEBUG #include <boost/spirit/include/qi.hpp> #include <iostream> namespace qi = boost::spirit::qi; template <typename Iterator> struct FractalListParser : qi::grammar<Iterator, std::vector<std::string>(), qi::space_type> { FractalListParser() : FractalListParser::base_type(start) { using namespace qi; identifier = alpha > *(alnum | char_('_')); block = '{' >> -block % +~char_("{}") >> '}'; start = *( no_case["fractal"] >> identifier >> block ); BOOST_SPIRIT_DEBUG_NODES((start)(block)(identifier)) } qi::rule<Iterator, std::vector<std::string>(), qi::space_type> start; // lexemes (just drop the skipper) qi::rule<Iterator, std::string()> identifier; qi::rule<Iterator> block; // leaving out the attribute means implicit `omit[]` }; int main() { using It = boost::spirit::istream_iterator; It f(std::cin >> std::noskipws), l; std::cout << "Read Test:" << std::endl; FractalListParser<It> parser; std::vector<std::string> data; bool r = qi::phrase_parse(f, l, parser, qi::space, data); if (r) { std::cout << "Parse success:\n"; for (auto& i : data) std::cout << "----\n" << i << "\n"; } else { std::cout << "Parse failed\n"; } if (f != l) std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n"; }

      4. 演示程序

        有了这些,我们可以有一个工作演示:

        <强> docs

        Read Test:
        Parse success:
        ----
        mand1
        ----
        mand2
        ----
        julia1
        

        打印:

         success: function (data) {
                       var res = $('#meetTable tr th#eventNames');
        
                       for (var i=0;i < res.length; i++){
                           if (data.indexOf(res[i].innerText) >=0)
                           {
                               res[i].style.color = 'green'
                           }
        
                       }