ScenarioMod/TSMod用のLuaインタプリタ

  • 概要

    ここでは、ScenarioMod/TSModに搭載されているLuaインタプリタの特殊な機能について解説しています。

  • 機能の概要

    • Luaのコアのバージョン

      ScenarioMod/TSMod用のLuaインタプリタは、Lua version 5.1.5 をコアとしています。

      Lua 5.1.5 の標準文法については、原則利用可能です。

    • cp932(sjis)の文字コードに強い

      レガシーゲームである天翔記のMod用途に作られましたので、
      cp932(sjis)の文字コードで記述されたLuaファイルが取り扱えるようになっています。
      cp932の文字の勘定等をするための専用の関数なども取りそろっています。

    • 5.2への対応

      独自拡張を施しており、その中には、Lua 5.2向きに書かれたスクリプトを互換動作させるような修正も含まれます。
      Lua5.2の深層機能を利用していない限り、Lua5.2のスクリプトも極めて高い確率で動作します。

      但し、独自の機能としてcontinue文を追加した代わりに、Lua5.2には存在するgoto文がありませんので注意して下さい。

    • 5.3への対応

      Lua5.3にて導入されたビット演算等や、追加のLua関数も、そのほとんどが追加されています。
      よって、Lua5.3向けに記述されたスクリプトすらも、それなりに高い確率で動作します。

    • UTF8のBOM付ファイルを読み込み実行可能

      luaの5.1は本来UTF8のBOM付ファイルは読み込みができずエラーとなりますが、
      jLuaでは、Lua5.2以降同様実行が可能となっています。

  • 特別な追加機能

    • ModDebuggerへの出力機能

      • dprint(...) 関数

        luaのprint関数と同じですが、出力先が「ModDebugger」となります。

      • print(...) 関数

        こちらも出力先が「ModDebugger」となります。
        (jLua.exeに関しては、こちらは、標準出力となります)

    • 追加ライブラリ

      • lfs(LuaFileSystem)

        準標準ライブラリともいえるlfsライブラリを追加しています。

      • bit32

        Lua5.2の資産でも流用しやすいように、5.2と同一動作のbit32ライブラリを追加しています。

      • cp932(sjis)用の関数

        stringライブラリやutf8ライブラリのcp932版として、
        (cp932.len、cp932.upper、cp932.lower、cp932.reverse、cp932.sub、cp932.char、cp932.codepoint、cp932.codes、cp932.encode(文字列, "utf8"))
        が追加されています。

      • math系関数

        luaの5.3にて新たに加えられたmath関数を持っています。
        (math.type、 math.ult、math.mininteger、math.maxinteger、math.tointeger、math.round(x [, precision ])))

      • string系関数

        正規表現系のエンジン(string.match等)に関して、cp932(sjis)に適した対応がなされています。

        luaの5.3にて新たに加えられたstring関数を持っています。
        (string.pack、string.unpack)

      • 文字列エンコードを推測する関数

        string.getencoding(文字列)
        が追加されています。
        日本語系で、利用する可能性が高い「メジャーどころ」に絞った高速な判定です。

      • table系関数

        nilもしくは空のテーブルであることを判定するtable.emptyが追加されています。

        luaの5.2の機能に準ずる 2つの関数を搭載しています。
        (table.unpack / table.pack)

        luaの5.3にて新たに加えられたtable関数を持っています。
        (table.move)

      • utf8系関数

        luaの5.3にて加えられたutf8系ライブラリを全て持っています。
        (utf8.encode(文字列, "cp932") )が特別に追加されています。

    • Lua5.2互換

      • load関数がlua5.2相当。

        luaの5.2に準ずるload関数を搭載しています。
        これにより、5.2準拠で記述されたload文を処理することができます。

      • rawlen の追加

        luaの5.2の機能に準ずる rawlen関数を搭載しています。
        __lenの振る舞いも、限りなく5.2へと準拠しています。

    • Lua5.3互換

      • ビット演算子

        Lua5.3 で実装された、ビット演算子(&,|,<<,>>,~)が実装されています。
        また、対応するメタメソッド(__band, __bor, __bxor, __bnot, __shl, __shr)も実装されています。

      • 整数除算

        Lua5.3 で実装された、整数とみなしての除算 (例: 10 // 3 ) が実装されています。
        また、対応するメタメソッド(__idiv)も実装されています。

    • 独自文法拡張

      • continue 文

        標準のluaには存在しない、continue文を持っています。

      • C風の「!=」比較演算子

        luaでは「~=」と記述しますが、独特ですので、多くの言語で採用されている「!=」も利用可能としています。

      • C風の「+=」「-=」「*=」「/=」「%=」「^=」の6つの代入演算子

        C風の「+=」「-=」「*=」「/=」「%=」「^=」の6つの代入演算子が使えます。
        これは簡単な演算の際、ソースコードを結構短くできるはずです。

      • 2進数表記

        16進数の「0xF0」 などと同様に、「0b10101」の2進数表記を受け付けます。

      • テーブルの中に、「代入型ではなく定義型でメソッド」を記述できる

        この場合、最初のパラメータは、暗黙で「self」となります。

        通常、luaのメソッド定義スタイルは

            tbl = {}
            function tbl:my_method(x, y) end
            
        もしくは
            tbl = {
                my_method = function(self, x, y) end
            }
            

        ですが、通常の関数と同じスタイルで、

             tbl = {
                function my_method(x, y) end
            }
            

        と、記述することが出来ます。
        この場合、function内には、暗黙の第1引数として「self」が存在します。

      • __usedindex の追加

        __newindexの逆の意味合いとなる__usedindexを持ちます。
        これは、値を代入しようとする度に実行されますので、読み取り専用テーブルを作成するのに役立ちます。

        function table.const(tbl)
            -- テーブルの要素を
            for k, v in pairs(tbl) do
                -- 要素がテーブルなら再帰
                if type(v) == "table" then
                    table.const(v)
                end
            end
        
            return setmetatable(tbl, {
             __usedindex = function(table, key, value)
                               dprint("警告!! このテーブルは読み取り専用です。\n")
                               dprint( string.format("\tキー:%s, 代入しようとした値:%s\n", tostring(key), tostring(value) ) )
                           end,
             __newindex  = function(table, key, value)
                               dprint("警告!! このテーブルは読み取り専用です。\n")
                               dprint( string.format("\tキー:%s, 代入しようとした値:%s\n", tostring(key), tostring(value) ) )
                           end,
           });
        end
        
        local mytable = table.const{ 1,2, a=3, b={1,2} }
        
        mytable[2] = 10 -- 直接の子要素も読み取り専用
        mytable.b[1] = 30 -- ネストのテーブル要素も読み取り専用となる。
        
      • __iter の追加

        テーブルにおいて、イテレータ関数を指定できます。

        local mytbl = {1,2,3}
        setmetatable(mytbl,{__iter=ipair})
        
        -- すでにイテレータが指定されているので、ipair関数を使う必要がない。もちろんipairがpairを使っても良い。
        for k, v in mytbl do
            dprint(k, v)
        end
        
      • classを完結に記述することが可能
        class 'Person'
        {
            __init__ = function(self, name)
                self.name = name
            end;
        
            say = function(self)
                print('Hello, my name is ' .. self.name .. '.')
                self:saySthElse()
            end;
        
            saySthElse = function(self)
            end
        }
        
        class 'Worker'
        {
            function __init__(id)
                self.id = id
            end;
        
            function showId()
                print('My worker id is ' .. self.id .. '.')
            end
        }
        
        class 'Employee: Person, Worker'
        {
            function __init__(name, salary, id)
                Person.__init__(self, name)
                Worker.__init__(self, id)
                self.salary = salary
            end;
        
            function saySthElse()
                print('My salary is ' .. self.salary .. '.')
            end
        }
        
        
        
        p = cls.Person('Bob')
        p:say()
        
        p2 = cls.Person('David')
        p2:say()
        
        print '--------------------------------------------'
        
        e = cls.Employee('Bob', 1000, 1)
        e:say()
        e:showId()
        
        e2 = cls.Employee('Alice', 10000, 2)
        e2:say()
        e2:showId()
        
        print '--------------------------------------------'
        
        if is_instance_of(e, cls.Person) then
            print 'e is an instance of Person'
        else
            print 'e is not an instance of Person'
        end
        
        if is_instance_of(e, cls.Worker) then
            print 'e is an instance of Worker'
        else
            print 'e is not an instance of Worker'
        end
        
        w = cls.Worker(100)
        
        if is_instance_of(w, cls.Person) then
            print 'w is an instance of Person'
        else
            print 'w is not an instance of Person'
        end
        
        if is_instance_of(w, cls.Worker) then
            print 'w is an instance of Worker'
        else
            print 'w is not an instance of Worker'
        end