Differences

This shows you the differences between two versions of the page.

Link to this comparison view

scripting [2019/08/12 12:13] – [Stages] adminscripting [Unknown date] (current) – removed - external edit (Unknown date) 127.0.0.1
Line 1: Line 1:
-====== GorillaScript ====== 
  
-GorillaScript is a precompiled scripting language, which parses delphi pascal code, compiles to a bytecode format and executes it afterwards. It is possible to use only the bytecode files in release state for faster execution. 
- 
-The scripting was not developed for ultra-fast execution, the more its intention is to use it as GUI interface on top of the firemonkey framework. 
-But of course we will optimize speed and performance in future. 
-You can write script methods for event types like the TNotifyEvent of a TTimer.OnTimer (extendable). 
- 
-GorillaScript syntax is inspired by new delphi syntax and the parser is based on the open-source Castalia Parser. Not all syntax elements are supported yet, but most of them like, classes, enumerations, sets, fields, functions, procedures, properties and much more. 
- 
-To combine your Gorilla3D application with the scripting, the system allows to register native types like classes, records, interfaces, enumerations and sets. 
- 
-It is possible to use GorillaScript with all Gorilla3D available components and classes. 
- 
-Besides the regular usage for FMX and Gorilla3D components, GorillaScript is intended to be used for writing shaders (not implemented yet!) 
-===== Features ===== 
-  * Registering of native classes, interfaces, records, enumeration types and set types 
-  * Usage of native data types: Int8, UInt8, ShortInt, Byte, Int16, UInt16, SmallInt, Word, Int32, UInt32, Integer, Cardinal, LongInt, LongWord, Int64, UInt64, Single, Double, String, Pointer, Boolean, TValue, TMethod, Nil 
-  * Usage of arrays with variant content 
-  * Declaring script classes, Interfaces, enumeration types, set types 
-  * Declaring local script variables 
-  * Declaring fields, properties and methods (function / procedure / constructor / destructor / class function / class procedure) in script classes 
-  * Class properties with accessors to fields and methods 
-  * Declaring global script functions / procedures 
-  * Mathmatical operations: add, subtract, multiply, divide, modulo 
-  * Boolean operations: equal, not-equal, less, less-equal, greater, greater-equal, not, and, or, xor 
-  * Bitwise operations: and, or, xor, shr, shl 
-  * Compiler defines (automatically all default delphi-defines will be available, f.e. "MSWINDOWS", "ANDROID", "CPUX86", "CPUX64", ...) 
-  * For-Loops, While-Do-Loops, Repeat-Loops 
-  * If-Then-Else-Statements 
-  * Case-Statements 
-  * In-Operator for checking enumerator in sets or strings in other strings 
-  * Addition and subtraction on sets 
-  * Possibility to intercept stdio operations with individual handlers 
-  * Registering new native event types for interacting with script 
-  * Utilities for math functions (System.Math.TMath), ui (FMX.UI.TUI), stdio functions (System) and more. 
- 
-==== Special Features ==== 
-GorillaScript provides some useful features for faster development, which are not compatible with Delphi. So don't use those, if you'd like to use your source in both (Delphi & GorillaScript) 
-  * Auto-TypeCasts for simple types, f.e. combine a string and integer without conversion 
-  <file pascal> 
-System.WriteLn('Value is ' + LInt32Val); 
-  </file> 
-  * Implicit rounding of floats, when needed on operations   
-  * Extended subtract operator on strings (String.Replace) 
-  <file pascal> 
-LValue := 'Hello World'; 
-LStr := LValue1 - 'Hello '; 
- 
-// will produce: "World" 
-System.WriteLn(LStr); 
-  </file>   
-  * Extended multiplication operator on strings (String.Repeat) 
-  <file pascal> 
-LValue := 'Hello '; 
-LStr := LValue * 3; 
- 
-// will produce: "Hello Hello Hello " 
-System.WriteLn(LStr); 
-  </file> 
-  * Extended division operator on strings (String.SubString) 
-  <file pascal> 
-LValue := 'Hello World'; 
-LArr := LValue / 4; 
- 
-// will produce an array with 4 elements ['Hel', 'lo ', 'Wor', 'ld'] 
-System.WriteLn(LArr.ToString()); 
- 
-LValue := 'Hello World'; 
-LArr := LValue / ' '; 
- 
-// will produce an array with 2 elements ['Hello', 'World'] 
-System.WriteLn(LArr.ToString()); 
-  </file> 
-  * Extended shift-left operator on strings (String.SubString) 
-  <file pascal> 
-LValue := 'Hello World'; 
-LStr := LValue shl 3; 
- 
-// will produce: "lo World" 
-System.WriteLn(LStr); 
-  </file> 
-  * Extended shift-right operator on strings (String.SubString) 
-  <file pascal> 
-LValue := 'Hello World'; 
-LStr := LValue shr 3; 
- 
-// will produce: "Hello Wo" 
-System.WriteLn(LStr); 
-  </file>  
-  * In-Operator for strings 
-  <file pascal> 
-if ('ello' in 'Hello World') then 
-  System.WriteLn('yes!'); 
-  </file> 
-  * case-statement labels allow all types (not only ordinal types) 
-  <file pascal> 
-LStr := 'Hello'; 
-case LStr of 
-  'Hellooo' : System.WriteLn('case #1');  
-  'Hallo'..'Hulu' : System.WriteLn('case #2'); 
- 
-  else System.WriteLn('case else'); 
-end; 
-  </file> 
-  * Variant arrays by default 
-  <file pascal> 
-var LArr1 : TArray; 
-begin 
-  LArr1 := TArray.Create(5); 
-  LArr1[0] := 'abc'; 
-  LArr1[1] := 123.25; 
-  LArr1[2] := true; 
-  LArr1[4] := LArr1[0]; 
-  System.WriteLn(LArr1.ToString() + #13#10); 
-end; 
-  </file> 
-====== Stages ====== 
-GorillaScript is based on the typical scripting stages:  
-  - parse 
-  - compile 
-  - execute 
- 
-Those stages describe a full execution process, but can be reduced to single stage, by only using the bytecode format. 
- 
-This means you can save a compiled bytecode to file and load it independently again to execute it. This may be useful for release versions of your app, where nobody should see any script source code. 
- 
-====== Implementation ====== 
-The scripting component provides 2 types of implementation. You're allowed to run a script temporarily or in keep-alive mode. 
- 
-===== Temporary Scripts ===== 
-Temporary script will be executed once and all instances destroyed afterwards. By temporary scripts no further interaction with created instance is possible. This feature is useful for quick manipulation purposes. 
-The most important usage may be simulating a Delphi context only by scripting, f.e. in a console application. 
- 
-<file pascal> 
-program ScriptDemo; 
- 
-{$APPTYPE CONSOLE} 
- 
-{$R *.res} 
- 
-uses 
-  System.SysUtils, 
-  Gorilla.Script in '..\..\lib\script\Gorilla.Script.pas'; 
- 
-var LEngine : TGorillaScriptEngine; 
-begin 
-  try    
-    WriteLn('*** GORILLA3D SCRIPTING TEST ***'); 
-    WriteLn(''); 
- 
-    LEngine := TGorillaScriptEngine.Create(nil); 
-    try 
-    {$IFDEF MSWINDOWS} 
-      LPath := ''; 
-    {$ENDIF} 
-    {$IFDEF ANDROID} 
-      LPath := IncludeTrailingPathDelimiter( 
-        System.IOUtils.TPath.GetHomePath()); 
-    {$ENDIF} 
- 
-      // load and parse program source code 
-      LEngine.LoadFromFile(LPath + 'app.pas'); 
- 
-      // compile parsed entities to bytecode 
-      LEngine.Compile(); 
- 
-      // execute bytecode 
-      LEngine.Execute(); 
-    finally 
-      FreeAndNil(LEngine); 
-    end; 
-  except 
-    on E: Exception do 
-      Writeln(E.ClassName, ': ', E.Message); 
-  end; 
-end. 
-</file> 
- 
-===== Keep-Alive Scripts ===== 
-Keep-Alive script integration allows users to execute GorillaScript on top of an existing Delphi application. This may be the most popular mode, because it allows to create visual components and to interact with them by events. 
- 
-By this mode you can setup a form completely by scripting, without loosing capabilities of the native Delphi application. 
- 
-<file pascal> 
-unit TestWinU; 
- 
-interface 
- 
-uses 
-  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 
-  FMX.Types, FMX.Controls, FMX.Forms, 
-  Gorilla.Script; 
- 
-type 
-  TTestWin = class(TForm) 
-    procedure FormCreate(Sender: TObject); 
-    procedure FormDestroy(Sender: TObject); 
-  protected 
-    FEngine : TGorillaScriptEngine; 
-  public 
-  end; 
- 
-var 
-  Form2: TForm2; 
- 
-implementation 
- 
-{$R *.fmx} 
- 
-uses 
-  System.IOUtils; 
- 
-{ TTestWin } 
- 
-procedure TTestWin.FormCreate(Sender: TObject); 
-var LPath : String; 
-begin 
-  // create script engine 
-  FEngine := TGorillaScriptEngine.Create(nil); 
- 
-{$IFDEF MSWINDOWS} 
-  LPath := ''; 
-{$ENDIF} 
-{$IFDEF ANDROID} 
-  LPath := IncludeTrailingPathDelimiter(System.IOUtils.TPath.GetHomePath()); 
-{$ENDIF} 
- 
-  // load and parse program source code 
-  FEngine.LoadFromFile(LPath + 'GorillaTest.pas'); 
- 
-  // compile parsed entities to bytecode 
-  FEngine.Compile(); 
-   
-  // execute bytecode in keep-alive mode (parameter = true) 
-  FEngine.Execute(true); 
-   
-  // script instances are still available from here... 
-end; 
- 
-procedure TTestWin.FormDestroy(Sender: TObject); 
-begin 
-  FreeAndNil(FEngine); 
-end; 
- 
-end. 
-</file> 
-===== IO-Interceptor ===== 
-In script you can use the functions "System.Write", "System.WriteLn", "System.Read" and "System.ReadLn" to read/write from/to standard input-output (stdio). 
- 
-For console applications no further treatment is needed. On GUI applications this leads to exceptions, because the stdio is not available. 
- 
-Due to that, we provide an interceptor class to catch stdio calls. 
- 
-<file pascal> 
-type 
-  TGorillaScriptIOHandlerGUI = class(TGorillaScriptIOHandler) 
-    public 
-      class var Memo : TMemo; 
- 
-      class procedure Write(AValue : String); override; 
-      class procedure WriteLn(AValue : String); override; 
-      class function Read() : String; override; 
-      class function ReadLn() : String; override; 
-  end; 
-   
-... 
- 
-{ TGorillaScriptIOHandlerGUI } 
- 
-class procedure TGorillaScriptIOHandlerGUI.Write(AValue : String); 
-begin 
-  TThread.Synchronize(nil, 
-    procedure() 
-    begin 
-      Memo.Lines.Add(AValue); 
-    end 
-  ); 
-end; 
- 
-class procedure TGorillaScriptIOHandlerGUI.WriteLn(AValue : String); 
-begin 
-  TThread.Synchronize(nil, 
-    procedure() 
-    begin 
-      Memo.Lines.Add(AValue); 
-    end 
-  ); 
-end; 
- 
-class function TGorillaScriptIOHandlerGUI.Read() : String; 
-begin 
-  Result := ''; 
-end; 
- 
-class function TGorillaScriptIOHandlerGUI.ReadLn() : String; 
-begin 
-  Result := ''; 
-end;   
-</file> 
- 
-The class type will register itself in class constructor. In this example you'll only need to assign you memo component. 
-====== Syntax ====== 
-===== Program ===== 
-Every GorillaScript needs a main unit, where you define a typical pascal program.  In the program initialization block you can call functions.  
- 
-**It is not possible to use variables here.** 
- 
-<file pascal> 
-program Test; 
- 
-interface 
- 
-procedure Main(); 
-begin 
-  System.WriteLn('Hello world!'); 
-end; 
- 
-begin 
-  Main(); 
-end. 
-</file> 
- 
-==== Includes ==== 
-Besides the System unit, you always need to include units, when using types of those. Even when using types indirectly, like as field or property type. 
-The complete qualified unit name is needed for successful inclusion, f.e. "System.Types". 
-Units can be included in interface section by the usage of the "uses" identifier. 
- 
-<file pascal> 
-unit Test; 
- 
-interface 
- 
-uses 
-  System.Types, System.Classes, FMX.UI, FMX.StdCtrls; 
-   
-  ... 
-</file> 
-===== Global Functions ===== 
-<file pascal> 
-procedure Main(); 
-begin 
-  // write some code here 
-end; 
-</file> 
- 
-===== Local Variables ===== 
-Local variables are declared like Delphi syntax by the "var" identifier. 
- 
-<file pascal> 
-procedure Test(); 
-var i : Integer; 
-    LStr : String; 
-    LFloat1, LFloat2 : Single; 
-begin 
-  // write some code here 
-end; 
-</file> 
-===== Classes ===== 
- 
-==== Self ==== 
-At the current development state it is always necessary to supply the self-reference variable when accessing fields, properties or methods of the class. 
-In future this will be removed like in Delphi syntax. 
- 
-Wrong syntax 
-<file pascal> 
-FMyField := 123; 
-</file> 
-Correct syntax 
-<file pascal> 
-Self.FMyField := 123; 
-</file> 
-==== Scripting ==== 
-GorillaScript allows to declare new classes in script. Class inheritance is provided, but only for script classes. **Only TObject and TInterbasedObject are allowed as native ancestors.** 
- 
-<file pascal> 
-... 
-type 
-  TMyClass = class(TObject) 
-    private 
-    protected 
-    public 
-    published 
-  end; 
-   
-  TMyClass2 = class(TMyClass) 
-  end; 
-   
-  TMyClass3 = class(TMyClass3) 
-  end; 
-  ... 
-</file> 
- 
-Scripting classes allow usage of visibility identifiers: private, protected, public and published. 
- 
-=== Fields === 
- 
-=== Methods === 
- 
-=== Properties === 
-==== Native ==== 
- 
-<file pascal> 
-procedure RegisterMyClasses(AEngine : TGorillaScriptEngine); 
-begin 
-  AEngine.RegisterNativeClasses([TMyNativeClass1, TMyNativeClass2]); 
-end; 
-</file> 
-===== Records ===== 
-==== Scripting ==== 
-Currently declaration of records in script is not possible. 
- 
-==== Native ==== 
-<file pascal> 
-procedure RegisterMyNativeRecords(AEngine : TGorillaScriptEngine); 
-begin 
-  AEngine.RegisterNativeStructs([TypeInfo(TPoint), TypeInfo(TRectF)]); 
-end; 
-</file> 
- 
-===== Interfaces ===== 
-==== Scripting ==== 
- 
-Script interfaces allow to declare properties and methods. Fields are not allowed in interfaces. 
- 
-<file pascal> 
-ITestInterface = interface(IInterface) 
-['{12345678-1234-1234-12345678}'] 
-end; 
-</file> 
- 
-=== Methods === 
- 
-=== Properties === 
- 
-==== Native ==== 
-Registration of native interfaces differs from the other registrations methods due to RTTI limitations. We do need the full qualified name of your native interface as string variable. 
- 
-<file pascal> 
-procedure RegisterMyInterfaces(AEngine : TGorillaScriptEngine); 
-begin 
-  AEngine.RegisterNativeInterfaces(['System.Classes.IControl']); 
-end; 
-</file> 
- 
-Due to missing RTTI of interfaces, it is Not possible to access native interface properties. Only interface methods are accessable. 
-===== Enumerations ===== 
-Native and script enumeration types are supported. 
- 
-==== Scripting ==== 
- 
-Enumeration values can be declared with or without a preset value. 
- 
-<file pascal> 
-TTestEnum = (teOne, teTwo = 1, teThree = 2); 
-</file> 
- 
-==== Native ==== 
-<file pascal> 
-procedure RegisterMyEnums(AEngine : TGorillaScriptEngine); 
-begin 
-  AEngine.RegisterNativeEnums([TypeInfo(TFontStyle), ...]); 
-end; 
-</file> 
-===== Sets ===== 
-Native and script set of enumeration types are supported. 
-==== Scripting ==== 
-In scripting sets always need an existing script/native enumeration type. Dynamic declarations like "TTestSet = set of (EnumVal1, EnumVal2, EnumVal3);" __are not allowed!__ 
- 
-<file pascal> 
-TTestSet = set of TTestEnum; 
-</file> 
- 
-==== Native ==== 
-<file pascal> 
-procedure RegisterMySets(AEngine : TGorillaScriptEngine); 
-begin 
-  AEngine.RegisterNativeSets([TypeInfo(TStyledSettings), TypeInfo(TShiftState)]); 
-end; 
-</file> 
-===== Events ===== 
-You are allowed to declare a scripting method with the same parameters like a native event and to assign to a native instance. 
-The script will manage to call your scripting method, when a native instance event getting called by delphi. 
- 
-<file pascal> 
-unit EventTest; 
- 
-interface 
- 
-uses 
-  FMX.UI; 
-   
-type 
-  TMyClass = class(TObject) 
-    protected 
-      FButton : TButton; 
-      procedure DoOnClick(ASender : TObject); 
-       
-    public 
-      constructor Create(); 
-  end; 
- 
-implementation 
- 
-{ TMyClass } 
-constructor TMyClass.Create(); 
-begin 
-  inherited Create(); 
-   
-  Self.FButton := TButton.Create(TUI.GetMainForm()); 
-   
-  // Link script method with native component event 
-  // When Delphi calls the native event, the script method will be called. 
-  Self.FButton.OnClick := @Self.DoOnClick; 
-end; 
- 
-procedure TMyClass.DoOnClick(ASender : TObject); 
-begin 
-  TUI.ShowMessage('Button clicked!'); 
-end; 
- 
-end. 
-</file> 
-====== Internals ====== 
-===== SearchPaths ===== 
-If no further search paths were set in your TGorillaScriptEngine component, the scripting will take the first script file directory as base directory to search from. 
-You have to set all necessary search paths __before__ parsing stage starts. 
- 
-<file pascal> 
-FEngine.SearchPaths.Add('./subdir/'); 
-FEngine.SearchPaths.Add('C:\test\dir\'); 
-</file> 
- 
-===== User-Specific Compiler Defines ===== 
-You are allowed to add global user specific compiler defines in your TGorillaScriptEngine component. You have to apply those defines __before__ parsing stage starts. 
- 
-<file pascal> 
-FEngine.CompilerDirectives.Add('THE_FORCE_IS_WITH_YOU'); 
-</file> 
- 
-===== Predefined Libs ===== 
-GorillaScript provides some basic libraries, like UI components (buttons, edits, ...) 
-or Gorilla3D components. Those libraries automatically register all necessary types for you, by simply calling their registration function. 
- 
-General libs: 
-  * Gorilla.Script.Lib.OS (automatically registered) 
-  * Gorilla.Script.Lib.Math (automatically registered) 
-  * Gorilla.Script.Lib.UI (RegisterUIComponents) 
- 
-Modular Gorilla3D libs: 
-  * Gorilla.Script.Lib.Gorilla (RegisterGorillaComponents) 
-  * Gorilla.Script.Lib.Particles (RegisterGorillaParticles) 
-  * Gorilla.Script.Lib.Physics (RegisterGorillaPhysics) 
-  * Gorilla.Script.Lib.Audio (RegisterGorillaAudio) 
-  * Gorilla.Script.Lib.Input (RegisterGorillaInput) 
-  * Gorilla.Script.Lib.Utils (RegisterGorillaUtils) 
-  * Gorilla.Script.Lib.GUI (RegisterGorillaGUI) 
- 
-<file pascal> 
-  Gorilla.Script.Lib.UI.RegisterUIComponents(FEngine); 
-  Gorilla.Script.Lib.Gorilla.RegisterGorillaComponents(FEngine); 
-  Gorilla.Script.Lib.Particles.RegisterGorillaParticles(FEngine); 
-  Gorilla.Script.Lib.Physics.RegisterGorillaPhysics(FEngine); 
-  Gorilla.Script.Lib.Audio.RegisterGorillaAudio(FEngine); 
-  Gorilla.Script.Lib.Input.RegisterGorillaInput(FEngine); 
-  Gorilla.Script.Lib.Utils.RegisterGorillaUtils(FEngine); 
-  Gorilla.Script.Lib.GUI.RegisterGorillaGUI(FEngine); 
-</file> 
-====== Examples ====== 
-==== A simple script ==== 
- 
-<file pascal> 
-program GorillaTest; 
- 
-uses 
-  System.SysUtils, GorillaTest.App; 
-  
-type 
-  procedure Main(); 
-  var LApp : TGorillaApp; 
-  begin  
-    // this script is used in keep-alive mode 
-    // the created script object will be destroyed automatically 
-    // when the executor will be destroyed. 
-    LApp := TGorillaApp.Create(); 
-  end; 
- 
-begin 
-  Main(); 
-end. 
-</file> 
- 
-<file pascal> 
-unit GorillaTest.App; 
- 
-interface 
- 
-uses 
-  System.SysUtils, FMX.OS, FMX.UI, FMX.Types, FMX.Forms, FMX.StdCtrls; 
- 
-type  
-  TGorillaApp = class 
-    protected 
-      FForm   : TForm; 
-      FButton : TButton; 
-      FEdit   : TEdit; 
-  
-    public 
-      property Form : TForm read FForm; 
-      property Button : TButton read FButton; 
-      property Edit : TEdit read FEdit; 
-  
-      constructor Create(); override; 
-      procedure DoOnButtonClicked(ASender : TObject); 
-  end; 
- 
-implementation 
- 
-constructor TGorillaApp.Create(); 
-begin 
-  inherited Create(); 
-   
-  Self.FForm := TUI.GetMainForm(); 
- 
-  Self.FEdit := TEdit.Create(Self.FForm); 
-  Self.FEdit.Parent := Self.FForm; 
-  Self.FEdit.Width := 200; 
-  Self.FEdit.Height := 32; 
-  Self.FEdit.Align := TAlignLayout.Top; 
-  Self.FEdit.Margins.Left := 8; 
-  Self.FEdit.Margins.Right := 8; 
-  Self.FEdit.Margins.Top := 4; 
-  Self.FEdit.Margins.Bottom := 4; 
-  
-  Self.FButton := TButton.Create(Self.FForm); 
-  Self.FButton.Parent := Self.FForm; 
-  Self.FButton.Position.Point := TPointF.Create(200, 128); 
-  Self.FButton.Text := 'Click me!'; 
-  Self.FButton.Size.Width := 128; 
-  Self.FButton.Size.Height := 48; 
-  Self.FButton.OnClick := @Self.DoOnButtonClicked; 
-end; 
- 
-procedure TGorillaApp.DoOnButtonClicked(ASender : TObject); 
-var LBtn : TButton; 
-begin 
-  // we just move the button randomly 
-  if (Self.Button = ASender) then 
-  begin 
-    LBtn := ASender; 
-    LBtn.Position.Point := TPointF.Create( 
-      System.Random(400), System.Random(400)); 
-  end; 
-end; 
- 
-end. 
-</file>