Парсинг грамматики с Духом Повышения

Я пытаюсь проанализировать 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)
)

9
задан Peter Mortensen 13 July 2010 в 12:41
поделиться

1 ответ

Что касается отладки, можно использовать нормальный подход к остановке и наблюдению. Это усложняется тем, как вы отформатировали правила. Если вы форматируете в соответствии с примерами духа (~ один синтаксический анализатор на строку, один оператор феникса на строку), точки останова будут гораздо более информативными.

В вашей структуре данных нет способа отличить 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;
};

Это большое преимущество использования структуры, обернутой слиянием, которая тщательно моделирует ввод.

12
ответ дан 2 November 2019 в 23:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: