/ «2006-07-31 (Mon) ^ 2006-08-13 (Sun)» ?
   西田 亙の本:GNU 開発ツール -- hello.c から a.out が誕生するまで --

Categories Books | Hard | Hardware | Linux | MCU | Misc | Publish | Radio | Repository | Thoughts | Time | UNIX | Writing | プロフィール


2006-08-05 (Sat)

[Books] Programming in Lua

Programming in Lua 2nd Ed 自費出版にいたる過程で、ひとつのきっかけになってくれた書籍をご紹介します。

その本の名は、"Programming in Lua"。 現在、各界で注目を浴びつつある "組込型スクリプト言語Lua" の設計者 Roberto Ierusalimschy 氏による解説書です。2003年に第一版が出版され、今年2006年に第二版が発売されました(第一版はオンラインで公開中)。

Lua is my favorite extensible language

Lua の詳細についてはいつかご紹介したいと思いますが、私が心惹かれている特徴は以下の通りです。

  • コンパクトで見通しの良いソースツリー(version 5.1 は約17000行)
  • ANSI C compiler でビルド可能
  • 真の "組込型" スクリプト言語
  • C 言語との相性の良さ
  • 設計者の姿勢

コンパクトで見通しの良いソースツリー

ソースツリーは次に示す通り、サブディレクトリなどは存在せず、非常に見通しの良い設計になっています。個人が読み込めるサイズに収まっており、教材としても最適でしょう。

Makefile    ldblib.c  lgc.c       lmem.c      lparser.c  ltable.h   lualib.h
lapi.c      ldebug.c  lgc.h       lmem.h      lparser.h  ltablib.c  lundump.c
lapi.h      ldebug.h  linit.c     loadlib.c   lstate.c   ltm.c      lundump.h
lauxlib.c   ldo.c     liolib.c    lobject.c   lstate.h   ltm.h      lvm.c
lauxlib.h   ldo.h     llex.c      lobject.h   lstring.c  lua.c      lvm.h
lbaselib.c  ldump.c   llex.h      lopcodes.c  lstring.h  lua.h      lzio.c
lcode.c     lfunc.c   llimits.h   lopcodes.h  lstrlib.c  luac.c     lzio.h
lcode.h     lfunc.h   lmathlib.c  loslib.c    ltable.c   luaconf.h  print.c

ANSI C compiler でビルド可能

Lua は、ANSI C common subset に対応した C コンパイラであればビルドできるように設計されています(添付されている Makefile も基本的なルールのみを使用)。

GCC と glibc を前提に設計されたソフトウェアが世の趨勢となっている現在、このようなアプローチを取る開発者は天然記念物ものと言えます(古いところでは、D. J. Bernstein 氏 が同様の手法でソフトウェアを開発)。

真の "組込型" スクリプト言語

最近は "組み込み" というキーワードが大流行しているようですが、私が知る限り、Lua は "組み込み" の名を冠することができる、数少ない言語のひとつです。

Lua を make install すると、lua インタープリタを起動できるようになります。

$ lua
Lua 5.1  Copyright (C) 1994-2006 Lua.org, PUC-Rio
> print "Hello, world!"
Hello, world!

ここまではごく当たり前の光景なのですが、Lua が威力を発揮するのは、他言語に "組み込まれた” 場合です。次に示すのは、最も単純な Lua インタープリタを実現する tinylua.c ソースファイルです。

#include <stdio.h>     // stdin, stderr, fprintf(), fgets()
#include <string.h>    // strlen()
#include <lua.h>       // lua_pcall(), lua_pop(), lua_close()
#include <lauxlib.h>   // LuaL_newstate(), luaL_loadbuffer()
#include <lualib.h>    // luaL_openlibs()

int main() {
  char buff[ 256 ];
  int error;

  lua_State* L = luaL_newstate();
  luaL_openlibs(L);

  while (fgets(buff, sizeof(buff), stdin) != NULL) {
    error = luaL_loadbuffer(L, buff, strlen(buff), "line") ||
             lua_pcall(L, 0, 0, 0);
    if (error) {
      fprintf(stderr, "%s", lua_tostring(L, -1));
      lua_pop(L, 1);
     }
   }

  lua_close(L);
  return 0;
 }

"これでホンマに動くの?" というほど、短いソースリストから作成したオブジェクトファイルを Lua パッケージが提供するライブラリ liblua.a とリンクすることで、Tiny Lua インタープリタが出来上がります。

$ ./tinylua
print "Hello, world!"
Hello, world!
twoargs = function () return 123, 456 end
a, b = twoargs()
print (a)
123
print (b)
456

ホンマでした。まさに "わんだほー" です。既にお気づきの通り、Lua のご本尊はパッケージに添付されている lua インタープリタではなく、liblua.a ライブラリです。lua インタープリタは、おまけに過ぎません。

