AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Projekte Sysygy Script Engine - Version 0.99h
Thema durchsuchen
Ansicht
Themen-Optionen

Sysygy Script Engine - Version 0.99h

Ein Thema von littleDave · begonnen am 13. Jun 2008 · letzter Beitrag vom 1. Sep 2009
Antwort Antwort
Seite 6 von 9   « Erste     456 78     Letzte »    
Benutzerbild von littleDave
littleDave
Registriert seit: 27. Apr 2006
Hallo liebe DP'ler

Ich meld mich mal wieder mit ner neuen Komponente

Einleitung
Nach dem Release meines Sysygy Image Viewers hab ich erstmal eine kleine Pause von dem Projekt gebraucht. Daher hab ich mich dazu entschlossen, meine Script-Engine, die ich bereits in dem Projekt verwende, weiter zu machen und allen zur Verfügung zu stellen.

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
  • Standard-Delphi-Typen: boolean, byte, shortint, word, smallint, integer, cardinal, int64, pointer, single, double, string, record (char kann über byte oder über string gelöst werden)
  • Variablen- und Typendeklarationen
  • uses-deklarationen
  • (natürlich) Kommentare im Script: //, (* *), { }
  • procedures und functions mit Parametern (optional auch var-Parameter)
  • direkter Aufruf von Delphi-Funktion aus dem Script heraus
  • Aufruf von einzelnen Script-Funktionen aus dem Programm heraus
  • try-finally und try-except Blöcke
  • Byte-Code in Stream speichern und später Ausführen
  • for, while und repeat-schleife
  • if und case-Anweisung (case auch mit strings)
  • Operatoren: and, or, xor, mod, shl, shr, not
  • "Klassen" mit Vererbung (wird weiter unten genauer erleutert)
  • Compiler-Anweisungen: {$IFDEF} {$IFNDEF} {$ELSE} {$DEFINE} {$UNDEF} {$INCLUDE} {$WARNINGS}
  • Lesen und schreiben von Script-variablen aus dem Program heraus
  • Code-Vervollständigung von SynEdit
  • Call-Stack zur Fehlerfindung aufrufbar

Was wird noch nicht unterstützt
Hier mal eine kurze Liste mit Featuren, die ich noch plane einzubauen:
  • 64-Bit-Typen (erledigt mit Version 0.98b) (extended und currency sind nicht geplant)
  • arrays
  • @ und ^ - Operator (da bin ich mir noch nicht sicher, ob die überhaupt gebraucht werden)
  • set und set of - Typen (erledigt mit Version 0.99h)
  • try-except und try-finally - Blöcke (kann noch etwas länger dauern) (erledigt mit Version 0.99d)
  • einzelne Funktionen im Script aus dem Programm heraus aufrufen (bisher kann nur das komplette Script ausgeführt werden) (erledigt; Feature eingeführt mit Version 0.99b)
  • Variablen umcasten (erledigt mit Version 0.99h)
  • div-Operator (Divisionen funktionieren schon, aber bitte unter "Enschränkung" nochmal genauer nachlesen) (hat sich erledigt, da "/" jetzt ordentlich funktioniert; letzte Änderung: Version: 0.99a)
  • Properties in den Script-Klassen erlauben

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:
type
  TMyClass = class(TComponent)
  private
    FSomeThing : string;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  
    function GetSomeThing: string;
  end;
Jetzt kann man in der Script-Sprache eine Pseudo-Klasse erstellen, mit deren Hilfe man auf diese Klasse zugreifen kann.
Script-Code
Delphi-Quellcode:
unit myClassUnit;

uses Classes; // für TComponent

type
  TMyClass = class(TComponent)
  public
    function Create(AOwner: TComponent): TMyClass;
    function GetSomeThing;
  end;
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:
Delphi-Quellcode:
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;
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.

