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

Quick Start

Boost , ,

Quick Start

Why would you want to use Spirit?

Дух разработан, чтобы быть практическим инструментом анализа. По крайней мере, способность генерировать полностью работающий парсер из формальной спецификации EBNF, описанной в C++, значительно сокращает время разработки. Хотя может быть практично использовать полномасштабный автономный парсер, такой как YACC или ANTLR, когда мы хотим разработать компьютерный язык, такой как C или Pascal, безусловно, слишком сложно использовать большие пушки, когда мы хотим писать чрезвычайно маленькие микро-парсеры. На этом конце спектра программисты обычно подходят к работе не как к формальной задаче разбора, а через специальные взломы с использованием примитивных инструментов, таких как scanf. Правда, существуют такие инструменты, как библиотеки регулярных выражений (такие как boost regex) или сканеры (такие как boost tokenizer), но эти инструменты плохо масштабируются, когда нам нужно писать более сложные парсеры. Попытка написать даже умеренно сложный парсер с помощью этих инструментов приводит к коду, который трудно понять и поддерживать.

Одна из главных целей — сделать инструмент простым в использовании. Когда вы думаете о генераторе парсера, обычной реакцией является «он должен быть большим и сложным с крутой кривой обучения». Не так. Дух предназначен для того, чтобы быть полностью масштабируемым. Структура структурирована по слоям. Это позволяет учиться по мере необходимости, только после изучения минимальных основных и базовых понятий.

Для простоты и простоты разработки в развертывании весь фреймворк состоит только из файлов заголовка, без библиотек для связывания или создания. Просто поместите распределение духа в свой путь, компиляцию и запуск. Размер кода? - очень плотно. В примере быстрого старта, который мы представим через короткое время, в размере кода преобладает инстанциация std::vector и std::iostream.

Trivial Example #1

Создайте парсер, который будет разбирать число с плавающей точкой.

    real_p

(Вы должны признать, что это тривиально!) Приведенный выше код фактически генерирует Spirit real_parser (встроенный парсер), который парсирует число с плавающей запятой. Обратите внимание, что парсеры, которые предназначены для использования непосредственно пользователем, заканчиваются на «_p» в их названиях как соглашение Духа. У Spirit есть много заранее определенных парсеров, и постоянные соглашения об именах помогают вам не сойти с ума!

Trivial Example #2

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

    real_p >> real_p

Здесь вы видите знакомый цифровой парсер с плавающей точкой real_p, используемый дважды, один раз для каждого числа. Что там делает оператор >>? Ну, их нужно было чем-то отделить, и это было выбрано оператором последовательности «следует». Вышеупомянутая программа создает парсер из двух более простых парсеров, склеивая их вместе с оператором последовательности. Результатом является парсер, который представляет собой композицию меньших парсеров. Белое пространство между числами может неявно потребляться в зависимости от того, как используется парсер (см. ниже).

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

Trivial Example #3

Создайте парсер, который примет произвольное число чисел с плавающей запятой. (Произвольное означает что-либо от нуля до бесконечности)

    *real_p

Это похоже на обычную Kleene Star, хотя синтаксис может показаться немного странным для программиста C++, который не привык видеть перегруженного оператора *. На самом деле, если вы знаете регулярные выражения, это может выглядеть странно, так как звезда до . Выражение, которое оно изменяет. С'ест ла Ви. Обвиняем его в том, что мы должны работать с синтаксическими правилами C++.

Любое выражение, которое оценивается парсером, может быть использовано со звездой Клин. Однако имейте в виду, что из-за правил приоритета оператора C++ вам может потребоваться поместить выражение в скобки для сложных выражений. Звезда Клин также известна как закрытие Клин, но мы называем ее звездой в большинстве мест.

Example #4 [ A Just Slightly Less Trivial Example ]

Этот пример создаст парсер, который принимает список чисел, ограниченный запятой, и поместит числа в вектор.

Step 1. Create the parser

    real_p >> *(ch_p(',') >> real_p)

Уведомление ch_p(','). Это буквальный парсер символов, который может распознавать запятую , . В этом случае звезда Клин изменяет более сложный парсер, а именно тот, который генерируется выражением:

    (ch_p(',') >> real_p)

Обратите внимание, что это тот случай, когда скобки необходимы. Звезда Клин содержит полное выражение выше.

