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

Porting from Spirit 1.8.x

Boost , Spirit 2.5.2 , Notes

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

Текущая версияSpiritявляется полным переписыванием более ранних версий (мы называем более ранние версииSpirit.Classic).. Парсерные генераторы теперь являются лишь частью всей библиотеки. Парсерный субмодульДухатеперь называется.Дух.Qi. Он концептуально отличается и раскрывает совершенно другой интерфейс. Как правило, нет простого (или автоматизированного) способа преобразования парсеров, написанных дляSpirit.toSpirit.Qi. Поэтому в этом разделе можно дать только рекомендации о том, как подойти к переносу старых парсеров на текущую версиюSpirit..

Include Files

Общая структура каталоговSpiritописана в разделеВключить структуруи запись FAQHeader Hell. Это должно дать вам хороший обзор того, как найти необходимые файлы заголовков для ваших новых парсеров. В каждой из нихСсылка на Qiсодержит файлы, необходимые для любого конкретного компонента.

Из названия файла заголовка можно узнать, к какой версии он принадлежит. Хотя все основные файлы включаютSpirit.Classicимеют строку 'classic_' в своем названии, например:

#include <boost/spirit/include/classic_core.hpp>

Мы назвали все основные файлы дляSpirit.Qi, чтобы строка «qi_» была частью их имени, например:

#include <boost/spirit/include/qi_core.hpp>

В следующей таблице приведен примерный список соответствующего файла заголовка междуSpirit.ClassicиSpirit.Qi, но это может быть использовано только в качестве отправной точки, так как несколько компонентов либо были перемещены в различные подмодули, либо могут больше не существовать в версии никогда. Список включает в себя только файлы для самых верхних подмодулей. Для файлов заголовков, необходимых для более низкоуровневых компонентов, пожалуйста, обратитесь к соответствующей справочной документации этого компонента.

classic.hpp

<qi.hpp>

classic_actor.hpp

<classic_attribute.hpp>

classic_core.hpp

<qi_core.hpp>

classic_debug.hpp

qi_debug.hpp

<classic_dynamic.hpp>

Нет, пользуйтесьSpirit.Qiпредикаты вместо if_p, тогда как_p, for_p (включено<qi_core.hpp>), эквивалент для lazy_p теперь включен<qi_auxiliary.hpp>

<classic_error_handling.hpp>

<qi_core.hpp>

classic_meta.hpp

Нет

classic_symbols.hpp

<qi_core.hpp>

classic_utility.hpp

Нет, не частьSpirit.Qiбольше, эти компоненты будут добавлены с течением времени вRepository

.
The Free Parse Functions

Изменены функции свободного разбора (основной API-интерфейс). Это включает в себя названия бесплатных функций, а также их интерфейс. ВSpirit.Classicвсе свободные функции были названы<parse>. ВSpirit.Qiони называются либо<qi::parse>, либо<qi::phrase_parse>в зависимости от того, следует ли делать разбор с помощью шкипера<qi::phrase_parse>или нет<qi::parse>. Все свободные функции возвращаются простым<bool>. Возврат<true>означает успех (т.е. парсер совпал) или<false>(т.е. парсер не совпадал). Это эквивалентно бывшему старому<parse_info>члену<hit>.Spirit.Qiбольше не поддерживает отслеживание согласованной длины входа. Старый<parse_info>элемент<full>можно эмулировать путем сравнения итераторов после возвращения<qi::parse>.

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

#include <boost/spirit/include/classic.hpp>
#include <boost/spirit/include/phoenix1.hpp>
#include <iostream>
#include <string>

using namespace boost::spirit::classic;

Дух.Qi:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <iostream>
#include <string>
#include <algorithm>

using namespace boost::spirit;

Следующие аналогичные примеры должны прояснить различия. Первый пример вSpirit.Classic:

std::string input("1,1");
parse_info<std::string::iterator> pi = parse(input.begin(), input.end(), int_p);
if (pi.hit)
    std::cout << "successful match!\n";
if (pi.full)
    std::cout << "full match!\n";
else
    std::cout << "stopped at: " << std::string(pi.stop, input.end()) << "\n";
std::cout << "matched length: " << pi.length << "\n";

И вот эквивалентная часть кода, использующаяSpirit.Qi:

std::string input("1,1");
std::string::iterator it = input.begin();
bool result = qi::parse(it, input.end(), qi::int_);
if (result)
    std::cout << "successful match!\n";
if (it == input.end())
    std::cout << "full match!\n";
else
    std::cout << "stopped at: " << std::string(it, input.end()) << "\n";
// seldomly needed: use std::distance to calculate the length of the match
std::cout << "matched length: " << std::distance(input.begin(), it) << "\n";

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