文法は単純で応用がきき、コンパクトで高速。このような小回りのきくインタープリタを自在にホスト言語から操ることが出来る点が、Lua の最大の特徴と言えるでしょう。"embeddable and extensible language" と呼ばれるにふさわしい特徴を兼ね備えています。既に述べたように、開発ツールを選ばない点とソースツリーの見通しの良さも、Lua の移植性と応用性をさらに高めています。

重厚長大なスクリプト言語で、このような芸当が果たして可能でしょうか?

C 言語との相性の良さ

Lua の内部構造は、設計者らによる "The implementation of Lua 5.0" が良い道しるべとなります。同論文によると、Lua インタープリタが内部で使用している仮想機械は、レジスタベースと記述されています。

ところが、ソースリストを読むと、確かに opcode はレジスタインデックス表記になっているのですが、仮想機械内部ではスタック領域をレジスタファイルとして扱っています。この点は "看板に偽りあり" の部分であり、私自身しばらく悩まされましたので、ご注意ください。

ということで、C 言語から Lua を使いこなすためには、スタックマシンの操作が必要になります。要するに昔なつかしの、"push and pop" ですね。Lua API の勉強がてら、以前このページでも紹介した RDTSC 命令を Lua ライブラリとして実装してみました。

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

#define read_tsc(v) asm volatile ("rdtsc" : "=A" (v))

static int read(lua_State* L) {
  unsigned long long tsc;
  lua_Number dx, ax;

  read_tsc(tsc);
  ax = (lua_Number) (tsc & 0xFFFFFFFFLL);
  dx = (lua_Number) (tsc >> 32);
  lua_pushnumber(L, dx);
  lua_pushnumber(L, ax);
  return 2;
 }

static const struct luaL_Reg functions[] = {
  { "read", read},
  { NULL, NULL }
 };

int luaopen_libtsc(lua_State* L) {
  luaL_register(L, "libtsc", functions);
  return 1;
 }

RDTSC 命令で読み込んだ64ビット値を High word と Low word に分離して、Lua お得意の "Multiple results" で返しています(lua_pushnumber 関数に注目)。

このソースファイルから共有オブジェクトを作成すると、直ちに Lua インタープリタから利用可能になります。

$ lua
Lua 5.1  Copyright (C) 1994-2006 Lua.org, PUC-Rio
> tsc = require("libtsc")
> print (tsc.read())
1680870 2218560536
> print (tsc.read())
1680872 1678474712

High word と Low word がスペースで区切られて表示されています。C 言語とスタック好きには、たまらない API であります。

このように素晴らしい環境を手にすると、思わず "世界は我が掌中にあり" と胸が高鳴るのですが、本当に Lua world を自分の手で実現してしまった人達がいます。

Lua worlds

ひとつめは、その名も PSP Lua。なんと、Playstation Portable に、Lua 開発環境が移植されています。Lua Player を通じて、様々なゲームが開発されていますが、その一部はギャラリーで閲覧できます。Hello, world! など、サンプル画面と実際のコードを見比べてみると "あぶない世界" の雰囲気を十分味わうことができます。

ふたつめは、Renesas Valuable Lua Terminal (ReVaLuaTe)。 "Renesas M16 組み込みマイコンに Lua 開発環境を移植してしまいました" という、ルーマニアのマイコン野郎 Bogdan Marinescu氏の作品です。これはマイコン野郎の聖典、Circuit Cellar 誌が主催するコンテストへの参加作品であり、アブストラクトはこちら。病気度はこちらが上ですが、"組み込み Lua" を具現化した貴重な資料です。

このような夢を簡単に実現できるのも、ひとえに Lua の設計の良さと、優れたドキュメント環境、そして真の意味で portable なソースコードのおかげであると言えるでしょう。

設計者の姿勢

Lua のサイトやドキュメントを見ていてつくづく感心させられることは、無駄がなく、それでいて必要なことはさりげなく記述されている点です。

例えば、Lua のリファレンスマニュアルはこちらから参照できますが、過去のバージョンに対応するすべてのマニュアルがリストアップされています。何気ないことではありますが、Lua 言語の歴史を記録に残し後続者に伝えようという意志と、従来のユーザに対する配慮が読み取れます。

Lua の世界は、良い意味での academicism に溢れているように思います。ライブラリ環境は必要最低限のものしか提供されていませんが、現代の "Bloated software" からは学ぶことのできない、多くのものを Lua は教えてくれることでしょう。

Despite the limited marketing, this avenue brings several benefits.

最後に、今回私が自費出版を決意するきっかけのひとつとなった第二版の序章から一部をご紹介しておきます。

After the release of the first edition of Programming in Lua, several publishers
contacted us showing interest in a second edition. In the end, however, we decided to
self publish second edition, as we did with the first one.

Despite the limited marketing, this avenue brings several benefits:
  • We have total control over the book contents.
  • We have freedom to choose when to release another edition.
  • We can ensure that the book does not go out of print.
  • We keep the full rights to offer the book in other forms.

大変勇気づけられました、Lua community に感謝します。