Lua
スクリプト言語およびその処理系の実装
From Wikipedia, the free encyclopedia
Lua(ルーア[2])はスクリプト言語およびその処理系の実装で、主にリオデジャネイロ・カトリカ大学のコンピュータ科学科 (Department of Computer Science) および/または同大学附属研究所のTecgraf/PUC-Rio[注 1]に所属するロベルト・イエルサリムスキー Roberto Ierusalimschy[4][5]、Waldemar Celes[6][7]、Luiz Henrique de Figueiredo[8]らによって設計開発された[12]。
| パラダイム |
マルチパラダイムプログラミング、オブジェクト指向プログラミング、関数型プログラミング、手続き型プログラミング、プロトタイプベース、命令型プログラミング |
|---|---|
| 登場時期 | |
| 開発者 |
ロベルト・イエルサリムスキー |
| 最新リリース | 5.4.7/ 2024年6月25日[1] |
| 型付け | ダック・タイピング |
| 主な処理系 | Lua、LuaJIT、LLVM-Lua、Lua Alchemy |
| 影響を受けた言語 |
C++、CLU、Simple Object Language、DEL、SNOBOL、Modula、Modula-2、Scheme |
| 影響を与えた言語 | Io, JavaScript, Julia, Ruby, Squirrel, MoonScript |
| プラットフォーム |
クロスプラットフォーム |
| ライセンス |
MITライセンス |
| ウェブサイト |
www |
| 拡張子 |
lua、luna、lunaire、anair |
手続き型言語として、またプロトタイプベースのオブジェクト指向言語としても利用することができ、関数型言語としての要素も併せ持っている[13]。
名称の由来
概要
Luaは、C言語のホストプログラムに組み込まれることを目的に設計されており、高速な動作と、高い移植性、組み込みの容易さが特徴である。いったんバイトコードにコンパイルされ、Lua VMで実行される。LuaJITは The Computer Language Benchmarks Game によると、動的型付けのスクリプト言語では最速の言語・処理系である[15]。
TIOBE Programming Community Indexでは2007年に人気が急上昇し、2011年6月には10番目に人気なプログラミング言語になったが、その後は人気が下がっている[16]。2009年2月の調査で、ゲーム開発者がイベントスクリプト等の内部処理に利用する言語として、最も利用例が多いと報告されるなど、近年[いつ?]はゲーム産業での利用が広がっている[17]。2013年3月以来、ウィキメディア財団のサイトではLuaをサポートしている[18]。
特徴
Luaの特徴としては、汎用性が高いが比較的容易に実装が可能である、というものである。実際のところLuaは、オブジェクト指向などといった他の要素としての働きを明白にはサポートしていないが、サポートしていない範囲においても容易に拡張が可能である。また前述のような、動作の高速性や優れた移植性なども大きな特徴である。
文法的な特徴としては、Pascalによく似た構文を採用していること、コルーチン(協調的マルチタスク)のサポート[20]、数値型は整数と浮動小数点数の区別がないこと(ただしLua 5.3では整数型が導入された)、関数を変数として扱えることなどが挙げられる。
Luaはいわゆる汎用スクリプト言語であり、特定の用途に限定されない性質を持つが、同じく汎用スクリプト言語であるPerl、Python、Rubyと比較して高速に動作する[要出典]。これはLuaの理念である、簡素、高効率、高移植性を目指した実装の産物である。また、Luaにおけるテーブル(連想配列)の実装はかなり最適化されており、特にキーに数値のみを使用した場合は、単純な配列としてさらに高速に動作するようになる。
Lua 5.0以前はメモリ管理にマーク & スイープ方式のガベージコレクションが使用されていたが、Lua 5.1ではメモリ管理にインクリメンタル・ガベージコレクションが採用され、リアルタイム用途における性能の改善が図られている。ガベージコレクションの実装形態も Lua の高速動作および高リアルタイム性能に一役買っている。
LuaJIT
Luaの歴史
Lua
- 1993年7月28日 - Lua 1.0 リリース。
- 1995年2月7日 - Lua 2.1 リリース。
- 1997年7月1日 - Lua 3.0 リリース。
- 2000年11月6日 - Lua 4.0 リリース。
- 2003年4月11日 - Lua 5.0 リリース。MITライセンスの採用。
- 2006年2月21日 - Lua 5.1 リリース。インクリメンタルGCの採用。
- 2008年8月22日 - Lua 5.1.4 リリース。
- 2010年5月14日 - Lua 5.1.4-2 リリース。
- 2011年12月16日 - Lua 5.2.0 リリース。ビット演算ライブラリをサポート。
- 2012年6月14日 - Lua 5.2.1 リリース。
- 2013年3月27日 - Lua 5.2.2 リリース。
- 2013年12月7日 - Lua 5.2.3 リリース。
- 2015年1月6日 - Lua 5.3.0 リリース。整数型およびビット演算子のサポートなど。
- 2020年6月29日 - Lua 5.4.0 リリース。
LuaJIT
- 2005年9月8日 - LuaJIT 1.0.3 リリース。最初の公開版。
- 2006年3月13日 - LuaJIT 1.1.0 リリース。Lua 5.1対応。
- 2006年6月24日 - LuaJIT 1.1.2 リリース。
- 2007年5月24日 - LuaJIT 1.1.3 リリース。
- 2008年2月5日 - LuaJIT 1.1.4 リリース。
- 2008年10月25日 - LuaJIT 1.1.5 リリース。
- 2010年3月28日 - LuaJIT 1.1.6 リリース。
- 2011年5月5日 - LuaJIT 1.1.7 リリース。
- 2012年4月16日 - LuaJIT 1.1.8 リリース。
- 2012年11月8日 - LuaJIT 2.0.0 リリース。
- 2013年2月19日 - LuaJIT 2.0.1 リリース。
- 2013年6月3日 - LuaJIT 2.0.2 リリース。
- 2014年3月12日 - LuaJIT 2.0.3 リリース。
- 2015年5月14日 - LuaJIT 2.0.4 リリース。
- 2017年5月1日 - LuaJIT 2.0.5 リリース。
コード例
Hello World
print("Hello World")
Hello World
挿入ソート
-- `--´から行末までコメント
local tabSort = {5, 3, 1, 4, 2} -- `{´と`}´はテーブルコンストラクタ
for ind = 2, #tabSort do -- `#´は長さ演算子であり、`#tabSort´はテーブルtabSortのサイズ(ここでは5)を返す
for innerIdx = ind, 2, -1 do -- ステップ -1 で ind から 2 まで逆向きに繰り返す(負のステップは減算を意味する)
if tabSort[innerIdx - 1] <= tabSort[innerIdx] then break end
-- innerIdx と innerIdx - 1 の位置にある値を入れ替える
tabSort[innerIdx], tabSort[innerIdx - 1] = tabSort[innerIdx - 1], tabSort[innerIdx]
end
end
-- ソート済みの結果を空白区切りの文字列として作成して出力
local res = ''; for ind = 1, #tabSort do res = res .. tabSort[ind] .. ' ' end; print(res)
1 2 3 4 5
コルーチン
コルーチンは状態遷移を記述するのに便利である。
-- Lua コルーチンの例:非同期の状態遷移を同期的に記述する。 -- 1 を返すと「動作継続」を示す。0 を返すと「完了」を示す。 local function doAction() -- 4 フレーム分左へ移動する。 for idx = 1, 4 do print("Move Left " .. idx) coroutine.yield(1) -- yield 1:次のフレームで続行する(1フレーム分のステップを示す) end -- 1 フレーム分一時停止する。 print("Pause") coroutine.yield(1) -- 1フレーム分のポーズを示す -- 3 フレーム分右へ移動する。 for idx = 1, 3 do print("Move Right " .. idx) coroutine.yield(1) -- 1フレーム分のステップ end print("End") return 0 end local doActionAsync = coroutine.wrap(doAction) -- コルーチンの動作テスト。 -- 実際の使用ではメイン/ゲームループからフレームごとに1回呼び出す。 -- ループはコルーチンを1回ずつ進め、0 を返したら停止する。 while doActionAsync() ~= 0 do -- コルーチンが 0 を返すまで繰り返し呼び出す -- ここは空。実際のフレームループではエンジンに戻り、次のフレームで再度呼び出す。 end
Move Left 1 Move Left 2 Move Left 3 Move Left 4 Pause Move Right 1 Move Right 2 Move Right 3 End
正規表現
LuaはPOSIXやECMAScript標準の正規表現とは異なる独自のカスタムパターンマッチングをサポートする[22]。
local myTable = { -- 解析する入力行 "Gnome,160,30", "Sylph,100,70", "Salamander,200,20", "Ondine,140,60", } for idx = 1, #myTable do -- 各行ごとに -- "([^,]+)" はカンマ以外の文字が1回以上続く部分をキャプチャ(名前) -- "," はカンマに一致する。カンマを "%" でエスケープする必要はない -- "([^,]+)" はカンマ以外の文字が1回以上続く部分をキャプチャ(HP) -- "," はもう一つのリテラルなカンマに一致 -- "(.+)" は行の残り全体をキャプチャ(MP) local name, hp, mp = string.match(myTable[idx], "([^,]+),([^,]+),(.+)") print(string.format("Name = %q, HP = %d, MP = %d", name, tonumber(hp), tonumber(mp))) end
Name = "Gnome", HP = 160, MP = 30 Name = "Sylph", HP = 100, MP = 70 Name = "Salamander", HP = 200, MP = 20 Name = "Ondine", HP = 140, MP = 60
LuaとC/C++の相互運用
LuaにはC言語向けの相互運用APIが用意されている。LuaからC/C++の関数を呼び出すためには以下の方法を用いる。下記のコードはC/C++の関数をLua VMに登録し、Luaスクリプト側から呼び出している。
#include <cstdio>
#include <cstdlib>
#include <lua.h>
#include <lauxlib.h>
int my_add(lua_State* L) {
const int x = (int)lua_tonumber(L, 1); // 第1引数の取得。
const int y = (int)lua_tonumber(L, 2); // 第2引数の取得。
lua_settop(L, 0); // スタックのクリア。
const int ret = x + y; // C/C++ 側での演算。
lua_pushnumber(L, ret); // 返却値をプッシュ。
return 1;
}
int main(int argc, char* argv[]) {
lua_State* L = luaL_newstate(); // Lua VM の初期化。
luaL_openlibs(L); // Lua の標準ライブラリを使えるようにする。
lua_register(L, "my_add", my_add); // Lua VM に C/C++ 関数を登録。
// my_add 関数を呼び出す Lua スクリプトを実行。
if (luaL_dostring(L, "print(my_add(5, 3))")) {
lua_close(L); // Lua VM を閉じる。
return EXIT_FAILURE; // エラー終了。
}
lua_close(L);
return EXIT_SUCCESS;
}
逆に、C/C++からLuaの関数を呼び出す際にもスタック操作が必要となる。
#include <cstdio>
#include <cstdlib>
#include <lua.h>
#include <lauxlib.h>
int main(int argc, char* argv[]) {
lua_State* L = luaL_newstate(); // Lua VM の初期化。
// add_func 関数を定義する Lua スクリプトを実行。
if (luaL_dostring(L, "function add_func(x, y) return x + y end")) {
lua_close(L); // Lua VM を閉じる。
return EXIT_FAILURE; // エラー終了。
}
lua_getglobal(L, "add_func"); // Lua のグローバルオブジェクトである「add_func」を取得し、スタックに積む。
lua_pushinteger(L, 5); // 整数値の「5」を Lua スタックにプッシュ。
lua_pushinteger(L, 3); // 整数値の「3」を Lua スタックにプッシュ。
lua_call(L, 2, 1); // Lua 側で実装した add_func 関数を呼び出す。引数の数は2、結果の数は1。
printf("Result: %d\n", lua_tointeger(L, -1)); // 結果を表示。
lua_close(L);
return EXIT_SUCCESS;
}
言語バインディングの例
Luaの他言語用バインディングは公式には提供されていないが、有志によるサードパーティ製ライブラリやツールがいくつか存在する。バインディングを使うと、前述のような煩雑なスタック操作を明示的に記述することなく、簡潔に相互運用できるようになる。
C++
LuaをC++言語で記述されたホストプログラムへ組み込むための省力化ツール(コードジェネレーター)および言語バインディングとして、toLua[23]、 tolua++(Lua 5.2非対応)[24][25]、Luabind(Lua 5.2非対応)[26]、Selene[27]、Sol[28]、Sol2[29]などが開発されている。
以下にSol2を使った例を示す(C++11およびC++14の機能を利用するため、対応コンパイラが必要)。
- LuaからC/C++の関数を呼び出す例:
#include <iostream>
#include <sol.hpp>
int add(int x, int y) {
return x + y;
}
int main() {
// Luaの初期化
sol::state lua;
// Luaの標準ライブラリをすべて開く
lua.open_libraries(sol::lib::base, sol::lib::coroutine, sol::lib::debug, sol::lib::debug,
sol::lib::io, sol::lib::math, sol::lib::os,
sol::lib::package, sol::lib::string, sol::lib::table, sol::lib::utf8);
// LuaにC/C++の関数を登録
lua["add"] = add;
// Luaスクリプトの読み込み
try {
lua.safe_script_file("test.lua");
} catch (const sol::error& e) {
std::cout << e.what() << std::endl;
}
}
C/C++の関数を呼び出すLuaスクリプト (test.lua):
print(add(100, 200)) -- 「300」と表示される
- C++からLuaの関数を呼び出す例:
#include <iostream>
#include <sol.hpp>
int main() {
// Luaの初期化
sol::state lua;
// Luaの標準ライブラリをすべて開く
lua.open_libraries(sol::lib::base, sol::lib::coroutine, sol::lib::debug, sol::lib::debug,
sol::lib::io, sol::lib::math, sol::lib::os,
sol::lib::package, sol::lib::string, sol::lib::table, sol::lib::utf8);
// Luaスクリプトの読み込み
try {
lua.safe_script_file("test.lua");
} catch (const sol::error& e) {
std::cout << e.what() << std::endl;
}
// Luaの関数を呼び出す
sol::function_result ret = lua["add"](100, 200);
// 結果を表示する
std::cout << ret.get<int>() << std::endl;
}
C++から呼び出される関数を定義するLuaスクリプト (test.lua):
function add(a, b)
return a + b
end
Java
LuajというJava仮想マシン向けの実装がある。Luaj 3.0は、Lua 5.2相当の仕様をJavaで実装しなおしたものであり、Javaのクラスからバインダ無しでインスタンスを生成したりメソッドを呼び出したりすることが可能である。そのほか、LuaのC APIをJNI経由でJavaから利用可能にするJNLua[30]が存在する。
.NET
C#やVB.NETといった.NET Framework言語向けのバインディングとして、LunaRoad[31]が存在する。C#で書かれたLuaインタプリタとしてMoonSharp[32]が存在する。また、DLR上に実装されたNeoLua[33]が存在する。
Luaを採用している製品
ゲーム
- Blue Mars
- CRYSIS[34]
- Far Cry
- Factorio
- From the depths
- Garry's MOD
- Lost Wind
- RagnarokOnline[35]
- Roblox
- Stormworks
- Xenepic Online Revo
- ソニック・ザ・ヘッジホッグ (2006年のゲーム)
- ソニック ワールドアドベンチャー(北米版のタイトルはSonic Unleashed)
- ティアーズ・トゥ・ティアラ 花冠の大地
- THE IDOLM@STER 2
- Warhammer Online:Age of Reckoning
- World of Warcraft[34]
- カンパニー・オブ・ヒーローズ
- GRAVITY DAZE
- ニンテンドークラシックミニ ファミリーコンピュータ (エミュレータシステム kachikachi のゲーム選択画面で使用)
- PHANTASY STAR ONLINE 2
- ファイナルファンタジーXIV[36]
- ドラゴンクエストX[37]
ゲーム以外
- 3DMLWプラグイン
- Adobe Photoshop Lightroom[34]
- Aegisub
- Anime Studio
- Apache mod_lua
- Asterisk extensions.lua
- AutoTouch[38]
- AviUtl
- Computercraft - MinecraftのMOD
- FlashAir - 無線LAN機能を搭載した東芝製SDHCメモリーカード[39]
- FLOW - 設定に使われている[40]
- FreeBSD - ブートローダのUI等を実装[41]
- LuaTeX
- MediaWiki(Scribunto拡張により)
- MySQL Proxy
- Nginx
- nmap
- OpenResty[42]
- OpenWrt
- osm2pgsql - OpenStreetMapのデータをPostGISに読み込むユーティリティ[43]
- Premake
- Redis
- RigidChips
- Renoise
- Strata 3D
- Tachyon
- TileMan[44]
- ヤマハのルータ製品 - RTXシリーズ(5000/3500/1300/1220/1210/1200/830/810) NVRシリーズ、FWX120、SRT100[45]
- Vim - ビルド時に有効化することで拡張スクリプト内でLuaを使用できる、派生のNeovimではLuaJITが内部的に利用されており組み込まれている
- VLC Media Player
- VOCALOID - ユーザー側で編集用スクリプトを自作できる(VOCALOID3〜4のみ)
- Synthesizer V - 同上。JavaScriptにも対応
- Wireshark[46]