class

bjam_grammarの精度を上げるにはmoduleとclassの実装が不可欠な気がしてきたので、bjamのclassについて調べてみました。


まずは単純な例です。

import "class" ;

class hoge
{
    # コンストラクタ
    rule __init__ ( arg )
    {
        self.name = $(arg) ;
    }

    rule hello ( )
    {
        ECHO hello $(self.name) ;
    }
}

local h = [ class.new hoge taro ] ;
$(h).hello ; # 「hello taro」を出力

クラスの定義はclassキーワードで行います。
クラスの実体はモジュールで、「class@クラス名」という名前になります。


クラスのインスタンス化もモジュールを作成することで行います。
インスタンス化を行うclass.newルールは、「objectクラス名@連番」というモジュールを組み込みルールINSTANCEでクラスと関連付け、__init__ルールを呼び出します。
メンバのルールは、このインスタンス用のモジュール内で動作するため、モジュールローカル変数がインスタンス変数の役割を果たします。
普通のモジュールと同じく、

import modules ;

ECHO [ modules.peek $(h) : self.name ] ; # 「taro」を出力

modules.poke $(h) : self.name : jiro ;
$(h).hello ; # 「jiro」を出力

のようにアクセスすることができます。
なお、インスタンス変数の名前が「self.」で始まっていますが、ただの慣例みたいで、「self.」をつけなくても問題ないようです。


次は継承です。

class base1
{
    rule __init__ ( )
    {
    }

    rule do_who ( )
    {
        return base1 ;
    }

    rule who ( )
    {
        return [ do_who ] ;
    }

    rule hello ( )
    {
        ECHO hello base1 ;
    }
}

class base2
{
    rule __init__ ( )
    {
    }

    rule hello ( )
    {
        ECHO hello base2 ;
    }
}

class derived : base1 base2
{
    rule __init__ ( )
    {
        base1.__init__ ;
        base2.__init__ ;
    }

    rule do_who ( )
    {
        return derived ;
    }
}

local d = [ class.new derived ] ;
ECHO [ $(d).who ] ; # 「derived」を出力
$(d).hello ; # 「hello base2」を出力

継承する場合は、クラス名の後ろを「:」で区切ってから基底クラスを並べます。
派生クラスのモジュールには基底クラスのルールがインポートされます。
派生クラスで基底クラスと同じ名前のルールを定義するとオーバーライドできますが、単にルールを再定義しているだけです。(たぶん)
基底クラスから継承するルール名が重複すると、後ろのクラスのものが優先されます。
これも基底クラスのルールを順にインポートしているからでしょう。
あと、基底クラスのコンストラクタ(__init__ルール)は自分で呼ぶ必要があります。


このようにbjamのクラスはモジュールの仕組みを利用しているので、モジュールさえ実装できれば比較的容易に実現できそうな感じです。
実装の方向で検討してみます。