![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
FAQBoost , ,
Q Вопрос: Почему это не компиляция?
Но если я убираю шки-парсера, все возвращается к норме снова:
Иногда вы захотите передать правило одной из функций, которые предоставляет Дух. Проблема в том, что правило - это класс шаблонов, который параметризируется типом сканера. Это довольно неловко, но неизбежно: правило привязано к сканеру. Что не очевидно, так это то, что этот сканер должен быть совместим с сканером, который в конечном итоге передается функции члена правила. В противном случае компилятор будет жаловаться. Почему первый звонок не компилируется? Из-за несовместимости сканера. За кулисами функция свободного парса создает сканер из итераторов, проходящих внутрь. В первом вызове для парза, созданный сканер является простой ваниль scanner<. Это совместимо с типом сканера по умолчанию правило<> [см. параметры шаблона по умолчанию правило]. Второй вызов создает сканер типа phrase_scanner_t. Таким образом, для того, чтобы второй вызов был успешным, правило должно быть параметризировано как правило
Обратите внимание, однако, что phrase_scanner_t совместим только тогда, когда вы используете char const* итераторов и space_p в качестве шкипера. Кроме того, вам придется найти правильный тип сканера. Это утомительно делать правильно. В свете этого вопроса лучше избегать правил как аргументов к функциям парса. Имейте в виду, что это происходит только с правилами. Правило является единственным парсером, который должен быть привязан к конкретному типу сканера. Например: parse("hello world", *anychar_p);
Q Вопрос: Я портировал грамматику из YACC. Это работа " Kinda " - сам парсер компилируется без ошибок. Но когда я пытаюсь разобраться, это дает мне " невероятную ошибку страницы " . Я нашел проблему для этого грамматического фрагмента: or_expr = xor_expr | (or_expr >> VBAR >> xor_expr); То, что вы должны сделать, это устранить прямое и косвенное левое восстание. Это вызывает неправильную ошибку страницы, потому что программа входит в бесконечный цикл. Код выше хорош для нижней вверх парсеров, таких как YACC, но не для LL-парсеров, таких как Дух. Это похоже на правило в Hartmut Kaiser's C parser (это должно быть доступно для загрузки с сайта Spirit, как только вы читаете это). inclusive_or_expression = exclusive_or_expression | inclusive_or_expression >> OR >> exclusive_or_expression ; Преобразование левой рекурсии в правую рекурсию, у нас есть: inclusive_or_expression = exclusive_or_expression >> inclusive_or_expression_helper ; inclusive_or_expression_helper = OR >> exclusive_or_expression >> inclusive_or_expression_helper | epsilon_p ; Я бы пошел дальше. После: r = a | epsilon_p; эквивалентно: r = !a; мы можем упростить инклюзивный_или_экспрессия_помощник таким образом: inclusive_or_expression_helper = !(OR >> exclusive_or_expression >> inclusive_or_expression_helper) ; Теперь, с: r = !(a >> r); эквивалентно: r = *a; мы имеем: inclusive_or_expression_helper = *(OR >> exclusive_or_expression) ; Теперь упрощая инклюзивный_или_выражение, мы имеем: inclusive_or_expression = exclusive_or_expression >> *(OR >> exclusive_or_expression) ; Напоминает мне калькуляторов. Короче говоря: a = b | a >> op >> b; в pseudo-YACC is: a = b >> *(op >> b); в Духе. Что может быть проще? Слушай, Ма, никаких рекурсий, только итерация. Реализация правильной ассоциации Q Вопрос: Я попытался добавить '^' в качестве оператора для вычисления мощности калькулятора граммера. Следующий код pow_expression = pow_operand >> *( '^' >> pow_operand [ & do_pow ] ) ; правильно анализирует вход, но я хочу, чтобы оператор был эвакуирован справа налево. Другими словами, выражение 2^3 должно иметь ту же семантику, что и 2^(3^4) вместо (2^3)^4. Как мне это сделать? «Рецепт учебника» для правильной ассоциативности - это правая рекурсия. В BNF это означает:
Но нам лучше не принимать теорию слишком буквально здесь, потому что если первая альтернатива терпит неудачу, семантические действия в рамках pow_operand могли быть выполнены уже, а затем будут казнены снова при попытке второй альтернативы. Итак, давайте применим левую факторизацию к фактору pow_operand:
Производство pow_expression_helper соответствует пустой строке ε, поэтому мы можем заменить альтернативный код Spirit. pow_expression = pow_operand >> !( '^' >> pow_expression [ & do_pow ] ) ; Теперь любые семантические действия в пределах pow_operand могут быть безопасно выполнены. Для оценки на основе стека, что означает, что каждый матч pow_operand оставит одно значение на стеке, и рекурсия гарантирует, что на стеке есть (по крайней мере) два значения, когда do_pow будет запущен, чтобы уменьшить эти два значения до их мощности. В тех случаях, когда эта методика не применима, например, присвоение в стиле C назначение = l value > = > | ternary_ conditional ; вы можете добавить | epsilon_p [ action ] >> nothing_p к парсеру, чтобы исправить семантический контекст при обратном слежении (в примере, который будет сбрасывать адрес, вытесненный l value из стека оценки): assignment = lvalue >> ( '=' >> assignment [ & do_store ] | epsilon_p [ & do_drop ] >> nothing_p ) | ternary_conditional ; Тем не менее, этот трюк ставит под угрозу четкое разделение синтаксиса и семантики, поэтому вы также можете рассмотреть возможность использования AST вместо семантических действий, чтобы вы могли просто перейти к первому определению уступки. Q Вопрос: Не поддерживает ли lexeme_d выражения, включающие правила? В приведенном ниже примере, определение атомного рулевого компилируется, rule<phrase_scanner_t> atomicRule = lexeme_d[(alpha_p | '_') >> *(alnum_p | '.' | '-' | '_')]; но если я перехожу alnum_p | '. | '-' | '_' в свое правило, компилятор жалуется на преобразование из const-сканер<...> в const expression_scaner_t&. rule<phrase_scanner_t> ch = alnum_p | '.' | '-' | '_'; rule<phrase_scanner_t> compositeRule = lexeme_d[(alpha_p | '_') >> *(ch)]; // <- error source Вы можете создать впечатление, что директива и правила lexeme_d не смешиваются. На самом деле, эта проблема связана с первой записью FAQ: The Scanner Business. Точнее, директива lexeme_d и правила с несовместимыми типами сканеров не смешиваются. Эта проблема более тонкая. То, что вызывает несовместимость сканера, является самой директивой. Директива lexeme_d преобразует сканер, который он получает, во что-то, что отключает шкипер. Этот нескользящий сканер, к сожалению, несовместим с оригинальным сканером до того, как произошла трансформация. Самое простое решение - не использовать правила в lexeme_d. Вместо этого вы можете определенно применить lexeme_d к подправилам и грамматикам, если вам действительно нужны более сложные парсеры внутри lexeme_d. Если вы действительно должны использовать правило, вам нужно знать точный сканер, используемый директивой. Метафункция lexeme_scanner - это ваш друг. Приведенный выше пример будет работать так, как ожидалось, как только мы дадим правилу ch правильный тип сканера: rule<lexeme_scanner<phrase_scanner_t>::type> ch = alnum_p | '.' | '-' | '_'; Примечание: следует добавить "имя типа " перед lexeme_scanner, когда это используется внутри класса шаблона или функции. То же самое происходит, когда правила используются внутри директивы as_lower_d. В таких случаях можно использовать as_lower_scanner. См. lexeme_scanner и as_lower_scanner.
Клейн Звезда бесконечная петля QВопрос: Почему это навсегда? rule<> optional = !(str_p("optional")); rule<> list_of_optional = *optional; Проблема в том, что звезда клейна будет продолжать петли, пока она не получит ни одного матча от закрытого парсера. Поскольку правило в обязательном порядке является необязательным, оно всегда возвращает матч. Даже если вход не соответствует " необязательно " , он вернет матч с нулевой длиной. list_of_факультативно будет оставаться необязательным, так как необязательный никогда не вернет флажок. Таким образом, в целом любое правило, которое может быть < < нулевым > > (что означает, что оно может вернуть матч с нулевой длиной), не должно быть помещено внутрь кленовой звезды. Q Вопрос: Существует Boost CVS и Spirit CVS. Что используется для дальнейшего развития Духа? Как правило, развитие происходит в CVS Духа. Однако время от времени новая версия Духа будет интегрирована в Boost. Когда это происходит развитие происходит в Boost CVS. Будут объявления в списках рассылки Духов всякий раз, когда меняется статус Духовного CVS.
Как уменьшить время компиляции со сложной грамматикой Духа Q Вопрос: Существуют ли какие-либо методы для минимизации времени компиляции с использованием духа? Для простых парсеров время компиляции не кажется большой проблемой, но недавно я создал парсер с примерно 78 правилами и потребовалось около 2 часов для компиляции. Я хотел бы разбить грамматику на мелкие кусочки, но это не так просто, как я думал, потому что правила в двух грамматических капсулах определяются друг с другом. Есть мысли? Единственный способ уменьшить время компиляции - это
Первая задача - просто логистика, вторая - скорее техническая. Хороший пример решения первой задачи приведен в примере Spirit cpp_lexer, написанном JCAB (вы можете найти его в хранилище приложений). Проблемы перекрестного ссылок могут быть решены посредством какого-то передового заявления, или, если это не работает, путем введения некоторого аргумента шаблона манекена в нетемблированные грамматики. Таким образом, позволяет откладывать время мгновенных сообщений до тех пор, пока компилятор не увидит все дефиниции: template <typename T = int> Вторая задача немного сложнее. Вы должны убедиться, что в первом компиляционном блоке компилятор видит только некоторую функцию / шаблон декларацию и во втором компиляционном блоке функцию / шаблон определение. До сих пор нет проблем, если нет шаблонов. Если шаблоны задействованы, необходимо вручную (объясненно) мгновеннировать эти шаблоны с правильными параметрами шаблона внутри отдельного блока компиляции. Таким образом, время компиляции делится между несколькими единицами компиляции, что значительно снижает общее требуемое время. Для образца, показывая, как этого достичь, вы можете посмотреть на библиотеку Wave, где эта техника широко используется. (это должно быть доступно для загрузки с сайта Spirit, как только вы читаете это). Q Вопрос: Когда я запускаю парсер, я получаю утверждение "frame.get() != 0 in file closures.hpp". Что я делаю неправильно? В основном, утверждение гасит, когда вы получаете доступ к переменной закрытия, которая еще не построена. Вот пример. У нас есть три правила a, b и c. Рассмотрим, что правило a имеет член закрытия m. Теперь: a = b; b = int_p[a.m = 123]; c = b; Когда применяется правило a, его рама устанавливается вместе с его членом m. Таким образом, когда b называется от a, семантическое действие [a.m = 123] будет хранить 123 в a закрытие m. С другой стороны, когда используется ссылка на c, и c пытается вызвать b, не установлена рама для a. Таким образом, когда b называется от c, семантическое действие [a.m = 123] вызовет пожар "frame.get()!= 0 в файле closures.hpp" Утверждение. QВопрос: Я задаюсь вопросом, почему это не сработает при разборе: a = +anychar_p; b = '(' >> a >> ')'; Попробуйте: a = +(anychar_p - ')'); b = '(' >> a >> ')'; Дэвид Хелл пишет: Это потому, что это как ланголиры - он все ест. Обычно вы хотите сказать, что он не должен есть, вычитая вымирающий персонаж из парсера. Моральное существо: Использование *anychar_p или +anychar_p Bad Thing™. Иными словами: Recursive Descent по своей сути жадный (однако, см. Exhaustive backtracking и жадный RD). Соблюдение правила во время строительства Q Вопрос: Код ниже заканчивается с ошибкой сегментации, но я (очевидно) запутался в том, что я делаю неправильно. rule<ScannerT, clos::context_t> id = int_p[id.i = arg1]; У вас есть правило id, которое строится. Прежде чем он будет построен, вы ссылаетесь на id.i в RHS конструктора. Это курица и яйца. Закрытие id.i пока не построено. Использование задания решит проблему. Попробуйте вместо этого: rule<ScannerT, clos::context_t> id; id = int_p[id.i = arg1]; Q Вопрос: Почему я не могу хранить правила в контейнерах STL для последующего использования и почему я не могу передавать и возвращать правила к функциям и от них по стоимости? EBNF в основном декларативный. Как в функциональном программировании, Это статичный рецепт, и нет никакого представления об этом. Тем не менее, в Spirit нам удалось коаксировать императив C++, чтобы принять в декларативном EBNF. Ха! Забавно... Мы сделали это, маскируя оператора назначения C++ для имитации EBNF ::=, среди прочего (например, >, |, & и т.д.). Мы использовали класс правил, чтобы позволить нам сделать это, давая его оператору уступки (и конструктору копий) другой смысл и семантику. Это сделало правило не похожим на любой другой объект C++. Ты не можешь это копировать. Ты не можешь его назначить. Вы не можете разместить его в контейнере (вектор, стек и т.д.). Черт, вы даже не можете вернуть его из функции *по стоимости *.
Как бы ни был хорош декларатив EBNF, динамический характер C++ может быть преимуществом. Мы видели это в действии здесь и там. Действительно, есть несколько интересных применений динамических парсеров, использующих Дух. Тем не менее, мы не полностью использовали силу динамического парсинга, если (!), у нас есть правило, которое не так чуждо C++ (то есть ведет себя как хороший объект C++). С таким зверем мы можем писать парсеры, которые определены во время бега, в отличие от времени компиляции. Теперь, когда я начал фокусироваться на правилах (хей, ознакомьтесь с неуклюжими новыми функциями правила), это может быть хорошее время для реализации правила-держателя. Это в основном просто правило, но с семантикой объекта C++. Но это не так просто. Без настоящей уборки мусора реализация будет немного сложной. Мы не можем просто использовать контрольный счет, потому что владелец правила (хей, кто-то здесь имеет лучшее имя?) *это-а* правило, и правила, как правило, рекурсивные и, следовательно, циклические. Проблема в том, кто будет владеть. Ладно... это пока будет сделано. В ближайшие дни вы обязательно увидите больше правителей. QВопрос: Я пытался оценить значение t или поплавков с директивой longest_d и поставить некоторые актеры на альтернативы для визуализации результатов. Когда я анализирую " 123.456 " , отчет о результатах:
Я этого не ожидал. Что мне не хватает? На самом деле проблема заключается в том, что оба семантических действия внутренней и реальной ветви будут срабатывать, потому что обе ветви будут испытаны. Это не очень нас покупает. Что на самом деле выигрывает в конце, так это то, что вы ожидали. Но нет простого способа узнать, кто победит. Проблема проистекает из двусмысленности.
Вместо того, чтобы использовать longest_d для анализа интов и реалов, я предлагаю устранить двусмысленность и использовать простые альтернативы короткого замыкания. Первый шаг - использовать strict_real_p сделать первый случай однозначным. В отличие от real_p, strict_real_p требует наличия точки для того, чтобы число считалось успешным. Ваша грамматика может быть написана однозначно: strict_real_p | int_p
Обратите внимание, что, поскольку двусмысленность решена, прикрепление к обеим отраслям безопасно. Срабатывает только один: strict_real_p[R] | int_p[I]
Опять же, как правило, всегда лучше решить как можно больше двусмысленности. Лучшими грамматиками являются те, которые не включают обратное отслеживание вообще: грамматика LL(1). Назад и семантические действия не смешиваются хорошо. BOOST_SPIRIT_DEBUG и отсутствует оператор< QВопрос: Мой код компилируется в режиме выпуска, но когда я пытаюсь определить BOOST_SPIRIT_DEBUG компилятор жалуется на отсутствующий оператор<<. Когда BOOST_SPIRIT_DEBUG определяется вывод отладки для парсеров духов. С этой целью ожидается, что каждый участник закрытия имеет оператор по умолчанию. Вы можете обеспечить перегрузку оператора либо в пространстве имен, где объявляется класс (будет найден через Argument Dependent Lookup), либо сделать его видимым, где он используется, то есть namespace boost::spirit. Вот пример для std::пар:
Применения, которые раньше были частью духа Q Вопрос: Где я могу найти Старые версии Духа включали приложения, построенные с ним. Для оптимизации распределения они были перемещены в отдельный репозиторий приложений. На этой странице вы найдете ссылки на полные приложения, которые используют структуру Spirit parser. Мы рекомендуем вам отправлять в свои собственные заявки на включение (см. страницу для инструкций). Вы также можете проверить репозиторий граммов.
Copyright © 1998-2003 Joel de Guzman
Статья FAQ раздела может быть полезна для разработчиков на c++ и boost. Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта. :: Главная :: ::
|
||||||||||||||||||||
©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007 |