BoostSpirit,如何扩展xml解析

Boost.Spirit, how to extend xml parsing?

本文关键字:扩展 xml 解析 何扩展 BoostSpirit      更新时间:2023-10-16

我想使用BoostSpirit扩展xml解析,并添加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属性的能力?

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属性是另一回事(这样你就可以在程序的其余部分中知道给定标签的属性名称和值),我现在不准备讨论这个问题。