概要。
これらの特別なトークン、、、、、はすべて、[
実行可能ファイル名としてスキャナーから出力され]
ます。とは、マークタイプオブジェクトを生成するように定義されています(したがって、これらはそれ自体は演算子ではありませんが、すべての演算子が存在する場所で定義された実行可能名です)。およびは、他のプロシージャまたは演算子と同じように実行されるプロシージャまたは演算子として定義されます。これらは、演算子を使用して開き角かっこを見つけます。ただし、これらのトークンはすべてスキャナーによって特別に処理されます。スキャナーは、区切り文字セットの一部であるため、空白を囲まずにトークンを認識します。<<
>>
[
<<
systemdict
]
>>
counttomark
詳細。
それはあなたがそれを見るときすべてに依存します。インタプリタがこれらのトークンで何をするかを追跡してみましょう。これを文字列で説明しますが、ファイルでも同じように機能します。
したがって、入力文字列がある場合
([4 5 6]) cvx exec
cvx
リテラルオブジェクトを実行可能にします。プログラムストリームは、実行可能ファイルというラベルの付いたファイルオブジェクトです。exec
オブジェクトを実行スタックにプッシュします。このオブジェクトは、内部インタープリター処理ループの次の反復でインタープリターによって検出されます。プログラムストリームを実行する場合、実行可能ファイルオブジェクトは実行スタックの最上位にあります。
インタプリタはtoken
スキャナーを呼び出すために使用します。スキャナーは最初の空白をスキップし、次の区切り文字までのすべての非空白文字を読み取り、文字列を数値として解釈しようとしますが、実行可能ファイル名になることに失敗します。角かっこは一連の区切り文字の一部であるため、「自己区切り」と呼ばれます。したがって、スキャナーは1つの角かっこ文字を読み取り、区切り文字であるため読み取りを停止し、数値にすることはできないことを検出して、実行可能ファイルの名前を生成します。
Top of Exec Stack | Operand Stack
(4 5 6]) [ |
次に、インタプリタループは実行可能ファイルを実行します(配列でない場合)。トークンを実行するということは、辞書からトークンをロードし、実行可能であれば定義を実行することを意味します。名前が定義されているのと同じように[
、オブジェクトとして定義されています。技術的には演算子やプロシージャではなく、単なる定義です。自動ロードは、実行可能フラグが設定された状態で名前がスキャナーから出力されるために発生します。-mark-
mark
(4 5 6]) | -mark-
次に、スキャナーは4、5、および6を生成します。これらは数値であり、オペランドスタックに直接プッシュされます。]
6は、ストリームにプッシュバックされるによって区切られます。
(]) | -mark- 4 5 6
実行可能ではないため、インタプリタは数値を実行しませんが、実行した場合はまったく同じになります。数値を実行するためのアクションは、単にそれをスタックにプッシュすることです。
次に、最後にスキャナーが右角かっこに遭遇します]
。そして、それが魔法が起こる場所です。自己区切りで、その後に空白を続ける必要はありません。スキャナーは実行可能ファイル名]
を生成し、インタープリターはロードしてそれを実行し、...を見つけます。
{ counttomark array astore exch pop }
あるいは、これを行う実際の演算子かもしれません。しかし、ええ。counttomark
要素の数を生成します。array
そのサイズの配列を作成します。astore
スタックからの要素で配列を埋めます。そしてexch pop
、その厄介なマークを完全に破棄します。
辞書の場合、<<
はとまったく同じ[
です。マークを落とします。次に、いくつかのキーと値のペアを並べ>>
ます。これは、何かを実行する手順です...
{ counttomark dup dict begin 2 idiv { def } repeat pop currentdict end }
辞書を作る。すべてのペアを定義します。マークをポップします。辞書を作成します。このバージョンのプロシージャは、ダブルサイズにすることで高速辞書を作成しようとします。2 idiv
前に移動しdup
て、小さな辞書を作成します。
したがって、哲学的になるために、counttomark
あなたが使用している演算子です。また、他には使用されない特別なオブジェクトタイプであるmarktypeオブジェクトが必要-mark-
です。残りは、線形データ構造を作成するためのこのスタックカウント機能にアクセスできるようにするための単なる構文上の砂糖です。
付録
からのインタープリターループの読み取りをモデル化する手順を次に示しますcurrentfile
。
{currentfile token not {exit} if dup type /arraytype ne {exec} if }loop
exec
load
実行可能ファイル名を実行(およびさらに実行)する責任があります。token
これから、実際にはスキャナーの名前であることがわかります。インタプリタループが直接遭遇するプロシージャ(配列)は実行されません( type /arraytype ne {exec} if
)。
ただし、文字列で使用すると、非常token
に優れた機能を実行できます。たとえば、名前を置き換えてプロシージャ本体を動的に作成できます。これはLispマクロに非常によく似ています。
/makeadder { % n . { n add }
1 dict begin
/n exch def
({//n add}) token % () {n add} true
pop exch pop % {n add}
end
} def
token
文字列からプロシージャ全体を読み取り、すぐに評価された名前 //n
を現在定義されている値に置き換えます。ここで、スキャナーが実行可能配列を一度に読み取り、戻る前に内部で効果的に実行していることに注意してください([
私] cvx
のような特定のインタープリターではxpost
、配列が構築されているため、スタックサイズの制限をバイパスして配列を構築できますただし、レベル2のガベージコレクションでは、これはほとんど関係ありません)。
bind
演算子名を演算子オブジェクト自体に置き換えることによってプロシージャを変更する演算子もあります。これらのトリックは、速度が重要な手順(内部ループなど)で名前の検索を除外するのに役立ちます。