目次

Xtalプログラムを実行する

Xtalを実行できる環境を作る

公式ドキュメントの「C++組み込みサンプル>Xtalの初期化」から持ってきただけです。

少なくともこのページではこれをベースに解説したいと思います。

#include <xtal.h>
#include <xtal_macro.h> // Xidなど便利なマクロが定義されている
 
#include <xtal_lib/xtal_cstdiostream.h>    // CStdioStdStreamLibのため
#include <xtal_lib/xtal_winthread.h>       // WinThreadLibのため
#include <xtal_lib/xtal_winfilesystem.h>   // WinFilesystemLibのため
#include <xtal_lib/xtal_chcode.h>          // SJISChCodeLibのため
#include <xtal_lib/xtal_errormessage.h>    // bind_error_messageのため
 
void exec_xtal(){
    using namespace xtal;
    // ここでXtalの実行を行う
}
 
int main(int argc, char* argv[]){
    using namespace xtal;
 
    CStdioStdStreamLib std_stream_lib;     // std*はCの標準ライブラリを使う
    WinThreadLib thread_lib;               // Windowsのスレッドを使う
    WinFilesystemLib filesystem_lib;       // Windowsのファイルシステムを使う
    SJISChCodeLib ch_code_lib;             // SJISを使う
 
    // 環境依存である機能についてどれを使うかを設定
    // 他にもアロケータなども設定できるが割愛
    Setting setting; 
    setting.std_stream_lib = &std_stream_lib;
    setting.thread_lib = &thread_lib;
    setting.filesystem_lib = &filesystem_lib;
    setting.ch_code_lib = &ch_code_lib;
 
    // ここで指定したstd_stream_libなどのポインタが示す先のオブジェクトは、
    // uninitializeを呼び出すまで存在している必要がある。
 
    // Xtalを初期化
    initialize(setting);
 
    // エラーメッセージをバインド
    bind_error_message();
 
    exec_xtal();
 
    // Xtalを破棄
    uninitialize();
 
    return 0;
}

コードを細かく説明していきます。

#include <xtal.h>
#include <xtal_macro.h>

これらは、Xtalを使うファイルでは常にincludeしてしまってもかまわないと思います。

xtal.hはほぼ全てのクラス、関数を含んでいます。
xtal_macro.hは便利に使うためのマクロが多数定義されています。

#include <xtal_lib/xtal_cstdiostream.h>    // CStdioStdStreamLibのため
#include <xtal_lib/xtal_winthread.h>       // WinThreadLibのため
#include <xtal_lib/xtal_winfilesystem.h>   // WinFilesystemLibのため
#include <xtal_lib/xtal_chcode.h>          // SJISChCodeLibのため
#include <xtal_lib/xtal_errormessage.h>    // bind_error_messageのため

これらは、xtal::initializeを呼び出すファイルでのみincludeすれば十分です。

Xtalは環境に依存する部分(標準入出力やファイル入出力、スレッド)を設定したり、アロケータをカスタマイズすることができます。
特にいじることがなければわずらうことの無いようにWindows,posixそれぞれの標準的なものが*thread.hや*filesystem.hに定義されています。
これらは自分で定義することができ、例えば「独自パッキングファイルに対応したファイルマネージャ」のようなものを用いるFileIOを利用する、といったこともできます。

また、スクリプトの文字コードを設定することもでき、chcode.hにはそれぞれの設定用クラスが定義されています。

