lobject

まず、 lobject.h を見ると、ここにはたくさんの型定義が書かれている。
lua のデータオブジェクトを表現するのは次の型宣言。

typedef struct lua_TObject {
  int tt;
  Value value;
} TObject;

TObject は「タグつきオブジェクト」の意。 tt がタグ、 Value は union 型で、いろんな型を表現している。 Value は次のとおり。

typedef union {
  GCObject *gc;
  void *p;
  lua_Number n;
  int b;
} Value;

b はたぶん真理値、nが数値、pは小さなユーザデータ、GCObjectは Collectable Object。 GCObject の定義じたいは lstate.h にあり、

union GCObject {
  GCheader gch;
  union TString ts;
  union Udata u;
  union Closure cl;
  struct Table h;
  struct Proto p;
  struct UpVal uv;
  struct lua_State th;  /* thread */
};

となっている。 ts が文字列、 u はユーザデータ、 cl はクロージャ、 h はハッシュテーブル th がスレッド。あとはよくわからない。追々見ていくことにしよう。

lobject.h には、型チェックや値をセットするためのマクロがたくさん定義されているがこれも略。

lobject.c に定義されている関数は用途が不明なので後で使うときに紹介する方がいいだろうと思う。

lua のファイル

さて、言語の詳細をあんまり詳しく解説することは目的ではないので次に進む。lua の実装にはどれくらいのプログラムが関わっているかというと次のようになっている(このほかに lua コマンド実装の lua/* と luac コマンド実装の luac/*、各種ライブラリの lib/* がある)。

lapi, lcode, ldebug, ldo.c, ldump, lfunc, lgc, llex, llimits, lmem, lobject, lopcodes, lparser, lstate, lstring, ltable, ltests, ltm, lundump, lvm, lzio

分類すると次のようになるか。

各種データ構造とその管理

lobject
基本的なデータオブジェクト(?)
lstring
文字列
ltable
テーブル
lfunc
クロージャ
ltm
タグつきメソッドの解釈(?)
lmem
メモリ管理
lgc
ガベコレ

VM実装とプログラムの実行

lvm
VM
lstate
VMの状態(?)
ldo
バイトコード実行

プログラムとバイトコード

lopcodes
バイトコードの詳細
lcode
バイトコードジェネレータ
ldump
バイトコードの保存
lundump
バイトコードの復元
llex
レキサ
lparser
パーサ

lapi
lua API の記述
ldebug
デバッガ?
ltests
デバッガ用の各種のlua内部のイベントの処理
llimits
lua専用の値の制限記述
lzio
入出力ライブラリ

上から順番に見るべきだろう。クロージャは後回しかも。

コルーチン

ところで Lua の言語的な特徴は「なんでもテーブル」ということかと思ったら、あとコルーチンもあることを知る。

コルーチンを作るのには、

  1. 関数を用意し、
  2. coroutine.create でコルーチン化し、
  3. coroutine.resume で実行、
  4. コルーチン内では coroutine.yield で実行を中断し、呼出し元に戻る

という手順(?)になる。

function co(x)
  local v
  while true do
    x = x + 1
    v = coroutine.yield(x)
    print("coroutine\n", x, v)
  end
end

f = coroutine.create(co)
print("main routine\n", coroutine.resume(f, 0))
print("main routine\n", coroutine.resume(f, 1))
print("main routine\n", coroutine.resume(f, 2))
print("main routine\n", coroutine.resume(f, 3))

yield でコルーチンは停止し、 resume に戻る。 resume はコルーチンが正常終了、または yield すると true が返るが、二番目の要素以降に return の返り値、ないしは yield の引数が与えられる。また、 yield したものを resume すると、そのときの引数は yield の返り値になる。ちなみに、終了しちゃったコルーチンを resume するとエラーになる。

といったところか。

ローカルスコープについて

luaの挙動の話は前回でおしまいの予定だったが、補足が2件ほどあった。ひとつめがローカルスコープと関数の扱い。

local 宣言について先週は「そこで新たにローカルスコープを作る」というタイプの表現をしたが、どちらかというと、「ある種の操作では自動的にローカルスコープが作られる」「local宣言でローカルスコープに変数を追加する」とする方がより素直な理解なのかも(実装はこれから)。

具体的な「ローカルスコープを積むもの」は、

  • do 〜 end
  • 関数

ところで、「local宣言されない初出の変数は自動的にグローバルな領域の変数とみなす」という限定がある。このため、たとえば、

function acm()
  x = 0
  return(function () x = x + 1; return x; end)
end  

のようにアキュムレータを作ると、

inc = acm()
print(inc())  ---> 0
print(inc())  ---> 1
x = 10
print(inc())  ---> 11

のようになる。これを避けるには明示的に local 宣言をする。

function acm()
  local x = 0
  return(function () x = x + 1; return x; end, function () x = x - 1; return x; end)
end

Lua の wiki

2chLua スレを見ていたら、 Lua など、C言語に組み込んで使うタイプのスクリプト言語wiki が立ち上がった。 http://wikiwiki.jp/lua/

っていってもまだ何もないみたい。

この文章は2週目にして飽き気味。三日坊主は避けたい。

変数のスコープ

レキシカルスコープ

a = 1
function f(x, y)
  return(x+a, y+a)
end
print(f(1, 2)) # => 2, 3
a = 2
print(f(1, 2)) # => 3, 4

また、 local 宣言でローカルスコープを積むことができる。ただしローカルスコープの終端が決定できないので、積むときには

do
local a = 1
end
print(a)  # => nil

などとする。

なお、グローバルスコープ全体を表現するテーブル _G というのがある。これを使うと、 local 宣言したものは(終端がわからないとしても)ローカルなスコープでしかないことが確かめられる。

a = 1
for i, j in _G do
  print(i, j)  # a がある
end
local b = 1
for i, j in _G do
  print(i, j)  # a はあるが b はない
end
local a = 2
print(a)    # => 2
print(_G.a) # => 1

制御構文

while ループ。

  while 条件 do
    ブロック
  end

until ループ

  repeat
    ブロック
  until 条件;

for ループ

  for i = 0, 10, 2 do
    print(i);
  end

0が初期値、10が終端値、2がステップ。ステップを省略すると1。テーブルにアクセスする場合には、

  for i, v in ary do
    print(i, v);
  end

i に添字、 v に値が入る。数値だけを順番に取り出す場合には、

  for i, v in ipairs(ary) do
    print(i, v);
  end

と書く。

breakがある。 continue に相当するものはない。