|
![]() |
Hallo liebe DP'ler
Ich meld mich mal wieder mit ner neuen Komponente ![]() Einleitung Nach dem Release meines ![]() Allgemeines Sysygy Script Engine ist eine Skript-Sprache die den Dialekt Pascal benutzt. Es werden bei weitem nicht alle Features der Delphi-Sprache unterstützt, aber immerhin ein kleiner Teil. Beim kompilieren erstellt der Parser Byte-Code, denn dann von einem Interpreter ausgeführt wird. Durch die Einbindung einer Script-Sprache ist es Möglich, ein Programm von Benutzern erweiterbar zu machen, ohne dass das Hauptprogramm neu kompiliert werden muss. Dadurch wird ein enormes Spektrum an neuen Möglichkeiten eröffnet. Man kann mit der Script-Sprache z.B. aufwendige, mathematische Funktionen berechnen, die einen einfachen Matheparser überfordern würden. Denn dadurch, dass Ergebnisse in Variablen zwischengespeichert werden können und diese dann mit if-Anweisungen verschieden weiterbearbeitet werden können, kann man z.B. verschachtelte Funktionen erstellen, die nicht an das Programm gebunden sind sondern vom Benutzer während der Laufzeit des Programms geändert werden können (z.B. ein Funktionszeichner). Ebenfalls ist es möglich, dass sich ein Programm selbst dynamisch anpasst: bei manchen Gelegenheiten ist es extrem schwer, bestimmte Situationen im Code zu verarbeiten. Möglich ist es zwar immer, doch manchmal kann der Aufwand extrem groß sein. An dieser Stelle könnte das Programm z.B. dynamisch neuen Quelltext erstellen, der dann von der Script-Sprache ausgeführt wird. Sehr hilfreich wird eine Script-Sprache, wenn man z.B. ein Programm per "Fernwartung" erweitern oder ausbessern möchte und man nicht so einfach an die Datei herankommt. Dann kann es z.B. ausreichen, ein neues/verbessertes Script einfach an das Programm zu schicken und somit die Funktionalität erweitern/ausbessern. Das mag jetzt zwar so klingen, als könnte man ganz einfach in einen PC eindringen - doch man sollte bedenken: das Script kann nur Funktionen ausführen, welche das Programm der ScriptEngine bereits zur Verfügung stellt. (Übertrieben) gesagt kann man nicht aus einer 2 KB-Exe ein Monsterprogramm machen. Ein kleines Beispiel wäre: ich habe eine StringListe, die sortiert werden soll. Die Vergleichsfunktion der einzelnen Zeilen lager ich in ein Script aus, das den Vergleich übernimmt. Das Script kann dann nur die Vergleichsfunktion beeinträchtigen aber z.B. nicht die Liste leeren. Was unterstützt die Script-Sprache
Was wird noch nicht unterstützt Hier mal eine kurze Liste mit Featuren, die ich noch plane einzubauen:
Sysygy "Klassen" vs Delphi Klassen ![]() In der Script Engine ist es möglich, externe procedures und functions zu deklarieren. Um diese besser und übersichtlicher zusammenzubringen und den OOP-Gedanken nachzugehen hab ich mich dazu entschlossen, externe Methoden in Klassen zusammenfassen zu können. Dabei unterstützten diese "Pseudo"-Klassen auch Vererbung. An einem Beispiel kann man das ganz gut erklären: Nehmen wir an, ich habe in Delphi folgende Komponente, die ich in der Script-Sprache verfügbar machen will:
Delphi-Quellcode:
Jetzt kann man in der Script-Sprache eine Pseudo-Klasse erstellen, mit deren Hilfe man auf diese Klasse zugreifen kann.
type
TMyClass = class(TComponent) private FSomeThing : string; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function GetSomeThing: string; end; Script-Code
Delphi-Quellcode:
Diese Klasse speist man jetzt in einen Exporter, der daraus eine Delphi-Unit erstellt (ist mit dabei). Diese Delphi-Unit bindet man dann in das Delphi-Projekt ein - den Rest übernimmt die Script-Sprache. Ein Script könnte jetzt z.B. so aussehen:
unit myClassUnit;
uses Classes; // für TComponent type TMyClass = class(TComponent) public function Create(AOwner: TComponent): TMyClass; function GetSomeThing; end;
Delphi-Quellcode:
Die Klassen in der Script-Engine sind also sozusaggen nur eine Zusammenfassung von externen Methoden. Es können keine Variablen oder Script-Interne Methoden hinzugefügt werden. Das will ich aber noch ändern, jedoch dauert das eine ganze weile.
program Test;
uses myClassUnit; var p: TMyClass; s: string; begin p := p.Create; // hier ist ein großer Unterschied zu Delphi s := p.GetSomeThing; if p.ComponentCount = 0 then // da die Klasse von TComponent abgeleitet ist exit; end; Bisherige Einschränkungen
So, genug der negativen Sachen ![]() Was muss ich zusätzlich noch an Komponenten installieren? Die Script-Sprache an sich braucht keine zusätzlichen Komponenten - es werden nur die Klassen benutzt, die bereits bei Delphi mit dabei sind. Auch die mitgelieferten Demos (bis auf eine) kommen mit den Komponenten aus. Jedoch werden für die Kompilierung der Haupt-GUI zwei große Sachen benötigt:
Wie installiere ich die Sysygy Script Engine? Der Parser sowie der Interpretor sind Komponenten mit relativ wenig properties. Von daher ist eine Installation eigentlich nicht vorgesehen. Am besten, ihr kopiert die Quelltexte in einen beliebigen Ordner und fügt diesen in den Suchpfad von Delphi dazu. Ein fertiges Package wird jedoch nicht mitgeliefert. Welche Delphi-Version muss ich haben? Also garantieren kann ich für nichts. Ich hab die Script-Sprache mit Delphi 7 erstellt, von daher sollte alles, was neuer als D7 ist, damit klar kommt. Ich weiß nicht, wie weit runter man mit den Delphi-Versionen gehen kann. Jedoch sollte es schon möglich sein, Funktionen als overloaded zu deklarieren. Sysygy Script Engine + FreePascal Mit Version 0.99e hab ich es geschafft, die Script-Engine FreePascal-kompatibel zu machen. Ich habe (bis auf ein paar einzelne Scripts) nicht viel ausprobiert, aber es hat alles bisher geklappt. Da ja FPC auf sehr vielen Plattformen funktioniert, weiß ich nicht, ob wirklich alles unterstützt wird. x86-CPUs sollten ziemlich sicher funktionieren, bei x86-64Bit-Systemen weiß ich nicht, ob alles funktioniert. Die die LCL ja nicht 100%ig kompatibel mit der VCL ist, sind einige Funktionen der Include-Dateien uSygInc*.pas nicht mit eingebunden. Zwar erscheinen diese Funktionen noch im Script, jedoch führt ein Aufruf zu einer Exception. Ich werde aber noch die neuen Includes {$IFDEF FPC} und {$IFDEF DELPHI} in die Header mit einbauen und somit die nicht verfügbaren Funktionen für das Script unsichtbar machen - jedoch dauert das noch etwas. Bisher gibt es noch Probleme mit den Units uSygIncGraphics und uSygIncWinCRT, die anderen sollten aber funktionieren. Wie benutze ich die Komponenten? Ich habe ein paar Demoprogramme in den Download mit hinein gepackt, an denen man (hoffentlich) gut sehen kann, wie man die Komponenten am einfachsten benutzt. Hier ist aber mal der grundlegende Aufbau zur Benutzung
Delphi-Quellcode:
Was ist in den Download dabei?
uses
uSygParser, // für TSygScript_Parser uSygRunTime; // für TSygScript_RunTime var Compiler : TSygScript_Parser; // das Objekt muss natürlich vorher erstellt werden Executor : TSygScript_RunTime; // ... und das Objekt sollte auch vorher erstellt werden function CompileAndExecute(ScriptSource: string): boolean; begin // Ergebniss vorinitalisieren result := False; // Script kompilieren if Compiler.ParseScript(ScriptSource) then begin // ByteCode im Executor speichern Executor.FillData(Compiler.OutputData); // Script ausführen Executor.Run; result := True; end; end;
Lizenz: Das "Hauptprogramm" sowie die Komponente ist unter der BSD-Lizenz lizenziert. Der Lizenztext liegt ebenfalls nochmal im Hauptordner der zip-Datei sowie als Header in jeder .pas-Datei. Falls jemand eine andere Lizenz haben will, bin ich gerne bereit, eine einzelne, personengebundene Lizenz nach Absprache zu erstellen. Und nun ... ... wünsch ich viel Spaß beim ausprobieren ![]() Change-Log Version: 0.99h (15.10.2008)
Jabber: littleDave@jabber.org
in case of 1 is 0 do external raise while in public class of object array else repeat until 1 is 0 |
Delphi 2005 Personal |
#51
Hallo Dave
Zuerst mal alle Achtung vor diesem Projekt! Fürs Verständnis aber ein paar Fragen: Ich habe ein Programm bei dem Objekte eine hierarchische Datenstruktur bilden. Der Aufbau sieht in etwa so aus: Projektgruppe->Projekte->Programme->... Auf ein Programm kann man z.B. über ProjektGruppe[0].Projekte[1] zugreifen. Sehr praktisch wäre nun, wenn man per Skript z.B. auf ein Objekt der Klasse TProgramm zugreifen und einzelne Methoden davon aufrufen könnte. Fall es mit einer Script-Engine überhaupt Sinn macht und funktioniert, wie würde die Vorgehensweise bei Sysygy dann bitte aussehen? Der Download von Deiner hat leider nicht geklappt Dave. Vielen Dank für die Inputs Geri
Gerhard
|
![]() |
Lazarus |
#52
Btw:
![]() Ich bins mal wieder xD
Wie kann ich dem Script eine vorhandene Variable / Klasse zuweisen? zb. Self:TForm ? Paar Bugs: Wie kann ich ne Klasse im Script erstellen? zb das funktioniert nicht:
Delphi-Quellcode:
type
TMyClass = Class public procedure FormPaint(Sender: TObject); end; procedure TMyClass.FormPaint(Sender: TObject); begin end; ![]() [Error] myprogram(12): Procedure "tmyclass" already exists
Could not compile the script
Delphi-Quellcode:
program myprogram;
uses Classes, Forms; type TTestRec = record FVar: String; end; TMyClass = Class private FMyVar: String; public function Create():TMyClass; end; ![]() [Error] myprogram(13): "end", "public" expected, but found scsIdentifier instead
[Error] myprogram(13): "end" expected, but found scsIdentifier instead [Error] myprogram(13): ";" expected, but found : instead [Error] myprogram(13): "=" expected, but found ; instead [Error] myprogram(14): scsIdentifier, "(", "set", "class", "record", "type" expected, but found public instead Could not compile the script Dann hätte ich noch nen Bug-Fix: Unit: uSygIncGraphics Zeilen: 688-692 Neuer Code:
Delphi-Quellcode:
Anscheinend ist Dave im Urlaub xD
function TBrush_Bitmap(Self: TBrush): TBitmap;
// function TBrush.Bitmap(Self : pointer) : TBitmap; begin result := {$IFDEF FPC}TBitmap(Self.Bitmap){$ELSE}Self.Bitmap{$ENDIF}; end; @Geri wieso regelst du nicht zb. die ProjektGruppen, Projekte etc im Main Programm, dann sagst du nur diese unitX ist das ProjektX, und dann wenn du genau dieses Projekt brauchst rufste die unitX mit der Script Engine auf? |
![]() |
Delphi 2005 Personal |
#53
Hallo Eugen
Vielen Dank für Deinen Hinweis. Das "Main"-Formular führt eine Referenz auf den Projektgruppenmanager. Dieser verwaltet eine Liste an Projekten. Jedes Projekt ist eine wie eine Projektgruppe ein Klasse mit Methoden und Eigenschaften. Die Struktur sieht in etwa so aus:
Delphi-Quellcode:
TfrmMain = class(TForm) ... CreateForm(...); public mProjectGroup:TProjectGroup; end; TProjectGroup = class(TComponent) ProjectList:TList; Constructor creaet(...); procedure CreateNewProject):TProject; // erzeuge ein Objekt der Klasse TProject und füge es in ProjectList ein. .... end; TProject = class(TComponent) ...... public Wert1, Wert2:Integer; procedure SetWert1(aWert1:Integer); .... end; Im Script würde ich dann z.B gerne
Delphi-Quellcode:
Anstatt der Referenz ThisProject könne ich auch gut mit dem Listenindex leben..
...
ThistProjecd:=mProjectGroup.CreateProject); ThisProject.SetWert1(1235); ... ![]() Geht so was, ich weiss nicht ob diese Aufgabe für eine script-Engine Sinn macht, für mich wäre es aber sehr praktisch. Beste Grüsse Geri
Gerhard
|
![]() |
Lazarus |
#54
Das müsste die Script Engine schaffen
jage mal TProjectGroup und TProject durch das ImportTool, diese setzt du dann unter uses im Script selbst musste dann uses uProjects (zb) bei var mProjectGroup : TProjectGroup exports; machen kurz bevor du das Script "Run'st" musste mit SetVariable ( kA welche parameter , einfach mal gucken ^^) diese Variable setzen und schon kannste mProjectGroup im Script nutzen Hoffe es ist das was du meintest und man kann es verstehen ^^ MfG, Eugen |
![]() |
Delphi 2005 Personal |
#55
Hallo zusammen
@Eugen: Vielen Dank für Deine Hinweise. Ich werde mal versuchen diesen Weg in die Tat umzusetzen ![]() ![]() Wenn ich richtig verstehe muss ich folgndermassen vorgehen: 1.) Units zuerst auf irgendeine Art importieren damit sie die Script-Engine kennt. (Importtool???) 2.) Im Skript eine Variable vom Typ TProjectGroup (z.B. ProjectGroup1:TProjectGroup) definieren. 3.) Auf Methoden und Eigenschaften der Klasse ProjectGroup und TProject zugreifen - Stimmts? z.B.
Delphi-Quellcode:
Habe ich das richtig verstanden? var ProjectGroup1:TProjectGroup; Project1: TProject; begin ProjectGroup1:=GetProjectGroupReference(); // Referen von der Mainform holen if Assgined(ProjectGroup1) then begin Project1:=ProjectGroup.GetProject(0); if Assigend(Project1) then Project1.SetProjectName('Testproject 0'); end; end; ![]() @Dave Ich bin absolut begeistert! Gibt es zu dieser Library auch eine Beschreibung oder ein Tutorial. Das wäre sehr praktisch, denn wenn man das Konzept versteht, dann kann man die Libary besser nutzen. Ja, jedenfalls eine super Sache und ich werde mal schauen, ob ich es schaffe, diese Engine für mein Programm zu nutzen. Beste Grüsse Geri
Gerhard
|
![]() |
Lazarus |
#56
zu 1.) Main Programm -> deine Unit laden -> Project -> Export to Delphi Unit
zu 2.) genau, das Beispiel von dir müsste ich auch mal testen ^^ ist leichter als mit export usw^^ zu 3.) genau Wobei ich nicht weiß ob Assigned in der Engine vorhanden ist Und die Projekte sollen dann auch noch mal ausführbare Scripts sein? Wird langsam zeit das sich Dave mal meldet *g*. |
![]() |
Delphi 2005 Personal |
#57
Hallo Eugen
Die Klasse Projekt enhält Methoden, welche im Programm bestimmte Aktionen ausführt. Der Inhalt eines Projektes selbst ist kein Script. Das Script wäre deshalb sehr praktisch, weil der Benutzer bestimmte Abläufe, die er immer wieder ausführt automatisieren könnte (ähnlich einem Macro). Ich versuche gerade mein Glück ![]() Beste Grüsse Geri
Gerhard
|
![]() |
Delphi 7 Professional |
#58
Hallo an alle,
sorry das ich so lange nicht mehr online war - ich habe im Moment sehr viel zu tun und hatte mein Delphi in den letzten 3 Monaten ca. 1 Woche mal gestartet ![]() Bevor ich auf eure Beiträge eingehe, habe eine gute und eine schlechte Nachricht für euch: Die Schlechte: Die Sysygy Script Engine werde ich nicht mehr weiterentwickeln ![]() Die Gute Nachricht: Ich arbeite gerade an einer neuen Script Engine ![]() Die neue Script-Engine wird einiges Anders und Besser machen, da ich viel aus dieser Script-Engine gelernt habe: Ich hab mal schnell eine Liste von Sachen zusammengeschrieben, die ich einbauen WILL:
Das ist schon eine sehr lange List und sehr komplexe Liste die ich nicht so schnell abarbeiten kann. Jedoch bin ich noch sehr optimistisch, dass ich das hinbekomme. ![]() irgendwie bin ich grade im Script-Wahn
![]() ![]() ![]() ![]() Da ich FPC nutze, musste ich in dieser Datei: uSygConstants
diesen Teil: ... auch unter das {$IFNDEF FPC} packen dann konnte ich es schon mal Compilieren ![]() ![]() Was meinste wann diese Sachen funktionieren werden?
Arrays Class Properties Gleichnamige Funktionen ( welcher aber verschiedene Parameter haben zB. x(a: Integer); x(a: Float); x(a,b : Integer); etc Class properties: wird es in der neuen Engine geben Overloading methods: wird es in der neuen Engine geben (funktioniert bereits) ![]() Wie füge ich Funktionen hinzu, auch wenn ich den UnitName nicht weiß?
Delphi-Quellcode:
Wenn ich das ohne UnitName='my...' mache bekomme ich nen Fehler das WriteLn schon existiert, gibt es vllt ne Funktion zum zu gucken ob diese Funktion schon existiert? oder wie löst man es am besten.
procedure TForm1.ParserOnAddCustomFunctions(Sender: TObject; UnitName: string);
begin if(UnitName='myprogram')then begin TSygScript_Parser(Sender).AddFunction(@MyWriteLn, 'procedure WriteLn(s: string); register;'); end; end; ![]() Ich würde vorschlagen das du die Scipt Engine auf einen SVN Server packst, dann könnte man immer nen aktuellen Code bekommen und man könnte leichter Patches erstellen
![]() ![]() Hast Du Deinen Bytecode komplett neu erfunden, oder hast Du eine Vorlage benutzt ?
Gibt es eine Beschreibung für den Bytecode ? ![]() (Ich möchte u.U,. den Bytecode-Interpreter in C implementieren, um auf einem Prozessor, für den es keinen Object-Pascal-Compiler gibt, Delphi-Code laufen zu lassen. )
@Geri: Wenn das Problem noch aktuell ist, will ich dir noch schnell mal ne grobe Anleitung schreiben:
Nun solltest du die Klassen in deinem Script verwenden können (uses-Einbindung nicht vergessen) Am besten schaust du dir nochmal die Beispiele an, die mit beim Download dabei sind Viele Grüße |
![]() |
Delphi 7 Professional |
#60
![]() Huhu
gibt es schon irgendwelche Updates, nutzbare Versionen o.ä? MfG Eugen Also benutzbare Versionen gibt es noch nicht. Aber ich komme gut vorran. Ich kann ja mal schnell nen Status-Update machen: Also der Compiler ist schon sehr weit fortgeschritten. Ich habe schon einen Unit-Cache eingebaut, was die Compile-Performance schon mal extrem verbessert: wenn man ein Script kompiliert, werden alle benutzen Units erstmal kompiliert. Sind diese erfolgreich kompiliert, werden diese Daten in einen Binärstream (im Moment im Speicher) gespeichert. Wird das Script jetzt nochmal neu kompiliert, werden die verwendeten Units nicht nochmal kompiliert sondern direkt aus dem Cache geladen. Dadurch schaff ich es, relativ "einfache" Scripts in ca. 20 ms zu kompilieren. Das ist natürlich sehr Vorteilhaft für die Code-Completion. Diese habe ich jetzt auch noch erweitert, so dass es jetzt eine Code-Completion und eine Param-Completion gibt (in Delphi Strg+Shift+Leertaste). Das funkioniert bisher ganz prima. Ein weiterer wichtiger Punkt ist die Run-Performance. Diese hab ich schon mal extrem verbessert. Hier mal eine kleine Tabelle (die einzelnen Test stehen unter der Tabelle), bei der ich die alte Script-Engine mit Pascal Script und mit meiner aktuellen vergleiche (alle Angaben in Sekunden)
Code:
Die einzelnen Test sind die Ausführungsgeschwindigkeiten von unterschiedlichen Scripten.
- Sysygy Script Engine Pascal Script ScriptEngine2
Test 1 4,76 0,8 0,9 Test 2 7,40 1,6 1,89 Test 3 12,75 2,3 2,8 Test 4 14,22 8,44 4,3 Test 5 26,10 16,81 8,7 Test 6 38,17 25,21 13,3 Test 7 31,39 26,27 11,0 Test 8 66,00 52,61 22,0 Test 9 99,10 79,08 33,0 ----------------------------------------------------------------------- Gesamt 299,89 213,12 97,89 Test 1
Delphi-Quellcode:
Test 2
program Test;
var i: integer; begin for i:=0 to 1000000 do ; end. Wie Test 1, nur zählen bis 2000000 Test 3 Wie Test 1, nur zählen bis 3000000 Test 4
Delphi-Quellcode:
Test 5
program Test;
var i: integer; begin for i:=0 to 1000000 do begin if i mod 2 = 0 then begin end; if i mod 3 = 0 then begin end; if i mod 4 = 0 then begin end; end; end. Wie Test 4, nur zählen bis 2000000 Test 6 Wie Test 4, nur zählen bis 3000000 Test 7
Delphi-Quellcode:
Test 8
program Test;
var i: integer; begin for i:=0 to 1000000 do begin if i mod 2 = 0 then begin end; if i mod 3 = 0 then begin end; if i mod 4 = 0 then begin end; if i mod 5 = 0 then begin end; if i mod 6 = 0 then begin end; if i mod 7 = 0 then begin end; if i mod 8 = 0 then begin end; if i mod 9 = 0 then begin end; if i mod 10 = 0 then begin end; end; end. Wie Test 7, nur zählen bis 2000000 Test 9 Wie Test 7, nur zählen bis 3000000 Wie man Anhand den Zeiten sieht, habe ich die Script-Engine gerade für komplexe Aktionen optimiert. Bei einer einfachen for-Schleife ist Pascal-Script noch ein wenig besser als meine Script-Engine. Sobald ich dann aber was in der for-Schleife mache, ist meine Script-Engine bei weitem schneller als Pascal-Script (und sowieso schneller als meine alte Script-Engine). Dies erreiche ich vorallem durch einen eingebauten Memory-Manager in die Script-Engine. Dieser Memory-Manager ist dabei nicht ein Carbage Collector für die Script-Variablen - meine Script-Engine wird erstmal keinen Managed-Code benutzen. Ein weiterer Performance-Schub ist der eingebaute Linker, der den erstellen Byte-Code noch optimiert. Mit dem Standard-Unit-Import komme ich auch schon ganz gut vorran. Bisher habe ich schonmal fast alle Funktionen der Unit Math eingebaut. Auch fast alle String-Operationen sind bereits eingebaut. Ach ja, DateTime wird auch kopmlett unterstützt (inklusive der Unit DateUtils). Da die Script-Engine ermöglicht, Units über mehrere Dateien zu spannen, muss man die einzelnen Units nicht mühsam einbinden. Hier mal ein kleines Demonstations-Script:
Delphi-Quellcode:
Das Ergebnis:
program Test;
var i: integer; d: TDateTime; begin // Aktuelles Datum ausgeben d := DateTime.Now; Console.WriteLine(DateTime.DateTimeToStr(d)); // Ne Schleife for i:=0 to 5 do // Overload-Test Console.Write(i); // Noch ein Overload-Test Console.WriteLine(); // Eine Funktion aus Math Console.WriteLine(Math.Max(5.0, 10.0)); // Eine Convert-Routine Console.WriteLine(Convert.BoolToStr(False, True)); // Oder einfach nur nen String Console.WriteLine('Ende'); Console.ReadKey; end.
Code:
Also es geht wirklich gut vorran. Was ich jedoch noch nicht gemacht habe ist: records, array, OOP, ... Es wird also noch ne ganze weile dauern, jedoch wenns so gut weitergeht, nicht mehr all zu lange
25.10.2009 16:33:24
012345 10 False Ende ![]() So, das war jetzt erstmal nen kleiner Status-Update. Sobald es wieder was wichtiges gibt, werd ich mich wieder melden. Grüße Dave |
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |