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

The Grammar

Boost , ,

The Grammar

грамматикаинкапсулирует набор правил. Классграмматикиявляется базовым классом протокола. По сути, это интерфейсный контракт.Грамматика— это класс шаблонов, который параметризируется его производным классоми его контекст,Контекст. Параметр шаблона ContextT по умолчанию соответствуетparser_context, предопределенному контексту.

Вам не нужно беспокоиться о параметре шаблона ContextT, если вы не хотите изменить поведение грамматики на низком уровне. Подробная информация о параметре шаблона ContextT представленав другом месте. Грамматикаопирается на шаблонный параметр DerivedT, подкласс грамматики для определения фактических правил.

Ниже представлен публичный API. ПослеContextTможет быть больше параметров шаблона. Все послеContextTне должно беспокоить клиента и предназначено исключительно для внутреннего использования.

    template<
        typename DerivedT,
        typename ContextT = parser_context<> >
    struct grammar;

Grammar definition

Конкретный подкласс, наследующий отграмматики, как ожидается, будет иметь вложенный класс шаблонов (или структуру), названныйопределением:

Это вложенный класс шаблонов с именем типаScannerTпараметр.
Его конструктор определяет правила грамматики.
Его конструктор передается в ссылке на фактическую грамматикусам.
Он имеет функцию члена под названиемstart, которая возвращает ссылку на правилоstart.

Grammar skeleton

    struct my_grammar : public grammar<my_grammar>
    {
        template <typename ScannerT>
        struct definition
        {
            rule<ScannerT>  r;
            definition(my_grammar const& self)  { r = /*..define here..*/; }
            rule<ScannerT> const& start() const { return r; }
        };
    };

Отделение типа сканера от правил, которые формируют грамматику, позволяет использовать грамматику в разных контекстах. Нам все равно, с каким сканером мы имеем дело. Пользовательскийmy_grammarможет быть использован слюбымтипом сканера. В отличие от правила, грамматика не привязана к определенному типу сканера. См."Scanner Business", чтобы понять, почему это важно, и получить дальнейшее понимание этой проблемы связи между сканером и управлением.

Instantiating and using my_grammar

Наша грамматика выше может быть начата и приведена в действие:

    my_grammar g;
    if (parse(first, last, g, space_p).full)
        cout << "parsing succeeded\n";
    else
        cout << "parsing failed\n";

my_grammarIS-Aпарсер и может использоваться в любом месте, где ожидается парсер, даже ссылаясь на другое правило:

    rule<>  r = g >> str_p("cool huh?");
Referencing grammars

Like the rule, the grammar is also held by reference when it is placed in the right hand side of an EBNF expression. It is the responsibility of the client to ensure that the referenced grammar stays in scope and does not get destructed while it is being referenced.

Full Grammar Example

Вспоминая наш оригинальный пример калькулятора, здесь он теперь переписан с помощью грамматики:

    struct calculator : public grammar<calculator>
    {
        template <typename ScannerT>
        struct definition
        {
            definition(calculator const& self)
            {
                group       = '(' >> expression >> ')';
                factor      = integer | group;
                term        = factor >> *(('*' >> factor) | ('/' >> factor));
                expression  = term >> *(('+' >> term) | ('-' >> term));
            }
            rule<ScannerT> expression, term, factor, group;
            rule<ScannerT> const&
            start() const { return expression; }
        };
    };

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

self

You might notice that the definition of the grammar has a constructor that accepts a const reference to the outer grammar. In the example above, notice that calculator::definition takes in a calculator const& self. While this is unused in the example above, in many cases, this is very useful. The self argument is the definition's window to the outside world. For example, the calculator class might have a reference to some state information that the definition can update while parsing proceeds through semantic actions.

Grammar Capsules

Поскольку грамматика усложняется, рекомендуется группировать части в логические модули. Например, при написании языка было бы разумно поместить выражения и утверждения в отдельные грамматические капсулы. Грамматика использует преимущества свойств инкапсуляции классов C++. Декларативный характер классов делает его идеально подходящим для определения грамматики. Поскольку грамматика - это не что иное, как объявление класса, мы можем удобно публиковать ее в файлах заголовков. Идея заключается в том, что после написания и полного тестирования грамматика может быть повторно использована во многих контекстах. Теперь у нас есть понятие грамматических библиотек.

