Top
Photo Drawings Software Writing Reading Memo Study Profile Bookmark 
Amethyst DumLambda PetitLambda Yamanba JSharp JFlat OOPS yash KumonoIto ML Toys 

Yamanba


Yamanbaとは

JAIST入学を控え、UNIXプログラミングになれておかねばと考え、作って みた。
当初の学習目標は、
  • UNIXの操作になれる。
  • UNIX環境でのプログラミング
  • Yacc,Lexの習得
  • STLの習得
とりあえずVisual Basic互換のインタプリタを目指していたが、 組むうちに行き当たりばったり的にいろいろな機構を組み込んでいった。
結局、Visual Basic風の構文に、高階関数や部分適用、クラス継承、関数オーバロードなどを加えた言語となっている。
ひととおり動作したことで満足。
が、かなり遅い。
さらに、テンプレートをむやみに使用しているのでバイナリがやたらにでかい(1〜2MB)。 g++のあるオプションを指定すればテンプレートをひとつのオブジェクト ファイルにまとめられるが、そのためには使っているテンプレートを明示的にインスタ ンシエートしなければならない。でもいまさらそれはかなりめんどう。

「Yamanba」の名は、「やまとだにのBasic」の意だが、「Marimba」から の連想でもある(全然関係ないが)。JavaにDukeがいるように、ヤマンバメイクのコギャルを イメージキャラクタにできるかもと思った。

(この部分は修論完成後に追加)
修論で作成した関数型言語向けのバイトコードインタプリタを Yamanbaでも使おうかと計画中。

ソース

学校のWS(sparc-solaris 2.7)とRedhat Linux7.1J(Linux 2.4.2)で動作確認
yamanba-0.2.tar.gz
コンパイルするにはlibffiが必要です。
libffi-1.20.tar.gz

注意

算術オペレータや入出力関数など基本的なライブラリもネイティブモジュー ルで実装しており、yamanbaでプログラムを実行する際には、これらのラ イブラリの定義ファイルもyamanbaに与える必要がある。定義ファイルは libディレクトリ以下の*.basファイル。

autoconf & automakeを使ってGNU標準パッケージに仕立てみたが、 共有ライブラリの名前解決がうまくいかなくなった。

おもな特徴

  • 高階関数
  • 関数の部分適用
  • クラスの継承
  • インターフェイスのサポート
  • 関数のオーバロード
  • イベント機構のサポート(WithEvent、RaiseEvent)
  • C++で記述されたネイティブクラスとのやり取りが可能(MSのCOMと同じ ように、vtblを利用。)
  • ファイルを単位としたモジュール機構
  • Gtk、正規表現などのライブラリを用意

インストール

通常のGNUパッケージと同様の手順にしたがう。
$ ./configure
$ gmake
$ gmake install
環境変数GBLDPATHに、*.soをインストールしたディレクトリ名を設定する。
$ export GBLDPATH=/usr/local/lib
(yamanbaroot)/libの*.basファイルを適当なディレクトリにコピーする。
環境変数GBMODULEPATHに、そのディレクトリ名を設定する。このディレク トリに配置したファイル名をyamanbaに渡すときに、ディレクトリ名を指 定する必要がなくなる。
$ export GBMODULEPATH=...../lib

    

実行方法

yamanbaには組み込み関数は用意されていない。したがって、 いまのところ、起動時に必要なライブラリを記述したファイルを すべて渡す必要がある。ライブラリファイルはlib以下にある。
$ yamanba GBLib.bas Collection.bas testapp.bas
>

プログラム例

高階関数

Function sum(a As Integer,b As Integer,c As Integer) As Integer
   Return (a + b + c)
End Function

' 関数を返す関数
Function getFunc() As Function(Integer,Integer,Integer) As Integer
   ' オーバロードとの兼ね合いで、関数の型をいちいち指定しなければならない。
   Return sum As Function(Integer,Integer,Integer) As Integer
End Function

Function main() As Integer
   ' 関数を値に取る変数
   Dim f As Function(Integer,Integer,Integer) As Integer

   ' 関数を変数に代入して実行する。
   f = getFunc()
   Call Print("\n4 + 5 + 6 =")
   Call Print(f(4,5,6))
End Function

関数の部分適用

' 3つの引数をとる関数
Function sum(a As Integer,b As Integer,c As Integer) As Integer
   Return (a + b + c)
End Function

