2016-05-10 18 views
0

Ich versuche zu verstehen, was "Anfügen" einer semantischen Aktion an einen Parser genau bedeutet, und genauer würde ich gerne verstehen, wann und für welchen Zeitraum die semantische Aktion an die Parser.semantische Aktionen an Parser mit Boost Spirit anhängen

Dazu modifizierte ich leicht das employee.cpp Beispiel der Boost-Geist-Bibliothek auf folgende Weise:

1 °/Es wird eine print() Funktion, dessen Ausgang nur, wenn verfolgt sie genannt wird:

void print(const struct employee & e) { std::cout << e.surname << "\n"} 

2 °/Am Ende des Konstruktors der Klasse employee_parser, gebunden I die print() Funktion des start Parser:

employee_parser() : employee_parser::base_type(start) 
    { 
     using qi::int_; 
     using qi::lit; 
     using qi::double_; 
     using qi::lexeme; 
     using ascii::char_; 

     quoted_string %= lexeme['"' >> +(char_ - '"') >> '"']; 

     start %= 
      lit("employee") 
      >> '{' 
      >> int_ >> ',' 
      >> quoted_string >> ',' 
      >> quoted_string >> ',' 
      >> double_ 
      >> '}' 
      ; 
     start[&print]; 
    } 

Obwohl es mir scheint, dass ich den start Parser mit der semantischen Aktion print verbunden habe, wie in der Dokumentation angegeben, wird die print() Funktion nie aufgerufen. Es scheint, dass die semantische Aktion am rechten Ende eines Parsers angehängt werden muss Definition, so oft wie der Parser in der gleichen Definition erscheint. Kann jemand ein wenig mehr dazu ausarbeiten?

Antwort

2

Im Geist ist ein Parser ein Funktionsobjekt, und zum größten Teil, die Betreiber, die um Sie zu neuen Parser machen, wie >> und so weiter, damit überlastet sind, kehren verschiedene Objekte Funktion, eher als das Original zu modifizieren.

Wenn Sie jemals Java verwendet und die unveränderlichen Java-Strings gefunden haben, können Sie sich das ähnlich vorstellen.

Wenn Sie einen Ausdruck wie

haben
rule1 = lit("employee"); 
rule2 = (rule1 >> lit(",") >> rule1) [ &print ]; 

, was passiert ist, dass ein neues Parser-Objekt wird auf variable rule2 erzeugt und zugewiesen, und das Parser-Objekt hat die semantische Aktion angebracht.

Tatsächlich gibt es ein neues temporäres Parser-Objekt für jeden Operator im Ausdruck. Der Overhead ist nur einmal, wenn der Parser erstellt wird, es spielt keine Rolle zur Parserzeit.

Wenn Sie

start[&print]; 

haben dies wie einen temporären Wert erzeugen, die sofort verworfen. Es hat keine Nebenwirkungen für den Wert in der start Variable. Deshalb wird Drucken nie aufgerufen.

Wenn es nicht so funktioniert, dann wäre es viel komplizierter, Grammatiken zu machen, möglicherweise.

Wenn Sie eine Grammatik wie in spirit Qi definieren, wird die Definition normalerweise im Konstruktor des Grammatikobjekts gemacht. Zuerst werden die Prototypen der Regeln angegeben, die ihre Typen, Skipper usw. angeben. Dann konstruieren Sie die Regeln nacheinander. Sie müssen sicherstellen, dass Sie vor der Initialisierung keine Regel in der Definition einer anderen Regel verwenden. Aber nachdem es initialisiert wurde, wird es sich in Bezug auf die Grammatik größtenteils nicht ändern. (Sie können jedoch Dinge wie Debug-Informationen ändern.)

Wenn sich alle Regeln möglicherweise ändern, nachdem sie initialisiert wurden, dann müssten sie sich gegenseitig über die Änderungen aktualisieren, und das wäre komplizierter.

Sie können sich vorstellen, dass dies vermieden wird, indem die Regeln Referenzen statt auf Werte speichern. Aber das impliziert Zeiger und dynamische Zuweisungen afaik und wäre langsamer. Teil des Geistes ist, dass es sich um Expression-Templates handelt - all diese "Pointer-Dereferenzen" sollen zur Kompilierzeit aufgelöst werden, wie ich es verstehe.

+1

Hinweis: Ich bin kein Experte für das Innere von Spirit, das ist genau das, was ich beim Lesen von Dokumenten und bei der Verwendung davon entdeckt habe. Wenn es jemanden gibt, der eine ausführlichere Erklärung geben kann als ich, oder die Spannung in meiner Antwort lösen kann, würde ich gerne lesen. –

+0

Für mich war das klar genug – Heyji

Verwandte Themen