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

The Static Lexer Model

Boost , Spirit 2.5.2 , Abstracts

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и языком хоста C++. Его большим недостатком является необходимость тратить дополнительное время выполнения для создания таблиц, что особенно может быть ограничением для более крупных лексических анализаторов. Модельстатическаястремится опираться на плавную интеграцию сSpiritи C++ и повторно использует большие частиSpirit.библиотека, как описано до сих пор, при преодолении дополнительных требований времени выполнения с использованием предварительно созданных таблиц и процедур токенизатора. Чтобы сделать генерацию кода максимально простой, статическая модель повторно использует типы определения токенов, разработанные длядинамическоймодели без каких-либо изменений. Как будет показано в этом разделе, создание генератора кода на основе существующего типа определения токена является вопросом написания 3 строк кода.

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

  1. генерирование кода C++ для статического анализатора (включая функцию токенизации и соответствующие таблицы);
  2. изменение динамического лексического анализатора для использования сгенерированного кода.

Оба этапа более подробно описаны в двух разделах ниже (для полного исходного кода, используемого в этом примере, см. код здесь:общее определение токена,генератор кода,сгенерированный кодистатический лексический анализатор).

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

enum tokenids
{
    IDANY = boost::spirit::lex::min_token_id + 1,
};

Важно отметить, что класс определения токена не отличается от аналогичного класса, который используется для динамического лексического анализатора. Библиотека была разработана таким образом, что все компоненты (динамический лексический анализатор, генератор кода и статический лексический анализатор) могут повторно использовать синтаксис определения токенов.

// This token definition class can be used without any change for all three
// possible use cases: a dynamic lexical analyzer, a code generator, and a
// static lexical analyzer.
template <typename BaseLexer>
struct word_count_tokens : boost::spirit::lex::lexer<BaseLexer>
{
    word_count_tokens()
      : word_count_tokens::base_type(
          boost::spirit::lex::match_flags::match_not_dot_newline)
    {
        // define tokens and associate them with the lexer
        word = "[^ \t\n]+";
        this->self = word | '\n' | boost::spirit::lex::token_def<>(".", IDANY);
    }
    boost::spirit::lex::token_def<std::string> word;
};

Единственное, что меняется между тремя различными вариантами использования, - это параметр шаблона, используемый для определения конкретного токена. Для динамической модели и генератора кода вы, вероятно, будете использовать шаблон<lex::lexertl::lexer<>>, где для статической модели вы будете использовать тип<lex::lexertl::static_lexer<>>в качестве параметра шаблона.

Этот пример не только показывает, как построить статический лексер, но также показывает, как такой лексер можно использовать для разбора в сочетании с.Дух.Qiграмматика. Для полноты мы приводим простую грамматику, используемую в этом примере. Как вы можете видеть, эта грамматика не имеет никаких зависимостей от статического лексического анализатора, и по этой причине она не отличается от грамматики, используемой либо без лексера, либо с использованием динамического лексического анализатора, как описано ранее.

//  This is an ordinary grammar definition following the rules defined by 
//  Spirit.Qi. There is nothing specific about it, except it gets the token
//  definition class instance passed to the constructor to allow accessing the
//  embedded token_def<> instances.
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;
        //  associate the defined tokens with the lexer, at the same time 
        //  defining the actions to be executed 
        start =  *(   tok.word          [ ++ref(w), ref(c) += size(_1) ]
                  |   lit('\n')         [ ++ref(l), ++ref(c) ]
                  |   qi::token(IDANY)  [ ++ref(c) ]
                  )
              ;
    }
    std::size_t c, w, l;      // counter for characters, words, and lines
    qi::rule<Iterator> start;
};

Generating the Static Analyzer

Первым дополнительным шагом для создания статического лексического анализатора является создание небольшой отдельной программы для создания таблиц лексера и соответствующей функции токенизации. Для этого библиотекаSpirit.Lexвыставляет специальный API — функцию<generate_static_dfa()>. Он реализует весь генератор кода, никакого дополнительного кода не требуется. Все, что требуется для вызова этой функции, - это предоставить экземпляр определения токена, выходной поток для использования для генерации кода и необязательную строку для использования в качестве суффикса для имени генерируемой функции. Всего несколько строк кода.

