Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Quickstart 3 - Counting Words Using a Parser

Boost , Spirit 2.5.2 , Spirit.Lex Tutorials

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Цель интеграцииSpirit.Lexкак часть библиотекиSpiritдолжна была добавить библиотеку, позволяющую слияние лексического анализа с процессом разбора, как определено грамматикойSpirit.Спиритпарсеры считывают свой вход из входной последовательности, доступной итераторам. Естественно, мы выбрали итераторы для использования в качестве интерфейса между лексером и парсером. Вторая цель интеграции лексера/парсера заключалась в обеспечении использования различных библиотек лексических анализаторов. Использование итераторов, по-видимому, было правильным выбором с этой точки зрения, главным образом потому, что они могут использоваться в качестве слоя абстракции, скрывающего особенности реализации используемой библиотеки лексеров. На рисункениже показано общее управление потоком, реализованное при разборе в сочетании с лексическим анализом.

Figure 7. The common flow control implemented while parsing combined with lexical analysis

The common flow control implemented while parsing combined with lexical analysis


Другая проблема, связанная с интеграцией лексического анализатора с парсером, заключалась в том, чтобы найти способ смешивания определенных токенов с синтаксисом определения грамматикиSpirit. Для токенов, определенных как экземпляры класса<token_def<>>, наиболее естественным способом интеграции было позволить напрямую использовать их в качестве парсерных компонентов. Семантически эти компоненты парсера успешно соответствуют их входу всякий раз, когда соответствующий тип токена был сопоставлен лексером. Этот пример быстрого начала продемонстрирует это (и многое другое), снова посчитав слова, просто сложив числа внутри семантических действий парсера (полный пример кода см. здесь:word_count.cpp).

Prerequisites

В этом примере используются два компонента библиотекиSpirit:Spirit.LexиSpirit.Qi, следовательно, мы должны<#include>соответствующие файлы заголовка. Опять же, нам нужно включить пару файлов заголовка из библиотекиBoost.Phoenix. В этом примере показано, как прикрепить функторы к парсерным компонентам, что можно сделать с использованием любого типа техники C++, что приводит к позывному объекту. ИспользованиеBoost.Phoenixдля этой задачи упрощает вещи и избегает добавления зависимостей в другие библиотекиBoost.Phoenixуже используется дляSpiritв любом случае.

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>
#include <boost/spirit/include/phoenix_container.hpp>

Чтобы сделать весь код ниже более читаемым, мы вводим следующие пространства имен.

using namespace boost::spirit;
using namespace boost::spirit::ascii;

Defining Tokens

Если сравнивать с двумя предыдущими примерами быстрого стартаLex Quickstart 1 - счетчик слов с использованиемSpirit.LexиLex Quickstart 2 - Лучший счетчик слов, использующийSpirit.Lexкласс определения токена для этого примера не показывает никаких сюрпризов. Тем не менее, он использует макросы определения токена лексера для упрощения состава регулярных выражений, которые будут более подробно описаны в разделеFIXME. Как правило, любое определение токена может быть использовано без модификации с лексического анализатора или в сочетании с парсером.

template <typename Lexer>
struct word_count_tokens : lex::lexer<Lexer>
{
    word_count_tokens()
    {
        // define patterns (lexer macros) to be used during token definition 
        // below
        this->self.add_pattern
            ("WORD", "[^ \t\n]+")
        ;
        // define tokens and associate them with the lexer
        word = "{WORD}";    // reference the pattern 'WORD' as defined above
        // this lexer will recognize 3 token types: words, newlines, and 
        // everything else
        this->self.add
            (word)          // no token id is needed here
            ('\n')          // characters are usable as tokens as well
            (".", IDANY)    // string literals will not be escaped by the library
        ;
    }
    // the token 'word' exposes the matched string as its parser attribute
    lex::token_def<std::string> word;
};

Using Token Definition Instances as Parsers

В то время как интеграция лексера и парсера в поток управления достигается с помощью специальных итераторов, обертывающих лексический анализатор, нам все еще нужно средство выражения в грамматике, какие токены соответствуют и где. Класс определения токена выше использует три различных способа определения токена:

  • Используя экземпляр<token_def<>>, который удобен, когда вам нужно указать атрибут токена (для получения дополнительной информации об атрибутах, связанных с лексером, пожалуйста, посмотрите здесь: атрибуты Лексера).
  • Используя один символ в качестве токена, в этом случае символ представляет собой токен, где идентификатор токена является значением символа ASCII.
  • Использование обычного выражения, представленного как строка, где идентификатор токена должен быть указан явно, чтобы сделать токен доступным с уровня грамматики.