std::string input(" 1, 1");
parse_info<std::string::iterator> pi = parse(input.begin(), input.end(), int_p, space_p);
if (pi.hit)
    std::cout << "successful match!\n";
if (pi.full)
    std::cout << "full match!\n";
else
    std::cout << "stopped at: " << std::string(pi.stop, input.end()) << "\n";
std::cout << "matched length: " << pi.length << "\n";

И вот эквивалентный пример вSpirit.Qi:

std::string input(" 1, 1");
std::string::iterator it = input.begin();
bool result = qi::phrase_parse(it, input.end(), qi::int_, ascii::space);
if (result)
    std::cout << "successful match!\n";
if (it == input.end())
    std::cout << "full match!\n";
else
    std::cout << "stopped at: " << std::string(it, input.end()) << "\n";
// seldomly needed: use std::distance to calculate the length of the match
std::cout << "matched length: " << std::distance(input.begin(), it) << "\n";

Обратите внимание, что парсеры символов находятся в отдельном пространстве имен (здесь<boost::spirit::ascii::space>) какSpirit.Qiтеперь поддерживает работу с различными наборами символов. См. разделПространство имен символовдля получения дополнительной информации.

Naming Conventions

ВSpirit.Classicвсе парсерные примитивы имеют суффиксы, прилагаемые к их именам, кодирующие их тип:<"_p">для парсеров,<"_a">для ленивых действий,<"_d">для директив и т. д. ВSpirit.Qiу нас нет ничего подобного. Единственными суффиксами являются отдельные буквы подчеркивания<"_">, применяемые там, где имя в противном случае противоречило бы ключевому слову или предопределенному имени (например,<int_>для целого парсера). В целом, большинство, если не все примитивные парсеры и директивы были переименованы. См.Qi Quick Referenceдля обзора имен различных доступных примитивов, директив и операторов.

Parser Attributes

Spirit.Classic. raw[] директива раскрывает пару итераторов указывает на соответствие последовательности его встроенного парсера. Даже если мы очень много поощрять вас переписать ваши парсеры, чтобы воспользоваться полученным специфические атрибуты, иногда полезно получить доступ к лежащая в основе последовательность ввода. .

Grammars and Rules

Типы<grammar<>>и<rule<>>одинаково важны дляДуха.Qi, как и дляДуха.Классика. Их основная цель все та же: они позволяют определять нетерминалы и являются основными строительными блоками для более сложных парсеров. Тем не менее, оба типа были переработаны, и их интерфейсы изменились. Давайте сначала рассмотрим два примера, а затем объясним различия. Вот простая грамматика и ее использование вSpirit.Classic:

struct roman : public grammar<roman>
{
    template <typename ScannerT>
    struct definition
    {
        definition(roman const& self)
        {
            hundreds.add
                ("C"  , 100)("CC"  , 200)("CCC"  , 300)("CD" , 400)("D" , 500)
                ("DC" , 600)("DCC" , 700)("DCCC" , 800)("CM" , 900) ;
            tens.add
                ("X"  , 10)("XX"  , 20)("XXX"  , 30)("XL" , 40)("L" , 50)
                ("LX" , 60)("LXX" , 70)("LXXX" , 80)("XC" , 90) ;
            ones.add
                ("I"  , 1)("II"  , 2)("III"  , 3)("IV" , 4)("V" , 5)
                ("VI" , 6)("VII" , 7)("VIII" , 8)("IX" , 9) ;
            first = eps_p         [phoenix::var(self.r) = phoenix::val(0)]
                >>  (  +ch_p('M') [phoenix::var(self.r) += phoenix::val(1000)]
                    ||  hundreds  [phoenix::var(self.r) += phoenix::_1]
                    ||  tens      [phoenix::var(self.r) += phoenix::_1]
                    ||  ones      [phoenix::var(self.r) += phoenix::_1]
                    ) ;
        }
        rule<ScannerT> first;
        symbols<unsigned> hundreds;
        symbols<unsigned> tens;
        symbols<unsigned> ones;
        rule<ScannerT> const& start() const { return first; }
    };
    roman(unsigned& r_) : r(r_) {}
    unsigned& r;
};

std::string input("MMIX");        // MMIX == 2009
unsigned value = 0;
roman r(value);
parse_info<std::string::iterator> pi = parse(input.begin(), input.end(), r);
if (pi.hit)
    std::cout << "successfully matched: " << value << "\n";

И вот аналогичная грамматика и ее использование вSpirit.Qi:

