Parsování textu pomocí knihovny Spirit

Spirit jako jedna z mnoha částí Boost knihoven je objektově orientovaný framework pro parsování textu. Velice snadno je možné pomoci syntaxe jazyka C++ vytvořit parser odpovídající zápisu gramatiky v rozšířené Backus-Naurově formě. Předpokladem pro efektivní používání je alespoň základní znalost šablon jazyka C++ a knihovny STL.

Elementární parsery

Spirit disponuje množstvím elementárních parserů, z kterých je možné pomocí různých operátorů skládat složitější pravidla. Příkladem elementárního parseru je real_p, který rozpozná číslo s plovoucí řádovou čárkou. Nejjednodušší funkční příklad by mohl vypadat např. takto:

#include <iostream>
#include <boost/spirit/core.hpp>

using namespace std;
using namespace boost::spirit;

int main() {
    parse_info<const char *> pInfo;

    pInfo = parse("ahoj", real_p, space_p);
    cout << pInfo.full << endl;        // pInfo.full = 0

    pInfo = parse("2.34", real_p, space_p);
    cout << pInfo.full << endl;        // pInfo.full = 1

    return 0;
}

Funkce parse

Funkce parse, která provede vlastní parsování, má jako první parametr řetězec s parsovaným textem, druhým parametrem je použitý parser a třetí parametr je tzv. skip parser. Jeho úkolem je určit, na jaké kusy textu bude parser aplikován. Použitý space_p rozpoznává tzv. bílé znaky, v našem příkladě tedy zjišťujeme, zda první slovo je reálné číslo.

Návratovou hodnotou je struktura parse_info, která obsahuje následující proměnné:

  • full – true v případě úplné shody, vstupní text byl rozpársován celý
  • hit – true v případě částečné shody. Vstupní text nemusel (ale mohl) být použit celý; pokud bysme tedy v našem příkladě parsovali text „2.34 je číslo“, proměnná full bude nastavená na false, hit bude true
  • length - počet rozparsovaných znaků; hodnota je platná pouze v případě částečné nebo úplné shody
  • stop – iterátor na pozici, po kterou byl vstupní text zpracován

Funkce parse je přetížená, její další prototypy lze nalézt v bohaté dokumentaci Spiritu.

Výběr elementárních parserů

Znakové parsery

anychar_p jakýkoliv znak
ch_p(char) zadaný znak
chset_p(charset) znak ze zadané množiny
range_p(char1, char2) znak z uvedeného intervalu (včetně)
space_p „bílý“ znak
blank_p mezera nebo tabulátor
print_p tisknutelný znak
graph_p tisknutelný znak mimo mezeru
alpha_p písmeno
digit_p číslice
alnum_p písmeno nebo číslice
lower_p malé písmeno
upper_p velké písmeno
xdigit_p hexadecimální číslice
punct_p interpunkční znak

Číselné parsery

real_p číslo s plovoucí řádovou čárkou
ureal_p číslo s plovoucí řádovou čárkou bez znaménka
strict_real_p číslo s plovoucí řádovou čárkou, musí obsahovat desetinnou tečku
int_p celé číslo
uint_p celé číslo bez znaménka
int_parser<type, base, min, max> číslo se základem číselné soustavy base s minimálním a maximálním počtem číslic min a max

Ostatní parsery

eol_p jakákoliv kombinace CR, LF
end_p EOF - end of file
nothing_p neodpovídá ničemu, porovnání vždy selže
regex_p(regex) regulární výraz
str_p(string) řetězec
str_p(iter1, iter2) řetězec

Operátor >>

Tento binární operátor je klíčový pro vytváření složitějších pravidel, umožňuje spojovat elementární parsery. Zatímco jedno reálné číslo jsme rozeznali parserem real_p, na dvě po sobě následující nám poslouží tato konstrukce:

real_p >> real_p

Pokud budeme mít dvě čísla oddělená čárkou, po nahlédnutí do předchozího přehledu elementární parserů snadno sestavíme tento výraz:

real_p >> ch_p(',') >> real_p

Elementární parsery mají operátor >> přetížený tak, že jako parametr kromě dalšího parseru zvládnou přímo znak, a tak předchozí pravidlo můžeme zjednodušit takto:

real_p >> ',' >> real_p

Pokud bysme ovšem chtěli začít parsováním znaku, musíme použít ch_p, protože typ char samozřejmě nemá nadefinovaný operátor >> s parametrem parser. Obdobně, jak funguje toto zjednodušování s parserem ch_p a znaky, můžeme nakládat s parserem str_p a řetězci.

Operátor *

Tento unární operátor zařídí opakování následujícího výrazu 0 až n-krát (n je celé číslo větší než 0). Libovolný počet reálných čísel lze rozparsovat takto:

*real_p

Reálná čísla oddělená čárkami zvládne tento parser:

real_p >> *(ch_p(',') >> real_p)

Výběr ostatních operátorů

+P opakování P 1 až n-krát
!P P nebo prázdný řetězec
~P cokoliv jiného než P
P1 % P2 Jeden nebo více výskytů P1 oddělených P2
P1 – P2 P1, ale ne P2
P1 | P2 P1 nebo P2

Sémantické akce

V dosud uvedených příkladech byl zadaný text pouze rozpoznáván. Nyní si ukážeme, jak rozpoznaný text také zpracovat. Vše se děje uvedením funkce nebo funktoru do hranatých závorek za parser. Pokud je F následující funkce

void F(double n) {
    cout << n << endl;
}

rozpoznání a vypsání reálného čísla zajistí tento parser:

real_p[&F]

Pokud by F byl funktor, zápis by byl následující:

real_p[F]

Spirit má pro svoje účely nadefinované různé generátory funktorů (funkce, které vrací funktor). Jedním z nich je např. push_back_a, pomocí kterého lze přidat hodnotu z parseru na konec zadaného kontejneru. Následující příklad demonstruje parsování reálných čísel oddělených mezerami a uložení jejich hodnot do vektoru proměnných double.

#include <iostream>
#include <vector>
#include <boost/spirit/core.hpp>

using namespace std;
using namespace boost::spirit;

int main() {
    string text("56 45.5 -7.45 0.5");
    vector<double> dblV;

    if (!parse(text.c_str(), *real_p[push_back_a(dblV)], space_p).full) {
        cout << "Chyba!" << endl;
        return 1;
    }

    for(vector<double>::iterator i = dblV.begin(); i != dblV.end(); i++)
        cout << *i << endl;

    return 0;
}
Přehledy, informace a tak na i-prehled.cz; copyright © 2008 - 2017 Libor Smékal , zveřejněno 18. 1. 2009
Navštivte také Guitarra nebo Anglický jazyk.