Я пытаюсь проанализировать C-функцию как древовидные выражения как следующее (использующий Платформу Синтаксического анализатора Духа):
F( A() , B( GREAT( SOME , NOT ) ) , C( YES ) )
Для этого я пытаюсь использовать три правила о следующей грамматике:
template< typename Iterator , typename ExpressionAST >
struct InputGrammar : qi::grammar {
InputGrammar() : InputGrammar::base_type( ) {
tag = ( qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9") )[ push_back( at_c<0>(qi::_val) , qi::_1 ) ];
command = tag [ at_c<0>(qi::_val) = at_c<0>(qi::_1) ] >> "(" >> (*instruction >> ",")
[ push_back( at_c<1>(qi::_val) , qi::_1 ) ] >> ")";
instruction = ( command | tag ) [qi::_val = qi::_1];
}
qi::rule< Iterator , ExpressionAST() , space_type > tag;
qi::rule< Iterator , ExpressionAST() , space_type > command;
qi::rule< Iterator , ExpressionAST() , space_type > instruction;
};
Заметьте, что мое правило тега просто пытается получить идентификаторы, используемые в выражениях ('функциональные' имена). Также заметьте, что подпись правила тега возвращает a ExpressionAST
вместо a std::string
, как в большинстве примеров. Причина я хочу сделать это как это, на самом деле довольно проста: Я очень не хочу использовать варианты, и я избегу их, если это возможно. Было бы замечательно сохранить пирог, и съесть его также я предполагаю.
Команда должна запуститься с тега (название текущего узла, сначала представить поле в виде строки узла AST), и переменное количество аргументов, включенных круглыми скобками, и каждым из аргументов может быть сам тег или другая команда.
Однако этот пример не работает вообще. Это компилирует и все, но во время выполнения этому не удается проанализировать все мои тестовые строки. И вещь, которая действительно раздражает меня, состоит в том, что я не могу изобразить, как зафиксировать его, так как я не могу действительно отладить вышеупомянутый код, по крайней мере, в традиционном значении слова. В основном единственный способ, которым я вижу, что могу исправить вышеупомянутый код, путем знания того, что я делаю неправильно.
Так, вопрос состоит в том, что я не знаю что не так с вышеупомянутым кодом. Как Вы определили бы вышеупомянутую грамматику?
ExpressionAST
введите я использую:
struct MockExpressionNode {
std::string name;
std::vector< MockExpressionNode > operands;
typedef std::vector< MockExpressionNode >::iterator iterator;
typedef std::vector< MockExpressionNode >::const_iterator const_iterator;
iterator begin() { return operands.begin(); }
const_iterator begin() const { return operands.begin(); }
iterator end() { return operands.end(); }
const_iterator end() const { return operands.end(); }
bool is_leaf() const {
return ( operands.begin() == operands.end() );
}
};
BOOST_FUSION_ADAPT_STRUCT(
MockExpressionNode,
(std::string, name)
(std::vector, operands)
)
Что касается отладки, можно использовать нормальный подход к остановке и наблюдению. Это усложняется тем, как вы отформатировали правила. Если вы форматируете в соответствии с примерами духа (~ один синтаксический анализатор на строку, один оператор феникса на строку), точки останова будут гораздо более информативными.
В вашей структуре данных нет способа отличить A ()
от НЕКОТОРЫЕ
в том смысле, что они оба являются листьями (дайте мне знать, если я что-то упустил). Судя по вашему варианту комментария, я не думаю, что это было вашим намерением, поэтому, чтобы различать эти два случая, я добавил переменную-член bool commandFlag
в MockExpressionNode (верно для A ()
и false для НЕКОТОРЫЕ
) с соответствующей линией адаптера слияния.
В частности, для кода вам необходимо передать стартовое правило базовому конструктору, то есть :
InputGrammar() : InputGrammar::base_type(instruction) {...}
Это точка входа в грамматику, и именно поэтому вы не получали никаких данных. Я удивлен, что он скомпилирован без него, я думал, что тип грамматики должен соответствовать типу первого правила. Тем не менее, это удобное соглашение.
Для правила тега
на самом деле существует два парсера qi :: char _ ("a-zA-Z _")
, то есть _1 с типом char
и * qi :: char _ ("a-zA-Z_0-9")
, который является _2 с типом (в основном) vector
.Невозможно преобразовать их в строку без автоправил, но это можно сделать, прикрепив правило к каждому анализируемому символу:
tag = qi::char_("a-zA-Z_")
[ at_c<0>(qi::_val) = qi::_1 ];
>> *qi::char_("a-zA-Z_0-9") //[] has precedence over *, so _1 is
[ at_c<0>(qi::_val) += qi::_1 ]; // a char rather than a vector<char>
Однако гораздо проще позволить духу выполнять это преобразование. Итак, определите новое правило:
qi::rule< Iterator , std::string(void) , ascii::space_type > identifier;
identifier %= qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
И не беспокойтесь об этом;). Тогда тег становится
tag = identifier
[
at_c<0>(qi::_val) = qi::_1,
ph::at_c<2>(qi::_val) = false //commandFlag
]
Для команды первая часть подходит, но есть пара проблем с (* инструкция >> ",") [push_back (at_c <1> (qi :: _ val), qi :: _1)]
. Это приведет к синтаксическому анализу нуля или нескольких правил инструкций, за которыми следует ",". Он также пытается отправить обратно вектор
(не уверен, почему это скомпилировано, возможно, не было создано из-за отсутствия правила запуска?). Я думаю, вам нужно следующее (с изменением идентификатора):
command =
identifier
[
ph::at_c<0>(qi::_val) = qi::_1,
ph::at_c<2>(qi::_val) = true //commandFlag
]
>> "("
>> -(instruction % ",")
[
ph::at_c<1>(qi::_val) = qi::_1
]
>> ")";
Здесь используется необязательный оператор -
и оператор списка %
, последний эквивалентен инструкции > > * ("," >> инструкция)
. Затем выражение phoenix просто назначает вектор непосредственно члену структуры, но вы также можете присоединить действие непосредственно к сопоставлению инструкций и использовать push_back.
Правило инструкций в порядке, я просто упомяну, что оно эквивалентно инструкции% = (command | tag)
.
И последнее: если на самом деле нет различий между A ()
и НЕКОТОРЫЕ
(то есть вашей исходной структурой без commandFlag
), вы можете написать этот синтаксический анализатор, использующий только автоправила:
template< typename Iterator , typename ExpressionAST >
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), ascii::space_type> {
InputGrammar() : InputGrammar::base_type( command ) {
identifier %=
qi::char_("a-zA-Z_")
>> *qi::char_("a-zA-Z_0-9");
command %=
identifier
>> -(
"("
>> -(command % ",")
>> ")");
}
qi::rule< Iterator , std::string(void) , ascii::space_type > identifier;
qi::rule< Iterator , ExpressionAST(void) , ascii::space_type > command;
};
Это большое преимущество использования структуры, обернутой слиянием, которая тщательно моделирует ввод.