Ausdruck in Token zerlegen



  • TyRoXx schrieb:

    pyhax schrieb:

    Ich glaube, du willst aber auch Fließkommazahlen wie -1.4568 einlesen, oder?

    Ah, du hast mich auf einen Bug in meinem Code aufmerksam gemacht 😃 sowas wie 1+-3. Außerdem denke ich jetzt, das es besser ist, eine Klasse CompoundToken einzuführen, die aus meheren Tokens besteht.

    Ne, schmeiß den Mist weg und setz ein vernünftiges Konzept um.
    Trenn den Scanner vom Parser und die Tokens von den Ausdrücken.

    Das Grundkonzept sieht üblicherweise so aus:

    Ah, danke, ich habe mir auch gerade überlegt, Tokens und Expressions zu trennen. Aber dein Beispiel ist natürlich schöner 🙂



  • pyhax schrieb:

    TyRoXx schrieb:

    pyhax schrieb:

    Ich glaube, du willst aber auch Fließkommazahlen wie -1.4568 einlesen, oder?

    Ah, du hast mich auf einen Bug in meinem Code aufmerksam gemacht 😃 sowas wie 1+-3. Außerdem denke ich jetzt, das es besser ist, eine Klasse CompoundToken einzuführen, die aus meheren Tokens besteht.

    Ne, schmeiß den Mist weg und setz ein vernünftiges Konzept um.
    Trenn den Scanner vom Parser und die Tokens von den Ausdrücken.

    Das Grundkonzept sieht üblicherweise so aus:

    Ah, danke, ich habe mir auch gerade überlegt, Tokens und Expressions zu trennen. Aber dein Beispiel ist natürlich schöner 🙂

    Hör vielleicht auch auf otze. Mit spirit kannst du auch ganz lust'ge Sachen basteln...

    Ich hab so angefangen (aber weil ich damit nicht erfahren war... 😃 )

    struct calculator : public boost::spirit::grammar<calculator>
    {
        struct print
        {
            template<typename type>
            void operator()(type a) const
            {
                std::cout << a << '\n';
            }
    
        };
    
        template <typename Scanner>
        struct definition
        {
            boost::spirit::rule<Scanner> term,
                                         object,
                                         p_sign,
                                         m_sign,
                                         multi_sign,
                                         divide_sign,
                                         any_sign,
                                         brace_enclosed_expression;
    
            definition(calculator const& self)
            {
                using namespace boost::spirit;
    
                term = object >> *(any_sign >> object);
    
                object = brace_enclosed_expression | real_p[print()];
                brace_enclosed_expression = ch_p("(") >> term >> ch_p(")");
    
                m_sign = ch_p("-");
                p_sign = ch_p("+");
                multi_sign = ch_p("*");
                divide_sign = ch_p("/");
    
                any_sign = m_sign | p_sign | multi_sign | divide_sign;
            }
    
            boost::spirit::rule<Scanner> const& start()
            {
                return term;
            }
        };
    };
    


  • Das muss aber schon eine Weile her sein - ist ja noch Spirit 1.



  • Hacker schrieb:

    Hör vielleicht auch auf otze. Mit spirit kannst du auch ganz lust'ge Sachen basteln...

    Korrigiert mich, falls ich falsch liege, aber ich glaube man kann einen mathematischen Term doch nicht mit LL parsen? Wie kann man einen Term wie "5-1-2" mit LL richtig zerlegen? (Also zu "- ( - 5 1 ) 2" )



  • Ich hab hier noch ne schöne Variante aus dem Buch "Der C++ Programmierer":

    /* cppbuch/loesungen/k3/5.cpp
       Beispiel zum Buch von Ulrich Breymann: Der C++ Programmierer; Hanser Verlag
       Diese Software ist freie Software. Website zum Buch: http://www.cppbuch.de/ 
    */
    #include<iostream>
    #include<cctype>
    using namespace std;
    
    // ********** Funktionsprototypen **********
    long zahl(char& c);
    long ausdruck(char& c);
    long summand(char& c);
    long faktor(char& c);
    
    // ********** Hauptprogramm **********
    int main() {
        char ch;
        do {
            cout << "\n>>";
            cin.get(ch);
            if (ch != 'e')
                cout << ausdruck(ch);
        } while(ch != 'e');
    }
    
    // ********** Funktionsdefinitionen **********
    long ausdruck(char& c) {         // Übergabe per Referenz!
        long a;                      // Hilfsvariable für Ausdruck
        if (c == '-') {
            cin.get(c);              // {\tt -} im Eingabestrom überspringen
            a = -summand(c);            // Rest an {\tt summand()} übergeben
        }
        else {
            if (c == '+')
                cin.get(c);          // {\tt +} überspringen
            a = summand(c);
        }
        while(c == '+' || c == '-')
            if (c == '+') {
               cin.get(c);           // {\tt +} überspringen
               a += summand(c);
            }
            else {
               cin.get(c);           // {\tt -} überspringen
               a -= summand(c);
            }
        return a;
    }
    
    long summand(char& c) {
        long s = faktor(c);
        while(c == '*' || c == '/')
            if (c == '*') {
                cin.get(c);          // {\tt *} überspringen
                s *= faktor(c);
            }
            else {
               cin.get(c);           // {\tt /} überspringen
               s /= faktor(c);
            }
        return s;
    }
    
    long faktor(char& c) {
        long f;
    
        if (c == '(') {
            cin.get(c);                    // {\tt (} überspringen
            f = ausdruck(c);
            if (c != ')' )
               cout << "Rechte Klammer fehlt!\n"; //*** s.u.
            else cin.get(c);               // {\tt )} überspringen
        }
        else f = zahl(c);
        return f;
    }
    
    long zahl(char& c) {
        long z = 0;
        while (isdigit(c)) {
            z = 10*z + long(c-'0');
            cin.get(c);
        }
        return z;
    }
    

    Find ich ziemlich hübsch.



  • Ja, das ist ein üblicher Top-Down Parser (oder auch "recursive descent parser" genannt), denn ich auch bei meiner Implementierung http://www.c-plusplus.net/forum/p1780654#1780654 benutzt habe.



  • seldon schrieb:

    Das muss aber schon eine Weile her sein - ist ja noch Spirit 1.

    Ich lerne es aus den highscore-tutorials.



  • GorbGorb schrieb:

    Korrigiert mich, falls ich falsch liege, aber ich glaube man kann einen mathematischen Term doch nicht mit LL parsen? Wie kann man einen Term wie "5-1-2" mit LL richtig zerlegen? (Also zu "- ( - 5 1 ) 2" )

    Unabhängig davon, dass die von dir gegebene Syntax für LL-Parser kein Problem ist (Beweis: Python wird von einem LL-Parser gelesen und Python kann sowas):
    Spirit ist nicht LL, sondern Kontextfrei (interpretiert EBNF)

    //edit3 nein, da geht sogar mehr: Spirit kann XML lesen. Und XML ist nicht Kontextfrei. (wer hat sich den Schmarrn eigentlich ausgedacht)



  • otze schrieb:

    GorbGorb schrieb:

    Korrigiert mich, falls ich falsch liege, aber ich glaube man kann einen mathematischen Term doch nicht mit LL parsen? Wie kann man einen Term wie "5-1-2" mit LL richtig zerlegen? (Also zu "- ( - 5 1 ) 2" )

    Unabhängig davon, dass die von dir gegebene Syntax für LL-Parser kein Problem ist (Beweis: Python wird von einem LL-Parser gelesen und Python kann sowas):
    Spirit ist nicht LL, sondern Kontextfrei (interpretiert EBNF)

    //edit3 nein, da geht sogar mehr: Spirit kann XML lesen. Und XML ist nicht Kontextfrei. (wer hat sich den Schmarrn eigentlich ausgedacht)

    Ich schiebs mal auf den Alkohol (schön wärs)... du hast natürlich Recht. Wikipedia sagt aber, dass spirit LL( inf ) ist.



  • @TyRoXx: Das umwandeln nach double ist imo nicht Aufgabe eines Lexers. Ein Lexer speichert Text bei mir nur als const_string, wobei const_string im Prinzip einfach ein Paar char const* ist.


Anmelden zum Antworten