Expression Operatoren



  • Richtig genau,

    ich brauche die regeln am beispiel C !

    Ich interpretier ja von rechts nach links

    Sei A int, B array of int

    A++-++B[0]

    d.h. erst B auflösen = (++(B[0]))

    dann A (A++)

    ergibt (A++) - (++(B[0]))

    aber mir fällt es schwer, wenn ich die linken unaer operatoren von B auflöse wie weit ich auflösen darf, also bis der "-" binäre operator kommt! diese regel brauche ich:) wo kann ich das nachlesen;)

    Grüße



  • P.S. in java geht das bspw.:

    f++--++g; // Fehler

    was wohl in c++ nich geht



  • NullBockException schrieb:

    es werden ja grudsätzlich erst die rechts assoziativen unären operatorn aufgelöst, befor die links asoozoziativen dran kommen oder?

    das wird in der grammatik der sprache definiert:
    http://en.cppreference.com/w/cpp/language/operator_precedence
    da siehst du nicht nur die bindungsstärke sondern auch die assoziativität.



  • Hat doch mit Precedence oder Associativity nix zu tun.
    -- ist immer --, und nie - -. und f ++ -- ++ g geht halt net. Man kann net zwei Variablen mit unären Operatoren verbinden.

    f ++ - - ++ g sollte kein Problem machen. Oder auch
    f ++ --- ++ g


  • Mod

    hustbaer schrieb:

    Hat doch mit Precedence oder Associativity nix zu tun.
    -- ist immer --, und nie - -. und f ++ -- ++ g geht halt net. Man kann net zwei Variablen mit unären Operatoren verbinden.

    edit: Hier stand Mist.

    Die Regeln, wie ich sie formulieren würde, ohne dass ich direkt auf eine passende Stelle im Standard zeigen kann, sind:
    1. Gehe von links nach rechts vor
    2. Versuche immer, möglichst viele Zeichen als einen Operator aufzufassen (Also zum Beispiel "--" als Postfix/Präfix, anstatt doppeltes unäres '-')

    Diese Regeln sollten sich indirekt aus der Grammatik der Sprache ergeben (z.B. Kapitel 5 im C++-Standard), aber diese rigoros her zu leiten wird gewiss sehr schwer.



  • Wenn ich die links assoziativen unären Operatoren auflöse, wann weiß ich aufzuhören bzw. wann weiß ich wenn der eigentliche binäre operator kommnt?`

    bei

    i++++ * - --++j

    ist klar dass ich beim *-operator mit den linken unären operatoren fertig bin!

    bei diesem beispiel ist es nich ganz klar

    i++++ - - --++j

    außer es gilt, dass nacheinem - postfix operator keine unäre ++ mehr kommen darf!

    Diese regelung brauch ich 🙂


  • Mod

    Hier ist klar, dass - - kein -- sein kann, da man -- nicht mit Leerzeichen dazwischen schreiben darf.

    Der Ausdruck i++++ - - --++j wird so geparsed:
    -Variable i entdeckt
    -"+" dahinter, gucke, ob das vielleicht ein "++" ist.
    -Ist wohl "++". i++ ist ein gültiger Ausdruck.
    -"+" dahinter, gucke, ob das vielleicht ein "++" ist.
    -Ist wohl "++". Darf "++" auf "i++" angewendet werden?
    -Falls Nein (z.B. weil i ein int ist und daher i++ kein l-value) -> Fehler
    -Falls Ja (mein Beispiel) -> Weiter geht's
    -"-" dahinter, gucke ob das vielleicht ein "--" ist
    -Ist es wohl nicht, muss daher binäres "-" sein. Brauche nun rechtsseitig einen Ausdruck, die Typen der beiden Ausdrücke müssen einen Operator-(Typ1, Typ2) haben
    -"-" entdeckt. Gucke, ob das vielleicht ein "--" ist.
    -ist es wohl nicht, muss daher ein unäres "-" sein. Brauche dahinter einen Ausdruck, dessen Typ ein unäres "-" erlaubt.
    -"-" entdeckt. Gucke, ob das vielleicht ein "--" ist.
    -Ist wohl "--". Brauche dahinter einen l-value, der einen Operator "--" kennt
    -"+" entdeckt, gucke, ob das ein "++" ist
    -Ist wohl ein "++". Brauche dahinter einen l-value, der einen Operator "++" kennt
    -Variable j gefunden.
    -Ist ++j ein gültiger Ausdruck?
    -Falls ja: Ist "--" auf "++j" ein gültiger Ausdruck?
    -Falls ja: Ist "-" auf "--(++j)" ein gültiger Ausdruck?
    -Falls ja: Kommt noch was nach dem j? Nein.
    -Ist ((i++)++) - (-(--(++j))) ein gültiger Ausdruck?
    -Fertig

    Falls da hingegen i++++ -- --++j stünde:
    -Variable i entdeckt
    -"+" dahinter, gucke, ob das vielleicht ein "++" ist.
    -Ist wohl "++". i++ ist ein gültiger Ausdruck.
    -"+" dahinter, gucke, ob das vielleicht ein "++" ist.
    -Ist wohl "++". Darf "++" auf "i++" angewendet werden?
    -Falls Nein (z.B. weil i ein int ist und daher i++ kein l-value) -> Fehler
    -Falls Ja (mein Beispiel) -> Weiter geht's
    -"-" dahinter, gucke ob das vielleicht ein "--" ist
    -Ist wohl "--". Ist ((i++)++)-- ein gültiger Ausdruck?
    -"-" dahinter, gucke ob das vielleicht ein "--" ist
    -Ist wohl "--". Ist (((i++)++)--)-- ein gültiger Ausdruck?
    -"+" dahinter, gucke ob das vielleicht ein "++" ist
    -Ist wohl "--". Ist ((((i++)++)--)--)++ ein gültiger Ausdruck?
    -j gefunden. Also steht da (gültiger Ausdruck)j;
    -Das kann nicht sein -> Fehler



  • Ich habe mir zwar den C++-Standard nie so genau angesehen, aber ich bezweifle, dass das Parsing davon abhaengt, ob ein Ausdruck ein LValue ist oder ob ein passender Overload eines Operators existiert. Solche Dinge sind ueblicherweise unabhaengig vom Parsing und gehoeren erst spaeter zur semantischen Analyse.


  • Mod

    Kellerautomat schrieb:

    Ich habe mir zwar den C++-Standard nie so genau angesehen, aber ich bezweifle, dass das Parsing davon abhaengt, ob ein Ausdruck ein LValue ist oder ob ein passender Overload eines Operators existiert. Solche Dinge sind ueblicherweise unabhaengig vom Parsing und gehoeren erst spaeter zur semantischen Analyse.

    Es wird ja auch immer das gleiche geparsed, aber im einen Fall ist das Ergebnis gültig, im anderen nicht. Ob dann intern vor dem Schmeißen der Fehlermeldung noch weiter geparsed wird oder nicht, ist doch egal.

    Diese Antwort gilt, wenn du dich auf meinen Post von 14:42 beziehst, wo das auch so dargestellt wird.

    Falls du dich auf meinen Beitrag von 13:51 beziehst: Das war tatsächlich falsch und ist auch um 14:25 korrigiert worden.



  • Da habe ich wohl tatsaechlich noch eine alte Version deines Posts von 13:51 gesehen. 🤡



  • SeppJ schrieb:

    hustbaer schrieb:

    Hat doch mit Precedence oder Associativity nix zu tun.
    -- ist immer --, und nie - -. und f ++ -- ++ g geht halt net. Man kann net zwei Variablen mit unären Operatoren verbinden.

    edit: Hier stand Mist.

    Die Regeln, wie ich sie formulieren würde, ohne dass ich direkt auf eine passende Stelle im Standard zeigen kann, sind:
    1. Gehe von links nach rechts vor
    2. Versuche immer, möglichst viele Zeichen als einen Operator aufzufassen (Also zum Beispiel "--" als Postfix/Präfix, anstatt doppeltes unäres '-')

    Diese Regeln sollten sich indirekt aus der Grammatik der Sprache ergeben (z.B. Kapitel 5 im C++-Standard), aber diese rigoros her zu leiten wird gewiss sehr schwer.

    Ja.
    Wobei es ab C++11 vermutlich etwas komplizierter ist, da in 11 ja Template<Template<T>> erlaubt ist.



  • Wird von links nach rechts geparst??? ich dachte eigentlich man fängt rechts an?

    Hier ist klar, dass - - kein -- sein kann, da man -- nicht mit Leerzeichen dazwischen schreiben darf.

    Der Ausdruck i++++ - - --++j wird so geparsed:
    -Variable i entdeckt
    -"+" dahinter, gucke, ob das vielleicht ein "++" ist.
    -Ist wohl "++". i++ ist ein gültiger Ausdruck.
    -"+" dahinter, gucke, ob das vielleicht ein "++" ist.
    -Ist wohl "++". Darf "++" auf "i++" angewendet werden?
    -Falls Nein (z.B. weil i ein int ist und daher i++ kein l-value) -> Fehler
    -Falls Ja (mein Beispiel) -> Weiter geht's
    -"-" dahinter, gucke ob das vielleicht ein "--" ist
    -Ist es wohl nicht, muss daher binäres "-" sein. Brauche nun rechtsseitig einen Ausdruck, die Typen der beiden Ausdrücke müssen einen Operator-(Typ1, Typ2) haben
    -"-" entdeckt. Gucke, ob das vielleicht ein "--" ist.
    -ist es wohl nicht, muss daher ein unäres "-" sein. Brauche dahinter einen Ausdruck, dessen Typ ein unäres "-" erlaubt.
    -"-" entdeckt. Gucke, ob das vielleicht ein "--" ist.
    -Ist wohl "--". Brauche dahinter einen l-value, der einen Operator "--" kennt
    -"+" entdeckt, gucke, ob das ein "++" ist
    -Ist wohl ein "++". Brauche dahinter einen l-value, der einen Operator "++" kennt
    -Variable j gefunden.
    -Ist ++j ein gültiger Ausdruck?
    -Falls ja: Ist "--" auf "++j" ein gültiger Ausdruck?
    -Falls ja: Ist "-" auf "--(++j)" ein gültiger Ausdruck?
    -Falls ja: Kommt noch was nach dem j? Nein.
    -Ist ((i++)++) - (-(--(++j))) ein gültiger Ausdruck?
    -Fertig

    Falls da hingegen i++++ -- --++j stünde:
    -Variable i entdeckt
    -"+" dahinter, gucke, ob das vielleicht ein "++" ist.
    -Ist wohl "++". i++ ist ein gültiger Ausdruck.
    -"+" dahinter, gucke, ob das vielleicht ein "++" ist.
    -Ist wohl "++". Darf "++" auf "i++" angewendet werden?
    -Falls Nein (z.B. weil i ein int ist und daher i++ kein l-value) -> Fehler
    -Falls Ja (mein Beispiel) -> Weiter geht's
    -"-" dahinter, gucke ob das vielleicht ein "--" ist
    -Ist wohl "--". Ist ((i++)++)-- ein gültiger Ausdruck?
    -"-" dahinter, gucke ob das vielleicht ein "--" ist
    -Ist wohl "--". Ist (((i++)++)--)-- ein gültiger Ausdruck?
    -"+" dahinter, gucke ob das vielleicht ein "++" ist
    -Ist wohl "--". Ist ((((i++)++)--)--)++ ein gültiger Ausdruck?
    -j gefunden. Also steht da (gültiger Ausdruck)j;
    -Das kann nicht sein -> Fehler

    _________________


  • Mod

    hustbaer schrieb:

    Ja.
    Wobei es ab C++11 vermutlich etwas komplizierter ist, da in 11 ja Template<Template<T>> erlaubt ist.

    Ja, die habe ich bewusst unterschlagen. Aber die Regeln zeigen schön den Grund, wieso das vorher nicht ging, da das >> eben immer als ein Shift-Operator gelesen wurde.

    NullBockException schrieb:

    Wird von links nach rechts geparst??? ich dachte eigentlich man fängt rechts an?

    Nein. Im Idealbild (wird aber ein bisschen von Templates in C++ kaputt gemacht, in C stimmt es noch halbwegs) geht der Compiler den Code genau einmal durch und das Zeichen für Zeichen von Vorne nach hinten. Du denkst vermutlich an eines der folgenden:
    1. Auswertungsreihenfolge von Unterausdrücken. Mag bei manchen Implementierungen immer von rechts nach links gehen, ist aber überhaupt nicht festgelegt und erzeugt undefiniertes Verhalten, wenn man sich da auf irgendwas verlässt.
    2. Wie man Sachen wie

    const int * ptr;
    

    liest. Das ist dann die Krücke, wie der Mensch sich merken kann, was das bedeutet. Der Compiler liest das trotzdem von links nach rechts und durch die Regeln der Grammatik ist dann auch klar, was gemeint ist. Ist für einen Menschen schwieriger so zu lesen, aber nicht unmöglich. Die Grammatik ist eben gezielt so definiert, dass das in beiden Richtungen lesbar ist.

    Der Compiler kann ja auch rein logisch gesehen gar nicht rechts anfangen, denn dafür müsste er ja zuerst wissen, wo das Ende ist. Dafür muss er aber erst einmal verstehen, was dazwischen steht, damit er das Ende überhaupt finden kann. einfach nur nach dem nächsten Semikolon gucken geht nicht, dazu ist die Sprache zu komplex. Das macht es auch so schwer, sich selber einen C++-Parser zu bauen. Damit der wirklich zuverlässig ist, muss er komplett C++ können, weil es einfach so viele Szenarien gibt, in denen einfache Heuristiken versagen.



  • Hallo Leute,

    danke für eurer Ausführungen. ich hab bisher von rechts nach links geparst, aber da hatte ich noch keine unären funktionalitäten im expression parser 😞 jetzt sieht es anders aus:)

    d.h. ich komm nich drum rum die unären operatoren richtig auszuwerten, wenn ich von rechts nach links parse korrekt? dann muss ich wohl!

    dann muss ich wohl erst alle unären operatoren eines ausdrucks auswerten, von rechts nach links, dann habe ich immer Term /BinärOperator/Term /BinärOperator liste, welche ich dann von rechts nach links auswerte! seh ich das richtig??

    Grüße und danke jungs



  • Da muß ich doch jetzt mal inhaltlich ein bißchen nachbessern:
    Zuersteinmal geht es hier bei der Erkennung der Operatoren um den Tokenizer (auch Scanner oder Lexer genannt). Dieser arbeitet bei C und C++ nach der sog. Longest match rule (d.h. es wird von links nach rechts solange gelesen, bis der größtmögliche Operator gefunden wurde).
    Und erst danach werden diese Tokens vom Parser anhand der C++ Grammatik interpretiert (üblicherweise als Abstract Syntax Tree (AST)).

    Unter Lecture 4: Implementing Lexers and Parsers gibt es einen Einblick in die Arbeitsweise.



  • Uff. Guter Einwand 🙂

    Ich hatte auch erst - richtigerweise - angenommen, dass der C++ Standard explizit mehrere Übersetzungsphasen definiert, und in einer davon Tokens generiert werden.

    Hab mich dann aber von SeppJ's Beiträgen verunsichern lassen.

    Hier übrigens das "right angle brackets" Proposal:

    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1757.html

    Soweit ich erkennen kann wurde das ziemlich 1:1 so umgesetzt.

    @SeppJ
    Du musst gar nicht tief in die Grammatik runtersteigen.
    2.5/3 beschreibt genau die "longest match rule".


Anmelden zum Antworten