ChaosScript

Kommentare anzeigen Worklog abonnieren

Worklogs ChaosScript

Gestatten, Editor mein Name

Dienstag, 30. September 2008 von hamZta
Ich hab mal kurzzeitig an etwas anderem gebastelt um mich in wxMax einzuarbeiten. Kann ich nur weiterempfehlen, ein sehr cooles Modul!

Das kam dabei raus:
user posted image

Ein Editor für ChaosScript Smile
Er unterstützt nicht sehr viele Features, hat aber dank Scintilla schnelles Highlighting, ist plattformunabhängig und hat dank mir Tabbed Editing!
Ausserdem kann man seine Quelltexte mit Knopfdruck compilieren lassen und somit auf Fehler prüfen.

Siehe hier:
user posted image
(Man verzeih das inaktive Fenster, ich hab was beim abknipsen falsch gemacht)

Wie man sieht zeigt der Editor den Fehler an, den der Parser ausgespuckt hat, der Cursor springt automatisch in die richtige Zeile. Irgendwie werde ich noch einbauen, dass die Zeile hervorgehoben wird oder so. Mal sehen.

An ChaosScript selbst hab ich aufgrund eines Seminares nicht weitergearbeitet. Außerdem beginnt jetzt die Uni, mal sehen wie es da vorangehen wird.

Grüße,
hamZta

Weiter, weiter!

Dienstag, 23. September 2008 von hamZta
Was gibts Neues?
Auch hier gehts voran! Ich habe das Member/Method-Handling des Parsers verbessert und damit folgenden Code ausführbar gemacht:
Code: [AUSKLAPPEN]
class Alter
{
   int zahl;
}

class Karl
{
   Alter _age;
   int test;
}

Karl myKarl = new Karl;
Alter zweitalter;
myKarl._age = new Alter;
myKarl._age.zahl = 5;
myKarl.test = 16;
print("Der Test ist " + myKarl.test);
print("Dein Karl ist " + myKarl._age.zahl + " Jahre alt!");

zweitalter = myKarl._age;
zweitalter.zahl = 100;

print("Dein Karl ist " + myKarl._age.zahl + " Jahre alt!");

Wundert euch nicht über seltsame Bezeichnungen und Namen, die kamen mir einfach als erstes in den Sinn Wink
Tjaaa, was sieht man hier?
Es werden 2 Klassen definiert und dann eine Instanz der Klasse Karl erstellt. Karl hat einen Member "_age" der Klasse Alter, dieser wird mit myKarl._age = new Alter; erstellt.
Zugegriffen auf die Member der Instanz in der Instanz ( Wink ) wird, wie gewohnt, per myKarl._age.zahl.

Die Ausgabe dieses Codes:
Zitat:
Der Test ist 16
Dein Karl ist 5 Jahre alt!
Dein Karl ist 100 Jahre alt!
* Execution Time: 1.38199997ms


Weiters habe ich externen Typen endlich Methoden verpasst!
Siehe hier:
Code: [AUSKLAPPEN]
extern
{   
   class Type
   {
      int test;
      int drueckMich();
   } myType;
}

myType.drueckMich();


Der dazugehörige Type in BlitzMax sieht wie folgt aus:
Code: [AUSKLAPPEN]
Type ExternalType
   
   Field test:Int
   
   Method drueckMich:Int()
      test = 1
      Print "Ich wurde gedrueckt!"
   End Method
   
EndType

myVM.registerExternalClass("Type", New ExternalType)


Und die Ausgabe:
Zitat:
Ich wurde gedrueckt!
* Execution Time: 0.141999990ms



Achja, mein Profiler erzeugt jetzt mal hässliche HTML-Files die dann circa so aussehen:
user posted image
Darin sieht man (sehe ich Wink ) ganz genau, wie oft jeder OP-Code aufgerufen wird, wieviel Zeit er im Durchschnitt verschlingt und wieviel Zeit wirklich gebraucht wird (Das Profiling braucht auch etwas Zeit und verfälscht dadurch das Ergebnis). Und wer ist der Übeltäter? Print. War ja klar Smile

