FORTH:自己定義の言葉

同じタイプの十分に多数の変数が使用されるFort言語のプロジェクトがあるとします。

たとえば、x、y、z、x '、y'、z '、x' '、y' '、z' '、s、mなど...

それらを決定するには、毎回VARIABLEという単語を書き出す必要がありますが、これは面倒で退屈でいです。 もっと楽しくする方法はありますか?

さらに変数が定義されることを示し、名前を書き込みます。

次のようなもの:

変数:
   xyzx 'y' z '
   x '' y '' z ''
   sm
 ;変数






SP-Forthに実装されたFort言語インタープリターは、コンテキストディクショナリで単語が見つからない場合、実行するのと同じコンテキストディクショナリで単語NOTFOUNDを検索します。 NOTFOUND入力パラメーターは、入力ストリームからのサブストリングのアドレスとカウンターです。 したがって、必要なことを行うようにNOTFOUNDを再定義する必要があります。



何が必要ですか?

これが見つからない単語を取り、変数として現在の辞書の先頭にコンパイルします。 定義を思い出す

  :可変作成0 、;


ただし、CREATEという単語自体が入力ストリームから次の単語を選択するため、スタック上のアドレスとカウンターを含む行から辞書エントリを作成する必要があります。 この場合、幸いなことにCREATEDという単語があります。この単語は、スタックからアドレスと行カウンタを取得し、辞書エントリを作成します。 残念ながら、NOTFOUNDと同様に、標準のANSI-94単語セットの一部ではありません。

このように

 :NOTFOUND(addr u-)CREATED 0 ,; 


しかし、そのような定義を基本FORTHリストに入れると、数字を入力できなくなります。 そのため、他のコンテキストではこの新しいNOTFOUNDを非表示にする必要があります。 変数辞書を取得しましょう。

語彙変数 


そしてそれを最新にします。

また、変数の定義


NOTFOUNDの定義をそこに置く



現在のコンテキストを返します

以前の定義




したがって、単語VARIABLES:は、コンテキストを変数に切り替え、必要なNOTFOUNDを使用可能にします。

 :変数:また、変数;


最後に、VARIABLESはコンテキストを返します。 当然、変数のコンテキスト内にある必要があります。



つまり、合計:

語彙変数 
また、変数の定義

 :NOTFOUND(addr u-)CREATED 0 ,; 
 :;前の変数; 

以前の定義

 :変数:また、変数;


そのため、わずか4行で、SP-Forthインタープリターを拡張し、変数の説明を簡略化しました。

ただし、VALUE変数、定数、および一般的な実行セマンティクスを持つ一般的な単語には、同様のアプローチを使用できます。 定義語を使用して定義された語。 原則として、定義する単語のペアがあると便利です。 単一の定義用に1つ、グループ定義用にペアになります。 実際、定義語は、共通のセマンティクスを持つ単語のグループを作成できるようにするために作成されます。 また、これらの定義がテキスト全体に分散されておらず、1つのブロックにまとめられていると便利です。



これをVALUE変数に実装してみましょう。

語彙の値
また、値の定義
 :見つかりません...


そして、ここでいくつかの問題に遭遇します。 修飾子ワードVALUEは、CREATEを介して定義されていません。 次のように定義されます。

 :VALUE  
       ヘッダー
        ['] _CONSTANT-CODE COMPILE 、、、
        ['] _TOVALUE-CODE COMPILE、
 ;


幸いなことに、入力ストリームから文字列を取得するHEADERという単語には、SHEADERという形式のペアがあります。これはCREATEDという単語と同義です。

一方を他方に置き換えて、必要なバージョンの単語を取得するだけです。

 :VALUED(n addr u ---)
      ヘッダー
       ['] _CONSTANT-CODE COMPILE 、、、
       ['] _TOVALUE-CODE COMPILE、
 ;




だから:

語彙の値
また、値の定義

 :;前回のドロップ;
 :NOTFOUND VALUED 0;

以前の定義 
  
 :値:また値0;


ただし、欠点が1つあります。 すべてのVALUEはゼロに初期化されます。 これをなくすといいでしょう。

実装にはいくつかのオプションがあります。

簡単に記録できます

値:
   11 AA   
   22 bb 
   33 cc
 ;値


これは判読できません。



このように書きましょう:

値:
    aa = 11
    bb = 22
    cc = 33 
 ;値


きれいに見えます。



明らかに、値のコンテキストに「等しい」という単語が存在している必要があります。 次の単語を選択し、数字として解釈する必要があります。 つまり、リテラルとほぼ同義語です。 別の「等しい」は、この値を最後に定義されたVALUE変数に割り当てます。



書く

語彙の値
また、値の定義

 :;前回のドロップ;
 := BL WORD?リテラルの最新名> 9 + EXECUTE;
 :NOTFOUND VALUED 0;

以前の定義 
  
 :値:また値0;




そのようなオプション

値:
   11からaa   
   22からbb 
   33からcc
 ;値
言語のパラダイムから外れることなく、さらに、計算された値でVALUE変数を初期化できるという点で価値があります。

値:
        11からaa   
   22 1980 * bbへ 
   aa bb + cc
 ;値


実装するために、NOTFOUNDを再定義する必要はありません。 TOという単語の意味のみが変更されます。 VALUES:ターミネーター単語間:; VALUES TOは通常のVALUEのように動作する必要があります。

語彙の値
また、値の定義

 :;前の値;
 :値へ;

以前の定義 
  
 :値:また値;




定数についても同様の記述方法を作成できます。

定数:
        11 IS AA   
   22 1980 * IS bb 
   aa bb + IS cc
 ;定数


このメソッドの実装は明らかだと思います。



一般に、このキャンペーンは新しいタイプの定義語-グループ定義語を形成します。 単純な定義語を使用すると、一般的なセマンティクスによって結合された語を作成できます。 同じ特性を持つグループは、ソーステキストの一部に同じ種類の単語の定義を集中する必要があります。 読みやすさと伴奏に良い影響を与えます。

SP-SP-Forthへのさらに楽しい追加は、WINAPI:という単語のグループ実装です。 特に、Winctlライブラリでは、WINAPI定義がテキスト全体に散らばっており、暗く見えます。

オプションとして:

 WINAPIS:
     LIB:USER32.DLL
              PostQuitMessage
              PostMessageA
              SetActiveWindow
     LIB:GDI32.DLL
              CreateFontA
              GetDeviceCaps
              DeleteDC
     LIB:COMCTL32.DLL
              InitCommonControlsEx
 ; WINAPIS


これを行うには、WINAPIという単語がどのように実装されているかを見てください。

spf_win_defwords.f


 :__WIN:(params "ProcedureName" "LibraryName"-)
  こちら> R
   0、\ winprocのアドレス
   0、\ライブラリ名のアドレス
   0、\関数名のアドレス
   、パラメーターの\#
   IS-TEMP-WL 0 =
   IF
    こちら、WINAPLINK @、WINAPLINK!  (通信)
  その後
  こちらDUP R @ CELL + CELL +!
  パース名の文字はこちらスワップ重複割り当て0 C、\関数名
  こちらDUP R>セル+!
   PARSE-NAME CHARS HERE SWAP DUP ALLOT MOVE 0 C、\ライブラリ名
   LoadLibraryA DUP 0 = IF -2009 THROW THEN \ ABORT「ライブラリが見つかりません」
   GetProcAddress 0 = IF -2010 THROW THEN \ ABORT「プロシージャが見つかりません」
 ;

 :WINAPI:( "プロシージャ名" "ライブラリ名"-)
   (WIN32プロシージャをインポートするために使用されます。
    結果の定義には、ProcedureNameという名前が付けられます。
     winprocフィールドのアドレスは、最初に入力されます
    結果の辞書エントリの実行。
    受信した「インポート」プロシージャパラメータを呼び出すには
    逆の順序でデータスタックにプッシュ
    このプロシージャのC呼び出しで。 機能結果
    スタックにプッシュされます。
   )
   NEW-WINAPI?
  ヘッダーの場合
  その他
     -1
     > IN @ HEADER> IN!
  その後
   ['] _WINAPI-CODE COMPILE、
   __WIN:
 ;




どうやらDLLの遅延ロードが実装されているようです。 WinAPI呼び出しコードへのリンクは、インポートされた関数の名前、いくつかのパラメーター、ライブラリファイルとその中のプロシージャの名前を含む辞書エントリにコンパイルされます。 次に、そのようなファイルとそのような手順が存在します。

このコードを私たちの希望に合わせて作り直すために、各単語が何をするかを決定します。

; WINAPIS-コンテキストを復元するだけです。

LIB:-入力ストリームから次の単語を入力し、一時バッファに保存します。 検証と組み合わせることができます。

残りの単語は、プロシージャの名前として認識されます。



だから:

stack.fへの文字列


 SP @ VALUE spstore 
 :sp-to-sp sp @ TO spstore;
 :sp-restore spstore SP!  ;
 :s-allot(nバイト-addr)sp-save spstore SWAP-ALIGNED DUP> R CELL- CELL- SP!  R>;
 :ss(-addr u)NextWord 2> RR @ s-allot DUP DUP R @ + 0!  2R >> R SWAP R @ CMOVE R>;
 :s-free spstore CELL + SP!  ;
 :3DUP 2 PICK 2 PICK 2 PICK;




winapis.f


語彙winlib
また、winlibsの定義

 :; WINAPIS s-free前;

 :LIB:(-addr u id)s-free ss CR OVER LoadLibraryA DUP 0 = IF -2009 THROW THEN;

 :NOTFOUND(addr u id addr u-addr u id) 
           2> R 3DUP 2R>    
           2DUPヘッダー
           ['] _WINAPI-CODE COMPILE、 
          こちら> R  
           0、\ winprocのアドレス
           0、\ライブラリ名のアドレス 
           0、\関数名のアドレス
           -1、パラメーターの数
           IS-TEMP-WL 0 =
                      IF
                        こちら、WINAPLINK @、WINAPLINK!  (通信)
                     その後 
              こちらDUP R @ CELL + CELL +!  > R 
                CHARS HERE SWAP DUP ALLOT MOVE 0 C、R> \関数名
              こちらR> CELL +!  2> R  
                 CHARS HERE SWAP DUP ALLOT MOVE 0 C、2R> \ライブラリ名 
               SWAP GetProcAddress 0 = IF -2010 THROW THEN \ ABORT「プロシージャが見つかりません」
 ;

 以前の定義

 :WINAPIS:sp-save 1 2 3またwinlibs; 











All Articles