Różnice między wybraną wersją a wersją aktualną.
Both sides previous revision Previous revision Next revision | Previous revision Last revision Both sides next revision | ||
spirit [2008/04/16 07:15] bwojcies |
spirit [2008/04/17 00:11] bwojcies |
||
---|---|---|---|
Linia 1: | Linia 1: | ||
+ | |||
====== Biblioteka Boost Spirit ====== | ====== Biblioteka Boost Spirit ====== | ||
Bartłomiej Wojcieszek G1SST | Bartłomiej Wojcieszek G1SST | ||
Linia 5: | Linia 6: | ||
Parser, zwany także analizatorem składniowym, pozwala na analize danych wejściowych i określenie czy są zgodne z daną gramatyką. Zazwyczaj parsery są wykorzystywane przy przetwarzaniu danych zrozumialych dla czlowieka w dane najbardziej wygodne dla komputera. | Parser, zwany także analizatorem składniowym, pozwala na analize danych wejściowych i określenie czy są zgodne z daną gramatyką. Zazwyczaj parsery są wykorzystywane przy przetwarzaniu danych zrozumialych dla czlowieka w dane najbardziej wygodne dla komputera. | ||
- | Biblioteka Spirit pozwala tworzyć własne złożone parsery lub wykorzytywać te najprostsze wbudowane w bibliotekę. Szablony wyrażeń zastosowanych w bibliotece pozwalają na zapis w notcacji bardzo podobnej do EBNF (Extended Backus-Normal Form): | + | Biblioteka Spirit pozwala tworzyć własne złożone parsery lub wykorzytywać te najprostsze wbudowane w bibliotekę. Szablony wyrażeń zastosowanych w bibliotece pozwalają na zapis w notacji bardzo podobnej do EBNF (Extended Backus-Normal Form): |
\\ Gramatyka EBNF: | \\ Gramatyka EBNF: | ||
<code cpp> | <code cpp> | ||
Linia 58: | Linia 59: | ||
- | ====Teraz trochę o gramatyce==== | + | |
+ | |||
+ | ====Teraz trochę o tworzeniu parserów==== | ||
Wyrażenia parsujące tworzy się za pomocą wbudowanych w bibliotekę prymitywów oraz operatorów. Kilka podstawowych prymitywów (więcej można znaleźć w dokumentacji): | Wyrażenia parsujące tworzy się za pomocą wbudowanych w bibliotekę prymitywów oraz operatorów. Kilka podstawowych prymitywów (więcej można znaleźć w dokumentacji): | ||
Linia 138: | Linia 141: | ||
Wywołana metoda ''full'' zwraca true jeżeli wszystkie dane zostały poprawnie sparsowane. | Wywołana metoda ''full'' zwraca true jeżeli wszystkie dane zostały poprawnie sparsowane. | ||
- | A co to za dziwne nawiasy kwadratowe za wyrażeniem? Jest to akcja semantyczna (ang. Semantic Action). Sam parser sprawdza tylko czy dane wyrażenie pasuje do wzorca, ale nic więcej z nim nie robi. Z pomocą przychodzą akcje semantyczne. Mogą być one dodane w dowolnym miejscu. Funkcja jest wywoływana w momencie, gdy parsowanie przebiegło pomyślnie. Jak to się robi?: | + | A co to za dziwne nawiasy kwadratowe za wyrażeniem? Jest to akcja semantyczna (ang. Semantic Action). Dzięki niej sparsowane liczby są przekazywane do wektora. Sam parser sprawdza tylko czy dane wyrażenie pasuje do wzorca, ale nic więcej z nim nie robi. Z pomocą przychodzą akcje semantyczne. Mogą być one dodane w dowolnym miejscu. Funkcja jest wywoływana w momencie, gdy parsowanie przebiegło pomyślnie. Jak to się robi?: |
<code cpp> | <code cpp> | ||
Linia 151: | Linia 154: | ||
</code> | </code> | ||
- | ==== Bardziej użyteczny przykład ==== | ||
+ | ====Reguły==== | ||
- | ==== Wykorzytanie funkcji ==== | + | Reguła czyli rule pozwala przechowywać skonstruowany przez nas parser. |
+ | Kilka przykładów: | ||
+ | |||
+ | <code cpp> | ||
+ | rule<> a_rule = *(ch_p('a') | ch_p('b')) & +(ch_p('c') | ch_p('d') | range_p('k','z')); | ||
+ | |||
+ | //można też odwołać się do innej reguły: | ||
+ | |||
+ | rule<> another_rule = int_p >> ch_p('a') >> str_p('bla') >> second_rule; | ||
+ | //należy tylko pamiętać,że jest to odwołanie przez referencję, do programisty więc należy pilnowanie | ||
+ | //aby dana reguła była w zasięgu i ciągle istniała | ||
+ | |||
+ | </code> | ||
+ | |||
+ | Dzięki regułom możemy tworzyć dynamiczne parsery w naszym programie, zalęznie od jego przebiegu: | ||
+ | |||
+ | <code cpp> | ||
+ | rule<> my_rule=digit_p; | ||
+ | |||
+ | if(some condition) | ||
+ | my_rule=str_p("blaBla"); | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ====Dyrektywy==== | ||
+ | |||
+ | Dyrektywy zmieniają zachowanie parsera. Jest kilka wbudowanych w bibliotekę dyrektyw, użytkownik ma także możliwość tworzenia własnych. | ||
+ | |||
+ | Przykłady: | ||
+ | |||
+ | <code cpp> | ||
+ | //Dyrektywa as_lower_d konwertuje wszystkie znaki z wejścia na małe znaki | ||
+ | rule = as_lower_d["hello"]; | ||
+ | //zaakceptuje "HeLlO" jak i "HELLO" oraz "hello" | ||
+ | |||
+ | //Dyrektywa lexeme_d wyłącza ignorowanie białych znaków, przydatna gdy chcemy wczytywać znaki oddzielone spacją np 1 2 345 bez tej dyrektywy może byc | ||
+ | //przyjęte jako 12345 a nie jako 3 liczby: | ||
+ | |||
+ | integer = lexeme_d[ !(ch_p('+') | '-') >> +digit ]; | ||
+ | |||
+ | //Dyrektywa limit_d ogranicza min i max w jakim może się znajdować parsowane wyrażenie | ||
+ | //Stwórzmy np. parser przyjmujący date w formacie: dd.mm.yyyy i zakładamy że interesują nas tylko daty z lat 2000 do 2010: | ||
+ | |||
+ | uint_parser<int, 10, 2, 2> uint2_p; | ||
+ | uint_parser<int, 10, 4, 4> year; | ||
+ | |||
+ | lexeme_d | ||
+ | [ | ||
+ | limit_d(1, 31)[uint2_p] >> '.' // dni od 1 do 31 | ||
+ | >> limit_d(1, 12)[uint2_p] >> '.' // miesiace od 1 do 12 | ||
+ | >> limit_d(2000,2010)[year] // rok ograniczony latami 2000 do 2010 | ||
+ | ] | ||
+ | |||
+ | |||
+ | </code> | ||
+ | |||
+ | |||
+ | ==== Akcje semantyczne ==== | ||
+ | |||
+ | Jak już wcześniej wspomniałem możemy użyć funkcji które będą wyzwalane w momencie pozytywnego sparsowania. Dzięki temu można łatwo tworzyć program w którym po sparsowaniu analizujemy to co było no wejściu. Za pomocą funkcji możemy także generować osrtrzeżenia o niepoprawnych argumentach itp. | ||
+ | Prosty przykład: | ||
+ | |||
+ | <code cpp> | ||
+ | void | ||
+ | action(char const* first, char const* last) | ||
+ | { | ||
+ | std::string str(first, last); | ||
+ | std::cout << str << std::endl; | ||
+ | } | ||
+ | |||
+ | rule<> myrule = (ch_p(a) | int_p >> *(int_p >> ch_p('x')))[&action]; | ||
+ | //w momencie poprawnego sparsowania, funkca wypisze w postaci stringa wszystkie sparsowane elementy | ||
+ | </code> | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==== Zbierzmy wszystko razem ==== | ||
+ | Po tych podstawach, przydałoby się napisać krótki program ilustrujący faktyczne działanie biblioteki Spirit. Podany program zamienia formatowanie w stylu wiki na dokument html. Aby rozpocząc wprowadzanie należy otworzyć klamrę { a po zakończonym wprowadzaniu zamknąć klamrą }. Podobnie jak w wiki nagłówki oznacza się za pomocą symboli "=" linki tak jak w wiki, kod za pomocą tagów <coding> </coding>. Program jest bardzo uproszczony, ale latwo mozna dodac więcej rozpoznawanych przez niego tagów. | ||
+ | |||
+ | |||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | |||
+ | #include <boost/spirit/core.hpp> | ||
+ | #include <iostream> | ||
+ | #include <string> | ||
+ | |||
+ | |||
+ | using namespace std; | ||
+ | using namespace boost::spirit; | ||
+ | |||
+ | //////////////////////////////////////////////////////////////////////////// | ||
+ | //TUTAJ PODAJEMY AKCJE JAKIE BEDA WYKONYWANE W ZALEZNOSCI OD WEJSCIA | ||
+ | //////////////////////////////////////////////////////////////////////////// | ||
+ | |||
+ | void | ||
+ | h1Action(char const* first,char const* last) | ||
+ | { | ||
+ | std::string str(first, last); | ||
+ | std::cout << "<h1>"<<str.substr(4,str.length()-8)<<"</h1>" << std::endl; | ||
+ | } | ||
+ | void h2Action(char const* first,char const* last) | ||
+ | { | ||
+ | std::string str(first, last); | ||
+ | std::cout << "<h2>"<<str.substr(3,str.length()-6)<<"</h2>" << std::endl; | ||
+ | } | ||
+ | void h3Action(char const* first,char const* last) | ||
+ | { | ||
+ | std::string str(first, last); | ||
+ | std::cout << "<h3>"<<str.substr(2,str.length()-4)<<"</h3>" << std::endl; | ||
+ | } | ||
+ | void h4Action(char const* first,char const* last) | ||
+ | { | ||
+ | std::string str(first, last); | ||
+ | std::cout << "<h4>"<<str.substr(1,str.length()-2)<<"</h4>" << std::endl; | ||
+ | } | ||
+ | void href(char const* first,char const* last) | ||
+ | { | ||
+ | std::string str(first, last); | ||
+ | std::cout << "<a href="+str.substr(2,str.find("|",2)-2)+">"+str.substr(str.find("|",2)+1,str.length()-5-(str.substr(2,str.find("|",2)-2)).length())+"</a>" << std::endl; | ||
+ | } | ||
+ | |||
+ | void code(char const* first,char const* last) | ||
+ | { | ||
+ | |||
+ | std::string str(first, last); | ||
+ | std::cout << "<div style=\"border-width: 1px; border-style: dashed; border-color: blue; \">"+str.substr(6,str.length()-13)+"</div>" << std::endl; | ||
+ | } | ||
+ | |||
+ | void tail(char const* first,char const* last) | ||
+ | { | ||
+ | std::string str(first, last); | ||
+ | std::cout << "</body>" << std::endl << "</html>" << std::endl; | ||
+ | } | ||
+ | void head(char const first) | ||
+ | { | ||
+ | std::cout <<"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""<<std::endl; | ||
+ | std::cout <<"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1i-strict.dtd\">"<<std::endl; | ||
+ | std::cout<<"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"pl\">"<<std::endl; | ||
+ | std::cout<<"<head>"<<std::endl<< "<link rel=\"stylesheet\" type=\"text/css\" href=\"main.css\" />"<<std::endl; | ||
+ | std::cout<<"</head>"<<std::endl<<"<body>"<<std::endl; | ||
+ | } | ||
+ | |||
+ | /////////////////////////////////////////////////////////////////////////////// | ||
+ | //TUTAJ DEFINIUJEMY REGULY PARSOWANIA | ||
+ | /////////////////////////////////////////////////////////////////////////////// | ||
+ | |||
+ | struct html : public grammar<html> | ||
+ | { | ||
+ | template <typename ScannerT> | ||
+ | struct definition | ||
+ | { | ||
+ | definition(html const& ) | ||
+ | { | ||
+ | header | ||
+ | = ch_p('{')[&head]; | ||
+ | tag | ||
+ | = (str_p("====")>>(*(~ch_p('=')))>>str_p("===="))[&h1Action] | ||
+ | | (str_p("===")>>(*(~ch_p('=')))>>str_p("==="))[&h2Action] | ||
+ | | (str_p("==")>>(*(~ch_p('=')))>>str_p("=="))[&h3Action] | ||
+ | | (str_p("=")>>(*(~ch_p('=')))>>str_p("="))[&h4Action] | ||
+ | | (str_p("<coding>")>>*(anychar_p-str_p("</coding>"))>>str_p("</coding>"))[&code] | ||
+ | | (str_p("[[")>>(*((~ch_p(']'))-ch_p('|')))>>ch_p('|')>>(*(~ch_p(']')))>>str_p("]]"))[&href]; | ||
+ | document | ||
+ | = header>>(*tag>>ch_p('}'))[&tail]; | ||
+ | |||
+ | |||
+ | |||
+ | } | ||
+ | |||
+ | rule<ScannerT> tag, document, header; | ||
+ | |||
+ | rule<ScannerT> const& | ||
+ | start() const { return document; } | ||
+ | }; | ||
+ | }; | ||
+ | |||
+ | |||
+ | int | ||
+ | main() | ||
+ | { | ||
+ | cout << "Simple parsing example\n"; | ||
+ | cout << "Type \"quit\" to exit program\n"; | ||
+ | |||
+ | html htmlParser; // Our parser | ||
+ | |||
+ | string str,tmp; | ||
+ | while (getline(cin, str)) | ||
+ | { | ||
+ | if (str=="quit") | ||
+ | break; | ||
+ | if(str[str.length()-1]=='}') | ||
+ | { | ||
+ | tmp+=str; | ||
+ | parse_info<> info = parse(tmp.c_str(), htmlParser, space_p); | ||
+ | if (info.full) | ||
+ | { | ||
+ | cout << "Parsing succeeded\n"; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | cout << "Parsing failed at:\n"<<info.stop; | ||
+ | } | ||
+ | |||
+ | tmp=""; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | tmp+=str; | ||
+ | } | ||
+ | } | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | </code> | ||
==== O bibliotece Spirit w sieci ==== | ==== O bibliotece Spirit w sieci ==== |