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

Roman Numerals

Boost , Spirit 2.5.2 , Tutorials

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Этот пример демонстрирует:

  • символ таблицы
  • правило
  • грамматика
Symbol Table

В таблице символов содержится словарь символов, где каждый символ представляет собой последовательность символов (char, wchar_t, int, перепись и т.д.). Класс шаблона, параметризируемый типом символов, может эффективно работать с 8, 16, 32 и даже 64 битными символами. Мюбильные данные типа T связаны с каждым символом.

Традиционно управление символической таблицей поддерживается отдельно за пределами грамматики BNF посредством семантических действий. Вопреки стандартной практике, класс таблицы символа Духа символы является парсером. Объект, который может быть использован в любом месте в грамматической спецификации EBNF. Это пример динамического парсера. Динамический парсер характеризуется своей способностью изменять свое поведение во время бега. Изначально объект пустых символов ничего не соответствует. В любое время символы могут быть добавлены или удалены, таким образом, динамически изменяя свое поведение.

Каждая запись в таблице символов имеет связанный с этим слот данных. В этой связи можно рассматривать таблицу символов как ассоциативный контейнер (или карту) пар ключевых значений, где ключи являются строками.

Класс символов ожидает два параметра шаблона. Первый параметр указывает тип символов. Второй указывает тип данных, связанный с каждым символом: его атрибут.

Вот парсер для римских сотен (100.900) с использованием таблицы символов. Имейте в виду, что данные, связанные с каждым слотом, являются атрибутом парсера (который передается на привязанные семантические действия).

struct hundreds_ : qi::symbols<char, unsigned>
{
    hundreds_()
    {
        add
            ("C"    , 100)
            ("CC"   , 200)
            ("CCC"  , 300)
            ("CD"   , 400)
            ("D"    , 500)
            ("DC"   , 600)
            ("DCC"  , 700)
            ("DCCC" , 800)
            ("CM"   , 900)
        ;
    }
} hundreds;

Вот парсер для римских десятков (10.90):

struct tens_ : qi::symbols<char, unsigned>
{
    tens_()
    {
        add
            ("X"    , 10)
            ("XX"   , 20)
            ("XXX"  , 30)
            ("XL"   , 40)
            ("L"    , 50)
            ("LX"   , 60)
            ("LXX"  , 70)
            ("LXXX" , 80)
            ("XC"   , 90)
        ;
    }
} tens;

и, наконец, для тех (1..9):

struct ones_ : qi::symbols<char, unsigned>
{
    ones_()
    {
        add
            ("I"    , 1)
            ("II"   , 2)
            ("III"  , 3)
            ("IV"   , 4)
            ("V"    , 5)
            ("VI"   , 6)
            ("VII"  , 7)
            ("VIII" , 8)
            ("IX"   , 9)
        ;
    }
} ones;

Теперь мы можем использовать hundreds, tens и ones где угодно в наших выражениях. Все это парсеры.

Rules

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

Парсерное выражение может быть присвоено тому, что называется «правилом». Существуют различные способы объявить правила. Самая простая форма:

rule<Iterator> r;

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

rule<Iterator, Skipper> r;

Пример:

rule<std::string::iterator, space_type> r;

Этот тип правила может использоваться как для phrase_parse, так и для parse.

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

rule<Iterator, Signature> r;

или

rule<Iterator, Signature, Skipper> r;
[Tip]Tip

Все аргументы шаблона правил после Итератора могут быть предоставлены в любом порядке.

Подпись указывает атрибуты правила. Вы видели, что у наших парсеров может быть атрибут. Напомним, что ДП_ parser имеет атрибут ДП. Точнее, это атрибуты synthesized. Парсер «синтезирует» значение атрибута. Думайте о них как о функциях возвращающих значений.

Существует еще один тип атрибута, называемый «наследованным» атрибутом. Пока они нам не понадобятся, но хорошо, что вы знаете о таких атрибутах. Вы можете думать о них как о функциональных аргументах. И, по праву, подпись правила - это функциональная подпись формы:

result(argN, argN,..., argN)

После объявления правила, теперь вы можете назначить ему любое выражение. Пример:

r = double_ >> *(',' >> double_);
Grammars

Грамматика инкапсулирует один или несколько правил. Он имеет те же параметры шаблона, что и правило. Вы объявляете грамматику:

  1. grammar шаблон класса
  2. объявить одно или несколько правил переменными
  3. инициализируйте базовый класс грамматики, давая ему правило начала (его первое правило, которое называется, когда грамматика начинает парить)
  4. инициализируйте свои правила в конструкторе

Римская цифра - очень хороший и простой пример грамматики:

template <typename Iterator>
struct roman : qi::grammar<Iterator, unsigned()>
{
    roman() : roman::base_type(start)
    {
        using qi::eps;
        using qi::lit;
        using qi::_val;
        using qi::_1;
        using ascii::char_;
        start = eps             [_val = 0] >>
            (
                +lit('M')       [_val += 1000]
                ||  hundreds    [_val += _1]
                ||  tens        [_val += _1]
                ||  ones        [_val += _1]
            )
        ;
    }
    qi::rule<Iterator, unsigned()> start;
};

Вещи, которые следует учитывать:

  • Неподписано(). Он имеет синтезированный атрибут (возвратное значение) типа неподписанный без унаследованных атрибутов (аргументов).
  • Мы не уточняли шкипер. Мы не хотим заходить между цифрами.
  • роман::base_type - это тип-дефицит для грамма<Итератор, неподписанный()>. Если roman не был шаблоном, вы могли бы просто написать: base_type(start)
  • Лучше всего сделать ваши грамматические шаблоны такими, что они могут быть повторно использованы для различных типов итераторов.
  • _val - это еще один Phoenix, представляющий синтезированный атрибут правила.
  • eps - это особый духовный парсер, который не потребляет никаких входов, но всегда успешен. Мы используем его для инициализации _val, синтезированного атрибута правила, до нуля перед чем-либо еще. Фактический парсер начинается по адресу +lit('M'), разбивая римские тысячи. Используя eps, этот способ хорош для предварительной и пост-инициализации.
  • Выражение a || b читает: совпадает a или b и последовательность. То есть, если и a, и b матч, он должен быть в последовательности; это эквивалентно a > -b b b, но более эффективно.
Let's Parse!

bool r = parse(iter, end, roman_parser, result);
if (r && iter == end)
{
    std::cout << "-------------------------\n";
    std::cout << "Parsing succeeded\n";
    std::cout << "result = " << result << std::endl;
    std::cout << "-------------------------\n";
}
else
{
    std::string rest(iter, end);
    std::cout << "-------------------------\n";
    std::cout << "Parsing failed\n";
    std::cout << "stopped at: \": " << rest << "\"\n";
    std::cout << "-------------------------\n";
}

roman_parser - это объект типа roman, наш римский числитель. На этот раз мы используем нескользящую версию функций парса. Мы не хотим пропустить какие-либо пространства! Мы также проходим в атрибуте , неподписанном результат, который получит парсированное значение.

The full cpp file for this example can be found here: ../../example/qi/roman.cpp


PrevUpHomeNext

Статья Roman Numerals раздела Spirit 2.5.2 Tutorials может быть полезна для разработчиков на c++ и boost.




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



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


реклама


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

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 20:06:42/0.010717868804932/1