ДокументацияSpirit.Lexдо сих пор главным образом было описание особенностейдинамической модели, где таблицы, необходимые для лексического анализа, генерируются из регулярных выражений во время выполнения. Большим преимуществом динамической модели является ее гибкость и интеграция с библиотекойSpiritи языком хоста C++. Его большим недостатком является необходимость тратить дополнительное время выполнения для создания таблиц, что особенно может быть ограничением для более крупных лексических анализаторов. Модельстатическаястремится опираться на плавную интеграцию сSpiritи C++ и повторно использует большие частиSpirit.библиотека, как описано до сих пор, при преодолении дополнительных требований времени выполнения с использованием предварительно созданных таблиц и процедур токенизатора. Чтобы сделать генерацию кода максимально простой, статическая модель повторно использует типы определения токенов, разработанные длядинамическоймодели без каких-либо изменений. Как будет показано в этом разделе, создание генератора кода на основе существующего типа определения токена является вопросом написания 3 строк кода.
Предполагая, что вы уже построили динамический лексер для своей задачи, есть еще два шага, необходимых для создания статического лексического анализатора с использованиемSpirit.Lex:
- генерирование кода C++ для статического анализатора (включая функцию токенизации и соответствующие таблицы);
- изменение динамического лексического анализатора для использования сгенерированного кода.
Оба этапа более подробно описаны в двух разделах ниже (для полного исходного кода, используемого в этом примере, см. код здесь:общее определение токена,генератор кода,сгенерированный кодистатический лексический анализатор).
Но сначала мы предоставляем фрагменты кода, необходимые для дальнейшего понимания описаний. Как определение используемого идентификатора токена, так и класс определения токена в этом примере помещаются в отдельный файл заголовка, чтобы сделать их доступными для генератора кода и статического лексического анализатора.
enum tokenids
{
IDANY = boost::spirit::lex::min_token_id + 1,
};
Важно отметить, что класс определения токена не отличается от аналогичного класса, который используется для динамического лексического анализатора. Библиотека была разработана таким образом, что все компоненты (динамический лексический анализатор, генератор кода и статический лексический анализатор) могут повторно использовать синтаксис определения токенов.
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)
{
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грамматика. Для полноты мы приводим простую грамматику, используемую в этом примере. Как вы можете видеть, эта грамматика не имеет никаких зависимостей от статического лексического анализатора, и по этой причине она не отличается от грамматики, используемой либо без лексера, либо с использованием динамического лексического анализатора, как описано ранее.
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(l), ++ref(c) ]
| qi::token(IDANY) [ ++ref(c) ]
)
;
}
std::size_t c, w, l;
qi::rule<Iterator> start;
};
Первым дополнительным шагом для создания статического лексического анализатора является создание небольшой отдельной программы для создания таблиц лексера и соответствующей функции токенизации. Для этого библиотекаSpirit.Lexвыставляет специальный API — функцию<generate_static_dfa()>. Он реализует весь генератор кода, никакого дополнительного кода не требуется. Все, что требуется для вызова этой функции, - это предоставить экземпляр определения токена, выходной поток для использования для генерации кода и необязательную строку для использования в качестве суффикса для имени генерируемой функции. Всего несколько строк кода.
int main(int argc, char* argv[])
{
word_count_tokens<lex::lexertl::lexer<> > word_count;
std::ofstream out(argc < 2 ? "word_count_static.hpp" : argv[1]);
return lex::lexertl::generate_static_dfa(word_count, out, "wc") ? 0 : -1;
}
Генератор показанного кода будет генерировать выход, который должен храниться в файле для последующего включения в статический лексический анализатор, как показано в следующей теме (полный сгенерированный код можно посмотреть здесь).
![[Note]](/img/note.png) | Note |
|---|
Сгенерированный код будет компилироваться в номере версии текущей библиотекиSpirit.Lex. Этот номер версии используется во время компиляции вашего объекта статического лексера, чтобы убедиться, что он составлен с использованием точно такой же версии.Spirit.Lexбиблиотека как лексер таблицы были созданы с. Если они не совпадают, вы увидите ошибку компиляции, в которой упоминается<incompatible_static_lexer_version>. |
Второй необходимый шаг для преобразования существующего динамического лексера в статический — это изменение основной программы в двух местах. Во-первых, вам нужно изменить тип используемого лексера (это параметр шаблона, используемый при создании класса определения токена). Хотя в динамической модели мы использовали шаблон<lex::lexertl::lexer<>>, теперь нам нужно изменить его на тип<lex::lexertl::static_lexer<>>. Второе изменение тесно связано с первым и предполагает исправление соответствующего<#include>утверждения:
#include <boost/spirit/include/lex_static_lexertl.hpp>
В противном случае основная программа не отличается от эквивалентной программы, использующей динамическую модель. Эта функция облегчает разработку лексера в динамическом режиме и переключение на статический режим после стабилизации кода. Простое приложение генератора, представленное выше, позволяет интегрировать генератор кода в любой существующий процесс сборки. Следующий фрагмент кода обеспечивает общую основную функцию, выделяя изменяемый код.
int main(int argc, char* argv[])
{
typedef lex::lexertl::token<
char const*, boost::mpl::vector<std::string>
> token_type;
typedef lex::lexertl::static_lexer<
token_type, lex::lexertl::static_::lexer_wc
> lexer_type;
typedef word_count_tokens<lexer_type>::iterator_type iterator_type;
word_count_tokens<lexer_type> word_count;
word_count_grammar<iterator_type> g (word_count);
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()];
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;
}
![[Important]](/img/important.png) | Important |
|---|
Созданный код для статического лексера содержит идентификаторы токенов, как они были назначены, либо явно программистом, либо неявно во время построения лексера. Вы несете ответственность за то, чтобы все экземпляры определенного типа статического лексера использовали одинаковые идентификаторы токенов. Конструктор объекта лексера имеет второй (по умолчанию) параметр, позволяющий ему обозначить идентификатор стартового токена, который будет использоваться при назначении идентификаторов определениям токена. Вышеприведенное требование выполняется по умолчанию до тех пор, пока во время построения статических лексеров не указано<first_id>. |