Frage

in diesem Artikel über boost Geist es semantische Aktionen, die erwähnt wird,

  

Es gibt 2 tatsächlich mehr Argumente   wird übergeben: der Parser Kontext und ein   Verweis auf eine Boolesche ‚Treffer‘   Parameter. Der Parser Kontext ist   nur dann sinnvoll, wenn die semantische Aktion   wird irgendwo auf der rechten Seite angebracht   Seite einer Regel. Wir werden sehen, mehr   Informationen über diese kurz. Das   boolean Wert auf false gesetzt werden   innerhalb der semantischen Aktion Ungültigmachungseinträge   das Spiel in retrospektiven, so dass die   Parser fehlschlagen.

Alle in Ordnung, aber ich habe versucht, ein Beispiel Leiten eines Funktionsobjekt als semantische Aktion zu finden, die die anderen Parameter (Parser Kontext und Hit boolean) verwendet, aber ich gefunden habe, nicht. Ich würde gerne ein Beispiel mit regulären Funktionen oder Funktionsobjekte zu sehen, wie ich kaum den Phönix Voodoo grok kann

War es hilfreich?

Lösung

Dies ist eine wirklich gute Frage (und auch eine Dose Würmer), weil es an der Schnittstelle von Qi und Phoenix wird. Ich habe nicht ein Beispiel entweder gesehen, so dass ich den Artikel ein wenig in dieser Richtung erstrecken würde.

Wie Sie sagen, Funktionen für semantische Aktionen kann bis zu drei Parametern übernehmen

  1. Matched Attribut - in dem Artikel bedeckt
  2. Kontext - enthält die Qi-Phönix-Schnittstelle
  3. Match flag - manipulieren das Spiel Zustand

Match-Flag

Wie die Artikel heißt es, ist der zweite Parameter nicht sinnvoll, wenn der Ausdruck Teil einer Regel ist, können so mit dem dritten starten. Ein Platzhalter für den zweiten Parameter ist nach wie vor, obwohl und für diese Verwendung boost::fusion::unused_type benötigt. So eine modifizierte Funktion aus dem Artikel den dritten Parameter zu verwenden:

#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>

void f(int attribute, const boost::fusion::unused_type& it, bool& mFlag){
    //output parameters
    std::cout << "matched integer: '" << attribute << "'" << std::endl
              << "match flag: " << mFlag << std::endl;

    //fiddle with match flag
    mFlag = false;
}

namespace qi = boost::spirit::qi;

int main(void){
   std::string input("1234 6543");
   std::string::const_iterator begin = input.begin(), end = input.end();

   bool returnVal = qi::phrase_parse(begin, end, qi::int_[f], qi::space);

   std::cout << "return: " << returnVal << std::endl;
   return 0;
}

die Ausgänge:

matched integer: '1234'
match flag: 1
return: 0

All dieses Beispiel macht, ist das Spiel zu einem nicht-Spiel wechseln, die in der Parser Ausgabe widerspiegeln. Nach hkaiser, im Boost 1,44 und bis das Spiel Flag auf false bewirkt, dass das Spiel in der normalen Art und Weise zum Scheitern verurteilt. Wenn Alternativen definiert sind, wird der Parser wieder ansetzen und versuchen, sie zu passen, wie man erwarten würde. Doch im Boost <= 1,43 ein Geist Bug verhindert Rückzieher, das seltsame Verhalten verursacht. Um dies zu sehen, add phoenix umfassen boost/spirit/include/phoenix.hpp und ändern Sie den Ausdruck

qi::int_[f] | qi::digit[std::cout << qi::_1 << "\n"]

Sie würden erwarten, dass, wenn die Qi :: int Parser fehlschlägt, die alternative Qi :: Ziffer den Anfang der Eingabe paßt auf „1“, aber der Ausgang ist:

matched integer: '1234'
match flag: 1
6
return: 1

Die 6 ist die erste Ziffer des zweiten int im Eingang, der die Alternative anzeigt, getroffen, um den Skipper mit und ohne Rückzieher. Beachten Sie auch, dass das Spiel erfolgreich betrachtet wird, auf der Grundlage der Alternative.

