![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
Quick StartBoost , ,
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Создайте парсер, который будет разбирать число с плавающей точкой.
(Вы должны признать, что это тривиально!) Приведенный выше код фактически генерирует Spirit real_parser (встроенный парсер), который парсирует число с плавающей запятой. Обратите внимание, что парсеры, которые предназначены для использования непосредственно пользователем, заканчиваются на «_p» в их названиях как соглашение Духа. У Spirit есть много заранее определенных парсеров, и постоянные соглашения об именах помогают вам не сойти с ума! Trivial Example #2Создайте парсер, который примет линию, состоящую из двух чисел с плавающей точкой.
Здесь вы видите знакомый цифровой парсер с плавающей точкой Примечание: когда мы объединяем парсеры, мы получаем «большой» парсер, но это все еще парсер. Парсеры могут становиться все больше и больше, гнездясь все больше и больше, но когда вы склеиваете два парсера вместе, вы в конечном итоге получаете один больший парсер. Это важная концепция. Trivial Example #3Создайте парсер, который примет произвольное число чисел с плавающей запятой. (Произвольное означает что-либо от нуля до бесконечности)
Это похоже на обычную Kleene Star, хотя синтаксис может показаться немного странным для программиста C++, который не привык видеть перегруженного оператора *. На самом деле, если вы знаете регулярные выражения, это может выглядеть странно, так как звезда до . Выражение, которое оно изменяет. С'ест ла Ви. Обвиняем его в том, что мы должны работать с синтаксическими правилами C++. Любое выражение, которое оценивается парсером, может быть использовано со звездой Клин. Однако имейте в виду, что из-за правил приоритета оператора C++ вам может потребоваться поместить выражение в скобки для сложных выражений. Звезда Клин также известна как закрытие Клин, но мы называем ее звездой в большинстве мест. Example #4 [ A Just Slightly Less Trivial Example ]Этот пример создаст парсер, который принимает список чисел, ограниченный запятой, и поместит числа в вектор. Step 1. Create the parser
Уведомление ch_p(','). Это буквальный парсер символов, который может распознавать запятую , . В этом случае звезда Клин изменяет более сложный парсер, а именно тот, который генерируется выражением:
Обратите внимание, что это тот случай, когда скобки необходимы. Звезда Клин содержит полное выражение выше. Step 2. Using a Parser (now that it's created)Теперь, когда мы создали парсер, как мы его используем? Как и результат любого временного объекта C++, мы можем либо хранить его в переменной, либо вызывать функции непосредственно на него. Мы закроем некоторые низкоуровневые детали C++ и просто перейдем к хорошему. Если r является правилом (не беспокойтесь о том, какие именно правила на данный момент). Это будет обсуждаться позже. Достаточно сказать, что правило является переменной заполнителя, которая может содержать парсер, тогда мы храним парсер как правило, как это:
Не слишком увлекательно, просто задание, как и любое другое выражение C++, которое вы использовали в течение многих лет. Замечательная вещь о хранении парсера в правиле заключается в следующем: правила являются парсерами, и теперь вы можете ссылаться на него по имени . (В этом случае имя r). Обратите внимание, что это теперь полное выражение назначения, поэтому мы заканчиваем его с запятой «;». Вот так. Мы закончили с определением парсера. Итак, следующий шаг теперь заключается в том, чтобы использовать этот парсер для выполнения своей работы. Есть несколько способов сделать это. На данный момент мы будем использовать бесплатную функцию parse, которая принимает значение char const*. Функция принимает три аргумента:
В нашем примере мы хотим пропустить пробелы и вкладки. Другой парсер под названием space_p включен в репертуар предварительно определенных парсеров Spirit. Это очень простой парсер, который просто распознает белое пространство. Мы будем использовать space_p в качестве парсера пропуска. Парсер пропуска - это тот, кто отвечает за пропуск символов между элементами парсера, такими как real_p и ch_p. Хорошо, теперь давайте разберемся!
Функция парса возвращает объект (называемый parse_info), который содержит, среди прочего, результат парса. В этом примере мы должны знать:
Чтобы получить полную картину того, что у нас есть до сих пор, давайте также завернем этот парсер в функцию:
Обратите внимание, что в этом случае мы отказались от названного правила и ввели парсер непосредственно в призыв к разбору. При вызове парса выражение оценивает в временный, безымянный парсер, который передается в функцию парса, используется, а затем уничтожается.
Обратите внимание, что объект, возвращенный из функции анализа, имеет элемент, называемый full. который возвращается истинным, если оба вышеперечисленных требования выполнены (т.е. парсер полностью разобрал вход). Step 3. Semantic ActionsНаш парсер выше не что иное, как распознающий. Он отвечает на вопрос «соответствовал ли вход нашей грамматике?», но он не помнит никаких данных и не выполняет никаких побочных эффектов. Помните: мы хотим поместить парсированные числа в вектор. Это делается в действии , которое связано с конкретным парсером. Например, когда мы анализируем реальное число, мы хотим сохранить его после успешного матча. Теперь мы хотим извлечь информацию из парсера. Семантические действия делают это. Семантические действия могут быть прикреплены к любой точке грамматической спецификации. Эти действия представляют собой функции C++ или функторы, которые называются всякий раз, когда часть парсера успешно распознает часть ввода. Скажем, у вас есть парсер P, а функция C++ F, вы можете сделать вызов парсера F всякий раз, когда он соответствует входу путем присоединения F:
Если F является функциональным объектом (функтором):
Подпись функции/функтора зависит от типа парсера, к которому он прикреплен. Парсер real_p передает один аргумент: парсируемое число. Таким образом, если мы должны были прикрепить функцию F к real_p, нам нужно, чтобы F было объявлено как:
В нашем примере, однако, опять же, мы можем воспользоваться некоторыми предопределенными семантическими функторами и генераторами функторов ( Наконец, вот наш полный парсер списка, разделенный запятой:
Это тот же парсер, что и выше. На этот раз с соответствующими семантическими действиями, прикрепленными к стратегическим местам, чтобы извлечь парсированные числа и вставить их в вектор v. Функция parse_numbers возвращается истинной в случае успеха.
Copyright © 1998-2003 Joel de Guzman Статья Quick Start раздела может быть полезна для разработчиков на c++ и boost. Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта. :: Главная :: ::
|
||||||||||||||||
©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007 |