![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
The Scanner and ParsingBoost , ,
Задачасканеразаключается в подаче последовательного входного потока данных в парсер. Сканер извлекает данные из ввода, посылки, потенциально модифицируя или фильтруя, а затем, наконец, отправляет результат отдельным элементам парсера по требованию, пока вход не исчерпан. Сканер состоит из двух передних итераторов STL, первого и последнего, где первый удерживается ссылкой, а последний - значением. Первый итератор удерживается путем ссылки, чтобы его можно было перепозиционировать. Следующая диаграмма иллюстрирует, что происходит:
Сканер управляет различными аспектами процесса анализа с помощью набора политик. Существует три набора политик, которые регулируют:
Эти политики в основном скрыты от глаз, и пользователи, как правило, не должны знать о них. Однако продвинутые пользователи могут предлагать свои собственные политики, которые отменяют те, которые уже существуют, чтобы точно настроить процесс анализа в соответствии с их собственными потребностями. Посмотрим, как это можно сделать. Это будет рассмотрено более подробно позже. сканерпредставляет собой класс шаблонов, ожидающий двух параметров:ИтераторТ, тип итератора иПолитикиТСовокупность правил.Итераторнеисправностьchar const*в то время какполитикапо умолчанию дляscanner_policies<>, предопределенный набор политик сканера, который мы можем использовать прямо из коробки.
Spirit использует те же концепции итератора и интерфейс, формально определенные стандартной библиотекой шаблонов C++ (STL). Мы можем использовать итераторы, поставляемые контейнерами STL (например,список,вектор,строкаи т. д.), как есть, или, возможно, написать наш собственный. Итераторы могут быть такими же простыми, как указатель (например,char const*). На другом конце спектра итераторы могут быть довольно сложными; например, адаптер итератора, который обертывает лексер, такой как LEX. The Free Parse FunctionsФреймворк предоставляет пару бесплатных функций, чтобы сделать разборку щелчком. Эти функции парсера имеют две формы. Первая форма работает на уровнесимволов. Вторую строчку мы видим встрокеи встроке. скип парсер— это почти любой парсер примитивный или композитный. Его цель состоит в том, чтобы переместить первый итератор сканерана действительные токены, пропуская белые пространства. В C, например, вкладка't', новая строка'n', возврат'r', пространство''и символы внутри комментариев/*...*/рассматриваются как белые пространства. Уравнение характеристик
Есть два варианта. Первый вариант принимаетпервую,последнююпару итераторов, как вы делаете алгоритмы STL. Второй вариант принимает нулевую законченную строку. Последним аргументом является парсерp, который будет использоваться для разбора входных данных. Уровни разбора фраз
Как и выше, есть два варианта. Первый вариант принимаетпервую,последнююпару итераторов, как вы делаете алгоритмы STL. Второй вариант принимает нулевую законченную строку.pявляется парсером, который будет использоваться для разбора входных данных. Последний аргументskip— это skip parser. Структура parse_info Вышеприведенные функции возвращают структуруparse_info, параметризованную пройденным типом итератора. Структура parse_info состоит из следующих элементов:
|
![]() On a successful match, the input is advanced accordingly. But what happens on an unsuccessful match? Be warned. It might be intuitive to think that the scanner position is reset to its initial position prior to parsing. No, the position is not reset. On an unsuccessful match, the position of the scanner is undefined! Usually, it is positioned at the farthest point where the error was found somewhere down the recursive descent. If this behavior is not desired, you may need to position the scanner yourself. The example in the numerics chapter illustrates how the scanner position can be saved and later restored. |
Еслиpявляется парсером, который мы хотим использовать, ипервый/последнийявляются парами итератора, относящимися к входу. Мы просто создаем сканер, учитывая итераторы. Тип сканера, который мы будем использовать здесь, использует по умолчаниюscanner_policies<>.
Ситуация немного сложнее, когда мы хотим работать на уровнефразы:
typedef skip_parser_iteration_policy<SkipT> iter_policy_t; typedef scanner_policies<iter_policy_t> scanner_policies_t; typedef scanner<IteratorT, scanner_policies_t> scanner_t; iter_policy_t iter_policy(skip); scanner_policies_t policies(iter_policy); scanner_t scan(first, last, policies); if (p.parse(scan)) { // Parsed successfully. If first == last, then we have // a full parse, the parser recognized the input in whole. } else { // Parsing failure. The parser failed to recognize the input }
ЕслиСкипТявляется типом скип-парсера,скип.pявляется парсером, который мы хотим использовать, ипервый/последнийявляются парами итератора, относящимися к входу. Учитывая тип пропуска-парсераSkipT,skip_parser_iteration_policyсоздает политику итерации сканера, которая пропускает части, которые распознаются пропуском-парсером. Это можно использовать для создания сканера. Классscanner_policiesохватывает все политики, связанные со сканером, включая политику итерации.
При переходе с уровня фразы на уровень символов, lexeme_d(см.?tid=6908) делает свою магию, отключая пропуск белых пространств. Это делается путем настройки сканера. Однако, когда мы делаем это, все парсеры внутри лексемы получают преобразованный тип сканера. В большинстве случаев это не должно быть проблемой. Однако, когда правила называются внутриlexeme_d, компилятор задыхается, если правило не имеет правильного типа сканера. Если правило должно использоваться внутриlexeme_d, то тип правила должен быть:
rule<lexeme_scanner<ScannerT>::type> r;
гдеScannerTявляется фактическим типом используемого сканера. Обратите внимание, чтоlexeme_scannerбудет работать только для сканеров уровня фраз.
Аналогично,as_lower_dвыполняет свою работу путем фильтрации и преобразования всех символов, полученных от сканера, в нижний регистр. Это также делается путем настройки сканера. Затем все парсеры внутрикак_lower_dполучают преобразованный тип сканера. Если правило должно использоваться внутрикак_lower_d, то тип правила должен быть:
rule<as_lower_scanner<ScannerT>::type> r;
гдеScannerTявляется фактическим типом используемого сканера.
![]() |
Опять же, директиваno_actions_dнастраивает сканер для отключения семантических действий. Как и раньше, все парсеры внутриno_actions_dполучают преобразованный тип сканера. Если правило должно использоваться внутриno_actions_d, то тип правила должен быть:
rule<no_actions_scanner<ScannerT>::type> r;
гдеScannerT- фактический тип используемого сканера.
![]() |
См.no_actions.cpp. Это часть духовного распределения.
![]() |
![]() |
![]() |
Copyright © 1998-2003 Joel de Guzman
Use, modification and distribution is subject to the Boost Software
License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
Статья The Scanner and Parsing раздела может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
:: Главная :: ::
реклама |