template <typename Iterator>
struct roman : qi::grammar<Iterator, unsigned()>
{
    roman() : roman::base_type(first)
    {
        hundreds.add
            ("C"  , 100)("CC"  , 200)("CCC"  , 300)("CD" , 400)("D" , 500)
            ("DC" , 600)("DCC" , 700)("DCCC" , 800)("CM" , 900) ;
        tens.add
            ("X"  , 10)("XX"  , 20)("XXX"  , 30)("XL" , 40)("L" , 50)
            ("LX" , 60)("LXX" , 70)("LXXX" , 80)("XC" , 90) ;
        ones.add
            ("I"  , 1)("II"  , 2)("III"  , 3)("IV" , 4)("V" , 5)
            ("VI" , 6)("VII" , 7)("VIII" , 8)("IX" , 9) ;
        // qi::_val refers to the attribute of the rule on the left hand side 
        first = eps          [qi::_val = 0]
            >>  (  +lit('M') [qi::_val += 1000]
                ||  hundreds [qi::_val += qi::_1]
                ||  tens     [qi::_val += qi::_1]
                ||  ones     [qi::_val += qi::_1]
                ) ;
    }
    qi::rule<Iterator, unsigned()> first;
    qi::symbols<char, unsigned> hundreds;
    qi::symbols<char, unsigned> tens;
    qi::symbols<char, unsigned> ones;
};

std::string input("MMIX");        // MMIX == 2009
std::string::iterator it = input.begin();
unsigned value = 0;
roman<std::string::iterator> r;
if (qi::parse(it, input.end(), r, value))
    std::cout << "successfully matched: " << value << "\n";

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

  • Ни грамматика, ни правила больше не зависят от типа сканера, оба зависят только от основного типа итератора. Это означает, что ужасный бизнес сканера больше не проблема!
  • Грамматики больше не имеют встроенного класса<definition>
  • Грамматики и правила могут иметь явный тип атрибута, указанный в их определении.
  • Грамматики больше не имеют четких правил запуска. Вместо этого одно из содержащихся правил используется в качестве правила начала по умолчанию.

The Scanner Business () долгое время был проблемой. Грамматика и типы правил были специально переработаны, чтобы избежать этой проблемы в будущее. Это также означает, что нам не нужна отсроченная мгновенная связь. внутреннего определения класса в грамматике. Так что редизайн не только помог решить давнюю проблему дизайна, это помогло упростить вещи значительно. .

ВсеSpirit.Qiпарсерные компоненты имеют четко определенные типы атрибутов. Грамматика и правила не являются исключением. Но поскольку оба должны быть достаточно общими, чтобы их можно было использовать для любого парсера, их тип атрибута должен быть четко указан. В примере выше грамматики<roman>и правила<first>оба имеют атрибут<unsigned>:

// grammar definition
template <typename Iterator>
struct roman : qi::grammar<Iterator, unsigned()> {...};
// rule definition
qi::rule<Iterator, unsigned()> first;

Используемая нотация напоминает определение типа функции. Это очень естественно, поскольку вы можете думать о синтезированном атрибуте грамматики и правиле как о его «возвращенной стоимости». На самом деле и правило, и грамматика «вернут» неподписанное значение — то значение, которое они соответствовали.

[Note]Note

Нотация типа функции позволяет также задавать параметры. Они интерпретируются как типы унаследованных атрибутов, которые, как ожидается, будут переданы во время разбора. Для получения дополнительной информации см. раздел о наследственных и синтезированных атрибутах для правил и грамматикАтрибуты.

Если нет желаемого атрибута, не нужно указывать. Тип атрибута по умолчанию как для грамматики, так и для правил<unused_type>, который является особым типом заполнителя. Как правило, использование<unused_type>в качестве атрибута парсера интерпретируется как «этот парсер не имеет атрибута». Это в основном используется для парсеров, применяемых к частям входа, не несущим никакой существенной информации, а скорее являющимся разграничителями или структурными элементами, необходимыми для правильной интерпретации входа.

Последнее различие может показаться довольно косметическим и незначительным. Но оказывается, что отсутствие необходимости указывать, какое правило в грамматике является правилом начала (путем возвращения его из функции<start()>), также означает, что любое правило в грамматике может быть непосредственно использовано в качестве правила начала. Тем не менее, базовый класс грамматики инициализируется с правилом, которое он должен использовать в качестве правила начала, если экземпляр грамматики непосредственно используется в качестве парсера.


PrevUpHomeNext

Статья Porting from Spirit 1.8.x раздела Spirit 2.5.2 Notes может быть полезна для разработчиков на c++ и boost.




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



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


реклама


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

Время компиляции файла: 2024-08-30 11:47:00
2025-05-20 03:18:47/0.0073530673980713/0