![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
TechniquesBoost , ,
Templatized FunctorsДля универсальности часто лучше сделать участника функтора оператором.Шаблон. Таким образом, нам не нужно беспокоиться о типе аргумента, который следует ожидать, пока поведение является уместным. Например, вместо жесткого кодированияchar const*в качестве аргумента родового семантического действия лучше сделать его шаблонной функцией члена. Таким образом, он может принимать любой тип итератора:
Обратите внимание, что это возможно только с функторами. Невозможно передать шаблонные функции в виде семантических действий, если вы не подставите его под правильную подпись функции; в этом случае вымономорфизируетефункцию. Это ясно показывает, что функторы превосходят простые функции. Rule With Multiple ScannersПри использовании v1.8.0 можно использовать один или несколько типов сканеров. Например, есть случаи, когда нам нужно правило, которое может работать на уровне фраз и символов. Несоответствие правил/сканеров было источником путаницы и является «нет». 1FAQ. Для решения этой проблемы у нас теперь естьподдержка нескольких сканеров. Вот пример грамматики с правиломr, которую можно назвать с 3 типами сканеров (уровень фразы, лексема и нижний регистр). См.правило,грамматика,lexeme_scannerикак_lower_scannerдля получения дополнительной информации. Вот грамматика (см.multiple_scanners.cpp): struct my_grammar : grammar<my_grammar> { template <typename ScannerT> struct definition { definition(my_grammar const& self) { r = lower_p; rr = +(lexeme_d[r] >> as_lower_d[r] >> r); } typedef scanner_list< ScannerT , typename lexeme_scanner<ScannerT>::type , typename as_lower_scanner<ScannerT>::type > scanners; rule<scanners> r; rule<ScannerT> rr; rule<ScannerT> const& start() const { return rr; } }; }; По умолчанию поддержка нескольких сканеров отключена. МакроBOOST_SPIRIT_RULE_SCANNERTYPE_LIMITдолжно быть определено максимальное количество сканеров, разрешенных в списке сканеров. Значение должно быть больше 1 для обеспечения работы нескольких сканеров. В приведенном выше примере для определения предела из трех сканеров списка перед включением заголовков Spirit в исходный файл должна быть вставлена следующая строка: #define BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT 3 Look Ma' No RulesТы используешь грамматики и много их используешь? Хотите грамматику без холестерина, супероптимизированную? Читайте в... У меня отношения любви и ненависти с правилами. Думаю, вы знаете причины. Многие проблемы связаны с ограничением правил. Динамический полиморфизм и статический полиморфизм в C++ плохо сочетаются. В C++ нет понятия виртуальных шаблонных функций, по крайней мере, пока. Таким образом, правилопривязано к определенному типу сканера. Это приводит к таким проблемам, какбизнес сканера, наш No 1 FAQ. Кроме того, виртуальные функции в правилах замедляют разбор, убивают всю метаинформацию и убивают наложение, следовательно, раздувая сгенерированный код, особенно для очень маленьких правил, таких как: r = ch_p('x') >> uint_p; Ограничение правила является основной причиной, по которой грамматика разработана так, как она есть сейчас, с вложенным классом определения шаблона. Ограничение правила также является причиной существования подправил. Действительно ли нам нужны правила? Конечно! До того, как C++ примет некоторый вид дедукции автотипа, такой как предложенный Дэвидом Абрахамсом в clc++m:
Мы привязаны к правилу как держатели RHS. В некоторых случаях мы можем обойтись без правил! Вместо того, чтобы писать:
Лучше написать:
Это тривиально. Но что, если правило довольно сложное? Хорошо, давайте пошагово... Я изучу простой skip_parser на основе грамматики C от Хартмута Кайзера. В основном грамматика пишется как (см.no_rule1.cpp):
Хорошо, пока так хорошо. Мы можем сделать лучше? Так как там нет рекурсивных правил (на самом деле есть только одно правило), вы можете расширить тип RHS правила как тип правила (см.no_rule2.cpp):
Ухххх! Как я это сделал? Как мне удалось попасть в сложный типдеф? Я сумасшедший? Ну, на самом деле, нет... есть трюк! То, что вы делаете, это сначала определите typedefskip_tкак int:
Попробуйте компилировать. Затем компилятор сгенерирует неприятное сообщение об ошибке, такое как:
Вот ты и идешь!У тебя такой тип! Я просто копирую и вставляю правильный тип (удаление явных квалификаций). Мы можем пойти дальше? Да. Помните, что грамматика была разработана для правил. Класс определения вложенного шаблона необходим, чтобы обойти ограничения правила. Без правил я предлагаю новый класс, называемыйsub_grammar, аналог грамматики с низким содержанием жира:
С классомsub_grammarмы можем определить нашу грамматику шкипера таким образом (см.no_rule3.cpp):
Но зачем, спросите вы? Вы можете просто использовать типstart_tвыше as-is. Это уже парсер! Мы можем просто напечатать:
Щиппер, как и любой парсер? Ну, тонкая разница в том, чтошкипер, используемый таким образом, будет встроенпо значению, когдавы сочиняете более сложные парсеры, используя его. То есть, если мы используемшкиперавнутри другого производства, то вся вещь будет храниться в композите. Тяжело! ПредлагаемаяподграммаOTOH будет содержать ссылку. Примечание:
Предлагаемаяподграммане имеет присущих ей ограничений правил, имеет очень большой вес и должна быть невероятно быстрой (может быть полностью выстроена и не использует виртуальные функции). Возможно, этот класс станет частью будущего духовного освобождения.
typeofНекоторые компиляторы уже поддерживаюттипключевого слова. Примерами являются g++ и Metrowerks CodeWarrior. Когда-нибудьтипстанет обычным явлением. Стоит отметить, что мы можем использоватьтипдля определения нерекурсивных правил без использования класса правил. Для примера мы будем использовать пример шкипера выше; на этот раз с использованиемтипа. Во-первых, чтобы избежать избыточности, мы введем макросRULE:
Тогда просто:
(см.typeof.cpp) Вот так! Теперь вы можете использовать шкипера так же, как любой парсер. Следует помнить, однако, чтошкипервыше будет встроен в значение, когдавы сочиняете более сложные парсеры, используя его (см.подграммаобоснование выше). Вы можете использовать классsub_grammar, чтобы избежать этой проблемы. Nabialek trickЭтот метод, я назову"Набиалек трюк"(от имени его изобретателя, Сэма Набиалека), может улучшить отправку правил от линейного недетерминированного к детерминистическому. Хитрость применима к грамматике, где ключевое слово (оператор и т.д.) предшествует производству. Есть много грамматик, похожих на это: r = keyword1 >> production1 | keyword2 >> production2 | keyword3 >> production3 | keyword4 >> production4 | keyword5 >> production5 /*** etc ***/ ; Каскадные альтернативы проверяются по одному за раз методом проб и ошибок, пока что-то не совпадет. Хитрость Набиалека использует свойства поиска таблицы символовдля оптимизации отправки альтернатив. Например, см.nabialek.cpp. Грамматика работает следующим образом. Есть два правилаодинидва. Когда [[[[]]][[[[[]]]][[[[[]]]]][[[[[[[]]]]]][[[[[[[[[]]]]]]][[[[[[[[[[]]]]]]]][[[[[[[[[[[]]]]]]]]]][[[[[[[[[[[[[]]]]]]]]]][[[[[[[[[[[[[]]]]]]]]]][[[[[[[[[[[[[[]]]]]]]]]]][[[[[[[[[[[[[[]]]]]]]]]]][[[[[[[[[[[[[[[[]]]]]]]]]]]][[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]] Когда [[[[]]][[[[[]]]][[[[[]]]]][[[[[[[]]]]]][[[[[[[[]]]]]]][[[[[[[[[]]]]]]]][[[[[[[[[]]]]]]]][[[[[[[[[[[[]]]]]]]]][[[[[[[[[[[[[]]]]]]]]]][[[[[[[[[[[[]]]]]]]]]][[[[[[[[[[[[]]]]]]]]]]][[[[[[[[[[[[[[]]]]]]]]]]][[[[[[[[[[[[[]]]]]]]]]]][[[[[[[[[]]]]]]]]]][[[[[[[[[[ Вот грамматика: one = name; two = name >> ',' >> name; continuations.add ("one", &one) ("two", &two) ; line = continuations[set_rest<rule_t>(rest)] >> rest; где продолжения - таблица символовс указателем на слоты rule_t. Один, два, имя, линия и отдых - правила: rule_t name; rule_t line; rule_t rest; rule_t one; rule_t two; symbols<rule_t*> continuations; set_rest, смысловое действие, прилагаемое к продолжениям: template <typename Rule> struct set_rest { set_rest(Rule& the_rule) : the_rule(the_rule) {} void operator()(Rule* newRule) const { m_theRule = *newRule; } Rule& the_rule; }; Обратите внимание, как правило 108устанавливается динамически при вызове действия set_rule. Динамическая грамматика анализирует входы, такие как: "one only" Здоровая часть заключается в том, что правилопокояустанавливается (по действиюset_rest) в зависимости от того, что получила таблица символов. Если он получил"один", то отдых = один. Если он получил"два", то отдых = два. Очень изящно! Этот метод должен быть очень быстрым, особенно когда есть много ключевых слов. Было бы неплохо добавить специальные средства, чтобы сделать это простым в использовании. Представляю: r = keywords >> rest; гдеключевых слов- специальный парсер (на основе таблицы символов), который автоматически устанавливает свой RHS (отдых) в зависимости от приобретенного символа. Это, я думаю, очень круто! Возможно, когда-нибудь...
Copyright © 1998-2003 Joel de Guzman
Статья Techniques раздела может быть полезна для разработчиков на c++ и boost. Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта. :: Главная :: ::
|
|||||||||||||||||||||||||
©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007 |