Dots

ブログとか

プログラミング言語Lua


⚠️ この記事はだいたいAIが書きました。

📝 はじめに

Luaは、軽量で高速な組み込み特化型スクリプト言語。

他の言語(C、Rust、Go等)に簡単に組み込めることが最大の特徴で、設定ファイル・プラグイ実行結果:

$ luajit ex4.lua
Hello, world!

DSL(Domain Specific Language)の実装

メタテーブルを活用して、設定ファイル用の独自文法を作成:

目的: 設定ファイルを書きやすくするために 「単語 + 波括弧」 を独自キーワードとして受け付ける
手順: “未定義変数への代入” を横取りしてキーと値を収集するクなどに広く使われている。

Luaの特徴:

🛠️ Lua環境の構築

Luaの開発環境構築は各OSで以下の手順で行える:

OS インストールコマンド 動作確認
macOS brew install lua lua -v (バージョン表示)
lua -e 'print(2+2)' (計算テスト)
Linux (Debian/Ubuntu) sudo apt-get install lua5.4 同上
Linux (Fedora/RHEL) sudo dnf install lua 同上
Windows LuaBinaries から 64-bit ZIP をダウンロード
② 展開先を PATH に追加
lua -v (コマンドプロンプトで実行)

🔧 他言語への組み込み

C言語からLuaを呼び出す

/* main.c  : gcc main.c -llua -lm -ldl */
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int main(void) {
  lua_State *L = luaL_newstate();   /* VM 作成 */
  luaL_openlibs(L);                 /* 標準ライブラリ */

  luaL_dostring(L, "print('C→Lua OK')");  /* スクリプト実行 */

  lua_getglobal(L, "math");         /* Lua テーブル取得 */
  lua_getfield(L, -1, "sqrt");      /*   math.sqrt 関数 */
  lua_pushnumber(L, 2.0);           /* 引数 */
  lua_call(L, 1, 1);                /* 呼び出し */
  double r = lua_tonumber(L, -1);
  printf("sqrt(2) = %.5f\n", r);    /* Lua→C 戻り値 */

  lua_close(L);
  return 0;
}
# Makefile for C Lua example
LUA_CFLAGS  := $(shell pkg-config --cflags lua5.4)
LUA_LDFLAGS := $(shell pkg-config --libs   lua5.4)

main: main.c
	$(CC) $< $(LUA_CFLAGS) $(LUA_LDFLAGS) -o $@

実行結果:

$ make
cc main.c -I/opt/homebrew/include/lua -L/opt/homebrew/lib -llua -lm -o main
$ ./main
C→Lua OK
sqrt(2) = 1.41421

RustからLuaを呼び出す

Rustから Lua を呼び出すためのライブラリ mlua を使った例:

プロジェクト作成:

$ cargo new rslua
    Creating binary (application) `rslua` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
$ cd rslua
$ cargo add mlua --features=lua54
// --- main.rs ---  Rust から Lua を呼び出す最小例
use mlua::{Lua, Result};