それらはxtal::initializeに渡すxtal::Setting構造体にポインタ渡しで登録して設定します。

    CStdioStdStreamLib std_stream_lib;     // std*はCの標準ライブラリを使う
    WinThreadLib thread_lib;               // Windowsのスレッドを使う
    WinFilesystemLib filesystem_lib;       // Windowsのファイルシステムを使う
    SJISChCodeLib ch_code_lib;             // SJISを使う
 
    // 環境依存である機能についてどれを使うかを設定
    // 他にもアロケータなども設定できるが割愛
    Setting setting; 
    setting.std_stream_lib = &std_stream_lib;
    setting.thread_lib = &thread_lib;
    setting.filesystem_lib = &filesystem_lib;
    setting.ch_code_lib = &ch_code_lib;
 
    // ここで指定したstd_stream_libなどのポインタが示す先のオブジェクトは、
    // uninitializeを呼び出すまで存在している必要がある。
 
    // Xtalを初期化
    initialize(setting);

実際に設定して初期化しています。

コメントにもあるように、uninitializeを呼び出すまではポインタの指す先が生きていなければなりません。mainの先頭で定義していればまず大丈夫ですし、staticにしておけばまあより大丈夫でしょう。

initialize→uninitializeをC++的RAIIにする場合は一緒に設定オブジェクトも管理してしまえば良いのではないでしょうか。

    // エラーメッセージをバインド
    bind_error_message();

これをやらないと、「XCE1001」みたいなエラーメッセージが表示されることになります。

やれば、「コンパイルエラー:存在しないメンバ_foo」みたいなエラーメッセージが表示されます。それだけです。

    // Xtalを破棄
    uninitialize();

initializeしたらuninitializeしないといけないのは当然ですね。

無事に成功すればXtalのオブジェクトが全て破棄されたことになります。

文字列のコンパイル・実行、例外の捕捉

スクリプトを実行できるようになったところで、まずは文字列を実行してみます。 ついでに例外を捕捉する方法もやってみましょう。

#include <xtal.h>
#include <xtal_macro.h> // Xidなど便利なマクロが定義されている
 
#include <xtal_lib/xtal_cstdiostream.h>    // CStdioStdStreamLibのため
#include <xtal_lib/xtal_winthread.h>       // WinThreadLibのため
#include <xtal_lib/xtal_winfilesystem.h>   // WinFilesystemLibのため
#include <xtal_lib/xtal_chcode.h>          // SJISChCodeLibのため
#include <xtal_lib/xtal_errormessage.h>    // bind_error_messageのため
 
void exec_xtal(){
    using namespace xtal;
    CodePtr pCode;
    // コンパイル
    pCode = compile("print(\"Hello, World!\");");
    if (pCode)    // コンパイルに失敗したときはxtal::nullが返る
        pCode->call();    // これが実行されるまでprintによる出力は無い
    XTAL_CATCH_EXCEPT(e){
        stderr_stream()->println(e);
    }
}
 
int main(int argc, char* argv[]){
    using namespace xtal;
 
    CStdioStdStreamLib std_stream_lib;     // std*はCの標準ライブラリを使う
    WinThreadLib thread_lib;               // Windowsのスレッドを使う
    WinFilesystemLib filesystem_lib;       // Windowsのファイルシステムを使う
    SJISChCodeLib ch_code_lib;             // SJISを使う
 
    // 環境依存である機能についてどれを使うかを設定
    // 他にもアロケータなども設定できるが割愛
    Setting setting; 
    setting.std_stream_lib = &std_stream_lib;
    setting.thread_lib = &thread_lib;
    setting.filesystem_lib = &filesystem_lib;
    setting.ch_code_lib = &ch_code_lib;
 
    // ここで指定したstd_stream_libなどのポインタが示す先のオブジェクトは、
    // uninitializeを呼び出すまで存在している必要がある。
 
    // Xtalを初期化
    initialize(setting);
 
    // エラーメッセージをバインド
    bind_error_message();
 
    exec_xtal();
 
    // Xtalを破棄
    uninitialize();
 
    return 0;
}

xtal::compileに文字列を渡すとコンパイルされてxtal::CodePtrが返されます。 Xtalは文字列をコンパイルすることによって、それなりに高速に実行することができる「バイトコード」というバイナリ列に変換します。それがCodePtrです。