Bisherige Einschränkungen
  • Man kann zwar schon Unterroutinen in Funktionen schreiben, jedoch kann man in jeder Routine nur auf die routineinternen Parameter, Variablen und auf die globalen Variablen zugreifen. Der Compiler spuckt eine Fehlermeldung aus, wenn man auf Variablen von "Parent"-Funktionen zugreifen will.
  • Bisher gibt es den Operator div nicht. Alle Divisionen werden mit dem / ausgeführt. Ob das Ergebniss ein float oder ein Integer ist, hängt dann von den Parametern ab. Wenn man jetzt die Integerdivision 1000 / 3 macht, kommt der Integerwert 333 raus - 1000.0 / 3.0: float-Wert mit 333,33333 usw. (bitte nächsten Punkt auch beachten)
  • Da intern sowie extern das Casten von integer auf float noch nicht möglich ist, gibt es eine Funktion "ToFloat(i: integer): single, die Integer-Variablen in eine Float-Variable umwandeln. Dass ist z.B. bei Divisionen mit Variablen, bei denen das Ergebniss ein float sein soll, praktisch. Das werd ich aber auf jeden fall noch ändern. (der Aufruf von "ToFloat" ist ab sofort obsolet)
  • Variablennamen und Funktionsnamen können sich [noch] überschneiden, will heißen: man kann noch einer Funktion den gleichen Namen wie einer Variable geben. Beim Aufruf wird man aber immer nur die Funktion bekommen, egal wo die Variable deklariert ist.
  • Ein Funktions/Variablen-Name darf nur einmal vorkommen (das ist nicht neu) - dabei werden die Funktionen der per uses eingebundenen Units mitgezählt (neu) - Man kann jede Funktion/Variable nur einmal definieren - aber nur innerhalb einer Unit. In mehrere Units können jetzt die gleichen Funktionsnamen/Variablen sein. Mann greift dann über UnitName.VariablenName drauf zu

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:
  • SynEdit für die Haupt-GUI und für die eine Demo (Code-Vervollständigung-Demo): http://synedit.sourceforge.net/
  • JEDI-Component-Library (JVCL) für die Haupt-GUI: http://www.delphi-jedi.org/
  • Da ich die TCoolBar und die TToolbar-Komponente in der Haupt-GUI verwende, ist die GUI unter Win95 wahrscheinlich nicht lauffähig (die Script-Engine an sich ist dadurch nicht betroffen). Für diese Komponenten wird die Datei COMCTL32.DLL in der Version 4.70 oder höher 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:
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;
Was ist in den Download dabei?
  • Der komplette Source-Code der Script-Sprache
  • 7 Demo-Programme, die die einzelnen Features und ihre Anwendung zeigen
  • eine vorkompilierte Version der Haupt-GUI
  • ein kleiner Massen-Datei-Umbenenner, der die Script-Engine für die neuen Dateinamen benutzt (als Anwendungsbeispiel)

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 und freu mich auf eure Meinungen.

Change-Log

Version: 0.99h (15.10.2008)
  • Direkte Type-Casts sind jetzt möglich [z.B. Pointer(cardinal(25) + integer(GetPointer))]
  • Enum-Types sind jetzt möglich [z.B. type TTest = (ttEins, ttZwei, ttDrei);]
  • OnError-Event in Interpretor geändert: CallStack wird jetzt als Parameter übergeben. Das liegt daran, dass das OnError-Event bei try-finally und try-except Blöcken verzögert ausgegeben wird und dann die Abfrage von CallstackTrack im OnError-Event dann nicht mehr an der wahren OnError-Position ist.
  • Kleines Problem mit cardinals behoben
  • Problem mit - behoben: es musste immer ein Leerzeichen vor einem Minus sein, wenn eine Zahl angegeben wurde, sonst gab es eine Fehlermeldung
  • Constanten können jetzt auch ein - haben
  • Identifier-Expression verbessert/ausgebaut:
  • TKlasse(Identifier).Funktion funktioniert jetzt
  • Unitname.Variablenname sowie Unitname.Funktionsname funktioniert jetzt
  • Es können jetzt mehrere Variablen/Funktionen den gleichen Namen haben - solange sie in verschiedenen Units deklariert sind. Über UnitName.Identifier kann dann auf die verschiedenen Variablen/Funktionen zugegriffen werden
  • Fehler im not-Operator behoben: der Operator hatte die falsche Priorität
  • Fehler in Operatoren "*/ mod and shr shl" behoben
  • Fehler in Vergleichsoperatoren behoben: komplexere Vergleiche ohne genügend Klammern lieferten ein falsches Ergebniss
  • Fehler in Vergleichsoperatoren behoben: grundlegende Vergleiche funktionierten zwar, jedoch konnte es passieren, dass das Negieren von Vergleichen nicht funktionierten
  • RunTimerOnError-Event: Parameter verändert