fn main() -> Result<()> {
    let lua = Lua::new();
    
    // Rust関数をLuaに登録
    let rust_function = lua.create_function(|_, name: String| {
        Ok(format!("Hello from Rust, {}!", name))
    })?;
    lua.globals().set("rust_greet", rust_function)?;
    
    // Lua スクリプトをその場で eval
    lua.load(r#"
        function lua_function(name) 
            return "Hello, " .. name 
        end
        
        -- Rust関数を呼び出し
        print(rust_greet("Lua"))
        
        -- Lua関数の戻り値をRustに返す
        return lua_function("World")
    "#).exec()?;

    // Lua関数を呼び出してRustで結果を取得
    let greet: mlua::Function = lua.globals().get("lua_function")?;
    let result: String = greet.call("Rust")?;
    println!("From Lua: {}", result);
    
    Ok(())
}

$ cargo run
  Downloaded ...(省略)...
  Compiling ...(省略)...
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.59s
     Running `target/debug/rslua`
Hello, world!

💡 Luaの基本機能とサンプルコード

テーブル(配列 + 連想配列)の活用

------------------------------------------------
-- Lua の「配列 + 連想配列 = テーブル」を確認
------------------------------------------------
local tbl = {x = 10, 20, 30}         -- x=10 は“キー付き”、20,30 は“添字 1,2”

print("-- 個別アクセス ------------------")
print("tbl.x   =", tbl.x)             --> 10
print("tbl[1]  =", tbl[1])            --> 20
print("tbl[2]  =", tbl[2])            --> 30

print("-- 配列部分を for で列挙 ---------")
for i = 1, #tbl do                    -- #tbl は「配列長」= 2
  print(i, tbl[i])                    -- 1 20 / 2 30
end

実行結果:

$ lua ex1.lua
-- 個別アクセス ------------------
tbl.x   =       10
tbl[1]  =       20
tbl[2]  =       30
-- 配列部分を for で列挙 ---------
1       20
2       30

メタテーブルによる演算子オーバーロード

メタテーブルを使えば、カスタムオブジェクトに演算子を定義できる:

------------------------------------------------
-- 2D ベクトルを「構造体 + 演算子」で実装
------------------------------------------------
local Vec = {}        -- ★クラス本体(メソッド置き場)
Vec.__index = Vec     -- ★メソッド検索はここを見る

function Vec.new(x, y)         -- ★“コンストラクタ”
  return setmetatable({x=x, y=y}, Vec)
end

function Vec.__add(a, b)       -- ★加算 a+b
  return Vec.new(a.x+b.x, a.y+b.y)
end

function Vec.__mul(v, s)       -- ★スカラー積 v*s
  return Vec.new(v.x*s, v.y*s)
end

-- ▼デモ
local v = (Vec.new(1,2) + Vec.new(3,4)) * 0.5
print(v.x, v.y)                --> 2   3

実行結果:

$ lua ex2.lua
2.0     3.0

コルーチンによる協調マルチタスク

Luaのコルーチン機能を使って疑似並列処理を実装:

------------------------------------------------
-- 2 タスクを 1 スレッドでインターリーブ実行
------------------------------------------------
local function makeTask(name, ticks)
  return coroutine.create(function()
    print("start", name)
    for _=1, ticks do coroutine.yield() end   -- ★擬似スリープ
    print("done",  name)
  end)
end

local tasks = {makeTask("A",3), makeTask("B",2)}

while #tasks > 0 do
  for i = #tasks, 1, -1 do
    local alive = coroutine.resume(tasks[i]) -- true:まだ動く / false:終了
    if not alive then table.remove(tasks, i) end
  end
end
-- 出力順: start A / start B / done B / done A

実行結果:

$ lua ex3.lua
start   B
start   A
done    B
done    A

LuaJIT FFIでC関数を直接呼び出し

LuaJITのFFI(Foreign Function Interface)を使ったC関数の直接呼び出し:

local ffi = require("ffi")

-- C の関数の宣言
ffi.cdef[[
  int printf(const char *fmt, ...);
]]

-- C の printf を呼び出す
ffi.C.printf("Hello, world!\n")

(出力)

$ luajit ex4.lua
Hello, world!

解説付き 5 行 DSL

目的 : 設定ファイルを書きやすくするために 「単語 + 波括弧」 を独自キーワードとして受け付ける。 手順 : “未定義変数への代入” を横取りしてキーと値を収集する。

-- ex5.lua
-----------------------------------------------------------
-- ★1 config: 解析後に格納されるテーブル
-- ★2 __index で未定義変数を関数として返す
-- ★3 設定ファイル内でのみDSL文法が有効
-----------------------------------------------------------
local config = {}

-- 設定文字列を解析
local function load_config_string(config_str)
  local env = setmetatable({}, {
    __index = function(_, key)
      return function(val)
        config[#config+1] = {key = key, value = val}
      end
    end
  })
  
  load(config_str, nil, "t", env)()
end

-- 設定内容( [[ ... ]] は、 `long string literal` )
load_config_string[[
host {"example.com"}
port {443}
route {"/api", "/status"}
]]

-- ★収集結果を表示
for _, item in ipairs(config) do
  print(item.key, table.concat(item.value, ","))  
end

文法についての説明

  • setmetatable / __index … Lua 標準 API/メタメソッド(予約済み)。
  • _G … Lua のグローバル環境テーブル(予約済み)。
  • host / port / route … 予約語ではなくユーザが自由に書く識別子

実行結果:

$ lua ex5.lua
host    example.com
port    443
route   /api,/status

🎯 Luaの実際のユースケース

分野 ユースケース例
ゲームエンジン AI・UI ロジックのホットリロード(Love2D, Defold, Unity + MoonSharp)
Web サーバー OpenResty で Nginx を Lua 拡張、高速 API Gateway を実装
DB スクリプト Redis EVAL でデータ集約・整合性チェックをサーバー側実行
組込み / IoT ファームの上に Lua VM を載せ、設定・機能追加をフィールドで更新

📋 まとめ

Luaは軽量性柔軟性を兼ね備えた優秀なスクリプト言語だ:

特に組み込み用途では、C APIの充実とVM自体の小ささが大きなアドバンテージとなっている。


この記事は ChatGPT o3 とClaude Sonnet 4 によって生成されました