[BACK]Return to prog1.txt CVS log [TXT][DIR] Up to [local] / OpenXM / src / k097 / Doc

File: [local] / OpenXM / src / k097 / Doc / prog1.txt (download)

Revision 1.2, Sat Jan 13 13:09:52 2001 UTC (23 years, 4 months ago) by takayama
Branch: MAIN
CVS Tags: R_1_3_1-2, RELEASE_1_3_1_13b, RELEASE_1_2_3_12, RELEASE_1_2_3, RELEASE_1_2_2_KNOPPIX_b, RELEASE_1_2_2_KNOPPIX, RELEASE_1_2_2, RELEASE_1_2_1, KNOPPIX_2006, HEAD, DEB_REL_1_2_3-9
Changes since 1.1: +10 -0 lines

Explanation on declaration of incetance variables and
generation of class incetances.


yacc/Doc/prog1.txt  (1997, 4/11, 4/17)
kan/k0 の内部構造とプログラミングに関する注意.

kan/k0 におけるクラスの取扱は C++ や Java に一見して似ているが,
実は全く違う.  むしろ, SmallTalk の方が近い.  

☆ クラスのインスタンス変数は class 宣言のすぐ後, 次のように宣言する.

   class  クラス名 extends スーパークラス名 {
      local インスタンス変数名1,
            インスタンス変数名2,
                ...
            インスタンス変数名n;
   
      メンバー関数定義
   }

   上の local 宣言を二つ以上書くとエラーになる.
   インスタンス変数名がないときは,
      local ;
   と宣言すること.