Version: 0.99g (06.08.2008)
  • Kleiner Fehler im Parser: int64-Typen wurden nicht zu int32-Typen umgewandelt, falls erforderlich
  • Code-Completion-Display etwas verbessert
  • Geschwindigkeit von TSygScript_RunTime.LoadFromStream extrem verbessert
  • Memory-Leak im Parser behoben
  • ein paar kleine Probleme mit Records behoben
  • manche Variablen wurden bei verschachtelten Funktionen ungültig
Version: 0.99f (24.07.2008)
  • Im Interpretor kann man jetzt mit Hilfe von Properties schnell Script-Variablen abrufen bzw. ändern
  • Die Angabe der Unit beim Registrieren von Methoden bzw. beim Lesen von Variablen ist jetzt optional. Es kann auch einfach ein Leerstring übergeben werden - dann wird die erste Variable genommen, die den gesuchten Namen hat
  • Das Script wurde beim Aufruf von Run nochmals intialsiert, obwohl "InitializeRun" bereits aufgerufen wurde
  • Kompiliergeschwindigkeit verdoppelt. Das kompilieren von langen Scripts mit vielen uses-Units dauert jetzt nicht mehr so lange
  • Fehler bei exit-Anweisung in verschachtelten try-finally-Blöcken behoben -> es wurde nur die letzte finally-Anweisung aufgerufen
  • Die Continue-Anweisung funktioniert jetzt auch in while und repeat - Schleifen korrekt
  • Kleiner Memory-Leak entfernt
  • Kritischer Fehler im Parser und im Interpretor behoben: beim Aufruf von tieferen Unterlementen (z.B. Application.MainForm.Caption - Application.MainForm an sich funktionierte bereits) wurden Variablen im Script-Stack überschrieben.
  • Schwerer Fehler mit der Script-String-List behoben. Bei bestimmten Unit-Konsterlationen wurden die falschen Strings im Script verwendet
Version: 0.99e (18.07.2008)
  • ScriptEngine mit Lazarus kompilierbar
  • Zwei neue Script-Compiler-Defines: {$IFDEF FPC} wenn Lazarus/FPC benutzt wird und {$IFDEF DELPHI} wenn Delphi benutzt wird
  • Einige Funktionen der VCL gibt es in der LCL nicht, daher sind diese auch nicht in der Script-Engine verfügbar. Die Klassen behinhalten zwar auch diese Funktionen, beim Aufruf wird aber eine Exception ausgelöst, wenn die entsprechende Funktion unter Lazarus nicht verfügbar ist.
  • Der Aufruf von Script-Funktionen aus dem Programm heraus funktioniert jetzt auch, wenn das Script währenddessen schon läuft
  • Die Parameter von TSygScript_Parser.FindFunction, TSygScript_Parser.Call abgeändert
  • var-Parameter beim Aufruf von Script-Funktionen aus dem Programm heraus funktioniert jetzt. Dabei muss beim Aufruf einfach ein Pointer auf die Variable übergeben werden
  • In der Unit WinCRT können jetzt Events registriert werden. Ein Demo-Script ("EventTestProgram.scs") für die Verwendung ist mit dabei
  • Das Beispielscript "TimeInformation.scs" etwas überarbeitet
