Interpreterbau - Ein Anfang
-
Dummie-Off schrieb:
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/Ok. Danke für den hinweis.
Dummie-Off schrieb:
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...
Hm.. joar vielleicht. Ich glaube aber es soll so sein muss ich mal mein Ausbilder fragen. Der gibt mir meistens vor wie ich es machen soll. Ob eine oder mehrere. Trotzdem danke nochmal.
P.S.: Dein Artikel ist btw ziemlich gut mag ich. Gut zum lernen geeignet.
-
Hey ich noch mal!
Ehm ... also irgendwie versteh ich nicht warum er bei mir bei einem String immer abbricht. Also nach dem er ein String Token gemacht hat hört er auf. Liegt das am "?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(isprint(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); };
std::string input = "{ :(); classa {public: function hello(){ console.log(""hello""); }};class b {public: function bye(){ console.log(""Bye""); }};class c : public a,b{public: function greet(){ console.log(""greeting""); }}";
Eingabe: "{ :(); classa {public: function hello(){ console.log(hello); }};class b {public: function bye(){ console.log(Bye); }};class c : public a,b{public: function greet(){ console.log(greeting); }}" TT_RBRACE = 0 TT_COLON = 0 TT_RBRACKET = 0 TT_LBRACKET = 0 TT_SEMI = 0 TT_STRING = 0
-
Überleg' mal was die while-Schleife macht....
-
Th69 schrieb:
Überleg' mal was die while-Schleife macht....
while(isprint(myCh)){ buf += myCh; readNextChar(); }
Ehm...
Solange myCh ein druckbarer char ist (frage ist ob der das nun nur für die cases oben macht oder ob für ihn alle druckbar sind?)
myCh in wird an buf ran gehangen
nächsten char lesen
-
Anstatt
isprint
musst duisalpha
verwenden, denn isprint sieht auch all die anderen Zeichen als druckbar an.http://www.cplusplus.com/reference/clibrary/cctype/isalpha/
Zur Not musst du auch einfach selber mal nach der Funktion im Internet suchen und dir die Dokumentation dieser ansehen.
Oft sind auch verwandete Funktionen dort beschrieben, die dann ggf. besser geeignet sind.Und noch was:
Warum benennst du "(" als RBRACKET und ")" als LBRACKET (selbiges gilt für BRACE)?Eigentlich wäre es genau andersrum sinnvoller, denn das R steht für Right und das L für Left.
-
Dummie schrieb:
Anstatt
isprint
musst duisalpha
verwenden, denn isprint sieht auch all die anderen Zeichen als druckbar an.Und noch was:
Warum benennst du "(" als RBRACKET und ")" als LBRACKET (selbiges gilt für BRACE)?Eigentlich wäre es genau andersrum sinnvoller, denn das R steht für Right und das L für Left.
Öhm ... ich hab mir gedacht "(" ist nach rechts geöffnet also RBRACKET.
EDIT: Ach so und warum muss ich den constructor löschen wenn ich was an dem string = input ändere? Wenn ich wieder was ändere muss ich ihn wieder rein machen. Versteh ich nicht.
Token::Token(TokenType type, int value) : myType(type), myValue(value){ }
-
Ich hatte es eben von der Perspektive gesehen, dass sie eben links steht (sind ja immer Klammerpaare). Das finde ich auch beim Schreiben bzw. Lesen schneller verstanden. Denn bei deiner Variante muss ich mir erst vorstellen, wie denn "(" aussieht und entsprechend entscheiden, ob ich L oder R meine.
Kannst du letztendlich aber eh so halten, wie du damit am besten arbeiten kannst.
-
Dummie schrieb:
Ich hatte es eben von der Perspektive gesehen, dass sie eben links steht (sind ja immer Klammerpaare). Das finde ich auch beim Schreiben bzw. Lesen schneller verstanden. Denn bei deiner Variante muss ich mir erst vorstellen, wie denn "(" aussieht und entsprechend entscheiden, ob ich L oder R meine.
Kannst du letztendlich aber eh so halten, wie du damit am besten arbeiten kannst.
Hmm... ok das klingt auch logisch. Ich glaub ich änder das.
-
Man schaue sich das hier einmal an:
So wird es im Code definiert:
case '}': readNextChar(); Toki.push_back(TT_LBRACE); return Token(TT_LBRACE); case '{': readNextChar(); Toki.push_back(TT_RBRACE); return Token(TT_RBRACE);
Lets twist it bei der Ausgabe:
Eingabe: "class a {" TT_CLASS = 0 TT_STRING = 0 TT_LBRACE = 0
TT_LBRACE sollte eigentlich nach dem Code TT_RBRACE heißen...
-
Hallo,
ich fände eine Weiterführung des Interpreters, vorallem aus dem mathematischen Raum hinaus eine wirklich interessante Artikelreihe
-
-
Ja, das stimmt natürlich und ich hatte es ja auch vor. Eventuell gehe ich das demnächst mal an. Es erfordert halt nur sehr viel Zeit.
Ich bin mir auch noch nicht sicher, was für eine Sprache es dann werden soll. Eventuell einfach eine Modellsprache, wie PL/0. Es geht ja in erster Linie um das Konzept.
PL/0 ist grundsätzlich eine recht vollständige Sprache mit Abfragen, Schleifen und so weiter, aber kennt nur den Datentyp Integer. Der Vorteil ist auch, dass es dazu sehr viele fertige Projekte und anderes Material gibt. Da kann man sein Wissen dann noch zusätzlich vertiefen. Mal sehen.
-
Hallo, Tutorials zu so leichten Sprachen findet man überall im Netz, ich fände ein Tutorial zu einem Webserver (für den man programmieren kann), also so wie PHP sehr viel spannender. Natürlich nicht so umfangreich
Danke
-
Hallo123 schrieb:
Hallo, Tutorials zu so leichten Sprachen findet man überall im Netz, ich fände ein Tutorial zu einem Webserver (für den man programmieren kann), also so wie PHP sehr viel spannender. Natürlich nicht so umfangreich
Danke
Schau dir mal Tntnet an: http://www.tntnet.org/
-
Hallo, ich habe ein kleines Problem mit meinem eigenen Interpreter, nämlich: Was mache ich, wenn ich mehrere Ausdrücke hintereinander habe (z.B.:
i = 0; a = 6;
)? Muss ich ein Array von Nodes machen? Und wie würde ich dann z.B. einen C++-SC in einen AST überführen? (ich weiß, dass das viel zu komplex ist ;))
Viel Dank im Voraus
-
C++ompiler schrieb:
Hallo, ich habe ein kleines Problem mit meinem eigenen Interpreter, nämlich: Was mache ich, wenn ich mehrere Ausdrücke hintereinander habe (z.B.:
i = 0; a = 6;
)? Muss ich ein Array von Nodes machen? Und wie würde ich dann z.B. einen C++-SC in einen AST überführen? (ich weiß, dass das viel zu komplex ist ;))
Viel Dank im Voraus
Das kommt ganz auf den Anwendungsfall an. Wenn du eine Sprache entwickelst, bei der das Programm direkt ausgeführt bzw. in eine andere Sprache kompiliert werden soll, dann kann es Sinn machen, dass du für jede Deklaration ein Node anlegst. Dann könntest du zur Laufzeit den Speicher berechnen bzw. bei der neue Sprache so die Deklaration generieren.
Ansonsten musst du eine Symboltabelle haben (solltest du für die semantische Analyse ohnehin schon haben) und für die Variablen eine Adresse berechnen. Dann kann jeder Ident mit dem entsprechenden Eintrag in der Symboltabelle verknüpft werden und bei der Codegenerierung weiß der Compiler so, wie er den Code zu generieren hat.
Das ganze ist dann im Detail doch etwas anspruchsvoller, also am besten mal eine fertige Implementierung suchen und davon abschauen. Suchwörter sind jedenfalls Symboltabelle, Adressgenerierung, usw.
-
Hey,
dein Tutorital hat mir sehr geholfen. Allerdings so 100% habe ich es wohl noch nicht verstanden.
Gibt es eine Möglichkeit mir die Zwischenergebnisse anzuzeigen? Im Parser kann ich mir ohne Probleme das aktuelle Ergebnis res ausgeben lassen, aber wie kann ich mir die aktuell Zusammengefasste Rechnung ausgeben lassen?
-
gilwell88 schrieb:
Hey,
dein Tutorital hat mir sehr geholfen. Allerdings so 100% habe ich es wohl noch nicht verstanden.
Gibt es eine Möglichkeit mir die Zwischenergebnisse anzuzeigen? Im Parser kann ich mir ohne Probleme das aktuelle Ergebnis res ausgeben lassen, aber wie kann ich mir die aktuell Zusammengefasste Rechnung ausgeben lassen?Schau dir mal das C# Programm an. Das stellt das ganze visuell dar und hilft vielleicht beim Verständnis. Oder was meinst du? Weil Zwischenergebnis und zusammengefasste Rechnung erscheinen mir identisch?
Bin mir nicht so sicher, wo dein Verständnisproblem genau ist und was du gerne als Hilfe ausgegeben haben willst.
-
Das C# habe ich mir angeschaut und auch gefunden wo der Baum erstellt wird, ich werde mal versuchen soetwas in C++ zu übertragen, mal sehen ob mir das gelingt.
Am liebsten wäre es mir, wenn in der Ausgabe solche Zwischenergebnisse ständen, wie man sie auch hat wenn man von Hand rechnet, also z.B.:
Eingabe: (20+1)/(2+1)
Ausgabe: = 21/(2+1)
Ausgabe: = 21/3
Ausgabe: =7
Diese Zwischenschritte sind alle irgendwie gespeichert und der Parser macht sie auch so, ich weiß nur nicht wie ich sie ausgebe.
-
Das ist wohl etwas tricky. Ich würde den AST nehmen und immer ein Node evaluieren lassen und die anhängenden löschen und durch das Ergebnis ersetzen. Dann Ausgeben. Und das ganze solange wiederholen bis man am Root Node ist...