Усовершенствуйте правило грамматики духа, чтобы извлекать только буквенно-цифровые токены

У меня есть лексема, как показано ниже, для слов, состоящих из букв и цифр.

атрибуты = лексема [+ (boost :: spirit :: qi :: alpha | boost :: spirit :: qi :: digit)];

Я хочу иметь правило грамматики, которое пропускает любые другие символы, которые не подходят для этого правила, и просто помещает их в вектор.

Например: ввод: STR1 + STR2% STR3 () STR4 = STR5 + STR6

           output: (STR1, STR2, STR3, STR4, STR6)

Я пробовал ниже грамматику, но она пропускает все после взятия первого слова в строке синтаксического анализа. Как я могу изменить его на синтаксический анализ, как я описал?

typedef std::vector<std::wstring> Attributes;
template <typename It, typename Skipper=boost::spirit::qi::space_type>
struct AttributeParser : boost::spirit::qi::grammar<It, Attributes(),  Skipper>
{
    AttributeParser() : AttributeParser::base_type(expression)
    {
        expression = 

            *( attributes [phx::push_back(qi::_val, qi::_1)])
            >> qi::omit[*qi:char_]
            ;

        attributes = qi::lexeme[+(boost::spirit::qi::alpha|qi::boost::spirit::qi::digit)];

        BOOST_SPIRIT_DEBUG_NODE(expression);
        BOOST_SPIRIT_DEBUG_NODE(attributes);
    }


private:
    boost::spirit::qi::rule<It, std::wstring() , Skipper> attributes;
    boost::spirit::qi::rule<It, Attributes() , Skipper> expression;

};

person kyy    schedule 28.05.2013    source источник


Ответы (3)


Я бы написал буквально то, что вы описываете:

    std::vector<std::wstring> parsed;
    bool ok = qi::phrase_parse(
            begin(input), end(input),
            *qi::lexeme [ +qi::alnum ],
            ~qi::alnum,
            parsed);

А именно:

  • синтаксический анализ (частичный) ввод
  • парсинг буквенно-цифровых лексем
  • пропуск чего-либо не буквенно-цифрового
  • поместите результат в вектор parsed

Вот полная программа

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

namespace qi = boost::spirit::qi;

int main()
{
    std::wstring input = L"STR1 + STR2 % STR3 () STR4 = STR5+ STR6";

    std::vector<std::wstring> parsed;
    bool ok = qi::phrase_parse(begin(input), end(input),
            *qi::lexeme [ +qi::alnum ],
            ~qi::alnum,
            parsed);

    for(auto& v : parsed)
        std::wcout << v << std::endl;
}

Это печатает

STR1
STR2
STR3
STR4
STR5
STR6
person sehe    schedule 29.05.2013

Здесь вы разбираете первую строку и вставляете ее в вектор:

*( attributes [phx::push_back(qi::_val, qi::_1)])

Далее вы опускаете все, что можно преобразовать в char:

>> qi::omit[*qi:char_]

Таким образом, вы в основном говорите своему синтаксическому анализатору пропустить оставшуюся часть строки, независимо от того, является ли символ буквенно-цифровым или нет. Если вы хотите, чтобы он работал, вам нужно изменить

qi::omit[*qi::char_] 

к чему-то вроде

qi::omit[*(qi::char_ - qi::alnum)]. 

и это должно опускать любые символы, кроме буквенно-цифровых, которые должны быть началом следующей строки, которую вы хотите сохранить. Я не могу сейчас попробовать код, но вы поняли.

person jay1189947    schedule 30.05.2013

Если вы не намерены использовать Spirit (например, это небольшая часть чего-то, что в противном случае часто использует Spirit), я бы сделал это с помощью пользовательского фасета ctype.

struct alpha_num: std::ctype<char> {
    alpha_num(): std::ctype<char>(get_table()) {}

    static std::ctype_base::mask const* get_table() {
        // As far as we care, everything is white-space:
        static std::vector<std::ctype_base::mask> 
            rc(std::ctype<char>::table_size,std::ctype_base::space);

        // except digits and letters:
        std::fill(&rc['0'], &rc['9'], std::ctype_base::digit);
        std::fill(&rc['a'], &rc['z'], std::ctype_base::alpha);
        std::fill(&rc['A'], &rc['Z'], std::ctype_base::alpha);
        return &rc[0];
    }
};

Оттуда довольно просто открыть файл, добавить в него языковой стандарт, используя этот фасет ctype, и прочитать токены. Вот вам быстрый тест:

int main() { 

    std::istringstream infile("STR1 + STR2 % STR3 () STR4 = STR5+ STR6");
    infile.imbue(std::locale(std::locale(), new alpha_num));

    // Initialize vector from file:
    std::vector<std::string> tokens((std::istream_iterator<std::string>(infile)),
                                     std::istream_iterator<std::string>());

    // show the tokens:
    for (auto const & s : tokens)
        std::cout << s << "\n";
    return 0;
}

Результат:

STR1
STR2
STR3
STR4
STR5
STR6
person Jerry Coffin    schedule 28.05.2013