Version: 0.99d (16.07.2008)
  • try-finally und try-except-Blöcke eingeführt
  • Neue Funktion in der System-Unit: RaiseException(msg: string);
  • Neue Funktion in der System-Unit: Assert(Condition: boolean; msg: string);
  • Neues Beispiel-Script für das Hauptprogramm: "ClearTempData" (löscht alle, für ein Source-Release unrelevanten, Dateien, die von Delphi erstellt wurden)
  • Resourcen-Schutz-Blöcke (try-finally) in die anderen Demo-Programme eingefügt
  • Windows-uses aus den Units entfernt, die die "Windows.pas" nicht benutzen. Falls die Windows.pas benutzt werden muss, wird diese per {$IFDEF WIN32} eingebunden
  • CopyMemory ersetzt durch "System.Move"
  • Alte Debug-Anweisungen im Parser gelöscht
  • Fehlermeldungen des Compilers in const-strings ausgelagert und etwas überarbeitet
Version: 0.99c (12.07.2008)
  • Kleiner Fehler im Parser und im Interpretor behoben: LoadFromStream/SaveToStream hat eine Untermethode aufgerufen, die fehlerhaften Code drinnen hatte
  • Demo 4: Quelltext der Demo war noch nicht auf die neuen UnitNamen aus Version 0.99b umgestellt
Version: 0.99b (10.07.2008)
  • In dieser Version haben sich einige externen Sachen (z.B. Funktionsnamen, Unitnamen, ...) geändert! Daher ist ein Update auf diese Version mit etwas Aufwand verbunden! Natürlich versuche ich solche groben Änderungen nicht zu machen; doch machmal hab ich keine andere Wahl.
  • Hauptprogramm: Toolbar eingebaut
  • Sinnvollere Unit-Namen vergeben
  • Einige Komponenten und Funktionen umbenannt
  • Aufbau vom Interpretor etwas verändert
  • Fehler im Interpretor im Operator disc behoben
  • Einzelne Script-Funktionen jetzt aufrufbar
  • Fehler bei Doubles in Records behoben
  • Größe des ByteCode-Streams verringert
  • Fehler in Records behoben: beim Aufruf von externen Funktionen, die ein Record mit einer String-Variable als Parameter hatten, wurde zu wenig Speicherplatz benutzt und es gab eine Exception
  • Fehler in Records behoben: beim Aufruf von externen Funktionen, die ein Record mit einer Single-Variable als Parameter hatten, wurde zu viel Speicherplatz benutzt und die Daten wurden ungültig
  • Code-Completion setzt die Property "GenerateCode" jetzt automatisch wieder zurück
  • Es gibt jetzt eine overloaded-procedure von ParseScript, bei der man den Script-Quelltext als Parameter mitliefern kann
  • TPoint und TRect in die Unit "System" verschoben
  • drei neue Script-Unit hinzugefügt: Forms, Menus, StdCtrls (weitere werden noch folgen)
  • Fehler in der uses-Deklaration behoben: identifiers aus weiteren uses-Units wurden nicht gefunden
  • Neue Demo hinzugefügt: Diese Demo zeigt, wie man einzelne Script-Funktionen aus dem Programm heraus aufruft
  • Neue overloaded-function im Interpretor: CallStackTrace kann jetzt auch ohne StringList ausgeführt werden. Der Inhalt der StringList (inc. Zeilenumbrüche) wird dann als Strings-Result-Wert zurückgegeben
Version: 0.99a (03.07.2008)
  • Code-Minimierung eingeführt: die Code-Minimierung reduziert den erstellen Byte-Code, in dem er unbenutzte Stellen löscht
  • Call-Stack jetzt erstellbar (zum Debuggen)
  • Kleiner Fehler im RunTime-Stack behoben (Ausführung wurde nicht beeinträchtigt)
  • Hauptprogramm: Export-Funktion durch Ausnutzung von neuen Features sehr viel schneller gemacht
  • Hauptprogramm: InstructionList-Viewer erweitert
  • Neue Script-Demo für das Hauptprogramm: TimeInformation.scs
  • Kleiner Fehler in der Code-Completion behoben
  • Konstanten werden nicht mehr im Stack gespeichert
  • Doppelte Einträge in der Runtime-StringList werden jetzt vermieden
  • kleiner Fehler im automatischen Type-Cast behoben: integer -> float wurde zu spät ausgeführt -> starke Rundungsfehler
  • ehemalige Debug-NOOPs im Code entfernt
