2010-02-27 10 views
11

Kennt jemand eine Online-Ressource, in der ich herausfinden kann, wie man mit Boost :: Spirit einen einfachen Ausdrucksparser schreibt?Beispiel für einen einfachen Ausdrucksparser mit Boost :: Spirit?

Ich muss nicht notwendigerweise den Ausdruck auswerten, aber ich muss es analysieren und in der Lage sein, einen booleschen Wert zurückzugeben, um anzuzeigen, ob der Ausdruck analysierbar ist oder nicht (z. B. nicht passende Klammern usw.).

Ich brauche den Parser, um Funktionsnamen (z. B. foo und foobar) zu erkennen, also wäre dies auch ein nützliches Beispiel, um mir beim Schreiben von BNF-Notation zu helfen.

Die Ausdrücke werden normale arithmetische Gleichungen, dh bestehend aus den folgenden Symbolen:

  1. Öffnungs-/Schließbügel
  2. arithmetischen Operatoren
  3. erkannten Funktionsnamen, und prüfen, ob ihre erforderlichen Argumente
+1

Haben Sie sich mit der Dokumentation von Spirits und Beispielen befasst? –

+1

Die Dokumentation von Spirit ist bei weitem nicht so einfach, wie ich es mir gewünscht hätte. Ich schaffte es, damit zu lernen, aber ein besseres Tutorial hätte das Lernen sicherlich erleichtert. – Tronic

+0

Danke Tronic. Das war meine Meinung, als ich durch die Dokumentation auf der Spirit-Homepage ging. –

Antwort

5

Hier ist ein alter Spirit Prototypcode, den ich herumliegen hatte:

#include <iostream> 
#include <algorithm> 
#include <vector> 
#include <string> 
#include <exception> 
#include <iterator> 
#include <sstream> 
#include <list> 

#include <boost/spirit.hpp> 
#include <boost/shared_ptr.hpp> 

using namespace std; 
using namespace boost::spirit; 
using namespace boost; 

void g(unsigned int i) 
{ 
    cout << "row: " << i << endl; 
} 

struct u 
{ 
    u(const char* c): s(c) {} 
    void operator()(const char* first, const char* last) const 
    { 
     cout << s << ": " << string(first, last) << endl; 
    } 
private: 
    string s; 
}; 


struct Exp 
{ 
}; 

struct Range: public Exp 
{ 
}; 

struct Index: public Exp 
{ 
}; 

struct String: public Exp 
{ 
}; 

struct Op 
{ 
    virtual ~Op() = 0; 
    virtual string name() = 0; 
}; 

Op::~Op() {} 

struct CountIf: public Op 
{ 
    string name() { return "CountIf"; } 
}; 

struct Sum: public Op 
{ 
    string name() { return "Sum"; } 
}; 

struct Statement 
{ 
    virtual ~Statement() = 0; 
    virtual void print() = 0; 
}; 

Statement::~Statement() {} 

struct Formula: public Statement 
{ 
    Formula(const char* first, const char* last): s(first, last), op(new CountIf) 
    { 
     typedef rule<phrase_scanner_t> r_t; 

     r_t r_index  = (+alpha_p)[u("col")] >> uint_p[&g]; 
     r_t r_range  = r_index >> ':' >> r_index; 
     r_t r_string = ch_p('\"') >> *alnum_p >> '\"'; 
     r_t r_exp  = r_range | r_index | r_string; // will invoke actions for index twice due to range 
     r_t r_list  = !(r_exp[u("arg")] % ','); 
     r_t r_op  = as_lower_d["countif"] | as_lower_d["sum"]; 
     r_t r_formula = r_op >> '(' >> r_list >> ')'; 

     cout << s << ": matched: " << boolalpha << parse(s.c_str(), r_formula, space_p).full << endl; 
    } 
    void print() { cout << "Formula: " << s << "/" << op->name() << endl; } 
private: 
    string s; 
    shared_ptr<Op> op; 
    list<shared_ptr<Exp> > exp_list; 
}; 

