Парсер не будет полным без обработки ошибок. Spirit2 позволяет легко адаптировать грамматику для обработки ошибок. На этот раз мы завернем учебник Qi другой версией мини-парсера xml с обработкой ошибок.
The full cpp file for this example can be found here: ../../example/qi/mini_xml3.cpp
Вот грамматика:
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::on_error;
using qi::fail;
using ascii::char_;
using ascii::string;
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
<< val(" here: \"")
<< construct<std::string>(_3, _2)
<< 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> start_tag;
qi::rule<Iterator, void(std::string), ascii::space_type> end_tag;
};
Что нового?
Во-первых, когда мы называем базовый класс, мы даем грамматике имя:
: mini_xml_grammar::base_type(xml, "xml")
Назовем все наши правила:
xml.name("xml");
node.name("node");
text.name("text");
start_tag.name("start_tag");
end_tag.name("end_tag");
<on_success
>объявляет обработчика, который применяется, когда правило успешно соответствует.
on_success(rule, handler)
Это указывает на то, что обработчик будет вызван, когда правило будет успешно согласовано. Обработчик имеет следующую подпись:
void handler(
fusion::vector<
Iterator& first,
Iterator const& last,
Iterator const& i> args,
Context& context)
<first
>указывает на положение во входной последовательности до соответствия правилу.<last
>указывает на последнюю позицию во входной последовательности.<i
>указывает на положение во входной последовательности после последнего символа, который был поглощен правилом.
Успешный обработчик может использоваться для аннотирования каждого согласованного правила в грамматике с дополнительной информацией о части ввода, которая соответствует правилу. В приложении компилятора это может быть комбинация файла, номера строки и номера столбца из потока ввода для сообщения диагностики или других сообщений.
<on_error
>Объявляет обработчик ошибок:
on_error<Action>(rule, handler)
Это будет определять, что мы будем делать, когда получим ошибку. Мы распечатаем сообщение об ошибке с помощью феникса:
on_error<fail>
(
xml
, std::cout
<< val("Error! Expecting ")
<< _4
<< val(" here: \"")
<< construct<std::string>(_3, _2)
<< val("\"")
<< std::endl
);
<fail
>В нашем примере<Action
>: Бросай и проваливай. Возврат No_match (ложь). Он может быть одним из:
<rule
>является правилом, к которому прилагается обработчик. В нашем случае мы придерживаемся правила<xml
>.
<handler
>является функцией обработки ошибок. Ожидаются 4 аргумента:
Возможно, вы не заметили этого, но некоторые из наших выражений изменились с<>>
>до<>
>. Посмотрите, например:
end_tag =
"</"
> lit(_r1)
> '>'
;
Что это? Этоожиданиеоператор. В грамматике у вас будут некоторые «детерминированные точки». Это те места, где отступлениене может произойти. Для нашего примера выше, когда вы получаете<"</"
>, вы определенно должны увидеть действительную конечную метка. Это должно быть то, что вы получили от начальной отметки. После этого у вас обязательно должен быть следующий<'>'
>. В противном случае нет смысла продолжать и пробовать другие отрасли, независимо от того, где они находятся. Вклад, безусловно, ошибочен. Когда это происходит, выпадает исключение «ожидание_неудача». Где-то вовне обработчик ошибок поймает исключение.
Попробуйте построить парсер:../../пример/qi/mini_xml3.cpp. Вы можете найти несколько примеров в../../пример/qi/mini_xml_samplesдля целей тестирования. "4.toyxml" имеет ошибку в нем:
<foo><bar></foo></bar>
Приведем пример с этим:
Error! Expecting "bar" here: "foo></bar>"
Error! Expecting end_tag here: "<bar></foo></bar>"
-------------------------
Parsing failed
-------------------------