Sobald Boost 1.44 out ist, wird die Übereinstimmungsflagge für die Anwendung Einstimmungskriterien nützlich sein, die sonst schwierig sein könnte, in einem Parser Sequenz zu exprimieren. Beachten Sie, dass das Spiel Flag kann in Phoenix Ausdrücke mit dem _pass Platzhalter manipuliert werden.

Context Parameter

Je interessanter Parameter ist die zweite, die das Qi-Phönix-Schnittstelle enthält, oder in Qi parlance, der Kontext der semantischen Aktion. Um dies zu verdeutlichen, untersucht zunächst eine Regel:

rule<Iterator, Attribute(Arg1,Arg2,...), qi::locals<Loc1,Loc2,...>, Skipper>

Der Kontextparameter verkörpert das Attribut, arg1, ... argN und Qi :: Einheimische Vorlage paramters, eingewickelt in einem boost :: spirit :: Kontext Vorlagentyp. Dieses Attribut unterscheidet sich von der Funktionsparameter: der Funktionsparameter Attribut ist der geparsten Wert, während dieses Attribut der Wert der Regel selbst ist. Eine semantische Aktion muss die ersteren zum letzteren abzubilden. Hier ist ein Beispiel für einen möglichen Kontexttyp (phoenix Ausdruck Äquivalente angegeben):

using namespace boost;
spirit::context<              //context template
    fusion::cons<             
        int&,                 //return int attribute (phoenix: _val)
        fusion::cons<
            char&,            //char argument1       (phoenix: _r1)
            fusion::cons<
                float&,       //float argument2      (phoenix: _r2) 
                fusion::nil   //end of cons list
            >,
        >,
    >,
    fusion::vector2<          //locals container
        char,                 //char local           (phoenix: _a)
        unsigned int          //unsigned int local   (phoenix: _b)
    > 
>

Beachten Sie die Rückkehr Attribut und Argumentliste in Form einer Lisp-Stil-Liste (ein cons Liste ). Um diese Variablen in einer Funktion zuzugreifen, Zugriff auf die attribute oder locals Mitglieder der context struct Vorlage mit Fusion :: at <> (). Zum Beispiel für eine Kontextvariable con

//assign return attribute
fusion::at_c<0>(con.attributes) = 1;

//get the second rule argument
float arg2 = fusion::at_c<2>(con.attributes);

//assign the first local
fusion::at_c<1>(con.locals) = 42;

den Artikel Beispiel modifizieren das zweite Argument zu verwenden, ändern Sie die Funktionsdefinition und phrase_parse Anrufe:

...
typedef 
    boost::spirit::context<
        boost::fusion::cons<int&, boost::fusion::nil>, 
        boost::fusion::vector0<> 
    > f_context;
void f(int attribute, const f_context& con, bool& mFlag){
   std::cout << "matched integer: '" << attribute << "'" << std::endl
             << "match flag: " << mFlag << std::endl;

   //assign output attribute from parsed value    
   boost::fusion::at_c<0>(con.attributes) = attribute;
}
...
int matchedInt;
qi::rule<std::string::const_iterator,int(void),ascii::space_type> 
    intRule = qi::int_[f];
qi::phrase_parse(begin, end, intRule, ascii::space, matchedInt);
std::cout << "matched: " << matchedInt << std::endl;
....

Dies ist ein sehr einfaches Beispiel, das nur ordnet den geparsten Wert in den Ausgabeattributwert, aber Erweiterungen sollten ziemlich offensichtlich sein. So stellen Sie die Kontext struct Template-Parameter der Regel Ausgang, Eingang und lokale Typen entsprechen. Beachten Sie, dass diese Art der direkten Übereinstimmung zwischen geparsten Typ / Wert Ausgabetyp / Wert kann automatisch Regeln mit Auto erfolgen, mit einem %= anstelle eines = bei der Definition der Regel:

qi::rule<std::string::const_iterator,int(void),ascii::space_type> 
    intRule %= qi::int_;