Version: 0.99 (23.06.2008)
  • kleiner Operator-Fehler im Interpretor beseitigt
  • ToFloat ist jetzt endlich obsolet
  • Return-Address-Typ geändert (ist für einen Stack-Trace wichtig, den ich später einbauen werde)
  • Neues Demoprogramm: ein Massen-Datei-Umbenenner, der den neuen Dateinamen anhand eines Scripts erstellt
  • Neue Funktion: "InitializeRun" in "TSygScript_RunTime": muss aufgerufen werden, wenn das Script mehrmals verwendet werden soll, ohne dass der ByteCode neu geladen werden soll (die Verwendung wird im neuen Demo-Programm gezeigt)
  • Formatierung in der Code-Completion verändert
  • kleinerer Download da die Demos nun nicht mehr vorkompiliert sind
  • ein paar kleine Bugs behoben
Version: 0.98b (18.06.2008)
  • 64-Bit-Typen hinzugefügt (int64, double)
  • TDateTime auf double geändert
  • Neue Funktionen in SysUtils: DiscSpace, DiscFree, FileSize
  • Viele Funktionen auf int64 bzw double geändert
  • Automatisches casten zwischen den Variablen (ToFloat sollte immer noch verwendet werden)
  • Viele Bugs beim Ausführen von Delphi-Methoden behoben (viele waren mit single-Typen)
  • GetVariable und SetVariable im Interpretor können nun auch mit double/int64 umgehen
  • extended aus der Typendeklaration entfert (sorgt sonst nur für Verwirrung)
  • rtlVclOptimize herausgenommen
  • Geschwindigkeit des Stacks erhöht
  • fehlende Funktion im Release-Paket hinzugefügt (hat anscheindend noch niemand gemerkt)
Version: 0.98a (13.06.2008)
  • Initial release
Miniaturansicht angehängter Grafiken
screen1_141.gif   screen2_812.gif  
Angehängte Dateien
Dateityp: zip sysygyscriptengine_704.zip (1,79 MB, 379x aufgerufen)
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
 
Geri

 
Delphi 2005 Personal
 
#51
  Alt 6. Jan 2009, 15:28
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
  Mit Zitat antworten Zitat
EugenB

 
Lazarus
 
#52
  Alt 7. Jan 2009, 13:04
Btw:
Zitat von EugenB:
Ich bins mal wieder xD

Wie kann ich dem Script eine vorhandene Variable / Klasse zuweisen? zb. Self:TForm ?
In einem Beispiel wird das schon erklärt, sorry ^^

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;
Zitat:
[Error] myprogram(12): Procedure "tmyclass" already exists
Could not compile the script
Oder wie kann ich Variablen in Klassen nutzen? in Records funktioniert es, in Klassen nicht

Delphi-Quellcode:
program myprogram;

uses
    Classes, Forms;

type
    TTestRec = record
       FVar: String;
    end;

    TMyClass = Class
    private
        FMyVar: String;
    public
        function Create():TMyClass;
    end;
Zitat:
[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:
function TBrush_Bitmap(Self: TBrush): TBitmap;
// function TBrush.Bitmap(Self : pointer) : TBitmap;
begin
  result := {$IFDEF FPC}TBitmap(Self.Bitmap){$ELSE}Self.Bitmap{$ENDIF};
end;
Anscheinend ist Dave im Urlaub xD

@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?
  Mit Zitat antworten Zitat
Geri

 
Delphi 2005 Personal
 
#53
  Alt 8. Jan 2009, 16:30
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:
  ...
  ThistProjecd:=mProjectGroup.CreateProject);
  ThisProject.SetWert1(1235);
  ...
