Ausdruck in Token zerlegen
-
Am anfang würde es mir reichen, Ganzzahlen einzulesen!
Der nächste Schritt wären dann Gleitkommazahlen und dann Konstanten(PI, Eulersche Zahl....) dann sin, cos, tan,... und so weiter!Ich lerne gerade mit dem Buch von Bjarne Stroustrup, und da ist so was drinnen!
Aber mein Problem liegt an dem Aufbau von seinem Programm! Er will nämlich auch beibringen, wie man es nicht machen soll und das führt bei mir irre zu Verwirrungen! Ab einem gewissen Punkt, an dem er sein Programm komplett neu überarbeitet, komm ich nicht mehr weiter!
-
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.
-
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:
enum TokenType { Tk_Plus, Tk_Minus, Tk_Times, Tk_Divide, Tk_Identifier, Tk_Number, }; struct Token { TokenType type; boost::variant<std::string, double> value; }; std::vector<Token> tokenize(const std::string &source); struct Expression { virtual ~Expression(); }; struct BinaryExpression : Expression { std::unique_ptr<Expression> left, right; }; struct Addition : BinaryExpression { }; struct Symbol : Expression { std::string name; }; struct Literal : Expression { double value; }; struct Negation : Expression { std::unique_ptr<Expression> negative; }; std::unique_ptr<Expression> parse(const std::vector<Token> &tokens);
-
Oder du benutzt boost::spirit. Für so kleine Sachen ist der Aufwand zur Trennung in lexer-parser overkill.
-
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.