Я застрял, пытаясь создать парсер Boost.Spirit для вывода инструмента callgrind, который является частью valgrind. Callgrind выводит специфичный для домена встроенный язык программирования (DSEL), который позволяет вам делать всякие интересные вещи, такие как пользовательские выражения для синтетических счетчиков, но это не так просто разобрать.
Я поместил некоторый пример вывода callgrind в https://gist.github.com/ned14/5452719#file-sample-callgrind-output. Я поместил мою лучшую попытку на лексер и анализатор Boost.Spirit на https://gist.github.com/ned14/5452719#file-callgrindparser-hpp а также https://gist.github.com/ned14/5452719#file-callgrindparser-cxx. Часть Lexer проста: она маркирует значения тегов, непробельный текст, комментарии, конец строк, целые числа, шестнадцатеричные числа, числа с плавающей точкой и операторы (игнорируйте знаки препинания в примере кода, они не используются). Пустое пространство пропущено.
Все идет нормально. Проблема заключается в разборе потока входных данных с токенами. Я еще даже не пробовал основные строфы, я все еще пытаюсь разобрать значения тегов, которые могут произойти в любой указать в файле. Значения тегов выглядят так:
tagtext: unknown series of tokens<eol>
Это может быть произвольный текст, например
desc: I1 cache: 32768 B, 64 B, 8-way associative, 157 picosec hit latency
В этой ситуации вы хотите преобразовать набор токенов в строку, т.е. в iterator_range и извлечь.
Однако это может быть выражение, например
event: EPpsec = 316 Ir + 1120 I1mr + 1120 D1mr + 1120 D1mw + 1362 ILmr + 1362 DLmr + 1362 DLmw
Это говорит о том, что с этого момента событие EPpsec должно быть синтезировано как Ir, умноженное на 316, добавленное к I1mr, умноженное на 1120, добавленное к … и т. Д.
Здесь я хочу подчеркнуть, что пары тег-значение должны накапливаться в виде произвольных наборов токенов и подвергаться последующей обработке во все, что мы превращаем в них позже.
С этой целью класс utree () Boost.Spirit выглядел в точности так, как я хотел, и это то, что использует пример кода. Но на VS2012 с использованием ноябрьского CTP-компилятора с переменными шаблонами в настоящее время я вижу эту ошибку компиляции:
1>C:\Users\ndouglas.RIMNET\documents\visual studio 2012\Projects\CallgrindParser\boost\boost/range/iterator_range_core.hpp(56): error C2440: 'static_cast' : cannot convert from 'boost::spirit::detail::list::node_iterator<const boost::spirit::utree>' to 'base_iterator_type'
1> No constructor could take the source type, or constructor overload resolution was ambiguous
1> C:\Users\ndouglas.RIMNET\documents\visual studio 2012\Projects\CallgrindParser\boost\boost/range/iterator_range_core.hpp(186) : see reference to function template instantiation 'IteratorT boost::iterator_range_detail::iterator_range_impl<IteratorT>::adl_begin<const Range>(ForwardRange &)' being compiled
1> with
1> [
1> IteratorT=base_iterator_type
1> , Range=boost::spirit::utree
1> , ForwardRange=boost::spirit::utree
1> ]
… который предполагает, что мой base_iterator_type, который является Boost.Spirit multi_pass<> Оберт istreambuf_iterator для прямой итераторной природы, как-то не понят реализацией utree () Boost.Spirit. Дело в том, что я не уверен, что это мой плохой код или плохой Boost.Spirit-код, который выглядит как line_pos_iterator<> не удалось правильно указать свой концептуальный тег forward_iterator.
Благодаря прошлой помощи Stackoverflow я мог написать чистую нетексенированную грамматику, но она была бы хрупкой. Правильное решение состоит в том, чтобы токенизировать и использовать грамматику произвольной формы, способную вводить произвольно. К сожалению, очень мало примеров того, как Лекс и Грамматика Boost.Spirit работают вместе на реальных примерах, а не на игрушечных примерах. Поэтому любая помощь будет принята с благодарностью.
Найл
Атрибут token предоставляет вариант, который, в дополнение к диапазону базовых итераторов, может _ссуждать типы, объявленные в token_type
ЬурейеЕ:
typedef lex::lexertl::token<base_iterator_type, mpl::vector<std::string, int, double>> token_type;
Так: string
, int
а также double
, Обратите внимание, что принуждение в один из возможных типов будет происходить только лениво, когда парсер на самом деле использует значение.
utree
это очень универсальный контейнер [1]. Следовательно, когда вы выставляете spirit::utree
атрибут по правилу и токен вариант значения содержит iterator_range, затем он пытается присвоить это в utree
объект (это не удается, потому что итераторы … «фанки»).
Самый простой способ получить желаемое поведение — сила Ци, чтобы интерпретировать атрибут tag
токен в виде строки, и есть тот назначен на utree
, Поэтому следующая строка представляет исправление, которое сделает компиляцию успешной:
unknowntagvalue = qi::as_string[tok.tag] >> restofline;
Сказав все это, я бы действительно предложил следующее
Рассмотрите возможность использования Nabialek Trick
отправлять разные ленивые правила в зависимости от tag
соответствует — это делает ненужным иметь дело с сырым utree
позже
Вы могли иметь успех, специализирующийся boost::spirit::traits::assign_to_XXXXXX
черты (см. документация)
рассмотреть возможность использования чистого парсера Qi. Хотя я могу «чувствовать» ваше чувство, что «оно будет ломким» [2] кажется, вы уже продемонстрировали, что это повышает сложность до такой степени, что может не иметь ничего общего:
lexer token semantic actions
в то время как часто устранение неоднозначности Фаза цино я расходлюсь 🙂
[1] например у них есть средства для очень легкой «ссылки» на диапазоны итераторов (например, для символов, или чтобы избежать копирование персонажей из исходного буфера в атрибут, если не требуется)
[2] По сути, только потому, что использование последовательного лексера (сканера) значительно уменьшает количество возможностей возврата, поэтому упрощает ментальную модель синтаксического анализатора. Тем не менее, вы можете использовать expectation points
к тому же эффекту.
Других решений пока нет …