Die Boost C++ Bibliotheken

Parser

Nachdem Sie im vorherigen Abschnitt die Funktionen kennengelernt haben, die Boost.Spirit zum Parsen anbietet, erfahren Sie im Folgenden, wie Sie Parser entwickeln. Sie greifen dabei üblicherweise auf existierende Parser zu – wie boost::spirit::ascii::digit oder boost::spirit::ascii::space. Indem Sie von Boost.Spirit angebotene Parser kombinieren, erstellen Sie Parser, die komplexe Formate beschreiben. Sie gehen dabei ähnlich vor, wie wenn Sie reguläre Ausdrücke erstellen, die ebenfalls aus Grundbausteinen zusammengesetzt werden.

Beispiel 11.5. Ein Parser für zwei aufeinander folgende Ziffern
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>

using namespace boost::spirit;

int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  bool match = qi::phrase_parse(it, s.end(), ascii::digit >> ascii::digit,
    ascii::space);
  std::cout << std::boolalpha << match << '\n';
  if (it != s.end())
    std::cout << std::string{it, s.end()} << '\n';
}

Im Beispiel 11.5 soll eine Eingabe auf zwei Ziffern überprüft werden. boost::spirit::qi::phrase_parse() soll nur dann true zurückgeben, wenn zwei Ziffern eingegeben werden. Leerzeichen werden ignoriert.

Um Ziffern erkennen zu können, wird wie in den vorherigen Beispielen auf boost::spirit::ascii::digit zugegriffen. Weil boost::spirit::ascii::digit genau ein Zeichen überprüft, wird dieser Parser zweimal hintereinander angegeben, um eine Eingabe auf zwei Ziffern zu überprüfen. Damit boost::spirit::ascii::digit zweimal hintereinander angegeben werden kann, muss ein entsprechender Operator verwendet werden. Boost.Spirit überlädt operator>>, um Parser verknüpfen zu können. Mit der Angabe ascii::digit >> ascii::digit wird ein Parser erstellt, der einen String daraufhin überprüft, ob er zwei Ziffern enthält.

Wenn Sie das Beispiel ausführen und zwei Ziffern eingeben, wird true ausgegeben. Geben Sie nur eine Ziffer ein, wird false ausgegeben.

Beachten Sie, dass auch dann true ausgegeben wird, wenn Sie zwischen den beiden eingegebenen Ziffern Leerzeichen angeben. Überall dort, wo in einem Parser >> verwendet wird, können Zeichen stehen, die von boost::spirit::qi::phrase_parse() ignoriert werden. Weil im Beispiel als Skipper boost::spirit::ascii::space an boost::spirit::qi::phrase_parse() übergeben wird, dürfen zwischen den beiden Ziffern beliebig viele Leerzeichen stehen.

Soll der Parser die Eingabe nur dann akzeptieren, wenn die Ziffern direkt hintereinander stehen, können Sie entweder boost::spirit::qi::parse() verwenden oder die Direktive boost::spirit::qi::lexeme.

Beispiel 11.6. Mit boost::spirit::qi::lexeme Zeichen für Zeichen parsen
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>

using namespace boost::spirit;

int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  bool match = qi::phrase_parse(it, s.end(),
    qi::lexeme[ascii::digit >> ascii::digit], ascii::space);
  std::cout << std::boolalpha << match << '\n';
  if (it != s.end())
    std::cout << std::string{it, s.end()} << '\n';
}

Im Beispiel 11.6 ist der Parser mit qi::lexeme[ascii::digit >> ascii::digit] angegeben. boost::spirit::qi::phrase_parse() gibt nur dann true zurück, wenn zwei Ziffern direkt hintereinander eingegeben werden – ohne trennende Leerzeichen.

boost::spirit::qi::lexeme ist eine von mehreren zur Verfügung stehenden Direktiven, mit denen das Verhalten von Parsern geändert werden kann. Sie verwenden boost::spirit::qi::lexeme, wenn Sie dort, wo der Operator operator>> angegeben ist, keine Zeichen dulden, die vom Skipper ignoriert würden.

Beispiel 11.7. Boost.Spirit-Regeln wie reguläre Ausdrücke
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>

using namespace boost::spirit;

int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  bool match = qi::phrase_parse(it, s.end(), +ascii::digit, ascii::space);
  std::cout << std::boolalpha << match << '\n';
  if (it != s.end())
    std::cout << std::string{it, s.end()} << '\n';
}

Im Beispiel 11.7 wird mit +ascii::digit ein Parser erstellt, der mindestens eine Ziffer erwartet. Diese Syntax ähnelt regulären Ausdrücken, in denen das Pluszeichen ebenfalls bedeutet, dass ein Zeichen oder eine Zeichengruppe mindestens einmal vorhanden sein muss. Wenn Sie das Beispiel ausführen und mindestens eine Ziffer eingeben, wird true ausgegeben. Dabei spielt keine Rolle, ob Sie die Ziffern durch Leerzeichen trennen. Soll der Parser lediglich Ziffern akzeptieren, die direkt hintereinander eingegeben werden, verwenden Sie wieder boost::spirit::qi::lexeme.

Beispiel 11.8. Nummerische Parser
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>

using namespace boost::spirit;

int main()
{
  std::string s;
  std::getline(std::cin, s);
  auto it = s.begin();
  bool match = qi::phrase_parse(it, s.end(), qi::int_, ascii::space);
  std::cout << std::boolalpha << match << '\n';
  if (it != s.end())
    std::cout << std::string{it, s.end()} << '\n';
}

Beispiel 11.8 erwartet die Eingabe einer Ganzzahl. Bei boost::spirit::qi::int_ handelt es sich um einen nummerischen Parser, der sowohl positive als auch negative Ganzzahlen lesen kann. Im Gegensatz zu einem Parser wie boost::spirit::ascii::digit überprüft boost::spirit::qi::int_ nicht nur ein Zeichen. Er kann zusammenhängende Zeichen wie +1 oder -23 als Ganzzahl erkennen.

Boost.Spirit bietet neben boost::spirit::qi::int_ weitere logische Parser an. Mit boost::spirit::qi::float_, boost::spirit::qi::double_ und boost::spirit::qi::bool_ stehen nummerische Parser zur Verfügung, die Kommazahlen und Wahrheitswerte lesen können. Mit boost::spirit::qi::eol existiert ein Parser, der auf ein Zeilenende überprüft. Mit boost::spirit::qi::byte_ und boost::spirit::qi::word können ein oder zwei Bytes gelesen werden. boost::spirit::qi::word und andere binäre Parser, die mehr als ein Byte lesen, berücksichtigen automatisch die Byte-Reihenfolge – auf Englisch endianness – der Plattform. Sie können mit boost::spirit::qi::little_word und boost::spirit::qi::big_word auch auf Parser zugreifen, die eine Byte-Reihenfolge voraussetzen.