公開技術情報

[English] [Japanese]

02. Hello world

今回は、 LuneScript を使った Hello world の紹介です。

lnsc コマンド

LuneScript を導入すると、lnsc コマンドがインストールされます。

lnsc コマンドは次のように利用します。

$ lnsc src.lns exe

ここで src.lns は、 LuneScript で作成したスクリプトのパスです。 exe は lnsc のオプションで、 指定したスクリプトを実行することを意味します。

Hello world

では LuneScript を使って、伝統の Hello world を実行してみましょう。

次の内容を持つファイル hello.lns を作成してください。

// @lnsFront: ok
print( "Hello world." );

そして、次のコマンドを実行します。

$ lnsc hello.lns exe

これで "Hello world" が出力されました。

これだけだと面白くもなんともないので、もう少し話を続けます。

まずは、 hello.lns を次のように少し変更します。

// @lnsFront: ok
let txt = "world";
print( "Hello %s." ( txt ) );

このスクリプトの結果も Hello world. になります。

では、次のコマンドを実行してみてください。

$ lnsc hello.lns lua

次が出力されたと思います。

--hello.lns
local _moduleObj = {}
local __mod__ = 'hello'
if not _lune then
   _lune = {}
end
local txt = "world"
print( string.format( "Hello %s.", txt) )
return _moduleObj

これは hello.lns を Lua に変換したコードです。

なんだかゴチャゴチャしていますが、 print( string.format( "Hello %s.", txt) ) が出力されているのが分かると思います。

これは、 LuneScript で書いた print( "Hello %s." ( txt ) ) が、 Lua にトランスコンパイルする際に print( string.format( "Hello %s.", txt) ) に展開されていることを示します。

では、次のコマンドを実行してください。

$ lnsc hello.lns save

これによって、 hello.lua ファイルが作成されました。 hello.lua ファイルの内容は、先ほど出力した Lua のコードと同じものです。

では、次のコマンドで hello.lua を実行してください。

$ lua5.3 hello.lua

Hello world. が出力されているでしょう。 Lua にトランスコンパイルしたコードは、 LuneScript に依存しない Lua のコードになります。

最初に実行した lnsc hello.lns exe は、 LuneScript のスクリプトをトランスコンパイルし、実行まで行なうコマンドです。

次に実行した lnsc hello.lns lua は、 LuneScript のスクリプトをトランスコンパイルし、Lua コードを標準出力するコマンドです。

最後に実行した lnsc hello.lns save は、 LuneScript のスクリプトをトランスコンパイルし、Lua コードを保存するコマンドです。

このドキュメントでは exe を利用しつつ、 変換後のコードを確認する際は save コマンドを利用していきます。

Main 関数

main 関数を定義することで、コマンドラインオプションを処理できます。

以下を参照してください。

../shebang_main

エラーメッセージ

LuneScript では、 区切り記号 ; が必須です。 次のように ; を終端に入れていないとエラーになります。

// @lnsFront: error
print( "Hello world." )

このとき、以下のエラーメッセージが出力されます。

mini.lns:1:23: error: EOF
lua5.3: ./lune/base/Util.lua:176: has error
stack traceback:
	[C]: in function 'error'
	./lune/base/Util.lua:176: in function 'lune.base.Util.err'
	./lune/base/TransUnit.lua:3465: in method 'error'
	./lune/base/TransUnit.lua:3538: in method 'getToken'
	./lune/base/TransUnit.lua:11641: in method 'analyzeStatement'
	./lune/base/TransUnit.lua:3710: in method 'analyzeStatementList'
	./lune/base/TransUnit.lua:5430: in function <./lune/base/TransUnit.lua:5393>
	(...tail calls...)
	./lune/base/front.lua:848: in method 'loadFileToLuaCode'
	./lune/base/front.lua:914: in method 'loadFile'
	./lune/base/front.lua:1066: in method 'loadModule'
	./lune/base/front.lua:1709: in method 'exec'
	./lune/base/front.lua:1744: in function 'lune.base.front.exec'
	lune/base/base.lua:1: in main chunk
	[C]: in ?

このエラー出力において、次のメッセージがコンパイルエラーを示します。