struct Comment: public Statement 
{ 
    Comment(const char* first, const char* last): comment(first, last) {} 
    void print() {cout << "Comment: " << comment << endl; } 
private: 
    string comment; 
}; 


struct MakeFormula 
{ 
    MakeFormula(list<shared_ptr<Statement> >& list_): list(list_) {} 
    void operator()(const char* first, const char* last) const 
    { 
     cout << "MakeFormula: " << string(first, last) << endl; 
     list.push_back(shared_ptr<Statement>(new Formula(first, last))); 
    } 
private: 
    list<shared_ptr<Statement> >& list; 
}; 

struct MakeComment 
{ 
    MakeComment(list<shared_ptr<Statement> >& list_): list(list_) {} 
    void operator()(const char* first, const char* last) const 
    { 
     cout << "MakeComment: " << string(first, last) << endl; 
     list.push_back(shared_ptr<Statement>(new Comment(first, last))); 
    } 
private: 
    list<shared_ptr<Statement> >& list; 
}; 


int main(int argc, char* argv[]) 
try 
{ 
    //typedef vector<string> v_t; 
    //v_t v(argv + 1, argv + argc); 
    // copy(v.begin(), v.end(), ostream_iterator<v_t::value_type>(cout, "\n")); 

    string s; 
    getline(cin, s); 

    //  =COUNTIF(J2:J36, "Abc") 

    typedef list<shared_ptr<Statement> > list_t; 
    list_t list; 

    typedef rule<phrase_scanner_t> r_t; 

    r_t r_index  = (+alpha_p)[u("col")] >> uint_p[&g]; 
    r_t r_range  = r_index >> ':' >> r_index; 
    r_t r_string = ch_p('\"') >> *alnum_p >> '\"'; 
    r_t r_exp  = r_range | r_index | r_string; // will invoke actions for index twice due to range 
    r_t r_list  = !(r_exp[u("arg")] % ','); 
    r_t r_op  = as_lower_d["countif"] | as_lower_d["sum"]; 
    r_t r_formula = r_op >> '(' >> r_list >> ')'; 
    r_t r_statement = (ch_p('=') >> r_formula [MakeFormula(list)]) 
        | (ch_p('\'') >> (*anychar_p)[MakeComment(list)]) 
        ; 

    cout << s << ": matched: " << boolalpha << parse(s.c_str(), r_statement, space_p).full << endl; 

    for (list_t::const_iterator it = list.begin(); it != list.end(); ++it) 
    { 
     (*it)->print(); 
    } 
} 
catch(const exception& ex) 
{ 
    cerr << "Error: " << ex.what() << endl; 
} 

Probieren Sie es und geben Sie eine Zeile wie läuft:

=COUNTIF(J2:J36, "Abc") 
+1

Schönes Beispiel! Nun, aber ist das wirklich _einfach_? – Vlad

+0

Nun, es bin nicht ganz trivial, aber es ist nach wie vor nur eine Seite (vielleicht doppelseitig!) Im Wert von gedruckten Code, den ich auf einem Inlandsflug schrieb. :) –

1

Ich bin mir nicht sicher, ob dies entweder als einfache qualifiziert, aber ich habe diese uri-Grammatik verfügbar bei http://code.google.com/p/uri-grammar/source/browse/trunk/src/uri/grammar.hpp verwendet. Es kann nicht sein trivial, aber zumindest seine Parsing, die Sie wahrscheinlich schon verstehen (URIs). Beim Lesen dieser Grammatiken ist es am besten, von unten nach oben zu lesen, da hier die allgemeinsten Token definiert sind.

5

Die aktuelle Version von Spirit (V2.x) enthält eine ganze Reihe von Rechenbeispielen vom einfachen bis zum vollwertigen Mini-C-Interpreter. Sie sollten einen Blick darauf werfen, da dies ein perfekter Ausgangspunkt für das Schreiben Ihres eigenen Ausdrucksparsers ist.