Boost.Spirit, как расширить парсинг xml? - PullRequest
1 голос
/ 28 февраля 2012

Я хотел бы расширить синтаксический анализ 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?

1 Ответ

2 голосов
/ 28 февраля 2012

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

using qi::eps;

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

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

Хотя это не может быть правдой.Атрибуты являются частью тега, а не вещами, которые следуют за тегом.Похоже, вы хотели сломать start_tag приложение, чтобы вы могли обрабатывать пустые теги.Если бы я делал это, я бы, вероятно, вместо этого создал бы правило 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 (чтобы вы могли знать имена и значения атрибутов для данного тега в остальной части вашей программы), и я 'Я не готов обсуждать это прямо сейчас.

...