はじめに
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