2 part forth (907レス)
1-

482: 2008/10/08(水)21:16 AAS
二つ目用の問題も書いておくよ。

: bar 1 2 3 >r >r 1 + r> r> ;
483: 2008/10/08(水)23:38 AAS
混乱してるようだから、
まず、ネイティブの場合とスレッディングの場合を分けて考えた方がいい。
ネイティブForthで自然な実装では、
SP=リターンスタックポインタ(RSP)
BP=データスタックポインタ(DSP)
となってる。
UNIX/Cの普通のスタックを知ってれば、機能的な対応は明瞭なはず。
リターンスタックが伸びても、DSPは別フレームに移らないのがForthのポイント。

ちなみに、Forthでも局所変数が使えるヤツがあって、
その局所変数には、リタースタック中にフレームを作って割り当てるのが普通。
これも、標準のスタックがわかってれば意味は明瞭。

スレッディング(直接・間接)方式だと、
呼出しはCallじゃないから、
BPをRSPにしてもかまわんが、
パラメタとリターンアドレスの混合は、
Forthでは無謀。動的にチェックが必要な上に、完璧にはできそうにない。
484
(1): 464 2008/10/09(木)01:15 AAS
446です。
forthは興味半分で使ったレベルでしかないですね……
concatenative俺言語の設計の参考にしているぐらいです。

>473
リターンスタックを「次に実行する命令の列」という形に抽象化すると、「現在処理中のWORD」と
「ソースコードを解釈したWORD」「Dictionaryに保持されているWORD列」…つまり呼び出されて
いないWORDを対称(等価/交換可能)に扱うことができるようになるので、バーチャルマシンの
構造を簡単化することができるかと思います。
……forthで許されているのかしらんけど。
485
(1): 2008/10/09(木)06:24 AAS
リターンスタックに積んであるリターンアドレスは、
「これから実行される命令列」へのポインタそのものと見なせるから、
そのアイデアが新しいとは思えないけどな。
Forthぐらいバーチャルマシンの実装が簡単な言語もないし。

ただ、リターンスタックの意味がよくわからないままに、
他の言語のように抽象構文木を再帰的に処理するような
実装にしていると、リターンスタック操作の実装で悩むの
かもしれない。
486
(2): 464 2008/10/09(木)08:54 AAS
>485
>463は呼び出したWORDを積むことを前提にしているし、>451 >472で言ってるのが >463 >473
思い切り否定されてるので、forthじゃそういう考え方無いのかな、と思った。
もしforthでもそういう使い方しているんだったらおいらの不勉強だね。
487: 2008/10/09(木)10:46 AAS
>>486
485のいってる意味は、
スレッディング方式のforthでは、
辞書は実行されるワードのポインタのリストとみなせるわけで、
リターンアドレスというのは、辞書内への戻りアドレス、
つまり、これから実行されるワードのリストへのポインタといえる
ということと思われる。

ワードのポインタを直接リターンスタックに積み込むような、
インストラクションキャッシュみたいな仕様のリターンスタックの実装は、
ちょっと聞いたことが無い。
というかそれじゃリターンスタックじゃない。
488
(2): 2008/10/09(木)20:52 AAS
487の言わんとすることを俺なりに解釈してみる…

: foo dup + ;
: bar foo drop ;
bar の処理中に foo を実行するときに、
foo の次の drop のアドレスをリターンスタックに積む。
それで、foo の実行終了時にリターンアドレスから戻り先を取る。
これが、さっき積んだ drop のアドレスということ。

で、「drop のアドレス」っていうのを「ポインタ」と呼んでいる。
489: 2008/10/09(木)21:08 AAS
fooが呼ばれたときのリターンアドレスは 「dropのアドレス」というより
「dropの直前のアドレス」だ。
微妙なニュアンスに聞こえるかもしれないが。

: bar foo ( ここ ) drop ;

( ここ ) と書いた部分に戻ってくる。
490
(1): 2008/10/09(木)21:12 AAS
: foo dup + ;
: bar dup * ;
: baz foo ( ここ ) bar ( そこ ) ;