Wie gehts weiter?
Ich habe vor, demnächst mal probeweise "Visibility" einzubauen, also die Möglichkeit, Methoden und Member als private oder public zu definieren. Da dies eine reine Sache der semantischen Analyse und beeinflusst somit die Geschwindigkeit der VM nicht.

Außerdem arbeite ich natürlich an Methoden für interne Klassen (oh, und Methodenparameter fehlen ja auch noch gänzlich ... oje, oje!).

Kleine Überlegungen bezüglich externer Types:
Ich finde es interessant, wenn man von externen Typen Instanzen erstellen könnte. An und für sich kein Problem, dank Reflection und so.
Zu überlegen wäre die interne Handhabung, ich habe mir das in etwa so vorgestellt:
Code: [AUSKLAPPEN]
Type ExternalType Abstract
   
   Global __list:TList
   
   Function onCreate(inst:Object)
      if __list = Null __list = CreateList()
      _list.AddLast(inst)
   End Function
   
End Type

Immer wenn in ChaosScript dann eine neue Instanz erstellt wird, wird automatisch onCreate aufgerufen und somit die Instanz intern gespeichert. Wenn man diese Eigenschaften an seinen eigenen Typ vererbt kann man diese Funktion auch überschreiben und sie somit für eigene Zwecke nutzen.
Wie gut das funktioniert weiß ich noch nicht, das wird auszuprobieren sein Smile

Bis zum nächsten Mal!

hamZta

Call me Jesus

Sonntag, 14. September 2008 von hamZta
Hallo!

Gerade eben hab ich einen weiteren Teil des geplanten OOPs fertiggestellt. Nachdems ja schon externe Funktionen gibt sollte es ja externe Klassen auch geben, oder?
Ja. Gibts.

main.bmx
Code: [AUSKLAPPEN]
Type Dog
   Field hungry:Int
   
   Method amIHungry()
      If hungry
         Print "I am sooo hungry!"
      Else   
         Print "I am not hungry at all!"
      EndIf
   EndMethod
EndType

Local myDog:Dog = New Dog
myDog.amIHungry()
myVM.registerExternalClass("Dog", myDog)
myVM.Execute()
myDog.amIHungry()


script.cs
Code: [AUSKLAPPEN]
extern
{
   class Dog
   {
      int hungry;
   } myDog;
}

myDog.hungry = 1;


spuckt ganz trocken
Zitat:
I am not hungry at all!
* Execution Time: 0ms
I am sooo hungry!

aus.

Ich bitte mich zu entschuldigen: BÄM!

Was BlitzMax mit dem Reflectionsmodul alles möglich macht ist fantastisch Smile Nun hat man aus der Scriptsprache heraus Vollzugriff auf BlitzMax-Typen, kann Felder verändern & auslesen wie es beliebt.

Nächster Halt: Methoden, sowohl intern als auch extern!

mfg,
hamZta

So geht das.

Samstag, 13. September 2008 von hamZta
Code: [AUSKLAPPEN]
class MyClass
{
   int count;
   string line;
}

void classPrint(MyClass tmp)
{
   print(tmp.line*tmp.count);
}

void increaseMe(MyClass tmp)
{
   tmp.count = tmp.count + 2;
}

MyClass inst1 = new MyClass;
inst1.line = "#";
inst1.count = 1;

MyClass inst2 = new MyClass;
inst2.line = "*";
inst2.count = 1;

MyClass myInst = inst1;

for(int i = 0; i < 10; i = i + 1;)
{
   classPrint(myInst);
   increaseMe(myInst);
}


Es ist vollbracht! Wink
Rudimentärer OOP-Support wurde in einer arbeitsintensiven Zeit implementiert.
Das Beispiel oben gibt
Code: [AUSKLAPPEN]
#
###
#####
#######
#########
###########
#############
###############
#################
###################

aus. Verändert man nun die Zeile "MyClass myInst = inst1;" und nimmt statt inst1 eben inst2 hat man dasselbe, nur eben mit Sternen!
Wie man sehen kann werden Instanzen per-reference übergeben, wenn man in einer Funktion also eine Eigenschaft ändert so ändert man direkt die Instanz und keine Kopie davon Smile

Außerdem sieht man hier auch noch die außerordentlich tolle Stringmultiplikation in voller Aktion! Wunderprächtig.

