AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Projekte ScriptEngine II (v. 0.6.1)
Thema durchsuchen
Ansicht
Themen-Optionen

ScriptEngine II (v. 0.6.1)

Ein Thema von littleDave · begonnen am 21. Sep 2009 · letzter Beitrag vom 4. Aug 2011
Antwort Antwort
Seite 12 von 12   « Erste     2101112   
Benutzerbild von littleDave
littleDave
Registriert seit: 27. Apr 2006
Hallo liebe DP'ler

ScriptEngineII


ist der Nachfolger meiner letzten ScriptEngine. Da es sich um ein komplettes Remake handelt, sind die beiden jedoch nicht mehr vergleichbar.

Kurzübersicht
  • Die Script-Engine bnutzt Object-Pascal als Script-Sprache und unterstützt objekt-orientierte Programmierung.
  • Der Quelltext wird in ByteCode übersetzt, der dann von einer optimierten RunTime ausgeführt wird
  • Trotz der Möglichkeiten ist das Interface der Komponente sehr klein und übersichtlich gehalten
  • Für den Einstieg habe ich eine kleine Doku in Form eines PDFs mit in den Download gepackt

Installation
In der ZIP-Datei befindet sich ein Unterordner "src". Diesen extrahiert ihr einfach in einen Ordner eurer Wahl und fügt diesen Ordner dann in den Bibliothekspfad von Delphi hinzu. Im Quelltextorder befindet sich die Datei "ScriptEngine.inc". Diese Datei kann zum konfigurieren der ScriptEngine benutzt werden. Die einzelnen Defines sind (hoffentlich) ausreichend erklärt.

Benötigte Delphi-Version
Bisher hab ich es nur mit Delphi 7 und mit Lazarus getestet. Unter FreePascal hab ich nur ein paar Tests ausgeführt und diese sind auch korrekt gelaufen. Unter Delphi 2009/2010 hab ich die ScriptEngine ebenfalls erfolgreich getestet.

Besonderheiten
  • wenn man innerhalb einer Klassenmethode auf andere Klassenmethoden oder -Variablen zugreifen will, muss man immer "Self" mit angeben
    Update 10.10.2010
    Mit der Version ist 0.5.4.1 ist das nun nicht mehr notwendig!
  • Records sind Referenz-Typen

Bisherige Probleme
Im Moment sind noch folgende Probleme vorhanden:
  • überladene Methoden haben bisher folgende Einschränkung:
    • sie müssen alle vom selben Typ sein (statisch, nicht statisch)
  • der Befehl reintroduce ist noch nicht vorhanden

Lizenz
MPL v1.1 , GPL v3.0 oder LGPL v3.0