foo が呼ばれたときリターンスタックには( ここ )が積まれてる。
bar が呼ばれたときリターンスタックには( そこ )が積まれてる。

bar というワード自身がリターンスタックに積まれているのではない。
491
(1): 2008/10/09(木)21:16 AAS
ついでに >>56 のリターンスタックを使ったパズルの説明でも書いておこう。

問題は、

: foo twice ." Hello" ;

で、

HelloHello

を出力する twice を定義しろというパズル。
492: 2008/10/09(木)21:22 AAS
解答は、

: twice r> dup >r >r ;

何が起きているか説明すると、twice が呼ばれたとき、リターンスタックには、

: foo twice ( ここ ) ." Hello" ;

上の( ここ )が積まれている。
twice は最初に r> を実行して、( ここ ) をリターンスタックからデータスタックに移している。
次の dup で ( ここ ) がデータスタックに二つ積まれた状態になる。
最後に、 二つの >r で ( ここ ) が二つリターンスタックに戻される。
493: 2008/10/09(木)21:27 AAS
さて、定義されたワードの終端に到達したので Forthは、
リターンスタックからリターンアドレスを一つ取り出してそこに戻ろうとする。

: foo twice ( ここ ) ." Hello" ;

↑これの ( ここ )に戻ってくるわけだね。
そして、Helloを出力する。

そしてまた定義されたワードの終端に到達するので、Forth は
リターンスタックからリターンアドレスを一つ取り出してそこに戻ろうとするわけだ。

つまり、もう一度 ( ここ ) に戻る。
もう一度、Helloが出力されたら、次にリターンスタックからpopされる
リターンアドレスは foo を呼び出したアドレスなので、ここでようやく、
foo の実行が終了することになる。
494: 2008/10/09(木)21:29 AAS
リターンスタックに積まれているリターンアドレスは、
次に実行すべきワード単体ではなくて、
それ以降、実行すべきコード全体の先頭を指し示すアドレスだ、
と、理解できたかしらん?
495
(2): 488 2008/10/10(金)00:34 AAS
(ここ) は分かってるつもりなんですが、
メモリアドレス的に的確に伝えるには難しいような気が。。。

: baz foo ( ここ ) bar ( そこ ) ;
16bitアドレス環境として、スレデッドコードで考えると、
(ここ)は foo のアドレスと同じか、それとも +1 でしょうか?
# foo のアドレス +2 すると bar のアドレスですよね

なんかアホなこと言っているようですみません。
496
(3): 2008/10/10(金)00:55 AAS
>>495
「barのアドレス」と書くとbazの定義の中のbarの呼び出しのあるアドレスなのか、
それともbar自身の定義のアドレスなのか混乱するから、
( ここ ) と表現したわけで、その違いがわかってるなら問題ないですよん。
あとポインタってわかるよね?
497
(3): 2008/10/10(金)01:03 AAS
>>495
その言い方で言えば、(ここ)はbarのアドレスですね。
正確には、bazの定義の中のbarのアドレス。
>>490 での例を少し補って、

: foo dup + ;
: bar ( あっち ) dup * ;
: baz foo ( ここ ) bar ( そこ ) ;

と書けば、「barというワード自身」というのは( あっち )のことになる。
498
(1): 496 2008/10/10(金)01:20 AAS
>>497
補足ありがとう。

>>486の書き込みの問題点を考えてみると、Forthのリターンスタックは、
明らかに「WORDを積」んではいない。
WORDを積むという表現で想起されるのは、>>497の( あっち ) を積むという
ことだとForth使いは受け取るだろうから。
そして ( ここ ) や ( そこ ) は明らかに>>484の「次に実行する命令の列」を
指し示しているわけなので、何が新しいのかよくわからない、という感想に
なるのだと思う。
499
(1): 464 2008/10/10(金)01:36 AAS
>498
新しいかどうかなんて知らんよ。単にWORDの扱いを正規化できてVMが簡素になるっつうだけの話。
>497で言及している(ここ)(そこ)みたいな間接ポインタをVMで扱う必要も無くなるし。

