close
コンテンツにスキップ

Wikipedia:Lua

2013年3月から追加されたMediaWikiの拡張機能「Scribunto」の実装により、ウィキペディア日本語版ではプログラミング言語Luaを利用したスクリプトを作成することが可能になりました。主にテンプレートの機能を向上させる目的で使用され、Luaのソースコードテンプレートに組み込んで、{{#invoke:}}という構文を使って呼び出すことができます。Scribunto拡張は2022年3月時点でLua 5.1.5をサポートしています。

Luaのソースコードは、モジュール空間(例:モジュール:Bananas)に置かれます。そして、各モジュールを、テンプレートとして別なページから呼び出せるようになっています(例えば、モジュール:Bananas/docでは、{{#invoke:Bananas|hello}}と書くことで、「Hello, world!」という出力を得ています)。

モジュールを実行する

[編集]

ウィキペディアの一般的なウィキページでモジュールを実行するには#invokeというパーサー関数(→Help:マジックワード)を使用します。#invokeの書き方はテンプレートのそれと似ているが、若干違うところがあります。すなわち、#invokeでは「関数名」を指定する必要があります。関数とは入力を取得して処理を行い、出力を返す一連の命令であり[注 1]、テンプレートと似ていますが、1つのページには1つのテンプレートしか定義できないのに対し、1つのLuaモジュールには複数の関数を定義できます。

また、Luaモジュールを直接実行することはできず、必ずモジュールの関数を1つ選んで実行しなければなりません。モジュールは関数のコンテナにすぎず、それ自体は何もしません。そのため、関数名を指定する必要があります。

ウィキページでモジュールを実行するには{{#invoke:モジュール名|関数名}}のように書きます。

例えば、モジュール:Bananas"hello"関数を実行するためには下記のように書きます。

{{#invoke:Bananas|hello}}

プレビューをクリックしてください。

Hello, world!

Lua学習用コンソール

[編集]
  1. モジュール:Bananasで、既存のコードを変更せずにソースを編集をクリックします。
  2. ページ下部のDebug consoleまでスクロールします。
  3. 任意のLua文を貼り付け、Enterキー「 Enter」を一度押して実行します。
print(p.hello()) -- Enterキー(↲)を一度押して実行します。
Hello, world!
  1. を押して、以前のLuaコマンドを順に表示してください。
  2. Luaエディターが開いているこのウィキページの先頭までスクロールしてください。
  3. キャンセルをクリックしてください。
  4. モジュール:Bananasは未変更です。

引数を渡す

[編集]

モジュールに引数を渡すときの書き方はテンプレートに引数を渡す書き方と同じです。しかし、1つ目のパイプ"|"の後ろに書くのは関数名なので、1つ目の名無し引数は2つ目のパイプの後ろに書かれます: {{#invoke:モジュール名|関数名|1つ目の名無し引数|2つ目の名無し引数|引数名=引数の値}}

例えば、モジュール:BananasArgs"hello"関数では1つ目の名無し引数の指定によって出力が違います。

  • {{#invoke:BananasArgs|hello|Kate}}Hello, Kate!
  • {{#invoke:BananasArgs|hello|Fred}}Hello, Fred!
  1. モジュール:BananasArgsで、既存のコードを変更せずにソースを編集をクリックします。
  2. ページ下部のDebug consoleまでスクロールします。
  3. 任意のLua文を貼り付け、Enterキー「 Enter」を一度押して実行します。
print(p.hello_world()) -- No arguments
frame = mw.getCurrentFrame(); frame.args[1] = "Kate"; print(p.hello(frame)) -- One argument
Hello, world!
Hello, Kate!

BananaArgsの"count_fruit"関数ではbananasapples引数を受け入れます。

  • {{#invoke:BananasArgs|count_fruit|apples=3|bananas=4}}I have 4 bananas and 3 apples
  • {{#invoke:BananasArgs|count_fruit|bananas=5|apples=2}}I have 5 bananas and 2 apples
frame = mw.getCurrentFrame()
frame.args["apples"] = "3"; frame.args["bananas"] = "4"; print(p.count_fruit(frame)) -- Named args
frame.args["bananas"] = "5"; frame.args["apples"] = "2"; print(p.count_fruit(frame)) -- Switch
I have 4 bananas and 3 apples
I have 5 bananas and 2 apples

多くのモジュールには解説サブページがあり、使える引数とその効果を説明します。

サンプルモジュール

[編集]

Luaスクリプトの作成依頼

[編集]

ウィキペディア日本語版で特定のタスクを行うためにLuaスクリプトが必要になった場合、プロジェクト:ウィキ技術部にて依頼を提出することができます。

導入までの経緯

[編集]

Luaは長年にわたる議論を経て、2012年にテスト用としてtest2.wikipedia.orgに導入され、すべての編集者に試験運用を勧める知らせが出されました(2012年8月のお知らせを参照)。その後、Luaはウィキメディアのテストウィキとmediawiki.orgでの試験運用を経て、2013年2月にウィキペディア英語版に導入され、3月にはウィキペディア日本語版に導入されました(2013年3月のお知らせを参照)。

Luaについて

[編集]

Luaはデータの解析、式の演算、文字の書式化などを、オブジェクト指向として行えるスクリプト言語です。言語自体は理解しやすいようシンプルになっていますが、表、動的関数、添字に文字列を使える連想配列など、複雑な構造も使えるようになっています。再帰のような複雑な構造もとれますが、いたずらに複雑なモジュールを作って、第三者が誰も理解できない、ということがないように注意してください。

以下に、モジュール:HelloWorldにあります、Hello, World!プログラムのソースコードを示します。

-- WikipediaのLuaモジュールは、外部からアクセスできる関数を入れる変数の
-- 宣言から始める必要があります。この変数の名前は何でもよく、変数にデータを入れることもできます。
local p = {}; -- mandatory package table: the only variable with single letter

-- そして、変数に関数を追加します。そうすることで、Wikipediaから#invokeして呼び出せるようになります。
-- frame変数には、呼び出し時にWikipediaから渡されるデータが入ります。
p.hello = function( frame ) -- the anonymous function expression assigned to p.hello means function p.hello(frame)

    -- ローカル変数を宣言して"Hello World!"に初期化
    local str = "Hello World!"

    -- str変数の中身をWikipedia側に戻して関数を脱出します。
    -- printのような関数は使えないので、出力はこのように文字列の返り値でやり取りします。
    return str

-- hello関数の終わり
end

-- モジュールの終わりでは、関数を含んだ変数をWikipedia側に返します。
return p -- returns the package table

-- こうすれば、{{#invoke: HelloWorld | hello }}のような形でこのモジュールを使えます。
-- #invokeでは、直後にモジュール自体の名前、次の引数に関数名を渡します。

MediaWikiでLuaを使う上でのガイドが、mw:Extension:Scribunto/Lua reference manual/jaにあります(翻訳途中)。

ユニットテスト

[編集]

ウィキペディアにおけるLuaスクリプトのユニットテストを行うためのフレームワークはモジュール:UnitTestsにあります。このフレームワークでは特定の入力でスクリプトを実行し、出力が予想値と同じであるかを確認できます。ユニットテストはスクリプトの変更により生じる問題を早期に発見できます。

慣例では特定のモジュール(例: モジュール:Bananas)のユニットテストはtestcasesサブページ(例: モジュール:Bananas/testcases)に置かれ、モジュール‐ノート:Bananas/testcases{{#invoke: Bananas/testcases | run_tests}}のように実行します。テスト用メソッドの名前は必ず"test"で始まります。下記にModule:Bananas/testcasesより簡単な例を1つ記します。

-- [[Module:Bananas]]のユニットテスト例です。ノートをクリックして動作確認をしてください。
local p = require('Module:UnitTests')

function p:test_hello()
    self:preprocess_equals('{{#invoke:Bananas | hello}}', 'Hello, world!')
end

return p

ユニットテストが作成されたモジュールの一覧は特別:リンク元/モジュール:UnitTestsをご参照ください。

Wikipedia固有のポイント

[編集]

概要

[編集]

Luaの入力としては、{{#invoke:}}で渡されたテキストの引数と、frameにある関数群だけがウィキペディアから情報を得るために使えます。そして、Luaからの出力では、テンプレートなど{{...}}で書くようなものを含むことはできません。さらに、Luaの実行時間は10秒以内という制限もあります(ページのソースを見れば、処理時間を確認できます)。また、通常のLuaから削ってある関数もいろいろとあります(mw:Extension:Scribunto/Lua reference manual#Differences from standard Luaに詳細があります)。

Luaへの入力

[編集]

ScribuntoにおけるLuaの実行は、ページのパース時に行われるので、ページに書き込んだ情報以外を使うこと、たとえば入力ボックスを作って入力させるとか、マウスの位置を検出するとかといったことは不可能です。Luaの入力としては、他のページをトランスクルードすることはできますが、SVGファイルを含めメディアファイルやカテゴリの中身、特別ページの内容は取得できません。

ウィキテキスト

[編集]

いわゆるパイプの裏技[[Wikipedia:ヘルプ|]])は、Luaの出力中では適用されないので、[[Wikipedia:ヘルプ|ヘルプ]]のように省略しない形で書く必要があります。また、~~~~が署名へ展開されることもありません。別なテンプレートの呼び出し、マジックワード、条件文など{{...}}で書くものや、<ref><nowiki>といった、MediaWiki側で処理するタグも、Luaの出力中では処理されません。

モジュールのインポート

[編集]

モジュール名前空間にある別のLuaスクリプトを使いたい場合はrequire()を使います。

extern_hello=require("モジュール:HelloWorld")としてモジュールを読み込んだ上で、extern_hello.hello()のように呼び出します。

Luaを使用するテンプレート

[編集]

Luaを使用するテンプレートの解説サブページには{{Lua}}テンプレートを追加してください。Luaが使用されていることを明示できます。

注釈

[編集]
  1. ウィキページで実行する関数では出力を1つだけ返すことが大半です。出力を2つ以上返す関数は一般的にはウィキページで実行することを想定しておらず、ほかのモジュールや引数で実行することを想定しています。

関連項目

[編集]

ウィキペディア内のページ

[編集]

MediaWikiにおける解説ページ

[編集]
print(mw.getCurrentFrame():callParserFunction("#language", "ja"))

デバッグコンソールはUnicode対応です。

日本語
local frame = mw.getCurrentFrame()
local candidates = {
  {"#language", "ja"},                     -- 出力言語を 'ja' に設定する(#language パーサ関数)
  {"#tag", {'isbn', '978-4-87311-471-2'}}, -- <isbn>タグを生成する(#tag パーサ関数に複数引数を渡す例)
  {"plural", {'42', 'その本がある。', 'それらの本がある。'}},  -- 数に応じて文を切り替える(plural パーサ関数の例)
  {"canonicalurl", "カテゴリ:User ja-N"},  -- ページの正規 URL を返す(canonicalurl パーサ関数)
}

local function args_to_string(obj)
  if type(obj) == "table" then return table.concat(obj, " ") else return tostring(obj or "") end
end

for _, pair in ipairs(candidates) do -- _ = インデックス(無視)、pair = 値;ipairs は数値キーを順に反復
  local fn, arg = pair[1], pair[2] -- 呼び出すパーサ関数名とその引数(文字列または引数リストの表)
  local ok, res
  if type(arg) == "table" then
    -- 引数が表なら複数引数として unpack して渡す
    ok, res = pcall(function() return frame:callParserFunction(fn, unpack(arg)) end)
  else
    -- 引数が単一値(文字列)ならそのまま渡す
    ok, res = pcall(function() return frame:callParserFunction(fn, arg or "") end) -- 安全呼び出し(pcall)
  end
  local out = ok and tostring(res) or ("<error>") -- 成功なら文字列化、失敗なら "<error>"
  -- 引数表は見やすく結合して表示。出力は Lua コンソール用(ウィキ本文に出す場合は return するなど調整)
  print(string.format('%s(%q) -> %s', fn, args_to_string(arg), out))
end
#language("ja") -> 日本語
#tag("isbn 978-4-87311-471-2") -> <isbn>978-4-87311-471-2</isbn>
plural("42 その本がある。 それらの本がある。") -> それらの本がある。
canonicalurl("カテゴリ:User ja-N") -> https://ja.wikipedia.org/wiki/Category:User_ja-N