mini.lns:1:23: error: EOF

このエラーは、 mini.lns の 1 行目の 23 バイト目で、 予期しない EOF エラーが発生したことを示しています。

これ以外のエラー出力は、 LuneScript 内部のエラーです。 LuneScript 内部のエラー出力を抑制するには、 次のオプション (diag –nodebug) を指定します。

$ lnsc hello.lns exe diag --nodebug
mini.lns:1:23: error: EOF
has error

現在のバージョンでは、デフォルトで内部エラー出力を抑制しています。

内部エラー出力を有効にする場合は –debug オプションを指定します。

ランタイム

ちょっと Hello world をネタにしている記事にしては重い内容ですが、 出力した Lua のコードを見たついでにランタイムについて説明します。

LuneScript から Lua に出力したコードには、 そのコードを動作させるために必要なランタイムが付加されます。

例えば、次の LuneScript のコードを Lua に変換すると、

// @lnsFront: ok
fn add( val:int! ):int {
   return 10 + unwrap val default 0;
}
print( add( 1 ) ); // 11
print( add( nil ) ); // 10

次のようになります。

--mini.lns
local _moduleObj = {}
local __mod__ = 'mini'
local _lune = {}
if _lune1 then
   _lune = _lune1
end
function _lune.unwrap( val )
   if val == nil then
      __luneScript:error( 'unwrap val is nil' )
   end
   return val
end
function _lune.unwrapDefault( val, defval )
   if val == nil then
      return defval
   end
   return val
end

if not _lune1 then
   _lune1 = _lune
end
local function add( val )

   return 10 + _lune.unwrapDefault( val, 0)
end

print( add( 1 ) )
print( add( nil ) )
return _moduleObj

そこそこの量のランタイムが出力されていることが分かると思います。 ちなみに、 local function add( val ) より上が、ランタイムです。

変換元の LuneScript のコードの内容によって挿入されるランタイムが増減します。 ランタイム全てを出力すると、サイズは約 10KB となっています。

このランタイムは、変換した全ての Lua コードに出力されます。

Lua コードにランタイムのコードが挿入されることが気になる場合、 lnsc のコマンドラインオプションに -r を指定することで、 次のようにランタイムの展開を require に置き換えることが出来ます。

--mini.lns
local _moduleObj = {}
local __mod__ = 'mini'
local _lune = require( "lune.base._lune1" )
if not _lune1 then
   _lune1 = _lune
end
local function add( val )

   return 10 + _lune.unwrapDefault( val, 0)
end

print( add( 1 ) )
print( add( nil ) )
return _moduleObj

ただしこの場合、 lune.base._lune1 を require することになるので、 lune.base._lune1 がロードできるようにロードパスを通しておく必要があります。

ここで、 _lune1 の 1 はランタイムのバージョンを示します。

Lua 版の LuneScript が動作している環境であれば気にする必要はないですが、 変換した Lua コードだけを別の環境で動かす場合は注意が必要です。

なお、 -r オプションの代わりに --runtime mod オプションを指定することで、

--mini.lns
local _moduleObj = {}
local __mod__ = 'mini'
local _lune = require( "mod" )
if not _lune1 then
   _lune1 = _lune
end
local function add( val )

   return 10 + _lune.unwrapDefault( val, 0)
end

print( add( 1 ) )
print( add( nil ) )
return _moduleObj

上記のように lune.base._lune をロードする代わりに、 指定の mod モジュールに切り替えることが出来ます。

LuneScript のバージョンが変わると、LuneScript のランタイムも変わることがあります。 もしも、異なるバージョンの LuneScript で変換した Lua モジュールが混在する場合、 デフォルトの lune.base._lune を使用すると正常に動作しないことがあります。

これを避けるために、 –runtime オプションを利用して、 意図しないバージョンのランタイムがロードされることを防止します。

なお、コマンドラインに -mklunemod path を指定することで、 指定の path にランタイムのモジュールファイルを生成します。

コメント

LuneScript におけるコメントは、 ///* */ です。

// は行末までをコメントとし、 /* */ は複数行をコメントとして扱います。

次回は LuneScript で扱う値について説明します。