はじめに
Luaとはスクリプト言語の一種で、他言語に組み込むことで威力を発揮します。
ここでは、Delphiから呼び出すことを前提にちびちびと書き足していきたいと思います。
Luaバイナリの入手先および日本語のマニュアル
- LuaBinaries
http://luabinaries.sourceforge.net/download.html - Lua 5.3 リファレンスマニュアル
http://milkpot.sakura.ne.jp/lua/lua53_manual_ja.html - Lua 5.2 リファレンスマニュアル
http://milkpot.sakura.ne.jp/lua/lua52_manual_ja.html - Lua 5.1 リファレンスマニュアル
http://milkpot.sakura.ne.jp/lua/lua51_manual_ja.html - Lua 5.0 リファレンスマニュアル
http://milkpot.sakura.ne.jp/lua/lua5_manual_ja.html - Luaプログラミング入門
https://densan-labs.net/tech/lua/
Delphi向けLuaライブラリの選択肢
どれを使おうか悩み中
- GitHub – Dennis1000/verysimplelua VerySimple.Lua: Lua 5.3 for Delphi
https://github.com/Dennis1000/verysimplelua - GitHub – exlunaproject/pLua-XE: Easily embed Lua into Delphi and Pascal projects – Lua 5.1.4
https://github.com/exlunaproject/pLua-XE - GitHub – d-mozulyov/CrystalLUA: Lua binging for Delphi
https://github.com/d-mozulyov/CrystalLUA - GitHub – exlunaproject/catarinka: A powerful set of multi-purpose libraries for Pascal/Delphi or Lua
https://github.com/exlunaproject/catarinka - GitHub – danieleteti/lua4delphi: Delphi binding for Lua 5.1 language
https://github.com/danieleteti/lua4delphi - GitHub – MvRens/DelphiLua: An API conversion and wrapper classes for integrating Lua 5.2 into Delphi projects
https://github.com/MvRens/DelphiLua - GitHub – rburgstaler/LuaDelphi: Lua Delphi Plugin
https://github.com/rburgstaler/LuaDelphi - GitHub – mvdhoning/dlua: How to use Lua from Delphi and Pascal
https://github.com/mvdhoning/dlua - GitHub – felipedaragon/LuaUtils: Useful functions to work with Lua in Delphi
https://github.com/felipedaragon/LuaUtils - GitHub – kravets-levko/lua4delphi: Lua bindings for Delphi XE and upper
https://github.com/kravets-levko/lua4delphi
基礎の基礎
Hello World!
Delphiに組み込む前に単体での入門から。
print ("Hello World!")
以上の内容をhello.luaというファイル名で保存。
コマンドプロンプト上で実行。
>lua52 hello.lua Hello World!
以下の6通りの記述は、すべて同じ結果となります。
print ("Hello World!")
print ('Hello World!')
print "Hello World!"
print 'Hello World!'
print ([[Hello World!]])
print [[Hello World!]]
文字列の途中に改行を加えたい場合、”\n”を用います。
print ("Hello\nWorld!")
print ('Hello\nWorld!')
print ([[Hello\nWorld!]])
上記のうち、上の2行は同じ動作をしますが、最後の1行は違います。
Hello World! Hello World! Hello\nWorld!
最後の1行は、文字列をそのままその通りに扱います。例えば以下のようになります。
print ([[Hello World!]])
Hello World!
コメント
1行コメントと複数行コメントがあります。
--1行コメント --[[ 複数行 コメントは このように 書きます ]]
VerySimple.Lua
Readme.txt内のサンプル(加工済み)
program Readme;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
VerySimple.Lua,
VerySimple.Lua.Lib;
type
TMyLua = class(TVerySimpleLua)
published
function HelloWorld(LuaState: lua_State): Integer;
end;
function TMyLua.HelloWorld(LuaState: lua_State): Integer;
var
ArgCount: Integer;
I: integer;
begin
ArgCount := Lua_GetTop(LuaState);
writeln('Delphi: Hello World');
writeln('Arguments: ', ArgCount);
for I := 1 to ArgCount do
writeln('Arg1', I, ': ', Lua_ToInteger(LuaState, I));
// Clear stack
Lua_Pop(LuaState, Lua_GetTop(LuaState));
// Push return values
Lua_PushInteger(LuaState, 101);
Lua_PushInteger(LuaState, 102);
Result := 2;
end;
var
MyLua: TVerySimpleLua;
begin
try
MyLua := TMyLua.Create;
MyLua.LibraryPath := '..\..\'+LUA_LIBRARY;
MyLua.DoFile('..\..\Helloworld.lua');
MyLua.Free;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
Helloworld.lua
print("LuaDelphi Test");
p1,p2 = HelloWorld(1,2,3)
print "Results:";
print (p1);
print (p2);
結果表示
LuaDelphi Test Delphi: Hello World Arguments: 3 Arg11: 1 Arg12: 2 Arg13: 3 Results: 101 102
DelphiLua
README.md内のサンプル(加工済み)
program README;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Lua.Api,
Lua;
var
lua: TLua;
returnValue: Integer;
begin
try
lua := TLua.Create;
try
lua.SetGlobalVariable('A', 5);
lua.SetGlobalVariable('B', 10);
lua.LoadFromString('C = A + B');
returnValue := lua.GetGlobalVariable('C').AsInteger;
Writeln('C = '+IntToStr(returnValue));
finally
Readln;
FreeAndNil(lua);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
「C = 15」と表示されれば成功です。
スクリプト関数の呼び出し
まずは、比較用にDelphiでの記述。自作アプリから該当箇所のみ抜粋。正規表現を使うのでusesにPerlRegExを追加。
//Delphi版のGetProgress
function GetProgress1(const S: string): string;
var
RegExp: TPerlRegEx;
begin
RegExp := TPerlRegEx.Create;
try
RegExp.Subject := UTF8String(S);
RegExp.RegEx := UTF8String('^created');
if RegExp.Match then begin
Result := '100';
end else begin
RegExp.RegEx := UTF8String('(\d+)% done');
if RegExp.Match then begin
Result := UnicodeString(RegExp.Groups[1]);
end else begin
Result := '';
end;
end;
finally
RegExp.Free;
end;
end;
DelphiからLuaスクリプトを呼び出します。usesにLua.ApiとLuaが必要。
//Lua版のGetProgress呼び出し準備
var
FLua: TLua;
procedure TWavPackGUIMain.FormCreate(Sender: TObject);
begin
FLua := TLua.Create;
FLua.LoadFromFile(ExtractFilePath(ParamStr(0))+'GetProgress_WavPackEnc.lua',False);
end;
procedure TWavPackGUIMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
FLua.Free;
end;
//Lua版のGetProgressを呼び出す
function GetProgress2(S: string): string;
begin
Result := FLua.Call('GetProgress',[S]).ToString;
end;
--Lua版のGetProgress function GetProgress(s) if string.match(s, '^created') ~= nil then return "100"; else return string.match(s, '(%d+)%% done'); end; end
文字列を読み込んで進捗度を返す関数です。
演算子 ~= は等値比較 (==) の否定です。
行頭がcreatedなら100、それ以外なら%の直前の数字を返します。
関数名やスクリプト名を見ればわかるように、WavPack用になってます。
WavPackの表示が少々変わってもスクリプト側で吸収できることが期待されます。
スタックの取り扱い
GetProgressは入出力が単純でした。これより多くの入出力を得ようと思った場合、スタックという機能を用います。
procedure dumpStack(L: lua_State; M: TMemo);
var
T: Integer; //lua_type
I: Integer;
stackSize: Integer;
begin
//スタックに積まれている数を取得する
stackSize := lua_gettop(L);
for I := stackSize downto 1 do begin
T := lua_type(L,i);
M.Text := M.Text + 'Stack['+Format('%2d-%10s',[i,lua_typename(L,T)])+'] : ';
case T of
LUA_TNUMBER: begin
M.Text := M.Text + FormatFloat('0.000000',lua_tonumber(L, i));
end;
LUA_TBOOLEAN: begin
if 0 <> lua_toboolean(L, i) then begin
M.Text := M.Text + 'true';
end else begin
M.Text := M.Text + 'false';
end;
end;
LUA_TSTRING: begin
M.Text := M.Text + lua_tostring(L, i);
end;
LUA_TNIL: ;
else begin
M.Text := M.Text + lua_typename(L, T);
end;
end;
M.Text := M.Text + #13#10;
end;
M.Text := M.Text + #13#10;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
L: TLua;
begin
L := TLua.Create;
try
lua_pushboolean(L.State, 1); //true をpush
dumpStack(L.State, Memo1);
lua_pushnumber(L.State, 10.5); //10.5 をpush
dumpStack(L.State, Memo1);
lua_pushinteger(L.State, 3); //3 をpush
dumpStack(L.State, Memo1);
lua_pushnil(L.State); //nil をpush
dumpStack(L.State, Memo1);
lua_pushstring(L.State, 'Hello world'); //hello world をpush
dumpStack(L.State, Memo1);
finally
L.Free;
end;
end;
結果表示。
Stack[ 1- boolean] : true Stack[ 2- number] : 10.500000 Stack[ 1- boolean] : true Stack[ 3- number] : 3.000000 Stack[ 2- number] : 10.500000 Stack[ 1- boolean] : true Stack[ 4- nil] : Stack[ 3- number] : 3.000000 Stack[ 2- number] : 10.500000 Stack[ 1- boolean] : true Stack[ 5- string] : Hello world Stack[ 4- nil] : Stack[ 3- number] : 3.000000 Stack[ 2- number] : 10.500000 Stack[ 1- boolean] : true
スタックに値をpushするたびにスタックの内容を表示しています。lua_toboolean関数はLuaの条件判定と同様、false と nil 以外のすべてのLuaの値に対して真を返します。
Copyright © 2021 y.ohm / Yoshimasa Ohmuro / 大室 喜正
e-mail : y.ohm@ohmix.net
PGP Public Key : DH/DSS