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 関数
エラーメッセージ
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 で扱う値について説明します。