Frage

Ich versuche, eine C-Funktion wie Baum Ausdrücke wie die folgenden zu analysieren (mit dem Geist Parser-Framework ):

F( A() , B( GREAT( SOME , NOT ) ) , C( YES ) )

Für diesen Ich versuche, die drei Regeln für die folgende Grammatik zu verwenden:

template< typename Iterator , typename ExpressionAST >
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), space_type> {

    InputGrammar() : InputGrammar::base_type( ) {
       tag = ( qi::char_("a-zA-Z_")  >> *qi::char_("a-zA-Z_0-9") )[ push_back( at_c<0>(qi::_val) , qi::_1 ) ];
       command =  tag [ at_c<0>(qi::_val) = at_c<0>(qi::_1) ] >> "(" >> (*instruction >> ",")
                                        [ push_back( at_c<1>(qi::_val) , qi::_1 ) ]  >> ")";
       instruction = ( command | tag ) [qi::_val = qi::_1];
    }
    qi::rule< Iterator , ExpressionAST() , space_type > tag;
    qi::rule< Iterator , ExpressionAST() , space_type > command;
    qi::rule< Iterator , ExpressionAST() , space_type > instruction;
};

Beachten Sie, dass mein Tag Regel versucht, nur die Kennungen in der Ausdrücken (die ‚Funktion‘ Namen) verwendet zu erfassen. Beachten Sie auch, dass die Unterschrift des Tags Regel liefert einen ExpressionAST anstelle eines std::string, wie in den meisten Beispielen. Der Grund, warum ich es so machen will, ist eigentlich ganz einfach: Ich hasse Varianten mit und ich werde sie, wenn möglich, vermeiden. Es wäre toll, den Kuchen zu halten und ihn auch essen, denke ich.

A-Befehl sollte mit einem Tag (der Name des aktuellen Knotens, ersten Zeichenfolgenfeld des AST-Knoten) und eine variable Anzahl von Argumenten eingeschlossen durch Klammern beginnen, und jedes der Argumente kann ein Tag selbst oder ein anderer Befehl sein.

Allerdings ist dieses Beispiel nicht funktionieren. Es kompiliert und alles, aber während der Laufzeit nicht er all meinen Tests Strings zu analysieren. Und die Sache, die mir wirklich ärgert ist, dass ich kann nicht verstehen, wie es zu beheben, da ich nicht wirklich den obigen Code debuggen kann, zumindest in der traditionellen Bedeutung des Wortes. Grundsätzlich ist der einzige Weg, ich sehe, kann ich den obigen Code beheben ist durch das Wissen, was ich falsch mache.

Also, die Frage ist, dass ich weiß nicht, was mit dem obigen Code falsch ist. Wie würden Sie die obige Grammatik definieren?

Der ExpressionAST Typ Ich verwende ist:

struct MockExpressionNode {
    std::string name;
    std::vector< MockExpressionNode > operands;

    typedef std::vector< MockExpressionNode >::iterator iterator;
    typedef std::vector< MockExpressionNode >::const_iterator const_iterator;

    iterator begin() { return operands.begin(); }
    const_iterator begin() const { return operands.begin(); }
    iterator end() { return operands.end(); }
    const_iterator end() const { return operands.end(); }

    bool is_leaf() const {
        return ( operands.begin() == operands.end() );
    }
};

BOOST_FUSION_ADAPT_STRUCT(
    MockExpressionNode,
    (std::string, name)
    (std::vector<MockExpressionNode>, operands)
)
War es hilfreich?

Lösung

Soweit das Debuggen, ist es möglich eine normale Pause und beobachtet Ansatz zu verwenden. Dies wird dadurch erschwert, wie Sie haben, obwohl die Regeln formatiert. Wenn Sie den Geist Beispiele Format pro (~ ein Parser pro Zeile, ein Phönix-Anweisung pro Zeile), Bruchstellen werden viel mehr informativ sein.

Ihre Datenstruktur keine Möglichkeit hat A() von SOME zu unterscheiden, dass sie beide Blätter sind (lassen Sie mich wissen, wenn ich etwas fehlt). Von Ihrer Variante Kommentar, glaube ich nicht, das Ihre Absicht war, so dass diese beiden Fälle zu unterscheiden, habe ich eine bool commandFlag Membervariable MockExpressionNode (true für A() und false für SOME) hinzugefügt, mit einer entsprechenden Fusionsadapterleitung.

