はじめに
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に組み込む前に単体での入門から。
| 1 | print ("Hello World!") | 
以上の内容をhello.luaというファイル名で保存。
コマンドプロンプト上で実行。
| 1 2 | >lua52 hello.lua Hello World! | 
以下の6通りの記述は、すべて同じ結果となります。
| 1 2 3 4 5 6 | print ("Hello World!") print ('Hello World!') print "Hello World!" print 'Hello World!' print ([[Hello World!]]) print [[Hello World!]] | 
文字列の途中に改行を加えたい場合、”\n”を用います。
| 1 2 3 | print ("Hello\nWorld!") print ('Hello\nWorld!') print ([[Hello\nWorld!]]) | 
上記のうち、上の2行は同じ動作をしますが、最後の1行は違います。
| 1 2 3 4 5 | Hello World! Hello World! Hello\nWorld! | 
最後の1行は、文字列をそのままその通りに扱います。例えば以下のようになります。
| 1 2 | print ([[Hello World!]]) | 
| 1 2 | Hello World! | 
コメント
1行コメントと複数行コメントがあります。
| 1 2 3 4 5 6 7 | --1行コメント --[[ 複数行 コメントは このように 書きます ]] | 
VerySimple.Lua
Readme.txt内のサンプル(加工済み)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 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
| 1 2 3 4 5 | print("LuaDelphi Test"); p1,p2 = HelloWorld(1,2,3) print "Results:"; print (p1); print (p2); | 
結果表示
| 1 2 3 4 5 6 7 8 9 | LuaDelphi Test Delphi: Hello World Arguments: 3 Arg11: 1 Arg12: 2 Arg13: 3 Results: 101 102 | 
DelphiLua
README.md内のサンプル(加工済み)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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を追加。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | //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が必要。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //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; | 
| 1 2 3 4 5 | --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は入出力が単純でした。これより多くの入出力を得ようと思った場合、スタックという機能を用います。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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; | 
結果表示。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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