Interpreterbau - Ein Anfang



  • Hallo Dummie,

    da Compilerbau mein Spezialgebiet an der Uni war, habe ich mich auch privat damit weiter beschäftigt.

    Zuerst in C++: http://www.bitel.net/dghm1164/downloads/FctParser-Sources.zip
    (für den Borland C++ Builder) - dieser ist auch in einigen Beiträgen hier im Forum schon verlinkt.

    Und ähnlich wie du habe ich dann einen Artikel verfasst (jedoch für C#): http://www.mycsharp.de/wbb2/thread.php?threadid=71995

    Mein MathParser ist jedoch so allgemein gehalten, daß man die Operatoren, Funktionen und Konstanten jeweils selber der Basisklasse übergeben kann (auch die interne Optimierung als Abstract Syntax Tree ist implementiert!), d.h. es gibt eine allgemeine NodeFunc-Klasse (welche knivil ja angemeckert hat -).

    Ich finde es schön, daß du dir die Mühe für diesen Artikel gemacht hast. Und ich wäre sehr an einer Fortsetzung interessiert (ich selber hatte vor ca. 13 Jahren mal eine eigene Scriptsprache in Delphi 2 (!) implementiert)...



  • Hallo Dummie

    Ich finde den Artikel sehr interessant und gut beschrieben. Das ganze schaut sehr ausbaufähig aus und auch ich wäre sehr interessiert an einer Fortsetzung.

    Wollt auch schon mal mit einer Scriptsprache anfangen aber ich schaff es immer nicht Projekte für die ich keinen direkten Sinn (Anwendung) finde zu Ende zu bringen :-). Deswegen großen Respekt an dir das du dir die ganze Mühe gemacht hast und auch noch einen Artikel dafür geschrieben hast.

    Mfg Marco



  • Hallo,

    danke für die positiven Kommentare 🙂

    knivil schrieb:

    Ich finde NodeFunc unguenstig. Warum nicht genau wie Add fuer jede Funktion eine extra Klasse wie:

    class Cos : public Node
    {
        public:
    	Cos(Node* right);
    
            virtual double eval();
    };
    

    Auch wuerde ich der eval-Methode (oder dem Konstruktor) einen Kontext mitgeben, in der Variablen und Konstanten definiert sind.

    In dem Fall müsste man beim Parsing entscheiden, welcher Node angelegt werden muss. Das lässt sich sicherlich gut über ein Array o. ä. erledigen - so ließen sich auch viel einfacher neue Funktion hinzufügen. Für diesen Artikel fand ich das Vorgehen über NodeFunc aber als verständlicher. 🙂

    Th69 schrieb:

    Mein MathParser ist jedoch so allgemein gehalten, daß man die Operatoren, Funktionen und Konstanten jeweils selber der Basisklasse übergeben kann (auch die interne Optimierung als Abstract Syntax Tree ist implementiert!), d.h. es gibt eine allgemeine NodeFunc-Klasse (welche knivil ja angemeckert hat -).

    Dein Matheparser ist auch wirklich sehr interessant, insbesondere da er so allgemein gehalten ist. Ich hatte ja auch im Artikel beschrieben, dass man das machen kann, damit haben wir ja nun auch dafür ein schönes Beispiel. 👍

    Marc-O schrieb:

    Wollt auch schon mal mit einer Scriptsprache anfangen aber ich schaff es immer nicht Projekte für die ich keinen direkten Sinn (Anwendung) finde zu Ende zu bringen :-). Deswegen großen Respekt an dir das du dir die ganze Mühe gemacht hast und auch noch einen Artikel dafür geschrieben hast.

    Ja, so eine Sprache ist auch schon ein etwas größerer Akt. Selbst eine sehr kleine Scriptsprache kann schon sehr viel Arbeit bedeuten und man stellt erst beim Programmieren fest, was moderne Sprachen tatsächlich alles für einen leisten. Das was man schon lange als selbstverständlich empfunden hat, vermisst man plötzlich bei der eigenen. 🤡



  • Hi,

    ich schreibe keine parser mehr selbst. boost::spirit kann das viel besser.

    Chris



  • Sehr schöner Artikel und sehr interessant!



  • chrmhoffmann schrieb:

    Hi,

    ich schreibe keine parser mehr selbst. boost::spirit kann das viel besser.

    Chris

    Habs mir mal angeschaut und muss sagen ist sehr interressant, wobei ich einiges aus dem Artikel direkt mitnehmen konnte für diese Bibliothek, und wenn es auch nur das EBNF ist 🙂

    Aber ansonst werd ich mir die mal genauer anschauen und versuchen nen kleinen XML-Parser damit zu bauen.

    Mfg marco



  • flex + bison 🙂



  • 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 iJake1111



  • Toller Artikel, hat mir sehr geholfen!



  • 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 iJake1111

    Leider ist es C++



  • 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 iJake1111

    Leider 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 Zeile

    if (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. 🙂


Log in to reply