dynamisches typsystem system implementieren



  • also,ich hab mich mal von meinem derzeitigen hauptprojekt abgewand, um mal wieder mit ergebnissen zu programmieren 😃

    also, ich wollte ein art interpreter für eine ganz primitive sprache schreiben, die sich hinterher so schreiben lassen soll:

    push 100//in speicher 1 ablegen
    push 2.57//in speicher 2 ablegen
    add 3 1 2//speicher 1+2 addieren und in 3 ablegen ergebnis 102.57
    pop 3//die obersten 3 speicherblöcke löschen
    push "Hallo"
    push 200
    add 3 1 2//ergebnis "Hallo200"
    pop//nur den obersten speicherblock löschen
    

    also geparst kriegt er den code schon 😃
    das problem ist im moment, dass ganze auf dem stack abzulegen. verschiedene Typen in ein array/container/was auch immer zu stecken ist ja nur möglich, wenn man sie von einer Basisklasse ableitet.
    Das problem was ich nun habe, ist zu erkennen, ob die aktion mit den datentypen überhaupt funktionieren kann, ein "Hallo"+200 funktioniert ja,und ist noch halbwegs logisch, aber was soll bei 200+"Hallo" passieren? "200Hallo"? 205?
    Und was ist dann erst mit 200*"Hallo" oder "Hallo"%200?
    Das ein Fehler fliegen muss ist klar,aber wie kann man bei einem dynamischen typsystem rechtzeitig erkennen, dass ein add 1 1 3 niemals zum ziel führen wird? muss ich dafür etwa die ganze zeit listen über die typ identitäten führen,oder soll ich exceptions fliegen lassen, wenn 2 nicht kompatible typen aufeinandertreffen? Oder soll ichs so wie php machen, und einfach alles auf teufel komm raus irgendwie kompatibel machen? da blieb natürlich immernoch die frage, wie man einen String durch einen integer teilt 😃



  • Na bei einem so kleinen Interpreter Exceptions einbauen ist etwas viel - oder meinst du eine Exception im Interpreter die nicht im selbstgebauten Script abfangbar ist?

    Meiner einer würde nicht von gemeinsamer Basisklasse ableiten, sondern alles nach void* casten und mir den Typ dazu merken. Typen gibts im Prinzip eh nur 3: String, Ganzzahl, Kommazahl. Die erkennst du ja problemlos per Parsing. Na und dann einfach sowas:

    enum type { STRING, INTEGER, FLOAT };
    struct elem
    {
        elem* next;
        type datatype;
        void* data;
    }
    

    Wenn nun 2 und 3 durchgeteilt werden sollen dann ist das eben nicht mit 2.datatype=STRING und 3.datatype=INTEGER möglich.

    MfG SideWinder



  • oder meinst du eine Exception im Interpreter die nicht im selbstgebauten Script abfangbar ist?

    genau.
    da hab ich dann in etwa an sowas gedacht:

    enum Type{Integer,String,...};
    class Int:public Types{
    //...
        Types* operator+(Types& operand){
            if(Operand.Type!=Integer){
                throw falseType("Fehlermeldung"); 
            }
        }
    };
    

    aber noch void casten find ich dann doch ziemlich schlecht, weil ichd ann einige funktionen nicht automatisieren könnte, und damit richtig viel code bei mir einzieht(mal abgesehen davon, dass ich das system gerne schnell erweitern können wollte)



  • Stimmt eigentlich, warum nicht einfach eine Klasse Typ, dann jeweils eine abgeleitete Int, Float, Stirng und die Operatoren zwar für jede Kombo überladen aber bei manchen eine Exception werfen. Im Hauptprogramm eine Typexception mit einem Abbruch des Programms und einer kleinen Ausgabe beenden 🙂

    Also da viel lieber Exception als "Hallo"/5 zu ermöglichen 🙂

    MfG SideWinder



  • *hüpf*
    hab soeben meinen ersten Integer auf den Stack gepusht und danach wieder gepopt 😃

    das ganze ist hochmotivierend 😃



  • SideWinder schrieb:

    Meiner einer würde nicht von gemeinsamer Basisklasse ableiten, sondern alles nach void* casten und mir den Typ dazu merken.

    Ich würde eher sowas wie Alexandrescus Loki::Variant-Klasse (oder boost::variant) verwenden. Die vereinigt gute Performanz und bequeme Benutzung.



  • Deswegen würde ich bei sowas auch eine Sprache mit discriminated Unions und Pattern-Matching machen. In C++ ist sowas viel zu aufwändig. Auch wenn boost::variant schon ganz in Ordnung ist und hier wohl auch das Mittel der Wahl ist.



  • das system,w elches ich im moment verwende ist eh absolut unausgereift^^

    ich meine,wenn eine funktion 2 strings annehmen wollte, sähe die passende implementation im hardcode im moment so aus:

    bool test(const string& param,vector<DataType*>& Stack){//so sieht jede funktion aus
         //string 1 auslesen:
         int sizeS1=*(int*)param.c_str();
         string str1(param.begin()+4,param.begin()+4+sizeS1);
    	 //string2 auslesen
         int sizeS2=*(int*)(param.c_str()+4+sizeS1);
    	 string str2(param.begin()+8+sizeS1,param.begin()+8+sizeS1+sizeS2);
         //...
    	 return true;
    }
    //das bekanntmachen der funktion in der main
    Parser<vector<DataType*> >::InstructionInfo Test;
    
    Test.InstructionName="test";//name innerhalb des sources
    Test.Token.push_back(String);//parameter
    Test.Token.push_back(String);
    Test.Function=&test;//funktionpointer
    
    P.AddInstruction(Test);//bekanntmachen
    

    zum glück will ich sowas mit strings nicht machen 😃

    //edit vielelicht sollte ich doch einfach einen vector übergeben wenn ich mir das so anschaue 😃


Anmelden zum Antworten