Home

Awesome

CrystalLUA

<img align="right" src="data/logo.png">

Lua is a lightweight, high-level, multi-paradigm programming language designed primarily for embedded use in applications. CrystalLUA is a high-level library for binding your native Delphi code and scripts in Lua language. The difference between CrystalLUA and other binding libraries is in high code performance and convenient registration based on RTTI. This project was developed many years ago, you can use an outdated version (Delphi 6-2007) of the library.

Now the library is actively developing, despite the fact that not all the features declared in the old versions have been implemented, many others are already supported: Unicode, 64 bits, extended RTTI, executable types (functions, events, references). In addition, the RLua fork is being developed, which over time will make CrystalLUA cross-platform and independent of dynamic libraries.

Below are a few examples that will help you quickly master CrystalLUA. Enjoy :).

Hello, World!

TLua is the main class of the library, which allows you to execute scripts and register types, functions and variables. Now it uses a dynamic library, so before calling the constructor, you must specify the relative or full path LuaLibraryPath. There are several ways to execute a script; the simplest is the RunScript method, which takes a script text parameter.

program HelloWorld;
{$APPTYPE CONSOLE}
uses
  SysUtils, CrystalLUA;
var
  Lua: TLua;
begin
  LuaLibraryPath := {$ifdef CPUX86}'lua.dll'{$else}'lua64.dll'{$endif};
  Lua := TLua.Create;
  try
    Lua.RunScript('print("Hello, World!\n")');
  finally
    Lua.Free;
  end;
  Write('Press Enter to quit');
  Readln;
end.
Hello, World!
Press Enter to quit

Script functions calling

You can load a script from a file, memory or resource using the LoadScript method. In this example, the script is loaded from a set of strings that are converted to TBytes. You can call any script function and get the result.

program ScriptFunctions;
{$APPTYPE CONSOLE}
uses
  SysUtils, CrystalLUA, Classes;
var
  Lua: TLua;
  a, b, c, r: Integer;
  Bytes: TBytes;
begin
  LuaLibraryPath := {$ifdef CPUX86}'lua.dll'{$else}'lua64.dll'{$endif};
  Lua := TLua.Create;
  try
    a := 1;
    b := 2;
    c := 3;
    Bytes := TEncoding.UTF8.GetPreamble + TEncoding.UTF8.GetBytes(
      'function myfunc(a, b, c)'#13 +
      '  return (a + b * c)'#13 +
      'end');
    Lua.LoadScript(Bytes);
    Write(a, ' + ', b, ' * ', c, ' = ');
    r := Lua.Call('myfunc', [a, b, c]).Items[0].AsInteger;
    Writeln(r);
  finally
    Lua.Free;
  end;
  Write('Press Enter to quit');
  Readln;
end.
1 + 2 * 3 = 7
Press Enter to quit

Involvement of components (VCL, FMX, etc.)

The library allows you to bind your native code and scripts. The CrystalLUA namespace contains only those elements that you register in it. But by default, all the information that was found in the RTTI gets into the namespace. Therefore, by registering the global variable Form1: TForm1, you get access to all its public properties, methods and child components.

procedure TForm1.FormCreate(Sender: TObject);
begin
  LuaLibraryPath := {$ifdef CPUX86}'lua.dll'{$else}'lua64.dll'{$endif};
  FLua := TLua.Create;
  FLua.RegVariable('Form1', Form1, TForm1);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FLua.Free;
end;
procedure TForm1.btnRunClick(Sender: TObject);
begin
  // FLua.RunScript('Form1.btnRun.Left = Form1.btnRun.Left + 10');
  FLua.RunScript('Form1.Caption = "Component property changed!"');
end;

Native functions calling

Modern Delphi allows you to receive declared methods RTTI. This example demonstrates how to call a native function from a script.

  public
    { Public declarations }
    function AskQuestion(const ATitle, AQuestion: string): Boolean;
  end;
  
function TForm1.AskQuestion(const ATitle, AQuestion: string): Boolean;
begin
  Result := Application.MessageBox(PChar(AQuestion), PChar(ATitle), MB_YESNO) = IDYES;
end;
procedure TForm1.btnRunClick(Sender: TObject);
begin
  // FLua.RunScript('Form1:Close()');
  FLua.RunScript('Form1.Caption = tostring(Form1:AskQuestion("My title", "Do you like the library?"))');
end;

Executable types

The library allows you to read, modify and execute types such as functions, events, references. Remember that references have RTTI if {$M+} directive declared. The example below shows how to read and execute a variable of your type.

type
  {$M+}
  TMyReference = reference to function(const ATitle, AQuestion: string): Boolean;
  TForm1 = class(TForm)
    btnRun: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnRunClick(Sender: TObject);
  private
    FLua: TLua;
    FMyReference: TMyReference;
    function AskQuestion(const ATitle, AQuestion: string): Boolean;
  public
    property MyReference: TMyReference read FMyReference write FMyReference;
  end;
  
procedure TForm1.FormCreate(Sender: TObject);
begin
  LuaLibraryPath := {$ifdef CPUX86}'lua.dll'{$else}'lua64.dll'{$endif};
  FLua := TLua.Create;
  FLua.RegVariable('Form1', Form1, TForm1);
  FMyReference := AskQuestion;  
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FLua.Free;
end;
procedure TForm1.btnRunClick(Sender: TObject);
begin
  FLua.RunScript(
    'local Func = Form1.MyReference'#13 +
    'Form1.Caption = tostring(Func("My title", "Do you like the library?"))');
end;