まあ、その皺寄せをWORDに押し込んでるだけなんだけどね。
500
(1): 496 2008/10/10(金)01:42 AAS
>>499
なんていうか…
「(ここ)(そこ)みたいな間接ポインタ」というそれそのものが、
アセンブリ言語の時代からある「リターンアドレス」という概念なんですよ…。
Forthはそれをリターンスタックに分離して保存・復帰しているだけのこと。
501
(1): 2008/10/10(金)06:35 AAS
>>491

>そして、Helloを出力する。

. "Hello"
がなんでHelloを出力することになるの?
"Hello" .
じゃないのはなんで?
文字列リテラルは特別扱い?
502
(1): 2008/10/10(金)08:32 AAS
>500
今は実際のリターンアドレスの話をしとらんよ。
リターンスタックを「次に実行する命令の列」という形に抽象化するっつうとるだろうに。
503
(1): 2008/10/10(金)08:46 AAS
>>501
." とか、 前付きの " は、Forthではただの引用符じゃなくて、一つのワード。
だから、Helloとの間に空白が要る。
但し、終わりの " はセパレーターだから、空白なしで良い。

前にも出てたけど、
Forthでは文字列リテラルはポインタと長さの二つの数値で表す。
「.」は、トップアイテムを一つpopして値をプリントするワードだから、

" Hello" .

だと5がプリントされるだけ。文字列ポインタがスタックに残る。

「."」 が 「次の " までの文字列をプリントする」というワード。
文字列をスタックに積んだときは

" Hello" type

とやる。
504
(2): 2008/10/10(金)08:52 AAS
>>502
だから、「次に実行する命令列」は辞書の中に既にあるんであって、
それはスタックである必要はなくて、いってみればアレイ。
辞書の中で実行は動的に行ったり戻ったりするから、
やっぱりリターンアドレスの保存は必要。
505: 2008/10/10(金)09:06 AAS
>>503
>「."」 が 「次の " までの文字列をプリントする」というワード。
なんじゃそら
ワロタ
506: 2008/10/10(金)17:24 AAS
factorだと文字列リテラルはあるよ
507: 488 2008/10/10(金)22:08 AAS
>>496,497 サンクス

スレデッドコードと書いておいて誤解がなかったようだ。
497 のレスだと、俺の中では「次のワード」という認識になる。
(あっち)という表現を使えば確かに誤解はなくなる。

なんだか、リターンスタックのデータ内容と、
(サブルーチン)リターンアドレスを混同した希ガス
508
(1): 464 2008/10/11(土)00:37 AAS
>504
「必要がないから積まない」じゃなくて、「VMの原理を簡単にするために積む」んだって。
VMが辞書の中を行ったり来たりしないようにするのが目的。
もちろん、VMの仕事をWORDに移管しただけの話だし、スタックが大きくなるデメリットもあるけどな。
509
(1): 504 2008/10/11(土)01:03 AAS
>>508
つまり、ワードを全部インラインにするということ?
それなら確かに理論的には可能だし、リターンスタック自体要らない。
普通は、大きくなるのはスタックじゃなくて辞書だな。

まあ、辞書からインラインで展開したものを
リターンスタックにコピーしてもいいけど、
ランタイムにやるなら相当動作が遅くなると思う。
それに、
なぜそのためにスタックというデータ構造を使うのかがわからない。
後ろから先に積んでいくことになるわけだが。

その発想はインストラクションキャッシュだと思う。
だから、むしろキューがいいと思う。
ソフトウェア的にやると相当遅いとは思うけど、
論理的には可能だと思うよ。
510: 2008/10/11(土)02:00 AAS
サンクス >693

5 ソースコード
6 boost::spirit
というので激しく不安になるな。

というか、スクリプトの入門というのなら、BASICタイプ言語の作成とかCタイプ言語の作成
とか分散しないで、どっちか一つに集中すべきじゃない?

入門だったら、むしろ構文はシンプルなforthライクにして、エンジンの中身に拘るべきだと思うけど……
511: 2008/10/11(土)02:02 AAS
誤爆スマソ
1-
あと 396 レスあります
スレ情報 赤レス抽出 画像レス抽出 歴の未読スレ

ぬこの手 ぬこTOP 0.013s