Boost.Spirit, как расширить синтаксический анализ xml?

Я хотел бы расширить синтаксический анализ xml с помощью Boost.Spirit и добавить синтаксический анализ атрибутов xml.

Вот пример из библиотеки и некоторые модификации от меня:

template <typename Iterator>
struct mini_xml_grammar
: qi::grammar<Iterator, mini_xml(), qi::locals<std::string>, ascii::space_type>
{
    mini_xml_grammar()
    : mini_xml_grammar::base_type(xml, "xml")
    {
        using qi::lit;
        using qi::lexeme;
        using qi::attr;
        using qi::on_error;
        using qi::fail;
        using ascii::char_;
        using ascii::string;
        using ascii::alnum;
        using ascii::space;

        using namespace qi::labels;

        using phoenix::construct;
        using phoenix::val;


        text %= lexeme[+(char_ - '<')];
        node %= xml | text;


        start_tag %=
        '<'
        >>  !lit('/')
        >   lexeme[+(char_ - '>')]
        >   '>'
        ;

        end_tag =
        "</"
        >   string(_r1)
        >   '>'
        ;

        xml %=
        start_tag[_a = _1]
        >   *node
        >   end_tag(_a)
        ;

        xml.name("xml");
        node.name("node");
        text.name("text");
        start_tag.name("start_tag");
        end_tag.name("end_tag");

        on_error<fail>
        (
         xml
         , std::cout
         << val("Error! Expecting ")
         << _4                               // what failed?
         << val(" here: \"")
         << construct<std::string>(_3, _2)   // iterators to error-pos, end
         << val("\"")
         << std::endl
         );
    }

    qi::rule<Iterator, mini_xml(), qi::locals<std::string>, ascii::space_type> xml;
    qi::rule<Iterator, mini_xml_node(), ascii::space_type> node;
    qi::rule<Iterator, std::string(), ascii::space_type> text;
    qi::rule<Iterator, std::string(), ascii::space_type> attribute;
    qi::rule<Iterator, std::string(), ascii::space_type> start_tag;
    qi::rule<Iterator, void(std::string), ascii::space_type> end_tag;
};

Я пробовал это, но он не компилируется с ошибкой «использование необъявленного идентификатора« eps »»:

        xml %= 
        start_tag[_a = _1] 
        > attribute 
        > (  "/>" > eps
            |  ">" > *node > end_tag(_a) 
            )
        ;

кто нибудь знает как это сделать? Как добавить возможность анализировать атрибуты xml?


person ruslan.berliner    schedule 27.02.2012    source источник
comment
Существует множество вариантов для разбора XML в C++. Вам действительно нужно собрать парсер Boost.Spirit для этого?   -  person Nicol Bolas    schedule 28.02.2012
comment
Да, я знаю, я работал с другими библиотеками. Я хотел бы изучить Boost.Spirit, чтобы понять, как это работает. RapidXML кажется хорошей библиотекой, и кажется, что библиотека RapidXML не имеет полной поддержки разбора XML.   -  person ruslan.berliner    schedule 28.02.2012
comment
@NicolBolas Спасибо за ваш пост о XML-библиотеках, отличное исследование этого вопроса.   -  person ruslan.berliner    schedule 28.02.2012
comment
Обратите внимание, что ваше правило eps на самом деле ничего не делает в грамматике. Он появляется в конце правила. Правилам уже неявно следуют ничто. Используйте eps, когда вы хотите присоединить обработку атрибутов Spirit к части грамматики, которая иначе ничему не соответствует, или в качестве заполнителя в списке альтернатив.   -  person Rob Kennedy    schedule 28.02.2012


Ответы (1)


Идентификатор eps, как и многие другие используемые вами идентификаторы, определены в пространстве имен qi. Остальные вносятся в глобальное пространство имен с помощью операторов using в верхней части вашего конструктора. Сделайте то же самое для eps:

using qi::eps;

Как только вы решите это, у вас возникнет более серьезная проблема: правильно ли вы представляете синтаксис и грамматику XML. Не похоже, что ты прав. У вас есть это:

xml %= 
      start_tag[_a = _1]
    > attribute
    > (   "/>" > eps
        | ">" > *node > end_tag(_a)
      )
    ;

Однако это не может быть правильным. Атрибуты являются частью тега, а не элементами, которые следуют за тегом. Похоже, вы хотели разбить start_tag apppart, чтобы можно было обрабатывать пустые теги. Если бы я делал это, я бы, вероятно, вместо этого создал правило empty_tag, а затем изменил xml на empty_tag | (start_tag > *node > end_tag). Вот как это делает рекомендация W3C по языку :

[39]  element   ::= EmptyElemTag
                    | STag content ETag

Но пока не беспокойтесь об этом. Помните, что ваша заявленная задача состоит в том, чтобы добавить атрибуты в синтаксический анализатор. Не отвлекайтесь на другие недостающие функции. Есть много тех, над которыми можно поработать позже.

Я упомянул документ W3C. Вы должны обращаться к этому часто; он определяет язык и даже показывает грамматику. Одной из целей дизайна Spirit было то, что он должен выглядеть как определение грамматики. Используйте это в своих интересах, пытаясь имитировать грамматику W3C в своем собственном коде. W3C определяет начальный тег следующим образом:

[40]  STag      ::= '<' Name (S Attribute)* S? '>'
[41]  Attribute ::= Name Eq AttValue    

Итак, напишите свой код следующим образом:

start_tag %=
    // Can't use operator> for "expect" because empty_tag
    // will be the same up to the final line.
       '<'
    >> !lit('/')
    >> name
    >> *attribute
    >> '>'
    ;

name %= ...; // see below

attribute %=
      name
    > '='
    > attribute_value
    ;

Спецификация определяет синтаксис значений атрибутов:

[10]  AttValue  ::= '"' ([^<&"] | Reference)* '"'
                    |  "'" ([^<&'] | Reference)* "'"

Я бы пока не беспокоился о ссылках на сущности. Как и пустые теги, ваш текущий код уже не поддерживает их, поэтому не важно добавлять их сейчас как часть атрибутов. Это упрощает определение attribute_value:

attribute_value %=
      '"' > *(char_ - char_("<&\"")) > '"'
    | '\'' > *(char_ - char_("<&'")) > '\''
    ;

Определение name пока не должно быть чем-то причудливым. Это сложно в спецификации, потому что обрабатывает весь диапазон символов Unicode, но вы можете начать с чего-то более простого и вернуться к нему позже, когда выясните, как обрабатывать символы Unicode во всем вашем синтаксическом анализаторе.

name %=
    lexeme[char_("a-zA-Z:_") >> *char_("-a-zA-Z0-9:_")]
    ;

Эти изменения должны позволить вам анализировать XML-атрибуты. Однако совсем другое дело извлекать результаты в виде атрибутов Spirit (чтобы вы могли знать имена и значения атрибутов для данного тега в остальной части вашей программы) и Я не готов обсуждать это прямо сейчас.

person Rob Kennedy    schedule 27.02.2012
comment
Большое спасибо, Роб! Отличный ответ! Да, вы правы, я тоже пытаюсь обрабатывать пустые теги. Теперь я немного больше понимаю про Boost.Spirit. Кажется, писать грамматику не так уж и сложно. - person ruslan.berliner; 28.02.2012