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

Quickstart 1 - A word counter using Spirit.Lex

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, но тем не менее может использоваться отдельно для создания отдельных лексических анализаторов. Первый пример быстрого запуска описывает отдельно стоящее приложение: подсчет символов, слов и строк в файле, очень похожий на то, что делает хорошо известная команда Unix<wc>(для полного примера кода см. здесь:word_count_functor.cpp)..

Prerequisites

Для этого требуется только<#include>Дух.Лексследует. Это обертка для всех необходимых определений, чтобы использоватьSpirit.Lexв одиночку, и поверх библиотекиLexertl. Для этого мы используем два из них:<boost::bind()>и<boost::ref()>.

#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>

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

namespace lex = boost::spirit::lex;

Defining Tokens

Наиболее важным шагом при создании лексера с использованиемSpirit.Lexявляется определение токенов, которые должны быть распознаны во входной последовательности. Обычно это делается путем определения регулярных выражений, описывающих соответствующие последовательности символов, и, необязательно, их соответствующих идентификаторов токенов. Кроме того, определенные токены должны быть связаны с экземпляром объекта лексера, как это предусмотрено библиотекой. Следующий фрагмент кода показывает, как это можно сделать с помощьюSpirit.Lex.

Шаблон<word_count_tokens>определяет три различных токена:<ID_WORD>,<ID_EOL>и<ID_CHAR>, представляя слово (все, кроме белого пространства или новой линии), символ новой линии и любой другой символ (<ID_WORD>,<ID_EOL>и<ID_CHAR>являются числовыми значениями, представляющими идентификаторы токена, но также могут быть и все остальное, конвертируемое в целое число). Прямой базовый класс любого класса определения токена должен быть шаблоном<lex::lexer<>>, где соответствующий параметр шаблона (здесь:<lex::lexertl::lexer<BaseIterator>>) определяет, какой базовый лексерный двигатель должен использоваться для обеспечения требуемой функциональности машины состояния. В этом примере мы используем лексерный двигатель на основе Lexertl в качестве основного типа лексера.

template <typename Lexer>
struct word_count_tokens : lex::lexer<Lexer>
{
    word_count_tokens()
    {
        // define tokens (the regular expression to match and the corresponding
        // token id) and add them to the lexer 
        this->self.add
            ("[^ \t\n]+", ID_WORD) // words (anything except ' ', '\t' or '\n')
            ("\n", ID_EOL)         // newline characters
            (".", ID_CHAR)         // anything else is a plain character
        ;
    }
};

Doing the Useful Work

Мы будем использовать настройку, где мы хотим, чтобы библиотекаSpirit.Lexвызывала заданную функцию после распознавания любого из генерируемых токенов. По этой причине нам нужно реализовать функтор, принимая по крайней мере сгенерированный токен в качестве аргумента и возвращая булево значение, позволяющее остановить процесс токенизации. Тип токена по умолчанию, используемый в этом примере, несет значение токена типа<boost::iterator_range><<BaseIterator>>, указывающее на соответствующий диапазон в базовой входной последовательности.

В этом примере структура «счетчик» используется в качестве функтора, подсчитывающего символы, слова и линии в анализируемой последовательности ввода, идентифицируя совпадающие токены, переданные из.Spirit.LexБиблиотека.

struct counter
{
    // the function operator gets called for each of the matched tokens
    // c, l, w are references to the counters used to keep track of the numbers
    template <typename Token>
    bool operator()(Token const& t, std::size_t& c, std::size_t& w, std::size_t& l) const
    {
        switch (t.id()) {
        case ID_WORD:       // matched a word
        // since we're using a default token type in this example, every 
        // token instance contains a `iterator_range<BaseIterator>` as its token
        // attribute pointing to the matched character sequence in the input 
            ++w; c += t.value().size();
            break;
        case ID_EOL:        // matched a newline character
            ++l; ++c;
            break;
        case ID_CHAR:       // matched something else
            ++c;
            break;
        }
        return true;        // always continue to tokenize
    }
};

Все, что осталось, это написать какой-то шаблонный код, помогающий связать воедино описанные фрагменты. Для упрощения этого примера мы называем функцию<lex::tokenize()>, реализованную вSpirit.Lex(более подробное описание этой функции см. здесь:FIXME), даже если бы мы могли написать цикл для итерации над итераторами лексера<first>,<last>).

Pulling Everything Together

Основная функция просто загружает данный файл в память (как<std::string>), инстанцирует экземпляр шаблона определения токена с использованием правильного типа итератора (<word_count_tokens<charconst*>>) и, наконец, вызывает<lex::tokenize>, пропуская экземпляр объекта функции счетчика. Возвратное значение<lex::tokenize()>будет<true>, если вся входная последовательность была успешно токенизирована, и<false>в противном случае.

int main(int argc, char* argv[])
{
    // these variables are used to count characters, words and lines
    std::size_t c = 0, w = 0, l = 0;
    // read input from the given file
    std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1]));
    // create the token definition instance needed to invoke the lexical analyzer
    word_count_tokens<lex::lexertl::lexer<> > word_count_functor;
    // tokenize the given string, the bound functor gets invoked for each of 
    // the matched tokens
    char const* first = str.c_str();
    char const* last = &first[str.size()];
    bool r = lex::tokenize(first, last, word_count_functor,
        boost::bind(counter(), _1, boost::ref(c), boost::ref(w), boost::ref(l)));
    // print results
    if (r) {
        std::cout << "lines: " << l << ", words: " << w
                  << ", characters: " << c << "\n";
    }
    else {
        std::string rest(first, last);
        std::cout << "Lexical analysis failed\n" << "stopped at: \""
                  << rest << "\"\n";
    }
    return 0;
}

Comparing Spirit.Lex with Flex

Этот пример был намеренно выбран, чтобы быть максимально похожим на эквивалентнуюFlexпрограмму (см. ниже), которая не слишком отличается от того, что должно быть написано при использованииSpirit.Lex.

[Note]Note

Интересно, что сравнения производительности лексических анализаторов, написанных с использованиемSpirit.Lexс эквивалентными программами, генерируемымиFlex, показывают, что оба имеют сопоставимые скорости исполнения! Как правило, благодаря высоко оптимизированной библиотекеLexertlи благодаря тщательно продуманной интеграции сSpiritштраф за абстракцию, который должен быть оплачен за использованиеSpirit.Lex, является незначительным.

Остальные примеры в этом руководстве будут использовать более сложные функцииSpirit.Lex, в основном, чтобы позволить дополнительное упрощение кода, который будет написан, сохраняя сходство с соответствующими функциямиFlex.Spirit.Lexбыл разработан, чтобы быть как можно более похожим наFlex. Именно поэтому эта документация предоставит соответствующийFlexкод для показанныхпримеров Spirit.Lexпрактически везде. Таким образом, здесь приведен кодFlex, соответствующий примеру, как показано выше.

%{
    #define ID_WORD 1000
    #define ID_EOL  1001
    #define ID_CHAR 1002
    int c = 0, w = 0, l = 0;
%}
%%
[^ \t\n]+  { return ID_WORD; }
\n         { return ID_EOL; }
.          { return ID_CHAR; }
%%
bool count(int tok)
{
    switch (tok) {
    case ID_WORD: ++w; c += yyleng; break;
    case ID_EOL:  ++l; ++c; break;
    case ID_CHAR: ++c; break;
    default:
        return false;
    }
    return true;
}
void main()
{
    int tok = EOF;
    do {
        tok = yylex();
        if (!count(tok))
            break;
    } while (EOF != tok);
    printf("%d %d %d\n", l, w, c);
}


PrevUpHomeNext

Статья Quickstart 1 - A word counter using Spirit.Lex раздела 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 19:08:15/0.0085508823394775/0