Reentrancy and multithreading

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

    #define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE

С другой стороны, если грамматика предназначена для использования в многопоточном коде, мы должны определитьBOOST_SPIRIT_THREADSAFE.перед включением любых файлов заголовка духов. В этом случае он также должен быть связан сBoost. Толчки

    #define BOOST_SPIRIT_THREADSAFE

Using more than one grammar start rule

Иногда желательно иметь более одной видимой точки входа в грамматику (кроме правила начала). Чтобы обеспечить дополнительные начальные точки, Spirit предоставляет шаблон помощникаgrammar_def, который может использоваться в качестве базового класса для определенияподкласса вашейграмматики. Вот пример:

    // this header has to be explicitly included
    #include <boost/spirit/utility/grammar_def.hpp> 
    struct calculator2 : public grammar<calculator2>
    {
        enum 
        {
            expression = 0,
            term = 1,
            factor = 2,
        };
        template <typename ScannerT>
        struct definition
        : public grammar_def<rule<ScannerT>, same, same>
        {
            definition(calculator2 const& self)
            {
                group       = '(' >> expression >> ')';
                factor      = integer | group;
                term        = factor >> *(('*' >> factor) | ('/' >> factor));
                expression  = term >> *(('+' >> term) | ('-' >> term));
                this->start_parsers(expression, term, factor); 
            }
            rule<ScannerT> expression, term, factor, group;
        };
    };

Шаблонgrammar_defдолжен быть инстанцирован с типами всех правил, которые вы хотите сделать видимыми извнеgrammar:

    grammar_def<rule<ScannerT>, same, same> 

Сокращенное обозначениетого жеиспользуется для обозначения того же типа, который используется в соответствии с предыдущим параметром шаблона (например,<rule<ScannerT>>). Очевидно, чтотот жене может использоваться в качестве первого параметра шаблона.

grammar_def start types

It may not be obvious, but it is interesting to note that aside from rule<>s, any parser type may be specified (e.g. chlit<>, strlit<>, int_parser<>, etc.).

Используя класс грамматики_def, больше нет необходимости предоставлять функциюstart(). Вместо этого вам нужно будет вставить вызов наэто->start_parsers()(который является частью шаблонаgrammar_def), чтобы определить начальные символы для вашейграмматики.Обратите внимание, что число и последовательность правил, используемых в качестве параметров для функцииstart_parsers(), должны соответствовать типам, указанным в шаблонеgrammar_def:

    this->start_parsers(expression, term, factor);

Точка входа в грамматику может быть определена с использованием следующего синтаксиса:

    g.use_parser<N>() // Where g is your grammar and N is the Nth entry.

Эта выборка показывает, как использоватьтерминправило изкалькулятора2грамматики выше:

    calculator2 g;
    if (parse(
            first, last, 
            g.use_parser<calculator2::term>(),
            space_p
        ).full)
    {
        cout << "parsing succeeded\n";
    }
    else {
        cout << "parsing failed\n";
    }

Параметр шаблона дляuse_parser<>шаблонного типа должен быть нулевым индексом в списке правил, указанных в вызове функцииstart_parsers().

use_parser<0>

Note, that using 0 (zero) as the template parameter to use_parser is equivalent to using the start rule, exported by conventional means through the start() function, as shown in the first calculator sample above. So this notation may be used even for grammars exporting one rule through its start() function only. On the other hand, calling a grammar without the use_parser notation will execute the rule specified as the first parameter to the start_parsers() function.

Максимальное количество полезных правил запуска ограничено константой препроцессора:

    BOOST_SPIRIT_GRAMMAR_STARTRULE_TYPE_LIMIT // defaults to 3


 

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




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



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


реклама


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

Время компиляции файла: 2024-08-30 11:47:00
2025-07-05 13:56:49/0.0057168006896973/0