2 part forth (907レス)
2 part forth http://mevius.5ch.net/test/read.cgi/tech/1073673931/
上
下
前
次
1-
新
通常表示
512バイト分割
レス栞
490: デフォルトの名無しさん [sage] 2008/10/09(木) 21:12:38 : foo dup + ; : bar dup * ; : baz foo ( ここ ) bar ( そこ ) ; foo が呼ばれたときリターンスタックには( ここ )が積まれてる。 bar が呼ばれたときリターンスタックには( そこ )が積まれてる。 bar というワード自身がリターンスタックに積まれているのではない。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/490
491: デフォルトの名無しさん [sage] 2008/10/09(木) 21:16:04 ついでに >>56 のリターンスタックを使ったパズルの説明でも書いておこう。 問題は、 : foo twice ." Hello" ; で、 HelloHello を出力する twice を定義しろというパズル。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/491
492: デフォルトの名無しさん [sage] 2008/10/09(木) 21:22:30 解答は、 : twice r> dup >r >r ; 何が起きているか説明すると、twice が呼ばれたとき、リターンスタックには、 : foo twice ( ここ ) ." Hello" ; 上の( ここ )が積まれている。 twice は最初に r> を実行して、( ここ ) をリターンスタックからデータスタックに移している。 次の dup で ( ここ ) がデータスタックに二つ積まれた状態になる。 最後に、 二つの >r で ( ここ ) が二つリターンスタックに戻される。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/492
493: デフォルトの名無しさん [sage] 2008/10/09(木) 21:27:12 さて、定義されたワードの終端に到達したので Forthは、 リターンスタックからリターンアドレスを一つ取り出してそこに戻ろうとする。 : foo twice ( ここ ) ." Hello" ; ↑これの ( ここ )に戻ってくるわけだね。 そして、Helloを出力する。 そしてまた定義されたワードの終端に到達するので、Forth は リターンスタックからリターンアドレスを一つ取り出してそこに戻ろうとするわけだ。 つまり、もう一度 ( ここ ) に戻る。 もう一度、Helloが出力されたら、次にリターンスタックからpopされる リターンアドレスは foo を呼び出したアドレスなので、ここでようやく、 foo の実行が終了することになる。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/493
494: デフォルトの名無しさん [sage] 2008/10/09(木) 21:29:45 リターンスタックに積まれているリターンアドレスは、 次に実行すべきワード単体ではなくて、 それ以降、実行すべきコード全体の先頭を指し示すアドレスだ、 と、理解できたかしらん? http://mevius.5ch.net/test/read.cgi/tech/1073673931/494
495: 488 [sage] 2008/10/10(金) 00:34:56 (ここ) は分かってるつもりなんですが、 メモリアドレス的に的確に伝えるには難しいような気が。。。 : baz foo ( ここ ) bar ( そこ ) ; 16bitアドレス環境として、スレデッドコードで考えると、 (ここ)は foo のアドレスと同じか、それとも +1 でしょうか? # foo のアドレス +2 すると bar のアドレスですよね なんかアホなこと言っているようですみません。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/495
496: デフォルトの名無しさん [sage] 2008/10/10(金) 00:55:01 >>495 「barのアドレス」と書くとbazの定義の中のbarの呼び出しのあるアドレスなのか、 それともbar自身の定義のアドレスなのか混乱するから、 ( ここ ) と表現したわけで、その違いがわかってるなら問題ないですよん。 あとポインタってわかるよね? http://mevius.5ch.net/test/read.cgi/tech/1073673931/496
497: デフォルトの名無しさん [sage] 2008/10/10(金) 01:03:18 >>495 その言い方で言えば、(ここ)はbarのアドレスですね。 正確には、bazの定義の中のbarのアドレス。 >>490 での例を少し補って、 : foo dup + ; : bar ( あっち ) dup * ; : baz foo ( ここ ) bar ( そこ ) ; と書けば、「barというワード自身」というのは( あっち )のことになる。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/497
498: 496 [sage] 2008/10/10(金) 01:20:54 >>497 補足ありがとう。 >>486の書き込みの問題点を考えてみると、Forthのリターンスタックは、 明らかに「WORDを積」んではいない。 WORDを積むという表現で想起されるのは、>>497の( あっち ) を積むという ことだとForth使いは受け取るだろうから。 そして ( ここ ) や ( そこ ) は明らかに>>484の「次に実行する命令の列」を 指し示しているわけなので、何が新しいのかよくわからない、という感想に なるのだと思う。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/498
499: 464 [sage] 2008/10/10(金) 01:36:27 >498 新しいかどうかなんて知らんよ。単にWORDの扱いを正規化できてVMが簡素になるっつうだけの話。 >497で言及している(ここ)(そこ)みたいな間接ポインタをVMで扱う必要も無くなるし。 まあ、その皺寄せをWORDに押し込んでるだけなんだけどね。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/499
500: 496 [sage] 2008/10/10(金) 01:42:51 >>499 なんていうか… 「(ここ)(そこ)みたいな間接ポインタ」というそれそのものが、 アセンブリ言語の時代からある「リターンアドレス」という概念なんですよ…。 Forthはそれをリターンスタックに分離して保存・復帰しているだけのこと。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/500
501: デフォルトの名無しさん [sage] 2008/10/10(金) 06:35:09 >>491 >そして、Helloを出力する。 . "Hello" がなんでHelloを出力することになるの? "Hello" . じゃないのはなんで? 文字列リテラルは特別扱い? http://mevius.5ch.net/test/read.cgi/tech/1073673931/501
502: デフォルトの名無しさん [sage] 2008/10/10(金) 08:32:35 >500 今は実際のリターンアドレスの話をしとらんよ。 リターンスタックを「次に実行する命令の列」という形に抽象化するっつうとるだろうに。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/502
503: デフォルトの名無しさん [sage] 2008/10/10(金) 08:46:05 >>501 ." とか、 前付きの " は、Forthではただの引用符じゃなくて、一つのワード。 だから、Helloとの間に空白が要る。 但し、終わりの " はセパレーターだから、空白なしで良い。 前にも出てたけど、 Forthでは文字列リテラルはポインタと長さの二つの数値で表す。 「.」は、トップアイテムを一つpopして値をプリントするワードだから、 " Hello" . だと5がプリントされるだけ。文字列ポインタがスタックに残る。 「."」 が 「次の " までの文字列をプリントする」というワード。 文字列をスタックに積んだときは " Hello" type とやる。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/503
504: デフォルトの名無しさん [sage] 2008/10/10(金) 08:52:55 >>502 だから、「次に実行する命令列」は辞書の中に既にあるんであって、 それはスタックである必要はなくて、いってみればアレイ。 辞書の中で実行は動的に行ったり戻ったりするから、 やっぱりリターンアドレスの保存は必要。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/504
505: デフォルトの名無しさん [sage] 2008/10/10(金) 09:06:11 >>503 >「."」 が 「次の " までの文字列をプリントする」というワード。 なんじゃそら ワロタ http://mevius.5ch.net/test/read.cgi/tech/1073673931/505
506: デフォルトの名無しさん [sage] 2008/10/10(金) 17:24:55 factorだと文字列リテラルはあるよ http://mevius.5ch.net/test/read.cgi/tech/1073673931/506
507: 488 [sage] 2008/10/10(金) 22:08:31 >>496,497 サンクス スレデッドコードと書いておいて誤解がなかったようだ。 497 のレスだと、俺の中では「次のワード」という認識になる。 (あっち)という表現を使えば確かに誤解はなくなる。 なんだか、リターンスタックのデータ内容と、 (サブルーチン)リターンアドレスを混同した希ガス http://mevius.5ch.net/test/read.cgi/tech/1073673931/507
508: 464 [sage] 2008/10/11(土) 00:37:41 >504 「必要がないから積まない」じゃなくて、「VMの原理を簡単にするために積む」んだって。 VMが辞書の中を行ったり来たりしないようにするのが目的。 もちろん、VMの仕事をWORDに移管しただけの話だし、スタックが大きくなるデメリットもあるけどな。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/508
509: 504 [sage] 2008/10/11(土) 01:03:24 >>508 つまり、ワードを全部インラインにするということ? それなら確かに理論的には可能だし、リターンスタック自体要らない。 普通は、大きくなるのはスタックじゃなくて辞書だな。 まあ、辞書からインラインで展開したものを リターンスタックにコピーしてもいいけど、 ランタイムにやるなら相当動作が遅くなると思う。 それに、 なぜそのためにスタックというデータ構造を使うのかがわからない。 後ろから先に積んでいくことになるわけだが。 その発想はインストラクションキャッシュだと思う。 だから、むしろキューがいいと思う。 ソフトウェア的にやると相当遅いとは思うけど、 論理的には可能だと思うよ。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/509
510: デフォルトの名無しさん [sage] 2008/10/11(土) 02:00:15 サンクス >693 5 ソースコード 6 boost::spirit というので激しく不安になるな。 というか、スクリプトの入門というのなら、BASICタイプ言語の作成とかCタイプ言語の作成 とか分散しないで、どっちか一つに集中すべきじゃない? 入門だったら、むしろ構文はシンプルなforthライクにして、エンジンの中身に拘るべきだと思うけど…… http://mevius.5ch.net/test/read.cgi/tech/1073673931/510
511: デフォルトの名無しさん [sage] 2008/10/11(土) 02:02:28 誤爆スマソ http://mevius.5ch.net/test/read.cgi/tech/1073673931/511
512: 464 [sage] 2008/10/11(土) 02:18:22 >509 いや、基本はWORD呼び出し時に展開(そのWORDに定義されたWORD列をスタックに押し込む)。 WORDコンパイル時に全部インラインに展開するわけじゃないです。 #高速化を目的として、WORDコンパイル時にある程度はインライン化することになると思うけど。 WORDに定義されたWORD列を、実行時にその場で積んでその場で処理する必要があるから、 FIFOの仕組みが必要になります。 まあ、小まめにWORDの積み降ろしをやらなきゃいけないから、確かに重そうだけどね。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/512
513: デフォルトの名無しさん [sage] 2008/10/11(土) 05:37:05 464のやり方は、ありえなくはないけどForth的じゃないね。 どっちかっていうとJava VMのJITコンパイラみたいな。 Forthはシンプルな実装で軽い、ってイメージ。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/513
514: デフォルトの名無しさん [sage] 2008/10/11(土) 06:54:12 >>464のやり方だと実行の度にリターンスタックに命令列のコピーが発生するわけだな。 対して普通のForthはリターンアドレス一つのコピーで済む。 Forthでは関数の戻り場所を実行時に入れ替えたりできる( >>59, >>62 ) わけだけど、 >>464のやり方だと命令列自体の入れ替えになるから相当面倒。 Forthの自由度をわざわざ減らしている気がするんだけどな。 でも作るというならがんがれ。 様々な進化があってこそ発展もある。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/514
515: 464 [sage] 2008/10/11(土) 13:07:00 >513 いや、実装はこっちの方がシンプルだよ。辞書の解釈を全部WORDに押し付けることができるから。 ただ、スタック操作が増えるから重くなる方向だけどね。 >514 リターンアドレス前提だと難しいよね。作業用スタックがもう一本ありゃいいんだけど。 俺言語のVMだと自前スタックで実装しているので大した話じゃないです。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/515
516: 464 [sage] 2008/10/11(土) 13:22:12 >514 ちょっと補足。 複数のWORDを押し込もうとすると確かに面倒だね。 俺言語でも 1. 無名WORDを作る 2. 1.のWORDに実行するWORDを押し込む 3. 1.のWORDをリターンスタックに押し込む といったパック化が必要になります。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/516
517: デフォルトの名無しさん [sage] 2008/10/11(土) 13:26:05 464のVMだが、 ハードウェアレベルではもう一般化してる インストラクションのプリフェッチとおなじだよね。 マシン語のデコーダレベルでのVMという感じか。 実装する場合の最難題は条件付きジャンプだと思う。 IFとか不定ループをどう載せるかが鍵だな。 これを考えると、VMが仕組みとして単純になるかどうかは微妙だと思う。 まあ、ベタでやればできそうな気はするが、 ハードウエアの仕組みをソフトウェアで二重にしてるだけのような気がしないでもない。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/517
518: 464 [sage] 2008/10/11(土) 13:38:04 >514 思い出した……>59を実現するにはreverse自体のパック化も必須なんだっけ。 そういや>59みたいな操作をどうしようか悩んだな。 ただ、こういったWORDを跨ぐ暗黙的なリターンスタック制御てけっこう危険じゃない? 個人的にはWORDはデータスタックの状態にのみ依存すべきだと思うけど、WORDを越えた 範囲までリターンスタックを操作できるようにすると、WORD同士の依存関係が出てしまって 連鎖性(concatenative)が崩れるような気がする。 forthの条件分岐でも、セパレーターを使った明示的な制御をしているわけだし。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/518
519: 464 [sage] 2008/10/11(土) 13:49:31 連投スマソ >517 そうか、CPUだともう一般的なのか……。さすがに天才的な変態が集まる業界だな。 やっぱりCPUのアーキテクチャ勉強しないといけないなあ。 IFは構文解析で逃げました。 block := block ? WORD1 ! WORD2 という三項演算子を用意して、条件分岐用WORDに解釈するようにしました。 不定ループの構文を用意するかどうかは検討中です。 http://mevius.5ch.net/test/read.cgi/tech/1073673931/519
メモ帳
(0/65535文字)
上
下
前
次
1-
新
書
関
写
板
覧
索
設
栞
歴
あと 388 レスあります
スレ情報
赤レス抽出
画像レス抽出
歴の未読スレ
Google検索
Wikipedia
ぬこの手
ぬこTOP
0.008s