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

The Rule

Boost , ,

The Rule

Правилоявляется полиморфным парсером, который действует как названный держатель места, фиксирующий поведение назначенного ему выражения EBNF. Наименование выражения EBNF позволяет ссылаться на него позже. Правило— это класс шаблонов, параметризованный типом сканера (ScannerT).), контекст правилаи егоТэг. Параметры шаблона по умолчанию предоставляются, чтобы упростить использование правила.

    template<
        typename ScannerT = scanner<>,
        typename ContextT = parser_context<>,
        typename TagT = parser_address_tag>
    class rule;

Параметры шаблона по умолчанию поставляются для обработки наиболее распространенного случая.ScannerTпо умолчанию длясканера<>, простой ванильный сканер, который действует наитераторы char const*и не делает ничего особенного, кроме итерации через все загоны в нулевом завершенном входе символа за раз. Тег правилаTagT, обычно используемый сASTs, используется для идентификации правила; он объясняетсяздесь. В тривиальных случаях достаточно объявить правилоправилом<>. Вам не нужно беспокоиться о параметре шаблонаContextT, если вы не хотите настроить поведение правила на низком уровне. Подробная информация по шаблонному параметруContextTпредставленав другом месте.

Order of parameters

По состоянию на v1.8.0,ScannerT,ContextTиTagTмогут быть указаны в любом порядке. Если параметр шаблона отсутствует, он будет предполагать по умолчанию. Примеры:

    rule<> rx1;
    rule<scanner<> > rx2;
    rule<parser_context<> > rx3;
    rule<parser_context<>, parser_address_tag> rx4;
    rule<parser_address_tag> rx5;
    rule<parser_address_tag, scanner<>, parser_context<> > rx6;
    rule<parser_context<>, scanner<>, parser_address_tag> rx7;

Multiple scanners

При использовании v1.8.0 можно использовать один или несколько типов сканеров. Например, есть случаи, когда нам нужно правило, которое может работать на уровне фраз и символов. Несоответствие правил и сканеров было источником путаницы и является No 1FAQ. Для решения этой проблемы у нас теперь есть поддержка нескольких сканеров. Пример:

    typedef scanner_list<scanner<>, phrase_scanner_t> scanners;
    rule<scanners>  r = +anychar_p;
    assert(parse("abcdefghijk", r).full);
    assert(parse("a b c d e f g h i j k", r, space_p).full);

Обратите внимание на то, как правилоrиспользуется как во фразе, так и в уровнях символов.

По умолчанию поддержка нескольких сканеров отключена. МакроBOOST_SPIRIT_RULE_SCANNERTYPE_LIMITдолжно быть определено максимальное количество сканеров, разрешенных в списке сканеров. Значение должно быть больше 1 для обеспечения работы нескольких сканеров. В приведенном выше примере для определения предела двух сканеров списка перед включением заголовков Spirit в исходный файл необходимо вставить следующую строку:

    #define BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT 2
See the techniques section for an example of a grammar using a multiple scanner enabled rule, lexeme_scanner and as_lower_scanner.

Rule Declarations

Типовое правило производства EBNF. Пример:

    rule<> a_rule = *(a | b) & +(c | d | e);

Тип и поведение выражения правой руки (rhs) EBNF, которое может быть произвольно сложным, закодировано в правиле под названием a_rule. a_rule теперь может упоминаться в другом месте грамматики:

    rule<> another_rule = f >> g >> h >> a_rule;
Referencing rules

When a rule is referenced anywhere in the right hand side of an EBNF expression, the rule is held by the expression by reference. It is the responsibility of the client to ensure that the referenced rule stays in scope and does not get destructed while it is being referenced.
    a = int_p;
    b = a;
    c = int_p >> b;

Copying Rules

Правило является странным гражданином C++, в отличие от любого другого объекта C++. Он не имеет надлежащей семантики копирования и назначения и не может храниться и передаваться по стоимости. Если вам нужно скопировать правило, вы должны явно назвать его функцию участника:

    r.copy();

Тем не менее, следует предупредить, что копирование правила не будет глубоко копировать другие упомянутые правила копируемого правила источника. Это может привести к помехе ссылок. Опять же, клиент несет ответственность за то, чтобы все упомянутые правила оставались в сфере применения и не разрушались во время ссылки. Пещерный пылесос.

Если вы скопируете правило, то вы захотите поместить его в хранилище где-нибудь. Проблема в том как? Хранение не может быть другим правилом:

    rule<> r2 = r.copy(); // BAD!