Function main() As Integer
   Dim f As Function(Integer,Integer,Integer) As Integer
   Dim f2 As Function(Integer,Integer) As Integer
   Dim f3 As Function(Integer) As Integer

   ' 関数を変数に代入する。
   f = sum As Function(Integer,Integer,Integer) As Integer

   ' 引数を一つだけ渡して新たな関数を得る。"{..}"を使用することに注意。
   f2 = f{1}
   Call Print("\n1 + 2 + 3 = ")
   Call Print(f2(2,3)) ' 残りの引数を渡して関数を実行する。

   ' 引数を二つ渡して別に新たな関数を得る。
   f3 = f{5,3}
   Call Print("\n5 + 3 + 1 = ")
   Call Print(f3(1))
End Function

関数の入れ子

Sub FuncWhile(cond As Function() As Boolean,act As Sub())
   While cond()
      Call act()
   Loop
End Sub

Function main() As Long
   Dim count As Long

   ' 入れ子になった関数
   Function cond() As Boolean
      ' 上位関数のローカル変数を参照している。
      If count > 10 Then
         count = 0
         Return False
      Else
         count = count + 1
         Return True
      End If
   End Function
   Sub act()
      Call PrintLn("Count = " + count)
   End Sub

   ' 入れ子の下位関数を渡す。
   Call FuncWhile(cond As Function() As Boolean,act As Sub())
End Function

C++で記述したクラスのインポート

NativeCls.C
// クラスのインターフェース
class NativeCls{
public:
  virtual SetLong(long l) = 0;
  virtual long GetLong() = 0;
  virtual ~NativeCls(){};
};

// 実装クラス
class NativeClsImpl:public NativeCls{
private:
  GBObject* obj;
  AbsClassType* cls;
  int indexOfValue;
  int indexOfEvent;
public:
  NativeClsImpl(){
    puts("NativeCls constructed");
  };
  virtual SetLong(long l){
    GetGBObject();

    printf("SetLong(%ld)\n",l);

    Variant val(l);
    obj->SetValue(indexOfValue,val);

    VariantList args;
    args.push_back(val);
    obj->RaiseEvent(indexOfEvent,&args);
  };
  virtual long GetLong(){
    Variant v = obj->GetValue(indexOfValue);
    printf("GetLong returns %d\n",v.GetLongVal());
    return v.GetLongVal();
  };
  virtual ~NativeClsImpl(){
    puts("NativeCls destructed");
  };
  GBObject* GetGBObject(){
    if(!obj)
      // このオブジェクトに対応するYamanbaオブジェクトを取得する。
      obj = Runtime::gRuntime->GetGBObjectByRawPtr(this);
    if(!cls){
      cls = obj->GetClass();
      indexOfValue = cls->GetFieldIndexByName("Value");
      indexOfEvent = cls->GetEventIndexByName("OnValueChange");
    }
    
    return obj;
  };
};

extern "C"{
NativeCls* CreateInstance(){
  NativeCls* obj = new NativeClsImpl();
  return obj;
};
void DestructInstance(NativeCls* obj){
  delete obj;
};
}

YamanbaからNativeClsを使用する
' C++で記述されるクラスのインターフェイスおよび実装ファイル名
Declare Class TestCls Constructor "CreateInstance" Destructor "DestructInstance" Lib "test/NativeCls.so"
   Dim Value As Long
   Event OnValueChange(val As Long) ' イベントも記述可能
   Public Sub SetLong(l As Long)
   Public Function GetLong() As Long
End Class

' C++で記述されたクラスを継承するYamanbaクラスを作成可能。
Class Child Extends TestCls
   ' オーバライド
   Public Function GetLong() As Long
      Return Value
   End Function
   Private Sub Class_Initialize()
      Call PrintLn("Child is created")
   End Sub
   Private Sub Class_Terminate()
      Call PrintLn("Child is terminate")
   End Sub
End Class

' イベントを受け取る側
Class Listener
   WithEvents Dim src As TestCls
   Private Sub src_OnValueChange(val As Long)
      Call PrintLn("Listener::src_OnValueChange(" + val + ")")
   End Sub
End Class

Function main() As Integer
   Dim c As TestCls
   Dim l As Listener

   ' どちらの方法でも生成可能
   c = (CreateObject("Child"))
#   c = New Child

   l = New Listener
   l.src = c
   Call c.SetLong(100)
   Call PrintLn("GetLong() = " + c.GetLong())
End Function