int main(int argc, char* argv[])
{
    // create the lexer object instance needed to invoke the generator
    word_count_tokens<lex::lexertl::lexer<> > word_count; // the token definition
    // open the output file, where the generated tokenizer function will be 
    // written to
    std::ofstream out(argc < 2 ? "word_count_static.hpp" : argv[1]);
    // invoke the generator, passing the token definition, the output stream 
    // and the name suffix of the tables and functions to be generated
    //
    // The suffix "wc" used below results in a type lexertl::static_::lexer_wc
    // to be generated, which needs to be passed as a template parameter to the 
    // lexertl::static_lexer template (see word_count_static.cpp).
    return lex::lexertl::generate_static_dfa(word_count, out, "wc") ? 0 : -1;
}

Генератор показанного кода будет генерировать выход, который должен храниться в файле для последующего включения в статический лексический анализатор, как показано в следующей теме (полный сгенерированный код можно посмотреть здесь).

[Note]Note

Сгенерированный код будет компилироваться в номере версии текущей библиотекиSpirit.Lex. Этот номер версии используется во время компиляции вашего объекта статического лексера, чтобы убедиться, что он составлен с использованием точно такой же версии.Spirit.Lexбиблиотека как лексер таблицы были созданы с. Если они не совпадают, вы увидите ошибку компиляции, в которой упоминается<incompatible_static_lexer_version>.

Modifying the Dynamic Analyzer

Второй необходимый шаг для преобразования существующего динамического лексера в статический — это изменение основной программы в двух местах. Во-первых, вам нужно изменить тип используемого лексера (это параметр шаблона, используемый при создании класса определения токена). Хотя в динамической модели мы использовали шаблон<lex::lexertl::lexer<>>, теперь нам нужно изменить его на тип<lex::lexertl::static_lexer<>>. Второе изменение тесно связано с первым и предполагает исправление соответствующего<#include>утверждения:

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

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

int main(int argc, char* argv[])
{
    // Define the token type to be used: 'std::string' is available as the type 
    // of the token value.
    typedef lex::lexertl::token<
        char const*, boost::mpl::vector<std::string>
    > token_type;
    // Define the lexer type to be used as the base class for our token 
    // definition.
    //
    // This is the only place where the code is different from an equivalent
    // dynamic lexical analyzer. We use the `lexertl::static_lexer<>` instead of
    // the `lexertl::lexer<>` as the base class for our token defintion type.
    //
    // As we specified the suffix "wc" while generating the static tables we 
    // need to pass the type lexertl::static_::lexer_wc as the second template
    // parameter below (see word_count_generate.cpp).
    typedef lex::lexertl::static_lexer<
        token_type, lex::lexertl::static_::lexer_wc
    > lexer_type;
    // Define the iterator type exposed by the lexer.
    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 into 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()];
    // Parsing is done based on the token stream, not the character stream.
    bool r = lex::tokenize_and_parse(first, last, word_count, g);
    if (r) {    // success
        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;
}

[Important]Important

Созданный код для статического лексера содержит идентификаторы токенов, как они были назначены, либо явно программистом, либо неявно во время построения лексера. Вы несете ответственность за то, чтобы все экземпляры определенного типа статического лексера использовали одинаковые идентификаторы токенов. Конструктор объекта лексера имеет второй (по умолчанию) параметр, позволяющий ему обозначить идентификатор стартового токена, который будет использоваться при назначении идентификаторов определениям токена. Вышеприведенное требование выполняется по умолчанию до тех пор, пока во время построения статических лексеров не указано<first_id>.


PrevUpHomeNext

Статья The Static Lexer Model раздела Spirit 2.5.2 Abstracts может быть полезна для разработчиков на c++ и boost.




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



:: Главная :: Abstracts ::


реклама


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

Время компиляции файла: 2024-08-30 11:47:00
2025-05-20 01:24:16/0.0059440135955811/0