Потому что правила странные и не имеют ожидаемого C++-конструктора и семантики назначения. Общее правило:Не вводите скопированное правило в другое!Вместо этого используйтесохраненное_правилодля этой цели.

Forward declarations

Правиломожет быть объявлено до того, как будет определено, чтобы позволить циклические структуры, обычно встречающиеся в декларациях BNF. Пример:

    rule<> a, b, c;
    a = b | a;
    b = c | a;

Recursion

Правая сторона правила может ссылаться на другие правила, включая его саму. Ограничение заключается в том, что прямое или косвенное левое повторение не допускается (это неконтролируемая ошибка времени выполнения, которая приводит к бесконечному циклу). Это типично для парсеров сверху вниз. Пример:

    a = a | b; // infinite loop!
What is left recursion?

Left recursion happens when you have a rule that calls itself before anything else. A top-down parser will go into an infinite loop when this happens. See the FAQ for details on how to eliminate left recursion.

Undefined rules

Неопределенное правило не соответствует ничему и семантически эквивалентноnothing_p.

Redeclarations

Как и любое другое задание C++, второе задание правилу является деструктивным и переопределит его. Старое определение утрачено. Правила динамичны. Правило может изменить свое определение в любое время:

    r = a_definition;
    r = another_definition;

Правилоrтеряет старое определение при выполнении второго задания. Как уже упоминалось, неопределенное правило не соответствует ничему и семантически эквивалентноnothing_p.

Динамические парсеры

Хостинг декларативного EBNF в императивном C++ дает интересную смесь. У нас есть лучшее из обоих миров. У нас есть возможность удобно изменять грамматику во время выполнения с использованием императивных конструкций, таких как, если,и другиеутверждения. Пример:

    if (feature_is_available)
        r = add_this_feature;

Правила по существу являются динамическими парсерами. Динамический парсер характеризуется способностью изменять свое поведение во время выполнения. Первоначально неопределенное правило не соответствует ничему. В любое время правило может быть определено и переопределено, таким образом, динамически изменяя его поведение.

No start rule

Как правило, парсеры имеют так называемый стартовый символ, выбранный как корень грамматики, где начинается парсинг. Система парсера Духа не имеет понятия о начальном символе. Любое правило может быть символом начала. Эта функция способствует поэтапному созданию парсеров. Мы можем создавать парсеры снизу вверх, полностью тестируя каждый уровень или модуль до тех пор, пока не достигнем самого верхнего уровня.

Parser Tags

Правила могут быть помечены для целей идентификации. Это необходимо, особенно при работе сразбором деревьев и AST, чтобы увидеть, какое правило создало конкретный узел дерева AST / разрез. Каждое правило имеет идентификатор типаparser_id. Этот идентификатор можно получить с помощью правилаid()Функция члена:

    my_rule.id(); //  get my_rule's id

Классparser_idобъявляется:

    class parser_id
{
public:
parser_id();
explicit parser_id(void const* p);
parser_id(std::size_t l); bool operator==(parser_id const& x) const;
bool operator!=(parser_id const& x) const; bool operator<(parser_id const& x) const; std::size_t to_long() const; };

parser_address_tag

Параметр шаблона правилаTagTпредоставляет этот идентификатор. По умолчаниюparser_address_tag.parser_address_tagиспользует адрес правила в качестве своего идентификатора. Это часто не самое удобное, так как не всегда можно получить адрес правила для сравнения.

parser_tag

Можно иметь конкретные постоянные целые числа для определения правила. Для этого мы можем использоватьparser_tag, где N — постоянное целое число:

    rule<parser_tag<123> > my_rule; //  set my_rule's id to 123

dynamic_parser_tag

parser_tagможет указывать только статический идентификатор, который определяется во время компиляции. Если вам нужен идентификатординамический(изменяемый во время выполнения), вы можете использовать классdynamic_parser_tagв качестве параметра шаблонаTagT. Этот параметр шаблона обеспечивает функциюset_id(), которая может использоваться для установки требуемого идентификатора во время выполнения:

    rule<dynamic_parser_tag> my_dynrule;
    my_dynrule.set_id(1234);    // set my_dynrule's id to 1234

Если функцияset_id()не называется, идентификатор парсера по умолчанию указывает на адрес правила в качестве своего идентификатора, как и параметр шаблонаparser_address_tag.



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




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



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


реклама


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

Время компиляции файла: 2024-08-30 11:47:00
2025-07-05 14:35:06/0.0059480667114258/0