Für den Code speziell, müssen Sie die Startregel an der Basis Konstruktor übergeben, das heißt:.

InputGrammar() : InputGrammar::base_type(instruction) {...}

Dies ist der Einstiegspunkt in der Grammatik, und ist, warum Sie nicht immer wurden analysiert alle Daten. Ich bin es, ohne es kompiliert überrascht, dachte ich, dass die Grammatik Typ erforderlich war, die Art der ersten Regel übereinstimmen. Trotzdem ist dies eine bequeme Konvention zu folgen.

Für die tag Regel gibt es eigentlich zwei Parser qi::char_("a-zA-Z_"), die _1 mit Typ char und *qi::char_("a-zA-Z_0-9") ist die _2 mit Typ (im Wesentlichen) vector<char> ist. Es ist nicht möglich, diese in einen String ohne autorules zu zwingen, aber es kann durch Anbringen einer Regel zu jedem geparsten char getan werden:

tag =   qi::char_("a-zA-Z_")
        [ at_c<0>(qi::_val) = qi::_1 ];
    >> *qi::char_("a-zA-Z_0-9")           //[] has precedence over *, so _1 is 
        [ at_c<0>(qi::_val) += qi::_1 ];  //  a char rather than a vector<char>

Allerdings ist es viel sauberer zu lassen Geist diese Umwandlung zu tun. So definieren Sie eine neue Regel:

qi::rule< Iterator , std::string(void) , ascii::space_type > identifier;
identifier %= qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");

Und keine Sorge darüber;). Dann wird Tag

tag = identifier
      [
          at_c<0>(qi::_val) = qi::_1,
          ph::at_c<2>(qi::_val) = false //commandFlag
      ]

Für Befehl ist der erste Teil in Ordnung, aber Theres ein paar Probleme mit (*instruction >> ",")[ push_back( at_c<1>(qi::_val) , qi::_1 ) ]. Dies wird analysiert Null oder mehrere Befehle Regeln gefolgt von einer „“. Es wird versucht, auch eine vector<MockExpressionNode> push_back (nicht sicher, warum dies entweder kompiliert, vielleicht auch nicht wegen der fehlenden Startregel instanziiert?). Ich glaube, Sie wollen die folgende (mit der Kennung Modifikation):

command =
        identifier
        [
           ph::at_c<0>(qi::_val) = qi::_1, 
           ph::at_c<2>(qi::_val) = true    //commandFlag
        ]
    >>  "("
    >> -(instruction % ",")
        [
           ph::at_c<1>(qi::_val) = qi::_1
        ]
    >>  ")";

Dies wird die optionalen Operator - und die Liste Operator %, letztere zu instruction >> *("," >> instruction) entspricht. Der Phönix Ausdruck weist dann nur den Vektor direkt mit dem Strukturelement, aber man könnte auch die Wirkung direkt auf die Anweisung Spiel und Verwendung push_back befestigen.

Die Anweisung Regel ist in Ordnung, ich würde nur erwähnen, dass es zu instruction %= (command|tag) entspricht.

Eine letzte Sache, wenn es eigentlich keinen Unterschied zwischen A() und SOME (das heißt Ihre ursprüngliche Struktur ohne commandFlag), können Sie diesen Parser schreiben nur autorules mit:

template< typename Iterator , typename ExpressionAST >
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), ascii::space_type> {
   InputGrammar() : InputGrammar::base_type( command ) {
      identifier %=
             qi::char_("a-zA-Z_")
         >> *qi::char_("a-zA-Z_0-9");
      command %=
            identifier
         >> -(
            "("
         >> -(command % ",")
         >>  ")");
    }
    qi::rule< Iterator , std::string(void) , ascii::space_type > identifier;
    qi::rule< Iterator , ExpressionAST(void) , ascii::space_type > command;
};

Das ist der große Vorteil einer Fusion gewickelt Struktur mit, dass die Modelle der Eingang eng.

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