Feature-Liste
  • Übersicht
    • Object-Pascal als Dialekt
    • Ganzzahltypen: byte, shortint, word, smallint, cardinal, integer, int64
    • Fließkommatypen: single, double
    • Stringtypen: string, UTF8String, WideString
    • Operatoren: + - * / div mod shr shl and or xor not @
    • Vergleich: = < > <= >= <> is
    • Schleifen: for while repeat for-in-do
    • Datenvergleich: if case
    • Spezielle Blocks: try-finally / try-except
    • Konstanten
    • verschachtelte Unit-Namen (z.B. Unit1.SubUnit.SubUnit)
    • Partielle Units
    • Ablaufkontrolle: continue break exit
    • Exception-Handling und Exception-Throw möglich
    • Multi-Threading-Scripts
  • Methoden
    • Methodentypen: procedure function constructor destructor
    • Methodenparametertypen: const var
    • Methodenoperatoren: virtual abstract override overload forward
    • Import von DLL-Methoden direkt im Script (z.B.: procedure Sleep(milliSec: DWORD); external 'kernel32.dllname 'Sleep'; stdcall; )
    • OOP-Features: inherited
    • Spezielle Operatoren: external export
    • Unterstützte Aufrufkonventionen: register pascal stdcall cdecl
    • Methoden-Pointer
    • Events
  • Records
    • Abschnitte: private protected public
    • Record-Constanten (const-Deklaration in der record-Deklaration)
    • (statische) Record-Methoden
    • (statische) Record-Variablen
    • (statische) Record-Properties
    • Property-Typen: read+write, read-only, write-only
    • Property-Zugriff: direkt, über Methoden, über Methoden mit Parametern
  • Klassen
    • Objekt-Orientierte Programmierung
    • Klassenvererbung
    • Methoden überschreiben
    • Klassen-Sektionen: private protected public
    • Class-Constanten (const-Deklaration in der Class-Deklaration)
    • (statische) Klassenmethoden
    • (statische) Klassenvariablen
    • (statische) Klassenproperties
    • Property-Typen: read+write, read-only, write-only
    • Property-Zugriff: direkt, über Methoden, über Methoden mit Parametern
    • Partielle Klassen (so ähnlich wie bei .NET)
  • Class Helpers
    • Es können zu jedem Datentyp beliebig viele "Helfer-Klassen" erstellt werden
    • gleicher Aufbau wie bei normalen Klassen, nur die Deklaration ist etwas anders:
      • TStringHelper = helper for string
    • Class Helpers erweitern eine Klasse/einen Datentyp ohne die Vererbung zu nutzen
  • Compiler
    • schneller Compiler
    • eingebauter Unit-Cache für schnelleres kompilieren (so wie Delphi-DCUs)
    • partielle Units – mehrere einzelne Units mit gleichem Namen werden zu einer Unit zusammengefasst
    • Linker zum zusammenstellen und zum optimieren des Byte-Codes
    • IntelliSense / Code-Completion und Parameter-Hints-Unterstützung
    • eingebaut als abstrakte Klasse – für SynEdit bereits vorhanden
    • Speichern des kompilierten ByteCodes in einen Stream
  • RunTime
    • schneller ByteCode-Interpreter
    • direkter Aufruf von Delphi-Funktionen
    • Script-Methoden aus dem Programm heraus aufrufbar
    • Script-Methoden als TMethod-Event nativ aufrufbar
    • einfacher Garbage Collector für Script-Klassen (nur falls das Objekt nicht manuell zerstört wurde)
    • eigener Memory-Manager mit Caching für schnelleres Ausführen und für weniger Speicherfragmentierung
    • Exception-Handling
    • Stack-Tracing
  • Quelltext
    • durchgehende Namenskonvention (Units starten mit uSE2 und Klassen mit TSE2)
    • bisher keine Memory-Leaks gefunden

Noch ein paar kurze Hinweise
- Diesmal habe ich leider nicht so viele Beispiele mit in den Download hinein gepackt - es werden aber noch ein paar nachgeliefert

SVN
Ich hab für die Script-Engine ein Source-Forge-Projekt erstellt. Dort sind alle Änderungen auch per SVN abrufbar. Wichtige Releases werd ich natürlich weiterhin hier hochladen

Mitgelieferte IDE
Ich habe die kompilierte Version meiner aktuellen IDE mit in das Download-Paket gepackt. Zusätzlich hab ich den Source in den Unterordner IDEsrc gepackt - zusammen mit den drei kompilierten Packages. Mit der IDE kann man ein paar Testprojekte öffnen, die sich im Unterordner "Projects" befinden.

Ich hoffe, euch gefällt die neue Script-Engine und vielleicht kann der eine oder andere sie ja benutzen.

Download
Auf SourceForge.net (ca. 2,6 MB)

Grüße
Miniaturansicht angehängter Grafiken
screenshot1.png  
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

Geändert von littleDave (10. Apr 2011 um 13:59 Uhr)
 
WorstNightmare

 
RAD-Studio 2010 Arc
 
#111
  Alt 2. Jan 2011, 14:58
Hi,

ich denke gerade darüber von RemObjects PascalScript auf deine ScriptEngine umzusteigen, aus dem einfachen Grund weil hier overloading unterstützt wird. Eines stört mich jedoch: Die Scripts müssen immer einen program-Header haben, in PascalScript konnte man den abschalten (über CompilerOptions). Kannst du das auch abschaltbar machen? Der verschwendet bei mir nur Platz, da ich ihn eh immer NPCScript oder so nennen würde und die Scripts über Dateinamen identifiziert werden.

Edit: Und gibt es irgendwo Inc() und Dec()? Wenn nein, warum nicht?

Edit 2: So, läuft ganz gut jetzt. Allerdings habe ich noch einen Feature-Wunsch:
Meine Scripts terminieren die Runtime aus sich heraus und wenn ich direkt nach Abort() Finalize() und Free() aufrufe, knallt es. Daher brauche ich ein Notify-Event, irgendwie OnAborted oder so in der Runtime. Im finally ganz unten in TSE2ExecutionContext.Call dann so aufrufen:
Delphi-Quellcode:
    if FDoAbort and Assigned(FOnAborted) then
      FOnAborted(TObject(FExecutionData.RunTime));

Geändert von WorstNightmare ( 2. Jan 2011 um 22:47 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von littleDave
littleDave

 
Delphi 7 Professional
 
#112
  Alt 4. Jan 2011, 20:23
Hallo WorstNightmare,

sorry für die späte Antwort, bin bisher nicht dazu gekommen

Zitat:
ich denke gerade darüber von RemObjects PascalScript auf deine ScriptEngine umzusteigen, aus dem einfachen Grund weil hier overloading unterstützt wird. Eines stört mich jedoch: Die Scripts müssen immer einen program-Header haben, in PascalScript konnte man den abschalten (über CompilerOptions). Kannst du das auch abschaltbar machen?
Ja, ich werde schauen, was sich da machen lässt. Wird jedoch noch etwas dauern, da ich mich endlich dazu überwunden habe und im Moment arrays einbaue. Ich bin dabei gerade mitten in der Implementierungs- und Testphase. Dadurch wäre es im Moment nicht so gut, den halbfertigen Code herauszugeben. Bis dahin kannst du ja vor dem Übergeben an den Compiler folgendes schreiben:
Delphi-Quellcode:
function CompileScript(const Source: string): TSE2PE;
begin
  result := Compiler.Compile('program program1; ' + Source + ' begin end.';
end;
Zitat:
Edit: Und gibt es irgendwo Inc() und Dec()? Wenn nein, warum nicht?
Diese Routinen habe ich zum einen aus Zeitgründen, zum anderen aus Featuregründen noch nicht drinnen (ich will eigentlich keine "Compiler-Magic" einbauen).
  • Zeitgrund: für jeden Typ müssen die Routinen alle manuell deklarieren muss (also für byte, integer, etc. - jeweils mit und ohne Parameter).
  • Feature-Grund: für ein einfaches "+1" eine einzelne Funktion aufrufen ist doch etwas overhead, daher wollte ich warten, bis ich "inline" - Methoden eingebaut habe.
Bei "pred()" und "succ()" hört der Spaß dann aber auf - die kommen mir nicht in die System-Unit (ich finde die total unübersichtlich)

Zitat:
Edit 2: So, läuft ganz gut jetzt. Allerdings habe ich noch einen Feature-Wunsch:
Meine Scripts terminieren die Runtime aus sich heraus und wenn ich direkt nach Abort() Finalize() und Free() aufrufe, knallt es. Daher brauche ich ein Notify-Event, irgendwie OnAborted oder so in der Runtime. Im finally ganz unten in TSE2ExecutionContext.Call dann so aufrufen:
Delphi-Quellcode:
    if FDoAbort and Assigned(FOnAborted) then
      FOnAborted(TObject(FExecutionData.RunTime));
Das versteh ich nicht, wieso das nötig sein sollte. Gehe ich richtig in der Annahme, dass du im Script einen Methode aufrufst, die die RunTime beenden und schließen soll? Das kann nicht funktionieren! denn so ziehst du dem Script die RunTime unter den Füßen weg und knallt somit auf den Boden. Die RunTime darf nicht von einer Funktion heraus gelöscht werden, wenn diese aus der RunTime heraus aufgerufen wird.
  Mit Zitat antworten Zitat
EugenB

 
Lazarus
 
#113
  Alt 5. Jan 2011, 18:47
Hi

Zitat:
Edit: Und gibt es irgendwo Inc() und Dec()? Wenn nein, warum nicht?
Diese Routinen habe ich zum einen aus Zeitgründen, zum anderen aus Featuregründen noch nicht drinnen (ich will eigentlich keine "Compiler-Magic" einbauen).
Wäre sowas nicht "einfach" durch ne Helper-Klasse besser realisiert?

IntVar.Inc;
IntVar.Dec;

Oder geht sowas nicht bei einfachen Variablen?

Btw, schön zu hören das es bald Arrays gibt, obwohl man jetzt sowieso alles mit Listen machen würde (ich zumindest

Btw2, ist es möglich / auf der Todo Liste, dass man Generics nutzen kann?

Gruß
Eugen
  Mit Zitat antworten Zitat
WorstNightmare

 
RAD-Studio 2010 Arc
 
#114
  Alt 5. Jan 2011, 22:57
Mein RTTI-ClassImporter fügt jetzt einfach noch nach der Klasse, mit der das Script aufs Hauptprogramm zugreift, Inc und Dec für Integer ein. Ich hatte einfach keine Lust das in allen 250 Scripts mit + bzw - 1 zu ersetzen.

Zum Abbrechen der Runtime:
Das habe ich dann auch schnell festgestellt^^
Deswegen möchte ich ja, dass wenn ich Abort aufrufe, ich benachrichtigt werde wenn es fertig mit Abbrechen ist und dann Free aufrufen kann. Eine andere Möglichkeit habe ich nicht, denn das Hauptprogramm weiß nicht automatisch, wann das Script fertig ist (die Teile haben eine Methode, die bei jedem Script unterschiedlich oft aufgerufen wird).
  Mit Zitat antworten Zitat
WorstNightmare

 
RAD-Studio 2010 Arc
 
#115
  Alt 18. Mär 2011, 14:26
Alle meine Scripts laufen jetzt schon seit längerer Zeit mit dieser Engine und ich muss sagen, ich bin sehr zufrieden.

Es gibt allerdings ein mittelschweres Problem: Der Speicherverbrauch.

Für ein simples Script wie dieses [Link] fallen knapp 5 (!) MB Speicher an, sobald Compile() ausgeführt wurde. Das Problem an diesen Event-Scripts ist, dass sie für jeden Channel-Server (wovon es durchschnittlich 6 gibt) extra instanziert werden müssen und die ganze Zeit aktiv sein müssen, da sie oft auch zeitgesteuerte Aufträge haben und Variablen über einen längeren Zeitraum speichern. Momentan gibt es zwar nur dieses eine, wenn allerdings später 15 vorhanden sind ist dieser extreme Verbrauch echt nicht mehr tragbar.

Ich bin nicht ganz sicher warum für ein 600 Byte langes Script plötzlich 4,5 MB verbraucht werden, könnte es mit der verwendeten Unit zusammenhängen? Diese setzt sich aus den Klassen TEventInstance und TEventManager aus dieser Datei und TScriptMapleCharacter zusammen. In der letzteren Datei ist auch der RTTI Class-Importer.
  Mit Zitat antworten Zitat
Benutzerbild von littleDave
littleDave

 
Delphi 7 Professional
 
#116
  Alt 18. Mär 2011, 15:16
Es gibt mehrere Caching-Systeme in der Script-Engine, die ich kurz erläutern will.
  • Compiler-Cache
    Der Compiler-Cache (TSE2UnitCacheMngr) hilft für die schnellere Re-Compilierung von Scripts. Er ist vergleichbar mit .DCU-Dateien in Delphi. Dort drinnen stehen relativ viele Informationen, d.h. es kann sehr schnell zur 5 MB Speicherverbrauch kommen. Auf den Manager kann ohne Probleme verzichtet werden, da er nur optional ist. Nachteil ist dann aber, dass bei jedem Compile() alle Script-Units erneut kompiliert werden müssen. Eine Zwischenlösung gibt es aber: die Klasse "TSE2UnitCacheMngr" bietet ein (noch undokumentiertes) Event an ("OnGetCacheStream"), in dem man einen Stream angeben kann, in der der Compiler-Output hinein- bzw. heraus-serialisiert wird. Dort könnte man z.B. einen TFileStream übergeben, womit der RAM entlastet wird. Setzt man in dem Event den Parameter "Cache" auf "nil", wird automatisch ein TMemoryStream angelegt und verwendet.

    Die Script-Engine ist beim kompilieren zwar nicht die langsamste, aber wirklich schnell ist sie nicht. Das liegt daran, dass ich eher darauf Wert gelegt habe, die fertig kompilierten Scripts zu speichern und diese dann schnell zu laden. Die fertig kompilierten Scripts sind relativ klein und können so zum einen schnell geladen werden und zum anderen verbrauchen diese nicht so viel RAM.
  • RunTime-Cache (pro RunTime-Instanz)
    Die RunTime hat auch einen Memory-Cache, der die Ausführungsgeschwindigkeit erhöht. Dieser Cache vermindert das ständige Abfragen und Freigeben von RAM vom/zum Betriebssystem, in dem es einen fixen Memory-Block anfragt und mit diesem arbeitet. Falls für ein Script mehr Arbeitsspeicher benötigt wird als in diesen Block passen, greift SEII wieder auf "GetMem()" und "FreeMem()" zurück.

    Die Größe dieses Caches ist in 3 Stufen einstellbar. Komplett aus - klein - und groß. Diese Konfiguration ist über die Datei "ScriptEngine.inc" zu lösen und somit während der Laufzeit nicht änderbar.
  • RunTime-Helper-Cache (pro Thread innerhalb eines RunTime-Objekts)
    Zusätzlich zum Memory-Cache gibt es noch einen Cache für die Verwaltungsobjekte innerhalb der Script-RunTime. Dieser Cache ist Thread-Bezogen, da er für den Execution-Stack der RunTime verwantwortlich ist.

    Dieser Cache ist nicht deaktivierbar, ist jedoch sehr klein (ca. 2,5 kb)
Ich hoffe, ich konnte dir mit der Beschreibung etwas weiterhelfen.

Wegen dem Implementierungsfortschritt: die Arrays sind soweit drinnen und funktionieren auch fast ohne Probleme (gibt noch ein paar Baustellen). Jedoch habe ich im Moment wenig Zeit, wodurch sich der Release leider etwas verzögert. Vielleicht bringe ich zur Überbrückung auch noch ein Zwischen-Release heraus, in der die Arrays noch deaktiviert sind, da ich doch noch ein paar kleine Fehler entdeckt habe.

Gruß
David
  Mit Zitat antworten Zitat
Denstern
 
#117
  Alt 23. Mär 2011, 08:01
Hallo an Alle. Zuerst muss ich sagen: SUPER Sache!!! Und grooooßßßßesss Lob an David!!!!
So ein SciptEngine ist gerade das was ich schon lange gesucht habe. Ich habe aber eine Frage diesbezüglich:
Im wesentlichen brauche ich nur ein Zugriff vom Hauptprogramm auf die Script-Funktionen.

Frage 1): Gibt es ein allgemeiner Aufruf einer Funktion? (ohne Benutzung von TSE2RunAccess.FindMethod ?) Bei mir ist es der Fall dass die Funktionen im Script vom Anwender Definiert
werden und ich diese dann zur Laufzeit aufrufen will.

Frage 2): Kann man auf Funktionen mit den Arrays für solche zwecke verwenden? Ich habe eigentlich in den ParamTypes kein Array gefunden.

Ich hoffe ihr könnt mir Helfen.

Danke im Voraus,
Denis.
  Mit Zitat antworten Zitat
Benutzerbild von littleDave
littleDave

 
Delphi 7 Professional
 
#118
  Alt 10. Apr 2011, 14:12
Neue Version Version 0.6.1

Vor längerer Zeit habe ich ja eine neue Version angekündigt. Nun ist es soweit - wobei ich gleich sagen muss: arrays sind immer noch nicht fertig eingebaut. Somit ist das hier eher ein Bug-Fix-Release sowie eine experimentelle Version für arrays. Es ist noch nicht alles drinnen aber ich habe Arrays jetzt erstmal nicht deaktiviert.

Falls also wer die Arrays mal ausprobieren will, es geht noch nicht alles. Zudem gibt es folgende wichtige Einschränkung: man kann arrays nicht "inline" deklarieren, also folgendes geht NOCH NICHT: var t: array of integer; . Stattdessen muss man immer vorher den Array-Typ deklarieren: type TIntArray = array of integer; .
  • Neuerungen
    • Basis-Array-Implementation eingebaut
    • Die Funktionen "System.Inc()" sowie "System.Dec()" sind jetzt vorhanden
  • Veränderungen
    • Performance bei Conditional-Jumps verbessert
    • Byte-Code-Optimizer erweitert und verbessert
  • Bug-Fixes
    • Fehler beim Aufrufen von überladenen Methoden mit var-Parameter behoben
    • Das Forwarden von Klassen war im Compiler nicht komplett - somit war die erstellte Klasse im Code nicht verwendbar. Dies trat in normalen Units nicht auf, da man da die Deklaration zuerst abschließen musste - in der "program" - Datei jedoch war das nicht zwingend notwendig.
    • Fehler beim Finden der korrekten, überlandenen Methode behoben, falls zwei verschiedene Klassentypen als Parameter-Varianz ausschlaggebend waren
    • Fehler bei break/continue innerhalb von try-except-Blöcken, die sich innerhalb eines try-finally-Blocks befanden, behoben
    • Fehler bei exit innerhalb eines try-finally-Blocks unter bestimmten Umständen
    • Fehler beim Aufrufen von Script-Methoden aus dem Delphi-Programm heraus mit double-var-Parametern (Danke an Denstern)

Download-Link ist im ersten Post.

Grüße
  Mit Zitat antworten Zitat
Piethan

 
Delphi XE Architect
 
#119
  Alt 4. Aug 2011, 09:23
Morgen David,

ich möchte mich in deine ScriptEngine einarbeiten, dabei plane ich Funktionen zu schreiben, welchen ich die Parameter als Array (type TMyRecordList = array of TMyRecord; ) übergeben möchte.

Folgenden Aufruf hatte ich daher von Seite der Delphi-Anwendung gedacht:
Delphi-Quellcode:
Method := RunTime.CodeAccess.FindMethod('get_Test', 'Records', [pmIn, pmResult], [btArray,btDouble]);
     if Method <> nil then
         ShowMessage(FloatToStr(RunTime.Call(Method, [MyRecordList])));

In der ScriptEngine sieht die Definition wie folgt aus:
Delphi-Quellcode:
type TMyRecordList = array of TMyRecordList;

implementation

function get_Test(MyRecordList: TMyRecordList): double; export;
begin
result:= MyRecordList[0].dbl_Betrag * 3.5;
end;
Die unit Records habe ich sowohl für Delphi und der ScriptEngine geschrieben. Da du ja bei den Arrays noch in der Entwicklung bist, interessiert mich, ob ich etwas falsch verstanden habe oder ob mein Weg so noch nicht möglich ist?

LG
Dirk
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 12 von 12   « Erste     2101112   


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 09:40 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