IMHO, wäre eine Funktion für jede Aktion Schreiben eher mühsam, im Vergleich zu den kurzen und lesbarem phoenix Ausdruck Äquivalente. Ich sympathisiere mit der Voodoo-Sicht, aber wenn man mit Phoenix für eine kleine Weile arbeiten, die Semantik und Syntax ist nicht sehr schwierig.

Bearbeiten: Zugriff Regel Kontext w / Phoenix

Die Kontextvariable ist nur definiert, wenn der Parser Teil einer Regel ist. Denken Sie an einen Parser als jeder Ausdruck, dass Verbraucht Eingang, wo in der Regel die Parser Werte übersetzt (qi :: _ 1) in der Regel Wert (qi :: _ val). Der Unterschied ist oft nicht trivial, zum Beispiel, wenn das Qi :: val einen Klasse-Typen hat, dass der Bedarf konstruiert wird von POD Werten analysiert. Im Folgenden finden Sie ein einfaches Beispiel.

Lassen Sie uns sagen Teil unserer Eingang ist eine Folge von drei CSV-Zahlen (x1, x2, x3), und wir kümmern uns nur eine arithmetische Funktion dieser drei Zahlen aus (f = x0 + (x1 + x2) * 3 x), wobei x 0 a Wert an anderer Stelle erhalten. Eine Option ist in den ganzen Zahlen und berechnen Sie die Funktion oder alternativ verwenden phoenix zu lesen, beides zu tun.

Für dieses Beispiel eine Regel mit einem Ausgangsattribut verwenden (der Funktionswert) und Eingang (x0) und einem lokalen (an Informationen zwischen einzelnen Parsern mit der Regel). Hier ist das vollständige Beispiel.

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <iostream>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

int main(void){
   std::string input("1234, 6543, 42");
   std::string::const_iterator begin = input.begin(), end = input.end();

   qi::rule<
      std::string::const_iterator,
      int(int),                    //output (_val) and input (_r1)
      qi::locals<int>,             //local int (_a)
      ascii::space_type
   >
      intRule =
            qi::int_[qi::_a = qi::_1]             //local = x1
         >> ","
         >> qi::int_[qi::_a += qi::_1]            //local = x1 + x2
         >> ","
         >> qi::int_
            [
               qi::_val = qi::_a*qi::_1 + qi::_r1 //output = local*x3 + x0
            ];

   int ruleValue, x0 = 10;
   qi::phrase_parse(begin, end, intRule(x0), ascii::space, ruleValue);
   std::cout << "rule value: " << ruleValue << std::endl;
   return 0;
}

Alternativ kann alle ints als Vektor analysiert werden können, und die Funktion mit einer einzigen semantischen Aktion ausgewertet (die % unten ist die Liste Operator und Elemente des Vektors sind mit Phoenix zugegriffen :: at):

namespace ph = boost::phoenix;
...
    qi::rule<
        std::string::const_iterator,
        int(int),
        ascii::space_type
    >
    intRule =
        (qi::int_ % ",")
        [
            qi::_val = (ph::at(qi::_1,0) + ph::at(qi::_1,1))
                      * ph::at(qi::_1,2) + qi::_r1
        ];
....

Für die oben, wenn der Eingang nicht korrekt ist (zwei Ints statt drei), könnten schlechte Sache zur Laufzeit geschehen, so dass es besser wäre, die Anzahl der analysierten Werte explizit angeben, wird so Parsing für eine schlechte Eingabe fehlschlagen . Die folgenden Anwendungen _1, _2 und _3 den ersten, zweiten und dritten Übereinstimmungswert zu verweisen:

(qi::int_ >> "," >> qi::int_ >> "," >> qi::int_)
[
    qi::_val = (qi::_1 + qi::_2) * qi::_3 + qi::_r1
];

Dies ist ein konstruiertes Beispiel, aber sollten Sie die Idee geben. Ich habe phoenix semantische Aktionen wirklich hilfreich direkt bei der Konstruktion komplexe Objekte aus Eingang; Dies ist möglich, weil Sie Konstrukteure und Elementfunktionen innerhalb semantischen Aktionen aufrufen können.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top