That's the way!

Donnerstag, 11. September 2008 von hamZta
Hab gerade den Parser komplett neu geschrieben weil der alte zwar funktionierte aber extrem hässlich war Wink

Der neue ist hübscher und schöner aufgeteilt und somit leichter zu erweitern.

Was gibts neues?
Wie angekündigt habe ich Floats eingebaut und einen eher nebensächlichen Datentyp (der hier aber erwähnenswert ist um eine tolle Überleitung zu basteln!), nämlich "void" für Funktionen die nichts zurückliefern!
Ach, wo wir gerade bei Funktionen sind: Sowohl interne als auch externe Funktionen besitzen nun Parameter die man ihnen übergeben kann. Mit dabei sind auch bekannte optionale Parameter, zB:
Code: [AUSKLAPPEN]
test(10);
test(10, 5);

void test(int param1, int param2 = 10)
{
   print("Ergebnis: " + (param1 * param2));
}

Dieses Beispiel gibt einmal "Ergebnis: 100" und einmal "Ergebnis: 50" aus. Überraschung, Überraschung.

Der Parser bedient sich des Multi-Pass-Verfahrens, somit können, wie man oben ohnehin sieht Funktionen vor ihrer Deklaration aufgerufen werden (wie es in Basic üblich ist).

Neu ist weiters der Modulo-Operator (%) und eine Funktion "len" die die Länge von Strings zurückliefert

Eine winzige Kleinigkeit hab ich noch eingebaut: Stringmultiplikation!
Vielleicht hat der eine oder andere sowas schonmal in anderen Scriptsprachen gesehen:
Code: [AUSKLAPPEN]
print("Hallo "*5);

gibt fünfmal "Hallo " aus Smile Das geht natürlich nicht nur mit Print sondern eigentlich überall.

Externe Funktionen funktionieren ebenfalls bestens, eine Unschönheit gibt es zu bemängeln:
Aufgrund der Beschaffenheit eines Stacks bekommt man in der Funktion den letzten Parameter zuerst. Vielleicht finde ich noch eine Lösung, aber das soll mich nicht weiter aufhalten Smile

Demnächst werde ich mich dann wohl auf Arrays stürzen, vielleicht mal einen Blick auf Preprocessor-Konstanten werfen und langsam den OOP-Hafen ansteuern. Viel Arbeit.

edit: Oh, fast hätte ichs vergessen! Der Stack wurde auch wieder neu gemacht und ich hab so noch ein paar Millisekunden gewonnen Wink Ausserdem reagiert der Stack auf Falsch"poppen" nun freundlicher und überprüft ob wirklich der zu holende Typ oben auf liegt. Wenn nicht, holt er ihn mit der korrekten Methode runter, castet ihn auf den richtigen Typ und gibt ihn dann zurück. Freundlicher Stack.

mfg,
hamZta

Stackoptimierung

Montag, 8. September 2008 von hamZta
Gerade eben hab ich den Stack von Grund auf neu geschrieben und dabei ein bisschen optimiert, hier die Ergebnisse:
Code: [AUSKLAPPEN]
PUSH 100.000 Integer:
 * New Stack: 355ms
 * Old Stack: 710ms
POP 100.000 Integer:
 * New Stack: 26ms
 * Old Stack: 33ms
PUSH 100.000 Strings ("Test"):
 * New Stack: 271ms
 * Old Stack: 545ms
POP 100.000 Strings:
 * New Stack: 124ms
 * Old Stack: 128ms


Wie man sieht hat sich vor allem im Integer-Bereich einiges getan, diese werden jetzt fast doppelt so schnell wie zuvor auf den Stack gelegt (und meist um die 10 Millisekunden schneller wieder runtergeholt Wink)
Die Werte sind etwas ungenau da ich eigentlich mehrere Durchläufe machen müsste um dann den Mittelwert zu berechnen.

Wer sich fragt wo der Test mit Floats geblieben ist: Der alte Stack konnte noch keine Floats pushen/poppen Smile

Wer mit den ganzen Zahlen nicht viel anfangen kann: Die Ausführzeit des im ersten Eintrag gezeigten Codebeispiels hat sich von 126 Millisekunden auf 60 Millisekunden verbessert!