Anstatt der Referenz ThisProject könne ich auch gut mit dem Listenindex leben..
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
  Mit Zitat antworten Zitat
EugenB

 
Lazarus
 
#54
  Alt 8. Jan 2009, 16:57
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
  Mit Zitat antworten Zitat
Geri

 
Delphi 2005 Personal
 
#55
  Alt 9. Jan 2009, 13:22
Hallo zusammen

@Eugen: Vielen Dank für Deine Hinweise. Ich werde mal versuchen diesen Weg in die Tat umzusetzen Kannst du mir bitte sagen, von welchem import-tool du sprichst? Ich bin die Demos mal durchgangen. Ein Tool habe ich nicht gefunden

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:

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;
Habe ich das richtig verstanden?


@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
  Mit Zitat antworten Zitat
EugenB

 
Lazarus
 
#56
  Alt 9. Jan 2009, 13:46
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*.
  Mit Zitat antworten Zitat
Geri

 
Delphi 2005 Personal
 
#57
  Alt 9. Jan 2009, 15:33
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
  Mit Zitat antworten Zitat
Benutzerbild von littleDave
littleDave

 
Delphi 7 Professional
 
#58
  Alt 3. Mär 2009, 19:56
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 . Aber ich will mir wieder etwas Zeit für meine Lieblingsprache nehmen.

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 . Das Ding ist vom Quelltext her zu unübersichtlich und die Einbindung zu umständlich.

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:
  • Schnellerer und besserer Compiler (bereits 70% der Features des alten Compilers fertig eingebaut)
  • Caching von bereits kompilierten Units (so wie in Delphi die .DCU - Dateien)
  • Einen Linker, der einen Optimizer behinhaltet (zu 70-80% fertig)
  • Eine kleinerer Byte-Code-Aufbau
  • Eine schnellere RunTime-Engine (ist schon zu ca. 40% fertig)
  • Richtiger Unit-Aufbau (unit ... interface ... implementation ... initialization ... finalization ... end.)
  • Object-Pascal!!! (eigene Klassen im Script erstellen) (noch nicht angefangen)
  • Records und Arrays (noch nicht angefangen)
  • Methoden überlaben (funktioniert schon)
  • Methoden forwarden (funktioniert schon)
  • Ein etwas anderer Unit-Aufbau (nicht vom Quelltext her, sondern von der Benutzung von Units):
    Ich arbeite zur Zeit sehr viel in C# und ich finde das generelle Design von .NET und den Klassen nicht schlecht. Ich hab mir gedacht, dass Units (wie die Namespaces in .NET) über mehrere Dateien gehen können
  • Statische Methoden (class procedure und class function) (funktioniert schon)
  • und noch so einiges mehr ...

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.


Zitat von EugenB:
irgendwie bin ich grade im Script-Wahn , zuerst hab ich Pascal Script probiert aber irgendwie sind da noch soviele Fehler, dann hab ich schon fast vergessen das es diese Script Engine noch gibt
Danke für die Blumen

Zitat von EugenB:
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
Ja, den FPC-Support hab ich leider etwas schleifen lassen

Zitat von EugenB:
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
Arrays: wird es in der neuen Engine geben
Class properties: wird es in der neuen Engine geben
Overloading methods: wird es in der neuen Engine geben (funktioniert bereits)

Zitat von EugenB:
Wie füge ich Funktionen hinzu, auch wenn ich den UnitName nicht weiß?
Delphi-Quellcode:
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;
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.
Die "AddFuntion" - Method ist noch von ganz früher, daher wollte diese nicht mehr verwendet werden. Am besten, du erstellst eine eigene Unit, die dann per Uses eingebunden wird.

Zitat von EugenB:
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
Die neue Version werd ich wahrscheinlich in ein SVN Packen, das ist jedoch jetzt noch zu früh

