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