Interpreterbau - Ein Anfang
-
leiderC++ schrieb:
iJake1111 schrieb:
Das ist wohl eines der Besten C Tutorials, das ich je gesehen hab, Respekt! Seit diesem Tut, sehe ich Parser schon mit ganz anderen Augen
Nochmal rechten Dank!
Mfg iJake1111Leider ist es C++
Ich habe die einfache Version in C portiert. Steht also ab jetzt auch zum Download bereit. Vielleicht interessiert sich ja jemand dafür.
-
Als Stichwort: Parser Combinator
-
OK. Ich habe mir das Tutorial zu Gemüte geführt und den Quelltext erstmal gedownloadet. Wenn ich diesen kompiliere, geht noch alles glatt. Wenn das Programm dann allerdings startet, kann ich auch ohne Probleme meinen String eingeben, nachdem ich 'Enter' gedrückt habe, kommt aber immer folgende Meldung: "Debug Assertion Failed! ....". Wenn ich dann auf ignorieren klicke, steht das Ergebnis auch da. Wie kriege ich also die Fehlermeldung weg?
Schonmal danke im Voraus.
-
Ich hab mir jetzt den Quellcode nicht angesehen, aber mein erster Anhaltspunkt wäre, dass man nach einem "assert" Ausschau hält und herausfindet, warum das fehlschlägt.
-
Hey,
probier mal in der Datei Scanner.cpp die Funktion
Scanner::readNextChar()
zu suchen und ersetz in dieser die Zeileif (myPos > myInput.length())
durch diese Zeile (es kommt ein = hinzu):
if (myPos >= myInput.length())
Warum aber der Fehler erst jetzt auftritt ist mir nicht klar. Vermutlich hat es was mit der neu herausgekommenen Visual Studio Version oder 32 Bit bzw. 64 Bit Systemen zu tun. Mir wäre der Fehler (der unabhängig von der Eingabe immer auftritt) sonst definitiv aufgefallen.
Vielen Dank für die Rückmeldung
Edit:
Falls jemand parallel zu neuren Versionen noch die alte Version installiert hat, wäre es wirklich sehr nett, wenn jemand überprüfen könnte, ob der Fehler dort auch auftritt und ob length() in den Versionen vielleicht unterschiedliche Werte zurückliefert oder ob in der Bibliothek einfach ein Assert dazugekommen ist.
-
* AUSGRABE*
Eine frage kann man den download auch so bekommen das man das aus Linux ohne VS öffnen kann?
Einfach .txt reicht mir.
-
Enno schrieb:
* AUSGRABE*
Eine frage kann man den download auch so bekommen das man das aus Linux ohne VS öffnen kann?
Einfach .txt reicht mir.Der Quellcode in C bzw. C++ ist plattformunabhängig und in den Downloads natürlich ebenso enthalten.
Also alles entpacken und in das entsprechende Verzeichnis gehen, wo die .h und .cpp Dateien liegen.
Dann für die C++ Variante einfach: g++ -o Prog *.h *.cpp
Und zum Starten: ./Prog
Ansonsten bietet es sich an, dass du den Artikel einfach Schritt für Schritt bearbeitest. Es ist immer sinvoller, wenn man auch die ganzen Hintergründe versteht.
-
Dummie schrieb:
Enno schrieb:
* AUSGRABE*
Eine frage kann man den download auch so bekommen das man das aus Linux ohne VS öffnen kann?
Einfach .txt reicht mir.Der Quellcode in C bzw. C++ ist plattformunabhängig und in den Downloads natürlich ebenso enthalten.
Also alles entpacken und in das entsprechende Verzeichnis gehen, wo die .h und .cpp Dateien liegen.
Dann für die C++ Variante einfach: g++ -o Prog *.h *.cpp
Und zum Starten: ./Prog
Ansonsten bietet es sich an, dass du den Artikel einfach Schritt für Schritt bearbeitest. Es ist immer sinvoller, wenn man auch die ganzen Hintergründe versteht.
Danke für deine antwort werd das gleich ausprobieren. Ich arbeite das grade durch und wollte nur mal nachgucken wo du die
enum TokenType
hingepackt hast.
EDIT: Da kommt bei mir gleich die frage auf ob man das nicht alles in 3 Datei machen kann? muss ich das alles so auslagern?
-
Enno schrieb:
Dummie schrieb:
Enno schrieb:
* AUSGRABE*
Eine frage kann man den download auch so bekommen das man das aus Linux ohne VS öffnen kann?
Einfach .txt reicht mir.Der Quellcode in C bzw. C++ ist plattformunabhängig und in den Downloads natürlich ebenso enthalten.
Also alles entpacken und in das entsprechende Verzeichnis gehen, wo die .h und .cpp Dateien liegen.
Dann für die C++ Variante einfach: g++ -o Prog *.h *.cpp
Und zum Starten: ./Prog
Ansonsten bietet es sich an, dass du den Artikel einfach Schritt für Schritt bearbeitest. Es ist immer sinvoller, wenn man auch die ganzen Hintergründe versteht.
Danke für deine antwort werd das gleich ausprobieren. Ich arbeite das grade durch und wollte nur mal nachgucken wo du die
enum TokenType
hingepackt hast.
EDIT: Da kommt bei mir gleich die frage auf ob man das nicht alles in 3 Datei machen kann? muss ich das alles so auslagern?
Wie für einen Artikel oder andere Erklärungen üblich ist der Quellcode in erster Linie darauf ausgerichtet, dass er sich gut vermitteln lässt.
Er ist daher auf gar keinen Fall perfekt oder endgültig.
Stattdessen dient er mehr als Anschauungsmaterial zum Ausprobieren und natürlich zur Orientierung (wofür du ihn ja z.B. auch gebraucht hast).
Wenn du also der Meinung bist, dass es anders besser wäre, dann kann das gut sein, dass du damit richtig liegst.
Da Software aber aus viel mehr Dingen besteht, kann das niemand "mal eben" beantworten.Da hilft es nur es auszuprobieren und eigene Erfahrungen zu sammeln oder sehr konkrete Fragen zu stellen.
-
Irgendwie versteh ich diesen Fehler nicht:
Fehler: »TokenType« bezeichnet keinen Typ
Eigentlich ist der doch durch
enum
bezeichnet? Warum meckert mein Compiler dann hier:
private: TokenType myType;
Bin bei dem Teil wo man zwischen durch mal was Testen kann. Also den Scanner.
Dummie schrieb:
An dieser Stelle ist der Scanner fertig. Er kann in einem Testlauf sehr einfach getestet werden:
#include <iostream> #include <string> #include "Scanner.h" int main() { std::string input = "10 + 5\n * \t10-\n5\t"; std::cout << "Eingabe: \"" << input << "\"\n" << std::endl; Scanner scanner(input); Token tok = scanner.getNextToken(); while (!tok.equal(TT_NIL)) { std::cout << tok.toString() << " = " << tok.getValue() << std::endl; tok = scanner.getNextToken(); } }
Bitte um Aufklärung. Danke.
-
Ist TokenType denn bekannt? Z.B. vor Token deklariert oder durch Inkludierung eingebunden?
Der Codeausschnitt enthält nicht den Teil, der den Fehler enthält... Man kann dir sehr schlecht helfen.
Oder hast du den Code aus der Zip genommen und dort kommt der Fehler?
Welchen Compiler nutzt du denn?
-
Dummie-Off schrieb:
Ist TokenType denn bekannt? Z.B. vor Token deklariert oder durch Inkludierung eingebunden?
enum TokenTyp{ // token definition TT_RBRACKET, TT_LBRACKET, TT_RBRACE, TT_LBRACE, TT_COMMA, TT_SEMI, TT_COLON, TT_DOT, TT_STRING, TT_NIL }; static const char *TokenTypStr[] = { // for error message "TT_RBRACKET", "TT_LBRACKET", "TT_RBRACE", "TT_LBRACE", "TT_COMMA", "TT_SEMI", "TT_COLON", "TT_DOT", "TT_STRING", "TT_NIL" };13 14 15 16 17 class Token{ private: TokenType myType; int myValue; std::string TokenTypeStr; public: Token(TokenType type = TT_NIL, int value = 0); TokenType getType() const { return myType; } int getvalue() const { return myValue; } const char *toString() const { return TokenTypeStr[myType]; } // error message bool equal(TokenType) const { return myType == type; } //comparison };
Dummie-Off schrieb:
Der Codeausschnitt enthält nicht den Teil, der den Fehler enthält... Man kann dir sehr schlecht helfen.
Doch genau der Fehler kommt zu dem Code teil. Der untere von dir Zitierte Code ist nur dafür da das man weiß wo ich in deinem Artikel grade am arbeiten bin.
Fehler:
Fehler: »TokenType« bezeichnet keinen Typ
Code:
private: TokenType myType;
Dummie-Off schrieb:
Oder hast du den Code aus der Zip genommen und dort kommt der Fehler?
Nein. Hab es selbst noch einmal geschrieben allerdings nur in 3 Datei. Hab nicht alles ausgelagert.
Dummie-Off schrieb:
Welchen Compiler nutzt du denn?
Einfach unter Linux/Debian mit CMake und dann make.
-
TokenTyp vs TokenType (mit e am Ende)
-
Dummie-Off schrieb:
TokenTyp vs TokenType (mit e am Ende)
Oh man. Ich hasse mich selbst. xD
Danke.
-
Hmmm ...
Jetzt bekomme ich noch diesen Fehler hier:Fehler: »type« wurde in diesem Gültigkeitsbereich nicht definiert
Irgendwie kommt da doch jetzt noch irgendwas nicht an.
EDIT: Hab eine Sache bemerkt ich bin ein dummes Kind. Das bleibt.^^
EDIT2: Hab es. Selbstdialog ftw.
EDIT3: Tja nun gib das hier:oopjs.cpp:27: undefined reference to `Token::Token(TokenType, int)' oopjs.cpp:27: undefined reference to `Token::Token(TokenType, int)' oopjs.cpp:30: undefined reference to `Token::Token(TokenType, int)' oopjs.cpp:33: undefined reference to `Token::Token(TokenType, int)' oopjs.cpp:36: undefined reference to `Token::Token(TokenType, int)' oopjs.cpp:39: undefined reference to `Token::Token(TokenType, int)' oopjs.cpp:42: more undefined references to `Token::Token(TokenType, int)'
Den Fehler bekomme ich auch wenn ich deine Version nehme.
enum TokenType{ // token definition TT_RBRACKET, TT_LBRACKET, TT_RBRACE, TT_LBRACE, TT_COMMA, TT_SEMI, TT_COLON, TT_DOT, TT_STRING, TT_NIL }; static const char *TokenTypStr[] = { // for error message "TT_RBRACKET", "TT_LBRACKET", "TT_RBRACE", "TT_LBRACE", "TT_COMMA", "TT_SEMI", "TT_COLON", "TT_DOT", "TT_STRING", "TT_NIL" }; class Token{ private: TokenType myType; int myValue; public: Token(TokenType type = TT_NIL, int value = 0); // HIER GEHT ES LOS! TokenType getType() const { return myType; } int getValue() const { return myValue; } const char *toString() const { return TokenTypStr[myType]; } // error message bool equal(TokenType type) const { return myType == type; } //comparison }; class Scanner{ public: Scanner(const std::string& input); Token getNextToken(); //HIER GEHT ES WEITER private: std::string myInput; unsigned int myPos; //Position at input char myCh; //last char private: void skipSpaces(); void readNextChar(); };
Token Scanner::getNextToken(){ std::string buf; skipSpaces(); //to skip chars which are not used switch(myCh){ case '(': readNextChar(); return Token(TT_RBRACKET);// HIER FINDET ER ES NICHT case ')': readNextChar(); return Token(TT_LBRACKET);// HIER FINDET ER ES NICHT case '{': readNextChar(); return Token(TT_RBRACE);// HIER FINDET ER ES NICHT case '}': readNextChar(); return Token(TT_LBRACE);// HIER FINDET ER ES NICHT case ',': readNextChar(); return Token(TT_COMMA);// HIER FINDET ER ES NICHT case ';': readNextChar(); return Token(TT_SEMI);// HIER FINDET ER ES NICHT case ':': readNextChar(); return Token(TT_COLON);//HIER WIRD NICHT GEMECKERT!!!! case '.': readNextChar(); return Token(TT_DOT);//HIER WIRD NICHT GEMECKERT!!!! //read as long its a letter or " case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '"': while(isdigit(myCh)){ buf += myCh; readNextChar(); } return Token(TT_STRING); default: if(myCh != 0){ std::cerr << "Error: not used Char '" << myCh << "'" << std::endl; } break; } return Token(TT_NIL); }
-
Okay, du hast den Sourcecode runtergeladen und er gibt dir also die gleiche Fehlermeldung aus? Eigentlich hatte ich alles vorher auch erfolgreich unter Linux getestet bevor ich es hochgeladen habe.
Inkludierst du auch Token.h? Gibst du dem Compiler alle notwendigen .cpp und .h Dateien mit?
Hast du überhaupt einen Konstruktor implementiert?
Token::Token(TokenType type, int value) : myType(type), myValue(value) { // ... }
Was für einen Compiler/IDE nutzt du überhaupt?
-
Dummie schrieb:
Inkludierst du auch Token.h? Gibst du dem Compiler alle notwendigen .cpp und .h Dateien mit?
Ja alles richtig.
Dummie schrieb:
Hast du überhaupt einen Konstruktor implementiert?
Token::Token(TokenType type, int value) : myType(type), myValue(value) { // ... }
Nein. das wars....
Dummie schrieb:
Was für einen Compiler/IDE nutzt du überhaupt?
Ehm... ich schreibe in Kate. Und bau den kram mit CMake im build Ordner mit make. Sag dir das was?
Danke.Achso hier hab es alles in 3 Dateien:
.h:#ifndef OOPJS_H #define OOPJS_H #include <iostream> #include <string> enum TokenType{ // token definition TT_RBRACKET, TT_LBRACKET, TT_RBRACE, TT_LBRACE, TT_COMMA, TT_SEMI, TT_COLON, TT_DOT, TT_STRING, TT_NIL }; static const char *TokenTypStr[] = { // for error message "TT_RBRACKET", "TT_LBRACKET", "TT_RBRACE", "TT_LBRACE", "TT_COMMA", "TT_SEMI", "TT_COLON", "TT_DOT", "TT_STRING", "TT_NIL" }; class Token{ private: TokenType myType; int myValue; public: Token(TokenType type = TT_NIL, int value = 0); TokenType getType() const { return myType; } int getValue() const { return myValue; } const char *toString() const { return TokenTypStr[myType]; } // error message bool equal(TokenType type) const { return myType == type; } //comparison }; class Scanner{ public: Scanner(const std::string& input); Token getNextToken(); private: std::string myInput; unsigned int myPos; //Position at input char myCh; //last char private: void skipSpaces(); void readNextChar(); }; Token::Token(TokenType type, int value) : myType(type), myValue(value){ } /* class Parser{ public: Parser(const std::string& input): int parse(); private: Scanner myScanner; Token myTok; private: int start(); void accept(TokenType); void getNextToken(); }; */ #endif
.cpp:
#include "oopjs.h" void Scanner::readNextChar(){ if(myPos > myInput.length()){ myCh = 0; return; } myCh = myInput[myPos++]; } void Scanner::skipSpaces(){ while(myCh == ' ' || myCh == '\t' || myCh == '\r' || myCh == '\n'){ readNextChar(); } } Scanner::Scanner(const std::string& input) : myInput(input), myPos(0){ readNextChar(); } Token Scanner::getNextToken(){ std::string buf; skipSpaces(); //to skip chars which are not used switch(myCh){ case '(': readNextChar(); return Token(TT_RBRACKET); case ')': readNextChar(); return Token(TT_LBRACKET); case '{': readNextChar(); return Token(TT_RBRACE); case '}': readNextChar(); return Token(TT_LBRACE); case ',': readNextChar(); return Token(TT_COMMA); case ';': readNextChar(); return Token(TT_SEMI); case ':': readNextChar(); return Token(TT_COLON); case '.': readNextChar(); return Token(TT_DOT); //read as long its a letter or " case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '"': while(isdigit(myCh)){ buf += myCh; readNextChar(); } return Token(TT_STRING); default: if(myCh != 0){ std::cerr << "Error: not used Char '" << myCh << "'" << std::endl; } break; } return Token(TT_NIL); } /* void Parser::getNextToken(){ myTok = myScanner.getNextToken(); } void Parser::accept(){ if(!myTok.equl(type)){ std::cerr << "Error: unexpected Token " << myTok.toString() << ", " << TokenTypeStr[type] << " expected" << std::edl; } getNextToken(); } Parser::Parser(const std::string& input) : myScanner(input){ getNextToken(); } int Parser::parse(){ int res = start(); accept(TT_NIL); return res; } void Parser::start(){ } */
main:
#include <iostream> #include <string> #include "oopjs.h" int main(){ std::string input = "class"; std::cout << "Eingabe: \"" << input << "\"\n" << std::endl; Scanner scanner(input); Token tok = scanner.getNextToken(); while (!tok.equal(TT_NIL)){ std::cout << tok.toString() << " = " << tok.getValue() << std::endl; tok = scanner.getNextToken(); } }
-
Funktioniert es denn nun korrekt?
Ich würde für Bezeichner und Strings eigene Token einführen.
Und für reservierte Keywords wie class, if, else... würde ich auch eigene Token einführen. Wenn du also einen Bezeichner eingelesen hast, kannst du prüfen, ob es evtl. ein Keyword ist...
Also in etwa so:
case 'a': case 'b': case 'c': ....... case 'z': // Weiter einlesen.... if (value == "else") return Token(TT_ELSE); else if (value == "if") return Token(TT_IF); return Token(TT_IDENT, value );
Edit: Und deine Codeaufteilung ist so leider nicht wirklich besser. Wenn du unbedingt nur eine Datei inkludieren willst, dann mach dir eben eine Datei oopjs.h die einfach alles inkludiert, was du immer brauchst.
-
Dummie schrieb:
Funktioniert es denn nun korrekt?
Nein ist eine endlos Schleife. Kommt immer TT_STRING = 0.
Dummie schrieb:
Ich würde für Bezeichner und Strings eigene Token einführen.
Ja klingt gut.
Dummie schrieb:
Und für reservierte Keywords wie class, if, else... würde ich auch eigene Token einführen. Wenn du also einen Bezeichner eingelesen hast, kannst du prüfen, ob es evtl. ein Keyword ist...
Also in etwa so:
case 'a': case 'b': case 'c': ....... case 'z': // Weiter einlesen.... if (value == "else") return Token(TT_ELSE); else if (value == "if") return Token(TT_IF); return Token(TT_IDENT, value );
Ok vielen Dank. Sowas hatte ich vor.
Dummie schrieb:
Edit: Und deine Codeaufteilung ist so leider nicht wirklich besser. Wenn du unbedingt nur eine Datei inkludieren willst, dann mach dir eben eine Datei oopjs.h die einfach alles inkludiert, was du immer brauchst.
Ehm ... hab ich das nicht? Hab doch eine oopjs.h die alles inkludiert? Oder versteh ich dich falsch?
Danke.
-
Ja, es ist natürlich eine Endlosschleife, weil du isdigit nutzt. Das fragt ab, ob es eine Zahl ist. Das ist bei "class" natürlich immer false.
Stattdessen musst du isprint verwenden, wenn du Buchstaben willst:
http://www.cplusplus.com/reference/clibrary/cctype/isprint/Deine Codegliederung ist insofern schlecht, weil du Scanner und Parser und Token alles in eine Datei packen willst. Stattdessen bietet es sich an, wenn du diese Dinge trennst.
Scanner.h
Parser.h
Token.h (evtl. sogar TokenType.h)Das wird sonst sehr unübersichtlich...