Zitat von mschnell:
Hast Du Deinen Bytecode komplett neu erfunden, oder hast Du eine Vorlage benutzt ?

Gibt es eine Beschreibung für den Bytecode ?
Der ByteCode ist von mir frei erfunden und hat auch leider keine Beschreibung. Jedoch hab ich das für die neue Version besser geplant und umgesetzt - der ByteCode wird dann besser zu verstehen sein.

Zitat von mschnell:
(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. )
Auch wenn ich das sehr interessant finde würd ich im Moment sagen: bitte warte, bis ich die neue Version veröffentlicht habe, da hat sich alles geändert und den Aufwand wäre umsonst, wenn du umsteigen wollen würdest (was ich dir bereits jetzt für später empfehlen würde)

@Geri:
Wenn das Problem noch aktuell ist, will ich dir noch schnell mal ne grobe Anleitung schreiben:
  • Nimm die Deklaration deiner Klassen (der Teil, der in Delphi im Interface-Teil steht) und kopier ihn in die Zwischenablage
  • Im Download-Paket gibt es im Ordner "main program\bin" eine vorkompilierte Exe, die einen Exporter zur Verfügung stellt. Einfach öffnen
  • Dort erstellst du einen neue Unit und setzt den Unit-Namen auf irgendwas (also "unit [der Name]; begin; end;")
  • Dann fügst du Zwischenablage ein und löscht alles, was im Private oder im Protected-Teil steht - darauf hast du von der Script-Engine sowieso keinen Zugriff
  • Ersetze jeden Constructor durch eine Funktion, die als Return-Wert die entsprechende Klasse hat ("function Create: TKlasse")
  • Lösche den destructor aus dem Script-Quelltext
  • Passe gegebenfalls den Quelltext an, so dass er sich kompilieren lässt
  • Gehe auf "File" - "Open" und öffne die Datei "FormatDelphiExportFunction.scs"
  • Wechle wieder zurück auf deine gerade bearbeitet Unit und wähle unter "Project" - "Export to Delphi Unit"
  • Beantworte die Frage mit ja
  • Kopiere den kompletten Quelltext, der in dem neuem Tab erstellt wurde und kopiere ihn in die Zwischenablage
  • Erstelle in deinem Delphi-Projekt eine neue Unit füge den Quelltext aus der Zwischenablage ein
  • Passe die Unit an, bis sie sich kompilieren lässt - FERTIG

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
  Mit Zitat antworten Zitat
EugenB

 
Lazarus
 
#59
  Alt 25. Mär 2009, 14:39
Huhu

gibt es schon irgendwelche Updates, nutzbare Versionen o.ä?

MfG
Eugen
  Mit Zitat antworten Zitat
Benutzerbild von littleDave
littleDave

 
Delphi 7 Professional
 
#60
  Alt 25. Mär 2009, 15:42
Zitat von EugenB:
Huhu

gibt es schon irgendwelche Updates, nutzbare Versionen o.ä?

MfG
Eugen
Hallo EugenB

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:
-          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
Die einzelnen Test sind die Ausführungsgeschwindigkeiten von unterschiedlichen Scripten.
Test 1
Delphi-Quellcode:
program Test;

var i: integer;
begin
  for i:=0 to 1000000 do ;
end.
Test 2
Wie Test 1, nur zählen bis 2000000

Test 3
Wie Test 1, nur zählen bis 3000000

Test 4
Delphi-Quellcode:
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.
Test 5
Wie Test 4, nur zählen bis 2000000

Test 6
Wie Test 4, nur zählen bis 3000000

Test 7
Delphi-Quellcode:
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.
Test 8
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:
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.
Das Ergebnis:
Code:
25.10.2009 16:33:24
012345
10
False
Ende
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 .

So, das war jetzt erstmal nen kleiner Status-Update. Sobald es wieder was wichtiges gibt, werd ich mich wieder melden.

Grüße
Dave
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 6 von 9   « Erste     456 78     Letzte »    


Forumregeln

Es 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

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:25 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz