Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

アホが書き散らすFactor入門 その3

書いていてなんだか意味がわからなくなってきたので今回からはテーマを決め少し短めにしようと思います。
今回は処理部、Quotation構成です。

Quotationの構成

Quotation([ ] で囲まれている部)は上から順に評価されます。
評価されるものとして大別して

  1. シンボル
  2. 複合文
  3. primitive
  4. undefined
  5. wrapper

に分けられます。
今回はそれらについて簡単に見ることにします。

変数

Factorはスタック指向です。
そのため例えばwordへ引数を渡たす、wordから返り値をもらうにはスタックに積む、とりだす事になります。
これはローカル変数でも同じです。
Factorの場合はローカル変数を定義なんてせず、そのままスタックに積めばいいだけなのです。
ですがwordをまたいで値を扱いたい場合などスタックだけではかなり不便です。
そのような場合は無理にスタックを使わず変数を使います。
もちろんFactorでも変数は定義できます。
一般的にはシンボルを使うようです。

シンボル

シンボルは実行されると自分自身をスタックに積みます。
変数値を持ちSYMBOL:を使って定義します。

SYMBOL: foo
foo .
foo

値を代入、取り出すにはget、setを使います。
getで値が取れない場合をfを返します。

SYMBOL: foo
1 foo set
foo get
1

その他に変数をいじるショートカット的なwordもあります。

  1. on tをセット
  2. off fをセット
  3. inc インクリメントしてセット
  4. dec デクリメントしてセット
  5. change スタックを操作するQuoteを渡し、それに基づきセット

get、setはシンボル以外にも扱えます。
(variableならなんでもいい)

1 "x" set
2 "y" set

"x" get
1
"y" get
2

シンボルと名前空間

シンボルは自身をスタックに積みますが、シンボル自体はどこのスタックに積まれている、あるいは辞書に登録されているのでしょうか。
シンボル自身はトップのvocabularyに登録されるようです。

SYMBOL: foo
1 foo set
get foo
1

[ SYMBOL: bar 2 bar set bar get . ] with-scope
10
! barはquotの中で作成しているが
bar .
bar

! 値は取れない
bar get .
f

! 実際にシンボルはどこか見てみる
! word-vocabularyはwordがどのvocabularyに属するか返す

foo word-vocabulary .
"scratchpad"
bar word-vocabulary .
"scratchpad"

with-scopeは変数のスコープを限定するwordです。
with-scopeを使えばスコープを限定できます。
barはquot内で値をセットされているのでquot外では値が取れません。
せっかくなのでどのような仕組みになっているか見てみましょう。
set wordの定義はこうなっています

USING: assocs ;
IN: namespaces
: set ( value variable -- ) namespace set-at ;

namespace wordは現在のnamespaceを返します。
(正確にはnamespaceのスタックの最後のものを返している)
set-atは引数にkey、value、storeするassoc(連想配列)を取ります。
set-atは引数のkey/valueを渡された連想配列に登録します。
このようにsetは現在のnamespaceに値を保存しています。

with-scopeの定義はこうなっています。

USING: kernel namespaces.private ;
IN: namespaces
: with-scope ( quot -- ) H{ } clone >n call ndrop ; inline

  1. 空のHashtableを作成
  2. コピー
  3. >nで作ったHashtableをnamespaceのスタックに積む
  4. callでquotを実行
  5. ndropでHashtableをnamespaceから取り除く

with-scopeではテンポラリの名前空間を作りってからquotを実行することにより
スコープを管理しています。
namespaceのスタックとかは後々やっていきたいと思います。

複合文

複合文は名前と処理(Quotation)を結びつけて定義します。
複合文はその名の通り文の中に他の文などが入っているものです。
ifなどがそれにあたります。
制御構文は後々やります。

primitive

Factor VM自身が持っているwordです。
low levelな操作を行う時に使います。
スタックを操作するdupなどはprimitiveです。
スタックを操作するwordについては後々やる予定です。

undefined

Errorが発生した場合に作成されます。
DEFER:が作成するようです。
DEFER:は後から定義したwordを参照できるようにするwordです。
Errorについては後々やります。

wrapper

wrapperはobjectをwrapします。
wrapperは評価された時に自分自身をstackに積みます。
wrapperは\で生成できます。

0 \ .
0

0 \ execute .
V{ }

wrapperはwordにも適用できます。そのためwordの評価を一時的に
制御したりすることができます。
(その分wordを評価するexcute wordを使う必要になりますが)

とりあえずざっくりと説明しました。
短くしようとしてても長いな。

ドキュメントの読み方にも少し慣れてきました。
とりあえずドキュメントだけはしっかりしてるので根気よく読めばわかってきそうです。

ツンデレ言語Factorシリーズ次回はvocabulary loader周り(USING:)をやる予定です。
(とりあえずいろいろやるにしてもvocabularyをloadしまくらないといけないので)