☆ インスタンス変数名は, そのクラス内での 局所変数名, メンバー関数引数名と
   して使えない.
 
   たとえば,
   class abc extends Object {
     local a,b;
     def foo(a) {

   は インスタンス変数名 a を引数として用いているのでエラーである.  
   現在のところ エラーメッセージを表示しないのでもっとたちがわるい.

☆ インスタンス変数名にたいする ピリオド . を用いた参照, 代入はできない.
    たとえば,  
          this.a 
    といった参照はできない.  (将来的には, できるようにしたい. )
    参照, 代入はかならず, メンバー関数を用いて行なう.

☆  生成子の書き方は, 次の形式に従うこと. (yacc/debug/graph.kk より)
   
    def new0(引数) {                   生成子名はなんでもいい.
      this = new();                    かならず, this = new(....) を書く.
                                       クラスの実体が PrimitiveObject の
                                       array として生成される.
      インスタンス変数の初期化
      return(this);                    かならず、this を戻す.
    }


    new() は, 上のように, new() とよんでもいいし,
        this = new(super.スーパークラスの生成子)
    と書いてもいい.
  ==> 現在この書き方は正しく機能しない. 2001, 1/13
      BUG: super は正しく動かないので使用しない方がよい.
      現在のクラスが class  Hoge extends Foo なら
        this = new( Foo.スーパークラスの生成子 )
      と生成すべし.  
      d0 で生成された sm1 のコードを参照.
      生成法:  cat object.kk ファイル | d0  

☆  class インスタンスの内部形式.

    class インスタンス は PrimitiveObject としては, array であり,
    その最初の要素は class タグである.
    インスタンス変数は, その array の要素である.

例えば,
   class Circle extends Object {
     local x,  /* 中心 */
           y,
           r;  /* 半径 */
     ;
   }

   class GraphicCircle extends Circle {
     local outline, fill;
     ;
   }

なる宣言をすると, Circle インスタンスは,
   [Circle, xの値, yの値, rの値]
なる array,   GraphicCircle インスタンスは,
   [GraphicCircle, xの値, yの値, rの値, outlineの値, fillの値]
なる array となる.
kan/k0 インタプリタは, たとえば,  x という変数があらわれると,
kan/sm1 の,  
       this 1 get  
なるコマンドを生成している.   インスタンス変数名と, その位置インデックス
の対応は, yacc/dic.c の関数で管理されている.

☆  extends により, 名前空間が, PrimitiveObject --- Object を頂点とする木構造に
なっている.  メソッドの検索は次のようにおこなわれる.

関数呼び出しは, 次のように翻訳される.
     aaa.foo(b,c)             aaa [b c] {foo} sendmsg2
     foo(b,c)                 this [b c] {foo} sendmsg2

すべての関数は二つの引数がスタックにあることを仮定している.
sendmsg2 は, 第一引数が, クラスのインスタンス であるばあい, 
名前空間 (コンテキスト)
をその クラス のものに変更して, 関数呼び出しをおこなう.
例えば, 上の例で, aaa が Graphic の インスタンスであると,
foo の実行は, Graphic の名前空間で実行される.
    

operator +,-,*,/ については次のように翻訳される.
    a + b                   a  b  {add} sendmsg2
sendmsg2 は, 第一引数または第2引数が, クラスのインスタンス であるばあい, 
名前空間 (コンテキスト)
をその クラス のものに変更して, 関数呼び出しをおこなう.
例:
   class Integer extends Object {
     local ival;
     def new0(i) {
        this = new();
        if (IsInteger(i)) ival = i;
        else {
           k00_error("new0","Argument must be integer."); sm1(" error ");
        }
        return(this);
     }
     def value() { return( ival ); }
     def operator add(b) {
       local r;
       r = Integer.new0(ival+b.value());
       return(r);
     }
   }
   
   a = Integer.new0(3);
   a + a :





なお, 上の4つ以外の,
<,>, = , == などの operator に関しては, override はできないことに注意.

    
  
☆  kan/k0 のエンジン部分は初期状態で, 4本のスタックと 一つのコンテキスト
   をもっている.

   Operand Stack  (標準のスタック, Kan/stackmachine.c では StandardStack)
   db.VariableStack                   
   db.ErrorStack
   db.DebugStack
   これらは var.sm1 で定義され,  debug/db.k で活用される.

   db.VariableStack は, 関数呼び出しのときの, 引数や 関数の局所変数を
   一次的に格納する stack である.

   db.DebugStack は, 関数呼び出しのとき, 現在実行中の関数を格納する
   stack である. --- engine error or interrupt :  in function xxxxx
   という表示は, このスタックの情報をもとにしている.
  
   db.ErrorStack は, engine エラーが積まれるスタックである.   


   コンテキストは, 
   PrimitiveContextp  (StandardContextp)
   が初期状態である.  incmac.sm1 を参照せよ.
   なお PrimitiveObject = [PrimitiveContextp]  である.


   SmallTalk のバイトコードマシンでは, コンテキストは, 名前辞書とスタック
   を合わせたものであるが, kan/k0 の場合, コンテキストとは, 名前辞書のことを
   さす.  名前辞書は名前とその値よりなる対応表である. 辞書はハッシュ法で  
   検索され, その辞書で値が見つからないと, その super 辞書へ検索が移行する.
   このような木構造での名前空間の実現については, Kan/stackmachine.c
   の  *userDictionary*  なる名前の関数を参照されたい.

☆ コンテキストスイッチについての注意.

   上で説明したような, 名前空間の動的な変更はエラー処理に関して混乱をひきお
   こす.  したがって, kan/k0 では, 次のような単純な, 解決法をとった.

   エンジンエラー,  ctrl-C の入力があった場合, 必ず CurrentContext を,
   PrimitiveContext へ戻す.
  
   この方針は別の問題を引き起こす.  たとえば, エラーが起きた時点の変数の
   値の検査が一般にできない.  いまのところ, kan/sm1 のコマンドをつかうしか
   ない.
   たとえば, class Abc でエラーが起きたとすると,
       sm1("Abc  0 get setcontext ");
   で, class Abc の名前空間に移行できる.
       sm1(" show_user_dictionary ") で現在の辞書に登録されている名前を
   みることができる.  また, 
        Println(変数名); 
   でその変数の値を検査できる.

   以上のような仕組みの問題点としては,
   Cleards();  による, 変数の値の回復ができないことをあげておく.
   (つまり現在の変数スタックは, どの名前空間かの情報をもっていないので、   
    変数の値の正しい回復ができない, また, context ごとに別のスタックを
    用意している訳でない.)
   したがって, class  を用いたプログラムは, エラーが起きたら, 再スタートするの
   が賢明である.

   
☆ Object については, 必ず概要, および, 実例を記述しておくこと.
   
   class を用いて書かれたプログラムは一般に読みにくい場合が多々ある.
   Object が中心にありプログラムをしているにもかかわらず, その Obeject の
   概要を理解するのがソースからはむづかしいことが多いのが, 
   その理由であると思われる.
   例えば, ??????????????   (考えること)