![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
The multi_passBoost , ,
Отслеживание в духе требует использования следующих типов итератора: прямой, двунаправленный или случайный доступ. Из-за обратного отслеживания итераторы ввода не могут использоваться. Поэтому стандартные библиотечные классы istreambuf_iterator и istream_iterator, подпадающие под категорию итераторов ввода, использовать нельзя. Другой интересующий итератор ввода - это тот, который обертывает лексер, такой как LEX.
К сожалению, с итератором ввода нет способа сохранить положение итератора, и, таким образом, итераторы ввода не будут работать с обратным отслеживанием в Spirit. Одним из решений этой проблемы является просто загрузка всех данных для разбора в контейнер, такой как вектор или деке, а затем передача начала и конца контейнера Духу. Этот метод может быть слишком интенсивным для определенных приложений, поэтому был создан итератор multi_pass. Многопропускной итератор преобразует любой входной итератор в передний итератор, подходящий для использования с Spirit. Multi_pass буферизирует данные при необходимости и отбрасывает буфер, когда существует только одна копия итератора. Грамматика должна быть разработана с осторожностью, если используется итератор multi_pass. Любое правило, которое, возможно, потребуется отменить, например, правило, содержащее альтернативу, приведет к буферизации данных. Оптимальными для использования являются последовательность и повторение. Последовательности формыa >>bне будут буферизировать данные вообще. Любое повторяющееся правило, такое как kleene_star (*a) или положительное, такое как+a), будет только буферизировать данные для текущего повторения. В типичных грамматиках двусмысленность и, следовательно, внешний вид часто локализованы. На самом деле, многие хорошо разработанные языки полностью детерминированы и не требуют никакого внимания. Заглядывая на первый символ с входа, сразу определится альтернативная ветвь, которую нужно взять. Тем не менее, даже при весьма неоднозначных грамматиках альтернативы часто имеют форму* (a | b | c | d). Входной итератор движется дальше и никогда не застревает в начале. Рассмотрим, например, фрагмент Паскаля:
Обратите внимание на альтернативы внутри звезды Клин в блоке правил. Правило поглощает вход линейно и выбрасывает прошлую историю с каждой итерацией. Поскольку это полностью детерминированная грамматика LL(1), каждая неудачная альтернатива должна заглядывать только в 1 символ (токен). Альтернатива, которая потребляет более 1 символа (токена), определенно является победителем. После чего звезда Клин переходит к следующей. Будьте внимательны, если вы используете функции бесплатного анализа. Все они делают копию переданного им итератора. Теперь, после лекции о функциях, с которыми нужно быть осторожным при использовании multi_pass, вы можете подумать, что multi_pass слишком ограничителен для использования. Это не так. Если ваша грамматика детерминирована, вы можете использовать flush_multi_pass в своей грамматике, чтобы гарантировать, что данные не буферизируются при необходимости. Опять же, следуя примеру, который мы начали использовать в разделе на сканере. Вот пример использования multi_pass: На этот раз мы извлекаем наш вход из входного потока с помощью istreambuf_iterator. #include <boost/spirit/core.hpp>
flush_multi_passСуществует предопределенный псевдопарсер под названием flush_multi_pass. Когда этот парсер используется с multi_pass, он будет называться multi_pass::clear_queue(). Это приведет к удалению любых буферных данных. Это также сделает недействительными все другие копии multi_pass, и они не должны использоваться. Если это так, то будет добавлено дополнение::illegal_backtracking. multi_pass PoliciesMulti_pass - это шаблонный класс, управляемый политикой. Описание multi_pass выше - это то, как он был первоначально реализован (до того, как он использовал политики), и является конфигурацией по умолчанию. Multi_pass способен на большее. Из-за открытого характера политики вы можете написать свою собственную политику, чтобы заставить multi_pass вести себя так, как мы никогда раньше не представляли. Класс multi_pass имеет пять параметров шаблона:
Predefined policiesВсе предопределенные политики multi_pass находятся в пространстве имен boost::spirit::multi_pass_policies. Predefined InputPolicy classesinput_iteratorЭта политика направляет multi_pass на чтение с итератора ввода типа InputT. lex_inputЭта политика получает свой вход, вызывая yylex(), который обычно предоставляется сканером, созданным LEX. Если вы используете эту политику, ваш код должен ссылаться на сканер, созданный LEX. functor_inputЭта политика ввода получает данные, вызывая функтор типа InputT. Функтор должен соответствовать определенным требованиям. Он должен иметь типдеф, называемый result_type, который должен быть типом, возвращенным от оператора(). Кроме того, поскольку политика ввода требует способа определения того, когда был достигнут конец ввода, функтор должен содержать статическую переменную, называемую eof, которая сопоставима с переменной типа результата. Predefined OwnershipPolicy classesref_countedЭтот класс использует схему подсчета ссылок. Multi_pass удалит общие компоненты, когда счет достигнет нуля. first_ownerПри использовании этой политики первым созданным мульти-пассом будет тот, который удаляет общие данные. Каждая копия не будет иметь права собственности на общие данные. Это хорошо работает для духа, так как динамическое распределение итераторов не выполняется. Все копии сделаны на стеке, поэтому оригинальный итератор имеет самый длинный срок службы. Predefined CheckingPolicy classesno_checkЭта политика вообще не проверяется. buf_id_checkbuf_id_check имеет буферный идентификатор или буферный возраст. Каждый раз, когда в мультипассивном итераторе вызывается Clear_queue(), возможно, что все другие итераторы становятся недействительными. Когда называется clear_queue(), buf_id_check увеличивает буферный идентификатор. Когда итератор отменяется, эта политика проверяет, что буферный идентификатор итератора соответствует общему буферному идентификатору. Эта политика наиболее эффективна при использовании вместе с Std_deque StoragePolicy. Он не должен использоваться с фиксированной_size_queue StoragePolicy, потому что он не будет обнаруживать отклонения итератора, которые находятся вне диапазона. full_checkЭта политика пока не реализована. Когда это произойдет, он будет отслеживать все итераторы и убедиться, что они все действительны. Predefined StoragePolicy classesstd_dequeЭта политика хранит все буферизованные данные в std::deque. Все данные хранятся до тех пор, пока существует более одного итератора. Как только счетчик итератора опускается до одного, и очередь больше не нужна, он очищается, освобождая память. Очередь также может быть принудительно очищена путем вызова multi_pass::clear_queue(). fixed_size_queue<N>Фиксированный_size_queue сохраняет круглый буфер размером N+1 и сохраняет N элементов. fix_size_queue - шаблон с параметром std::size_t, который определяет размер очереди. Вы несете ответственность за то, чтобы N был достаточно большим для вашего парсера. Всякий раз, когда главный итератор увеличивается, последний символ буфера автоматически стирается. В настоящее время нет возможности сказать, если итератор слишком далеко позади и стал недействительным. Никакое динамическое распределение не осуществляется этой политикой во время нормальной работы итератора, только при первоначальном строительстве. Использование памяти этой политики хранения устанавливается на N+1 байт, в отличие от std_deque, который не ограничен. Combinations: How to specify your own custom multi_passПрелесть дизайна, основанного на политике, заключается в том, что вы можете смешивать и сопоставлять политики для создания собственного пользовательского класса, выбирая политики, которые вы хотите. Вот пример того, как указать пользовательский multi_pass, который обертывает istream_iterator
Параметрами шаблона по умолчанию для multi_pass являются: input_iterator InputPolicy, ref_counted OwnershipPolicy, buf_id_check CheckingPolicy и std_deque StoragePolicy. Таким образом, если вы используете multi_pass Существует еще один класс под названием look_ahead. look_ahead имеет два шаблонных параметра: InputT, тип итератора ввода для обертывания и std::size_t N, который определяет размер буфера для политики фиксированной_size_queue. В то время как конфигурация multi_pass по умолчанию предназначена для безопасности, look_ahead предназначен для скорости. look_ahead происходит от multi_pass со следующими политиками: input_iterator InputPolicy, first_owner OwnershipPolicy, no_check CheckingPolicy и fixed_size_queue How to write a functor for use with the functor_input InputPolicyЕсли вы хотите использовать Functor_input InputPolicy, вы можете написать свой собственный Functor, который будет поставлять вход в multi_pass. Функтор должен удовлетворять двум требованиям. Он должен иметь тип typedef result_type, который определяет тип возврата оператора(). Это стандартная практика в STL. Кроме того, он должен предоставить статическую переменную, называемую eof, которая сравнивается с тем, чтобы знать, достиг ли вход конца. Вот пример:
How to write policies for use with multi_passInputPolicyInputPolicy должна иметь следующий интерфейс:
Из-за того, что multi_pass разделяет буфер и вход между несколькими копиями, внутренняя часть класса должна содержать указатель на вход. Конструктор должен просто скопировать указатель. Уничтожить (уничтожить). same_input должен сравнивать указатели. Более подробно см. различные реализации классов InputPolicy. OwnershipPolicyПолитика владения должна иметь следующий интерфейс:
CheckingPolicyКонтрольная политика должна иметь следующий интерфейс:
StoragePolicyПолитика хранения должна иметь следующий интерфейс:
Политика хранения — самая сложная политика для написания. Вы должны изучить и понять существующие классы, прежде чем пытаться написать свой собственный.
Copyright © 2001-2002 Daniel C. Nuffer
Статья The multi_pass раздела может быть полезна для разработчиков на c++ и boost. Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта. :: Главная :: ::
|
||||||||||||||||
©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007 |