xtal::compileを呼び出しただけでは実行されません。Code::callを呼び出すことで初めて実行されます。

また、XTAL_CATCH_EXCEPTマクロを使うと、Xtalの実行関連で例外が設定されていたときに、例外の内容を表す文字列eとともにブロックの中に入ります。コンパイルエラー、型の不整合、nullアクセス、などいろいろと考えられますが、今回はそれを表示するだけにとどめています。入力を待って再度読み込んで実行する、などとすることもできます。

ファイルの実行

いよいよスクリプトファイルの実行をしてみます。 難しいことはなくて、compileしていたところをcompile_fileにするだけです(もちろん引数も変わりますが)。

#include <xtal.h>
#include <xtal_macro.h> // Xidなど便利なマクロが定義されている
 
#include <xtal_lib/xtal_cstdiostream.h>    // CStdioStdStreamLibのため
#include <xtal_lib/xtal_winthread.h>       // WinThreadLibのため
#include <xtal_lib/xtal_winfilesystem.h>   // WinFilesystemLibのため
#include <xtal_lib/xtal_chcode.h>          // SJISChCodeLibのため
#include <xtal_lib/xtal_errormessage.h>    // bind_error_messageのため
 
void exec_xtal(){
    using namespace xtal;
    CodePtr pCode;
    // ファイルのコンパイル
    pCode = compile_file("test.xtal");
    if (pCode)    // コンパイルに失敗したときはxtal::nullが返る
        pCode->call();    // これが実行されるまでprintによる出力は無い
    XTAL_CATCH_EXCEPT(e){
        stderr_stream()->println(e);
    }
}
 
int main(int argc, char* argv[]){
    using namespace xtal;
 
    CStdioStdStreamLib std_stream_lib;     // std*はCの標準ライブラリを使う
    WinThreadLib thread_lib;               // Windowsのスレッドを使う
    WinFilesystemLib filesystem_lib;       // Windowsのファイルシステムを使う
    SJISChCodeLib ch_code_lib;             // SJISを使う
 
    // 環境依存である機能についてどれを使うかを設定
    // 他にもアロケータなども設定できるが割愛
    Setting setting; 
    setting.std_stream_lib = &std_stream_lib;
    setting.thread_lib = &thread_lib;
    setting.filesystem_lib = &filesystem_lib;
    setting.ch_code_lib = &ch_code_lib;
 
    // ここで指定したstd_stream_libなどのポインタが示す先のオブジェクトは、
    // uninitializeを呼び出すまで存在している必要がある。
 
    // Xtalを初期化
    initialize(setting);
 
    // エラーメッセージをバインド
    bind_error_message();
 
    exec_xtal();
 
    // Xtalを破棄
    uninitialize();
 
    return 0;
}
// test.xtal
println("Hello, World!");

なお、xtal::compile_file + xtal::callを一度にやってくれる関数も用意されていて、それはxtal::loadといいます。引数はファイル名です。とりあえずファイルを実行する、となったらこっちを使うのも十分にアリです。

    xtal::load("test.xtal");

ただし、filelocalにアクセスする手段がない、など細かい制御ができません。

プロジェクトの文字コードとファイルの文字コード

VC++はプロジェクトの設定で、使用する文字セットを「Unicode文字セット」、「マルチバイト文字セット」から選ぶことができますし、Xtalはinitializeのときの設定構造体によって使用する文字セットを変えられます。

「じゃあどんな組み合わせでもうまく動くのかー」と思いきや、そうではありません。

以下の表は、実際に実験してみたときにうまく動作したかどうかの表です。

プロジェクトの設定 SJIS JIS EUC UTF8 UTF16(No-BOM)1)
Unicode × × × ×
マルチバイト ×

○:正常動作 ×:スクリプトのコンパイルエラー △:コンパイルできたが出力が文字化け(localeの問題?)

1) UTFにはBOMというシグネチャをつけることができますが、Xtalはそれがついていると動作しません