Step 2. Using a Parser (now that it's created)

Теперь, когда мы создали парсер, как мы его используем? Как и результат любого временного объекта C++, мы можем либо хранить его в переменной, либо вызывать функции непосредственно на него.

Мы закроем некоторые низкоуровневые детали C++ и просто перейдем к хорошему.

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

    r = real_p >> *(ch_p(',') >> real_p);

Не слишком увлекательно, просто задание, как и любое другое выражение C++, которое вы использовали в течение многих лет. Замечательная вещь о хранении парсера в правиле заключается в следующем: правила являются парсерами, и теперь вы можете ссылаться на него по имени . (В этом случае имя r). Обратите внимание, что это теперь полное выражение назначения, поэтому мы заканчиваем его с запятой «;».

Вот так. Мы закончили с определением парсера. Итак, следующий шаг теперь заключается в том, чтобы использовать этот парсер для выполнения своей работы. Есть несколько способов сделать это. На данный момент мы будем использовать бесплатную функцию parse, которая принимает значение char const*. Функция принимает три аргумента:

null-terminated const char* input
Объект парсера
Другой парсер называется skip парсер

В нашем примере мы хотим пропустить пробелы и вкладки. Другой парсер под названием space_p включен в репертуар предварительно определенных парсеров Spirit. Это очень простой парсер, который просто распознает белое пространство. Мы будем использовать space_p в качестве парсера пропуска. Парсер пропуска - это тот, кто отвечает за пропуск символов между элементами парсера, такими как real_p и ch_p.

Хорошо, теперь давайте разберемся!

    r = real_p >> *(ch_p(',') >> real_p);
    parse(str, r, space_p) // Not a full statement yet, patience...

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

Парсер успешно распознал вход str?
Парсер полностью Поглощать и потреблять вход до конца?

Чтобы получить полную картину того, что у нас есть до сих пор, давайте также завернем этот парсер в функцию:

    bool
    parse_numbers(char const* str)
    {
        return parse(str, real_p >> *(',' >> real_p), space_p).full;
    }

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

char and wchar_t operands

The careful reader may notice that the parser expression has ',' instead of ch_p(',') as the previous examples did. This is ok due to C++ syntax rules of conversion. There are >> operators that are overloaded to accept a char or wchar_t argument on its left or right (but not both). An operator may be overloaded if at least one of its parameters is a user-defined type. In this case, the real_p is the 2nd argument to operator>>, and so the proper overload of >> is used, converting ',' into a character literal parser.

The problem with omiting the ch_p call should be obvious: 'a' >> 'b' is not a spirit parser, it is a numeric expression, right-shifting the ASCII (or another encoding) value of 'a' by the ASCII value of 'b'. However, both ch_p('a') >> 'b' and 'a' >> ch_p('b') are Spirit sequence parsers for the letter 'a' followed by 'b'. You'll get used to it, sooner or later.

Обратите внимание, что объект, возвращенный из функции анализа, имеет элемент, называемый full. который возвращается истинным, если оба вышеперечисленных требования выполнены (т.е. парсер полностью разобрал вход).

Step 3. Semantic Actions

Наш парсер выше не что иное, как распознающий. Он отвечает на вопрос «соответствовал ли вход нашей грамматике?», но он не помнит никаких данных и не выполняет никаких побочных эффектов. Помните: мы хотим поместить парсированные числа в вектор. Это делается в действии , которое связано с конкретным парсером. Например, когда мы анализируем реальное число, мы хотим сохранить его после успешного матча. Теперь мы хотим извлечь информацию из парсера. Семантические действия делают это. Семантические действия могут быть прикреплены к любой точке грамматической спецификации. Эти действия представляют собой функции C++ или функторы, которые называются всякий раз, когда часть парсера успешно распознает часть ввода. Скажем, у вас есть парсер P, а функция C++ F, вы можете сделать вызов парсера F всякий раз, когда он соответствует входу путем присоединения F:

    P[&F]

Если F является функциональным объектом (функтором):

    P[F]

Подпись функции/функтора зависит от типа парсера, к которому он прикреплен. Парсер real_p передает один аргумент: парсируемое число. Таким образом, если мы должны были прикрепить функцию F к real_p, нам нужно, чтобы F было объявлено как:

    void F(double n);

В нашем примере, однако, опять же, мы можем воспользоваться некоторыми предопределенными семантическими функторами и генераторами функторов ( Генератор функтора - это функция, которая возвращает функтор). Для наших целей Spirit имеет генератор функторов push_back_a(c). Короче говоря, это семантическое действие, когда оно называется , добавляет . значение, которое он получает от парсера, к которому он прикреплен, к контейнеру c.

Наконец, вот наш полный парсер списка, разделенный запятой:

    bool
    parse_numbers(char const* str, vector<double>& v)
    {
        return parse(str,
            //  Begin grammar
            (
                real_p[push_back_a(v)] >> *(',' >> real_p[push_back_a(v)])
            )
            ,
            //  End grammar
            space_p).full;
    }

Это тот же парсер, что и выше. На этот раз с соответствующими семантическими действиями, прикрепленными к стратегическим местам, чтобы извлечь парсированные числа и вставить их в вектор v. Функция parse_numbers возвращается истинной в случае успеха.

Полный исходный код можно посмотреть здесь . Это часть духовного распределения.



 

Статья Quick Start раздела может быть полезна для разработчиков на c++ и boost.




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



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


реклама


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

Время компиляции файла: 2024-08-30 11:47:00
2025-05-20 03:33:57/0.0092480182647705/1