Ich werde mich jetzt dann mal dransetzen, dass Floats ihren Einzug finden.

mfg,
hamZta

ChaosScript

Montag, 8. September 2008 von hamZta
Wie so manch anderer arbeite ich im Moment auch an einer Scriptsprache zur Einbindung in Spielen & Programmen.

Um den aktuellen Stand zu zeigen am besten ein kurzes Stück Code:
script.cs:
Code: [AUSKLAPPEN]
include "extern.cs"

// Komplexe Terme
int test = (1+2*3-4)*5-6/7*8+6;

// If-Abfragen
if ((test > 10 && test < 20) || (test > 20 && test < 30))
{
   print("Test ist zwischen 10 und 20 oder 20 und 30!!!11");
   print("Test ist naemlich: " + test);

   // Beliebig tief verschachtelbar
   if (test == 13)
      print("Voll ins schwarze!");
}

// While-Schleifen
int f = 0, g = 0;
while(f < 10)
{
   g = g + 2;
   f = f + 1;
}

// Stringkonkatenation
print("g: " + g + " f: " + f);

// For-Schleifen
for(int i = 0; i < 5; i++;)
{
   // GetTime ist eine Funktion in BlitzMax
   print("Es ist gerade: " + GetTime());
}

// Euklidischer Algorithmus
int a = 345345, b = 100;

if (a == 0)
   print("ggT: " + b);
else
{
   while(b != 0)
   {
      if (a > b)
         a = a - b;
      else
         b = b - a;
   }
   print("ggT: " + a);
}

int zahl = Zehn() + Zehn() + Fuenf();
print("Zahl: " + zahl);

int Zehn()
   return 10;

int Fuenf()
   return 5;


extern.cs:
Code: [AUSKLAPPEN]
extern
{
   string GetTime();      // Gibt die aktuelle Uhrzeit zurück
}


Output:
Code: [AUSKLAPPEN]
Test ist zwischen 10 und 20 oder 20 und 30!!!11
Test ist naemlich: 13
Voll ins schwarze!
g: 20 f: 10
Es ist gerade: 23:38:22
Es ist gerade: 23:38:22
Es ist gerade: 23:38:22
Es ist gerade: 23:38:22
Es ist gerade: 23:38:22
ggT: 5
Zahl: 25
 * Execution Time: 126ms


Wie man sieht hab ich mich größteils an die C-Syntax gehalten weil ich die einfach übersichtlich und sympathisch finde Smile
In dem Beispiel zeige ich einfach ein paar simple Funktionen der Sprache her, Schleifen, Bedingungen, Funktionen (interne wie externe).

Einbinden von Funktionen geht ähnlich einfach wie in Lua:
Code: [AUSKLAPPEN]
Function GetTime:Int(vm:TVM)
   vm.GetStack().PushString(CurrentTime())
End Function

myVM.registerExternFunction(DT_STRING, "GetTime", GetTime)


Die Sprache wird "kompiliert", d.h. es wird ein Bytecode erzeugt mit relativ low-leveligen Operationen wie PUSH, ADD, SUB oder JMP. Dieser Bytecode kommt in eine Datei und wird von der VM ausgeführt (Bei Spielen muss man somit nicht den Quelltext der Spiele mitliefern).

Mein Ziel ist es, einen einfachen Ersatz für Lua in BMax zu bieten. Dass ich an die Geschwindigkeit von Lua nicht rankomme ist klar, aber ich denke, dass sich die Ergebnisse sehen lassen können Smile

Woran ich momentan arbeite sind Funktionsparameter für sowohl externe als auch interne Funktionen und der Einbindung des dritten Datentyps float.

Weiters habe ich vor, Listen und Arrays einzubauen und ein einfaches Klassen-System und somit OOP einzuführen.

Vielen, vielen herzlichen Dank an LordChaos der mir am Anfang ausdauernd auf die Sprünge geholfen hat Smile
Oh, und bevor ichs vergesse: Vielen Dank auch an BlitzCoder für seine CollIDE mit der mir das Arbeiten gleich viel leichter fällt Wink

mfg,
hamZta