Все три метода определения токенов требуют другого метода интеграции грамматики. Но, как вы можете видеть из следующего фрагмента кода, каждый из этих методов прост и естественным образом смешивает соответствующие экземпляры токенов с окружающими.Spirit.Qiсинтаксис грамматики.

Определение маркера

Парсерная интеграция

<token_def<>>

Пример<token_def<>>непосредственно используется в качестве парсерного компонента. Парсирование этого компонента будет успешным, если регулярное выражение, используемое для определения этого, будет успешно сопоставлено.

Одиночный персонаж

Единый символ непосредственно используется в грамматике. Однако при определенных обстоятельствах его необходимо обернуть парсерным компонентом<char_()>. Парсирование этого компонента будет успешным, если один персонаж будет сопоставлен.

явный токен id

Использование явного идентификатора токена вSpirit.QiГрамматика, которую вы должны обернуть специальным<token()>парсерным компонентом. Парсирование этого компонента будет успешным, если текущий токен имеет тот же идентификатор токена, который указан в выражении<token(<id>)>

.

В приведенном ниже определении грамматики используется каждый из трех типов, демонстрирующих их использование.

template <typename Iterator>
struct word_count_grammar : qi::grammar<Iterator>
{
    template <typename TokenDef>
    word_count_grammar(TokenDef const& tok)
      : word_count_grammar::base_type(start)
      , c(0), w(0), l(0)
    {
        using boost::phoenix::ref;
        using boost::phoenix::size;
        start =  *(   tok.word          [++ref(w), ref(c) += size(_1)]
                  |   lit('\n')         [++ref(c), ++ref(l)]
                  |   qi::token(IDANY)  [++ref(c)]
                  )
              ;
    }
    std::size_t c, w, l;
    qi::rule<Iterator> start;
};

Как уже было сказано (см.:Атрибуты),Spirit.Qiпарсерная библиотека построена на наборе полностью приписываемых парсерных компонентов. Следовательно, все определения токенов также поддерживают эту модель атрибутов. Наиболее естественным способом реализации этого было использование значений токенов в качестве атрибутов, раскрытых компонентом парсера, соответствующим определению токена (подробнее об этой теме можно прочитать здесь:О токенах и значениях токенов). В приведенном выше примере используется полная интеграция значений токена в качестве атрибутов парсера<token_def<>>: определение токена<word>объявляется как<token_def<std::string>>, что делает каждый экземпляр токена<word>нести представление строки согласованной входной последовательности в качестве ее значения. Семантическое действие, прилагаемое к<tok.word>, получает эту строку (представленную<_1>заполнителем) и использует ее для вычисления числа совпадающих символов:<ref(c)+= size(_1)>.

Pulling Everything Together

Основная функция должна реализовать немного больше логики, так как мы должны инициализировать и начать не только лексический анализ, но и процесс анализа. Три определения типов (93) упрощают создание лексического анализатора и грамматики. После прочтения содержимого данного файла в память он вызывает функцию<tokenize_and_parse()>для инициализации лексического анализа и разбора процессов.

int main(int argc, char* argv[])
{
1  typedef lex::lexertl::token<
        char const*, boost::mpl::vector<std::string>
    > token_type;
2  typedef lex::lexertl::lexer<token_type> lexer_type;
3  typedef word_count_tokens<lexer_type>::iterator_type iterator_type;
    // now we use the types defined above to create the lexer and grammar
    // object instances needed to invoke the parsing process
    word_count_tokens<lexer_type> word_count;          // Our lexer
    word_count_grammar<iterator_type> g (word_count);  // Our parser 
    // read in the file int memory
    std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1]));
    char const* first = str.c_str();
    char const* last = &first[str.size()];
4  bool r = lex::tokenize_and_parse(first, last, word_count, g);
    if (r) {
        std::cout << "lines: " << g.l << ", words: " << g.w
                  << ", characters: " << g.c << "\n";
    }
    else {
        std::string rest(first, last);
        std::cerr << "Parsing failed\n" << "stopped at: \""
                  << rest << "\"\n";
    }
    return 0;
}

1

Определите тип токена, который будет использоваться:<std::string>доступен как тип атрибута токена.

2

Определить тип лексера для использования реализации государственной машины

3

Определить тип итератора, подверженный типу лексера

4

Парсинг выполняется на основе потока токенов, а не потока символов, считываемого с ввода. Функция<tokenize_and_parse()>заворачивает пройденный диапазон итераторов<[first,last)>лексическим анализатором и использует его открытые итераторы для анализа потока токенов.


PrevUpHomeNext

Статья Quickstart 3 - Counting Words Using a Parser раздела Spirit 2.5.2 Spirit.Lex Tutorials может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Spirit.Lex Tutorials ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 18:52:40/0.0091040134429932/0