int main( int argc, int argv ) return argc+3; endってゆーのが、
$ ./mcc < test.mc $ gcc -x assembler test.asm $ ./a.out; echo $? 4
こんなふうになるまではいけた。型チェックが無い、というより、int型しか無い。関数呼び出しできない。 他色々
あとyaccは思ってたよりずっと便利だった。自分でパーサ書いてた頃が馬鹿らしい。
名前はとりあえず、 more C で。mcc っていうのは"more c compiler"の略っていうことにしてください。
C を名乗りながら、表記が C っぽくないのは、まあ、思い付き。 C#とかObjective CとかがCの思想を受け継いでないのに表記がCに似てるからって、 C名乗ってるのってどうなんだろうか。とかそんな感じで。
--
表記っていうのは結構問題ではなかろうか。 表記が悪ければいくら強力な言語でも見向きもされなくなるかもしれない。 わかりやすい表記を使うことは使い易い言語になるために欠かせないことだ。
で、その表記なんだけど、一番簡単なのは C っぽくする事だと思う。
よくある宣伝の、「Cに似てるから修得が楽」っていうのは経営上の戦略でしかないと思うけど、 とりあえずコンパイラ作る側は、完成された物を参考できるわけだから楽だろう。
けど、なんていうか、Cみたいなのはなんとなく嫌だとか、そういう程度の理由で Cっぽくしないでおこう。と決めたわけだ。
で、Rubyのを盗ってこようと思ったんだけど、 諸事情によりRubyの特徴(だと思う)セミコロン無くても大丈夫っていうのを 盗ってこれなかったので全然Rubyっぽくならなかったのである。
まあ、思ったことは、表記で違いを出すっていっても、せいぜいブロックに'{ }'を使うか'begin-end'を使うのか ぐらいの違いしか出せないな。っていうこと。あとは細かいところしか変えられない。 どうせ、CPUが根本的に変わらない限り結局表現するものは変わらないんだし(多分)。
っていうわけで、表記は全然問題では無かったのである。
斬新な表記。
hello world は
「こんにちは」と、言う。 おわり
パーサがすごいと思う
前回の
int main( int argc, int argv ) return argc; end
っていうのが動いたっていう話なんだけど、どうやら間違いだったようだ。
実装によるんだけど、大体の場合引数ってのは、一番目から順番に積まれていくから、 スタック的に一番目のほうが深いところにあるわけ。
でも、mainはプログラムの引数を使うか、環境変数を使うか使わないかとかいうのがあって、 一番目(argc)がスタック的に浅いところにあるようで、前回動いてたのは、 僕がebpオフセット間違ってるとかっていうのとが重なった偶然だったようだ。
まあ、要するに、テストが甘いっていう話。なんかおかしいとは思ってたんだけど。
これなんとかしようと思ったら、__cdeclとか、そういう方向のを実装しないといけないんだよなぁ…ああああ。
つか、retしたあと、espの位置戻すのを呼び出し側にさせてるんだったら、 わざわざ戻り値にeaxとか使わなくても、もっとスタックとか使ったらええんだよ。適当なこと言うけど。
void nanika( int a1, int a2 ) { a1 = 4; a2 = 3; } int foo() { nanika( 10, 9 ); return nanika.a1; /* a1 はオーバーヘッド無しで取り出せる */ }
この省資源が叫ばれる時代に僕等は戻り値複数にできるチャンスをたった
subl x, %esp
という命令列で台無しにしてるのである。これを有効利用できればわざわざ参照渡しとかしなくて いいのに。もっと地球に優しく生きるべきだ。eiffelとかDとかのoutはこれ使ってるかもしれない。 確かめようとは思わないけど。どうでもいいし。
あと、x86のidivって使用レジスタeax,edx固定っていうのは知らんかった。 実際、回路的にはebxとかで除算とかもできるんだろうけど、まあ、命令フォーマット的に 無理なんだろうなぁ。とか、考えて、まあ、それで終わったらいいんだけど、 除算が現れたら、eaxとedx空けないといけないとかそういうの実装しようとすると 糞面倒なんだよねぇ…あー、ARMとかがx86駆逐しないかなー。
ゲーム屋行ったら「ハイパーオリンピック」が置いてあったのでやってみたらけっこー楽しかった。 15年振りくらいかもしれない。
でもあれって落ち付いて考えたらただの連射ゲームなわけで、別に楽しくもなんともないはずなんだけど。 あの、「気合い出せば出すほど9秒に近付いていく」っていう微妙にリアルな数字が良いんだと思う。 パーティーとかでやったら結構盛り上がると思う。30分くらい。
--
ケツイ、5ボスがあと2ミリ。最後がどうやったらいいか全然わからん。 stage 5道中がノーミスノーボムでいけた。あの集中力を持続できればいいんだけど。
ハリガネムシきもい。
なんか高専祭だったけど、何もなかった。
今のアイフルのCM(犬の翻訳する奴) がなんか嫌。サラ金のCMって 全般的になんか方向間違ってるような。「取り立て上手!」とかっていう CMとかもありだと思うけど。
彼は、プロミス男爵との戦いで失った右腕にチワワ砲を装備することによって Dr.竹富士との最終決戦に備えるのだった。
ケツイ2週目行けた…2-2、1.3億。エスプレイド2740万いけた。
と、いうわけでガルーダに間にあった。 ガルーダっていうと、動画(http://www.cave.co.jp/arcade/espgaluda/movie.html)見れるんだけど、 一回見ただけなのに、夢に出てきた…。なんか、駄目だなぁ…。
あーガルーダやりてー。近くのゲーセンにサイヴァリア2が入らないのはガルーダが入るからだ。 と、信じだい。
10月分のログが上がってなかった。
importとかinclude、それと、関数型言語
C言語の欠点を挙げてくれ。っていわれたら、何を挙げるだろうか。 僕は間違いなく、#include の存在を挙げると思う。 モジュールファイル間で型情報等をやりとりするのに「同じファイルをインクルードしてる筈」というなんとも 中途半端な方法しかないのである。makeとかに頼らないと使い物にならないコンパイラ なんてコンパイラとして弱いとしかいいようがない。 さらに、.cと.hに同じことを二回書かないといけない。冗長性っていうのはプログラマが 最も嫌うものなのに。コンパイラCが.cソースを解釈して、.hを生成するのが正しい姿の 筈である。 もうひとつ、何度も.hをパースすることによる速度低下というのもある。
プリプロセッサに頼るコンパイラの姿というのは何とも頼り無い。 新しくコンパイラを書こうと思った動機はC言語のこの辺を何とかしたいっていうのがあったからだ。
まあ、これはCが生まれた頃はコンパイルを1パスで終わらさないといけない っていうのがあったせいだと思う。仕方ないだろう。このへんで参考にすべき実装は Javaだろうか。クラスファイルに型情報も保存しておいて、 それと import とか package とかをうまくやって、依存関係とか 冗長な宣言とか全然考えなくてもよくなってるのである。 まあ、javacの場合起動の遅さのせいでコンパイル速度向上の恩恵を受けられないんだけど。 Dとかはちょっと見た感じ、コンパイルの度に必要なモジュールを1パスだけコンパイルして 型情報だけを取り出してる感じだった。これはJavaでも相互依存した時なんかに発生する 矛盾も無くすことができるのでいい感じだ。1パス分速度低下しそうな感じだけど、 Dのコンパイルは十分速いので、これでいいのかもしれない。
まあ、これからコンパイラ作るなら、多少パス増えてもいいからこのくらい実装して当然なのかもしれない。 と、いうことである。
参考: C プリプロセッサ vs D
--
で、ocamlという言語がある。関数型言語でも知っとかないけないと思った ので、コンパイラのプロトタイプはこれで書いていたりする。
このocamlは上のモジュール結合の部分にはCとJavaの中間みたいな感じの手法が使われている。 ひとつの.mlソースに対して、そのソースの型情報なんかを記述したヘッダファイルみたいな .mli っていうのを書いて、 その .mli をコンパイルして .cmi っていうのを作るのである。 Cと違い、 .cmi がコンパイル済みなので、コンパイル速度の速さはかなりの物なんだけど、 mlとmliに同じようなことを書かないといけないという冗長性がある、というわけだ。 (型推論とか実装できるんだったら、mlから型情報取り出すくらい楽だと思うんだけど…なんでこんな仕様なんかなぁ…) ただ、Cと違って、コンパイル後のオブジェクトファイルに型情報が保存してあるので、 型が矛盾したままリンクするとリンクエラーになったりするのが救いか。矛盾したまま実行して 「謎のバグ」っていうようなことは起こらないはずである。
ただ、これ問題があって、相互依存ができないというのがあるのである。 どういうことかわからないけど、まあ、できないのだ。
それで、マニュアルに 書いてあんだけど、以下のようにするらしい。
mod1.ml: let nanika1 arg1 = ... Mod2.nanika2 arg2 と、 mod2.ml: let nanika2 arg1 = ... Mod1.nanika1 arg1
っていうふうに別ファイルにあった場合っていうのは、
mod1.ml: let nanika1 func arg1 = ... func arg1 (* ←引数 arg1 に func を適用 *) と mod2.ml: let rec nanika2 arg1 = ... Mod1.nanika1 nanika2 arg1 (* ←自身(nanika2)を引数にしてnanika1を呼び出す *)
というふうに自身を引数として渡して依存を無くすというわけである。
これ見た時、「あー糞面倒、ocaml終わってんなー」と思ったわけだが、 全然そんなことはなかった、というか、これこそが関数型言語の真の強さだったわけである。 昨日、何故かいきなり理解した。。
さて、話は少しずれるが、考えてみてほしい。何故にあれほどまでに再利用を考えたはずのコードも 実際再利用できる部分っていうのはほんの少ししかないのだろうか。 何故結局同じようなコードを何回も書くはめになるのか。
答えは、そのモジュールがある型に依存しているからである。
抽象的な話になってわかりづらいけど、何かモジュールを作ったとして、 そのモジュールの中には本来型には依存しない処理が必ず含まれているはずである。 にも関わらず、C、もしくはCっぽい、いわゆる型有り手続き型言語で書いた場合、 必ず、その処理を適用するオブジェクトはどんな型であるか、 を書いておかないといけない。処理と型アクセスの情報、型の情報を分離することができないのである。
ところが、関数型言語には多相型というものがある。 型情報を必要としない処理には型情報を与えなくてもいいのである。 上のnanika1,nanika2の例だと、手続き型言語の場合だと、mod1.mlがarg1の型情報 を必要とするので、mod1は嫌でもmod2に依存することになってしまう。 しかし、関数型言語の場合だと、arg1にを操作する機能(nanika2)をmod2.mlに全て隠蔽し、 mod1.ml の nanika1 は arg1 の型情報への依存を無くすことができるのである。 すなわち、nanika1の抽象度が上がるのである。void*とは違って、型情報を失うことなく。
何を書いてるかよくわからなくなってきた。話が変わってくるが、 オブジェクト指向言語を使ったからといってオブジェクト指向なプログラムを 書けないのと同様、関数型言語を使ったからといってそれっぽいプログラムが書けるわけではない。 それっぽいプログラムを書こうと思えば、 常にオブジェクトへのアクセスとデータの操作を分けてプログラムを考え、それを書く必要がある。 面倒だが、その恩恵は大きい。データ処理を完全にデータの詳細と分割することができ、 処理(function)を「本当のモジュール」として扱うことができる。がんがん抽象度を上げていくことができる。 さらに、関数型言語の利点、すなわち関数が first class object である、ということを 利用すれば、機能と機能を組み合わせて、新しい機能を作ることも難しくない。 構造化プログラミングもオブジェクト指向プログラミングも成すことができなかった、 本当の意味での「コードの再利用」というものが実現できるのである。
ただ、そういう関数型ちっくなプログラミングは非常に役立つかというと、 全然そんなことはない。むしろ使うべきではないと思う。
何が問題なのかっていうと、理解が難しいのである。
僕もこういうふうにことばで理解はしているものの、実際そういうプログラムを書くのは無理である。 いや、ほんま、偉そうなこと書いてますが。 こういう状況は、初心者が処理を「関数に分けるべきだ。」と頭で理解はしていても、 実際には書けないような感じだ。オブジェクト指向において、 「仮想メソッドによるポリモーフィズム」というのが何かわかっていても、 実際どうしたらいいのかわからないような感じ。 普通に手続き型言語で書くときには無い、全く新しい概念、「オブジェクトへのアクセスと オブジェクトの処理の分離」というのを頭ではなく、体で理解しないといけない。 そのためには、本を読むとかではなくて、実際にそういうコードを大量に読み、 そして大量に書く必要があるのである。 そういうことを実践する人はどれだけ存在するのか。 「なんとなく求人があったからプログラマになった人」 にそれを理解させることはそもそも可能なのか。
要するに、内定式行ったら、そういう大学生がいっぱいいて、 現実を見た、っていう話。
型チェック付き関数呼び出しと条件分岐が動くようになったので、 まあ、基本部分はそこそこ動いてるはず。一時変数というものを知らないので レジスタが足らなくなると死ぬ。 前方宣言が必要ないのは唯一Cに勝ってる点。
全体的に悪い、いくつか目も当てられないような汚い&効率悪い部分あり。
やる気があるときに少しずつ実装していってるだけなので開発はゆっくり。
あとはレコード型が実装できればまあ趣味で作るコンパイラとしては 合格ラインだと思う。何が合格なのかはわからないけど。
ただ、レコード型の実装で微妙な問題があって悩む…
コンストラクタ、デストラクタの甘い罠
レコード型、すなわち、構造体とかクラスだとかいうものの話なのであるが、 言語的流行にのっとるならば、こういう物を実装する場合、メソッドも一緒に 実装べきだろう。要するに、
record Nanika { int x; int getx() { return x; } int setx( int x ) { this.x = x; } } Nanika n; n.setx(3);
と、いうような物である。
世間的にこういうのを実装したら「オブジェクト指向言語」と呼ばれるようだが、 これはそんな革新的なものというわけではない。実際は 「メッセージ受け取り人を明確にできる強力な名前空間機構」みたいな感じのものだと思う。 ただ単にスコープをちょっと便利にした物でしかない。
まあ、これは全てのコンパイラが実装しておくべき機能だと思う。 操作する対象を明確に、かつ簡潔に書くことができ、 それでいて、実行時オーバーヘッドは無いのである。 実装しない理由はあるだろうか。
ついでに言っておくと、これを実装することによって生じる言語の複雑さは仕方無いものだろう。 その複雑さは、コンパイラ、もしくはプログラマのどちらかが必ず背負わなければいけない複雑さだ。 こういう場合、複雑さはコンパイラが背負うべきだろう。(「C++再考」の最後のほうの話)
同様に、継承と、メソッドオーバーライドも必ず実装しておくべきだ。 この辺言語のサポートがあるのと無いのとでは結構違ってくる。
問題は、コンストラクタ、デストラクタなのである。 上の展開的に「このへんも実装して当然」と、なりそうなのだが、 これらを実装しようと思えば、実はランタイムライブラリが必要になってくるのである。
C++で説明すると、
class A { int x; A() { x = 4; } }; A a; /* 初期化は誰がするの? */ int main() { A *p = new A(); /* メモリアロケートは誰がするの? */ }
わかるだろうか。問題はグローバルインスタンスとヒープからの割り当てなのである。 ランタイムライブラリ無しで何とかなる問題ではない。 だからと言って、コンストラクタ、デストラクタを捨てるわけにもいかない。 このふたつ、特にデストラクタは便利&面白い機能であることは間違い無い。 しかし、「ランタイムライブラリを使わない」という目標も捨てるわけにいかない。
どうしようか。とりあえず、妥協案として、 C++(gcc)みたいな感じ、「new のメモリ割り当ては基本的に malloc で。けど、 何らかの方法でオーバーロードできるようにしておく。グローバル変数の初期化は 環境に合わせる」っていうのを考えてるんだけど、どうでしょう。
配列宣言ができない… Cの配列宣言の記法だったらいけるんだけど、 あれ、結構面倒だ。
googleで「富豪的」を 検索 すると、中国語ばっかで、中国における貧富の差の激しさの現状を思い知った次第。
えーと、まあ掲示板が使いにくいとかいう話もあった。 けど、それよりもこういう風にblogみたいな感じでやってんだったら、 その日の文章にコメント付けれるシステムがあるほうがいいのかもしれない。
しかし、しかしである。どうも、「index読む度にCGI呼ばれる」というのが 精神的に耐えられない。殆どの場合に同じ文章を生成するだけなのに、 その度にperlスクリプトはパースされるし、perl実行コンテキストが生成 される。そういうのが心の健康に良くない。 自分のサーバーで動かしてるとか、どこかのサーバーで動かしてるとか、 そういうのは問題ではない。とにかく、indexが動的に生成されているのを 見る度に胸が痛むのである。blog系はいばらの道と言っても いいかもしれない。
僕に富豪的プログラミングは無理。あと掲示板は面倒なので当分変更しないと思う。
エスプガルーダやりに行った。
色々ゲーセン回って、どこにも見つからなくて泣きそうだった。結局日本橋まで行った。 厳しいなぁ…
で、発見。誰かがやってたのでちょっと見てたんだけど、 なんか普通にALLしそうな勢いだったので激しく萎えた。 で、プロギアやった。5ボスで5回やられた。 「あかんなぁ…」と思って、ケツイやる。2面で終わる。
で、空いてたので適当にガルーダやった。
グラフィックがかなり良いと思う。特に背景。やたら綺麗。ドッターさん凄いなぁ…。 あと、ザコ兵士が死ぬ時のアニメーションとかがやばい。なんじゃありゃ。
結構簡単。一回目で5ボスまで行けた。3回目で最終ボス最終形態(多分)。 覚聖とオートバリアのおかげなんだけど。弾が遅い。覚聖したらもっと遅い。 エスプレイドをクリアできる人だったら全部初見でも避けれると思う。 ラスボスの最終形態以外。
--
f氏発言より。
blog系にする場合、変更がなければキャッシュを出力すればいいと思う。
それか、静的にhtmlを出力して、コメント部分だけ、cgiを別に呼ぶとかね。
チャットじゃない場合、要するに、読む人数の方が書き込む人数よりもはるかに多いような場合、 CGIは出力をhtmlにして、cgi自身はリダイレクトを吐くだけというのが一番効率が良い、はず。 けど、世間にあるCGIの大半はそういう実装にはなってないんだよなぁ…なんで?
あと、「コメント部分だけ、cgiを別に呼ぶ」っていうのが何なのかよくわからんのですが。
とりあえずこんな感じで。今のところjavascript無いと見れないけど。
index.html含めても、GET 3回。perlスクリプト実行は書き込み時のみ。 で、index.htmlはHTMLでそのまま書けるし、コメントの表示の制御は javascript(diary-comment.js)でDOM制御だし、結構良い感じなんじゃないかと。 「1日1ファイル」っていうのがちょっと裕福な感じ。
お、javascript無しでも見れると思う。 というか、このくらい二時間くらいでサクっとやっちゃう予定だったのに… 色々面倒だった。まだ何かバグだらけだと思う。
続きは明日だ。明日。
<input type=text>...</textarea>って何だよ…テスト甘過ぎ。 というか、まともなテストなんてしてないけど。実地試験というやつです。
学校の帰り道でガルーダ発見。 あの店、ケツイの時も真っ先に入荷されたんだし、最初に確認すべきだった。
ラスボス倒せない…ひょっとしてこのまま永遠に倒せないんじゃないだろうか… とか思ってしまう。あー、あー。ラスボス最終形態2300万くらい。2700万くらいまでは何も考えなくても いけそう…。
コンパイラ途中
ocaml ver 3.07以降でコンパイルして実行($ mcc ファイル名)。
アセンブラ代わりにGCC使ってます。あと、test.mcとか参考に。
実際使い物にならないと思うけど。
とりあえず、モジュール間の問題だけはD,Java並に高機能。make要らない。 あと、レコード型実装しようとしたら、色々 ocaml 的に問題(typeとclassで相互再帰な構造が作れない。多分) あって、なんかやる気無くしたので、プロトタイブ版では実装しない。
オーバーロードはずっと syntax sugar だと思ってたんだけど、 よく考えたら、コンストラクタ実装しようとしたら、どうやっても必要なわけで。 あと、演算子オーバーロードなんだけど、これも実は syntax sugar じゃない。 総称性(genericity)とかがあって、そうなった時に、 演算子オーバーロードが有ったら、プリミティブな型(intとか)と、 自作クラスを一般的に扱うことができたりするし。
C/C++コンパイラを超えろ -最強のコンパイラへの道-
実装しなければならない物は見えてきた。それぞれ理由は前に書いた通りである。
「GCは実装しないの?」と、f氏が言ってたような気がする。先週。
GCは実装しない。してはいけないのである。
話は少し変わるが、言語の使い易さはコンパイラの強さだけからなるものではない。 実行環境、ライブラリ、ドキュメント、その他多くの要因が関係してくるものである。 C++コンパイラとJavaコンパイラはどちらが強力か、 C++言語とJava言語はどちらが使い易いか。 というか、Javaなんかに至っては、使い易さの為にコンパイラの強さを抑制している程だ。
しかし、だからと言って、最近の言語は少しコンパイラの力を弱め過ぎではないか? CPUのスピードとかなんとか言って、Lightweight Languageなるような言葉まで 生まれてくるような時代である。 はっきり言って、インタプリタ言語のコンパイラの強さは終わってると言っていい (いや、LLが駄目と言ってるのではないです)。 もはやただのパーサとしてしか使われていない。 言語の強さは完全に評価器及びライブラリの動作に依る物になってしまっている。
それでいいのか?
「プログラム」と、いう物は常に機械が読めるバイナリであるべきではないのか? プログラミング言語はリストの機能を実装するのではなく、 オーバーヘッドを最小限にしながらリストを書けるだけの能力を実装するべきではないのか?
言いたいことは、ひとつ。
「使い易さを放棄した最強のコンパイラがあってもいいじゃないか」
と、いうことである。言語は実行環境、ランタイムライブラリに頼らない。 言語の機能は全てコンパイラを通して、機械語に変換される。 そういうのは素晴らしいと思わないのか? Cで書くプログラムは他では味わえない「何か」があると思わないのか?
で、まあ、そういう話はちょっと大袈裟だと思うけど、 とりあえず、今の所最強のコンパイラはC++コンパイラだと思う。 最強になるには、C++を超えないといけない。 どうすれば超えられるか。 C++で実装されているもの+αすればいいの?+αは何?
C++の致命的な点、#includeは超えられる、というか、これだけは超えた。 しかし、それは何かインパクトが無い。 もっと、飛び抜けた何かが欲しい。
僕は、「メタプログラミング」という概念はその「何か」として十分だと思う。
メタプログラミングはmore C言語の中で多分、最も目玉の機能である、はず…実装されれば。 なのに、何故、今まで、その話題に触れなかったかというと、 やっぱり、一番おいしいところは最後に持ってきたほうが面白いと思ったから、というか、 まあ、なんていうか、こういうのは理系の文章的にこういうのは最も悪い例なんだけど。 こういう文章を好んで書くから、中間報告も「何書いてあるかわからん」と一蹴されてしまうんだよね。 あまり関係無いけど。
と、いうわけで明日からはメタプログラミングの話。 これとか これ(途中で止まってるけど)とか Modern C++の1-3章とかあたりがちょっとだけ参考になるはず。
あと、メタプログラミング実装されてる言語ってどのくらいあるかなぁ。 思い付くのはC++テンプレートとLispマクロくらいなんだけど。 Perlとかみたいな"eval"持ってる言語は一応メタプログラム可能な言語に含まれるかも。
3回やって3回ラスボスでやられる…もう駄目かもしれない…
haskell+meta template(7月17日)
Haskellでもやっちゃった例があるらしい。 と、いうわけで、なんか結構あるような気がしてきて、 大きくやる気を落としたわけです。
で、全然関係無い話。
変態言語を極める。
今日、なんとなく、GCC frontendでも見てたら、 Shakespeare Programming Language(SPL)というものを発見してしまう。
さて、変態言語っていうと、Brainfuckの類い が有名だと思う。whitespaceとか ookとか。
そういう系統の変態言語は、チューリングマシンというか、 計算機学的趣きを持っているんだけど、SPLはそういうのではなくて、 完全にふざけた言語だ。多分。
実際に サンプル を読めばわかると思うけど、劇を書くかのような感じでプログラムが書けるという代物だ。 「Perlは詩のようなプログラムを書ける」という話もあるけど、一度SPLを知ってしまえば、 はっきり言って「Perlなんて糞」と思えるようになると思う。
少しだけ説明しておく。 まず、変数は登場人物というのを理解しないといけない。実はスタックになってて、今の状態を心に刻んだり(remember)、 思い出したり(recall)できる。変数宣言の横に書いてある人物紹介は実は只のコメントなんだけど、書かないといけない。 あと、変数名は実際にシェークスピアの作品に出てくる人物の名前しか許されていないという制約も あったりして何がなんだかよくわからない。
で、舞台に登った登場人物(変数)が会話のやりとりをするようにプログラムが 進んでいくというものだ。キーワードの数は世界一だと思う。
ドキュメント もハイテンションだったりとかではなく、技術書の文体で書かれていて、 センスを感じさせる一品。一読するのをお勧めします。
エスプガルーダ、クリアできた。ひとつ愚痴っておくなら、どのゲーセン言っても音小さくてBGMが聞こえない。
mozilla + XHTMLは文章整形に耐えうるか
LaTeXを使いたくない。なんていうか、とにかく使いたくない。
と、いうことで、代わりに XHTML 使おうと思った。 別に HTML でもいいけど、なんとなく XHTML のほうが綺麗にできそうな気がする。なんとなく。
mozillaだと、SVG とか mathML とか使えるので、結構いい感じにできるのじゃないか、 多少複雑な処理もJavaScriptで適当に処理できちゃったりするんじゃないか、 という目論見である。これでなんとかできれば、TeXなんていう印刷業務に関する知識が無い人間には 謎だらけのシステムから解放されるわけである。多分。
で、前途多難とでもいうべきか。まずマージンをmm単位で合わせる方法がわからん。 body { padding:0; margin:0; } ってやって、印刷オプションでマージン 0 にしてもまだマージンが入っているのが謎。
"CSS Printing"とか"CSS 印刷"とかでググれば資料は結構出てくる。 @page{ size: 210mm 297mm; margin: 3cm; } こういう感じ? けど、なんかどうもうまくいかないんだよな…また今度試すか…
--
でも、はっきり言って、マージンの話なんて、些細な問題なのかもしれない。
MozillaFirebirdでPostScriptに出力した図。
仕様…?
あー、やる気出ねぇ…
周期的にやる気が出ない時期があるというよりは、 やる気が無い状態が基本で、周期的にやる気が出る時期があると 言ったほうが正しいような。
ただ、今回のやる気の無さはこれまでとは違う。 近くのゲーセンからシューティングが消えていってるのである。 ああ、余った時間をどの方向に向けていけばいいのか…。
今月に入って、 ケツイ、大往生、式神、ドラゴンブレイズx2、プロギア、1945 II の消滅を確認
大往生が式神に変わったのを除けば全部シューティング以外に変わったしな…。 どこもサイヴァリア2もガルーダも無いしな…。 あと、大往生ブラックレーベルがインベーダに変わってたりとか…
--
XMLの見た目をなんとかする XSL FO っていうのがあるらしい。試してみるか。
FOPっていうのを使えばそのまま PDF生成も可能。
エディタを変えたい。
別にemacsに不満があるわけでは無いけど、うちのクラスの人の 大半が(多分他にエディタが無いからだろうけど)emacs派なので、 なんか面白味に欠けるのだ。
で、インテリ補完が欲しいと思ったので、Eclipseをやってみた。思ったよりも動作が軽かったので、良い感じだと思ったんだけど、 メイン開発マシン(Pen 150MHz 32M)ではいくらなんでも無理だろうというのと、 エディタが欲しいのであってIDEが欲しいのではない (あーいうごちゃごちゃしたウィンドウとかメニューとか見ると萎える) とか思ったので、Eclipseは止めておくことにした。
そうすると、他の選択肢を考えるんだけど、どうしても 他のエディタは機能が低過ぎるように感じてしまう。 基準がEmacsレベルなんだよな…
最近は編集とw3m以外はEmacs利用してなかったので、依存度は低いと思ってたんだけど、 どうやら全然そんなことはないのかもしれない。 C-x 2でフレーム分割されないとやりにくいし、manual-entryは相当使ってるし、 compileのC-x `も指が自然に動くし、細かい所も考えれば相当な量の物を Emacsに依存してるんじゃないだろうか。
一旦Emacs使い始めたら止めるのには相当な苦労が必要だと思う。 別に離れる理由も無いし、やっぱりずっとEmacs使うんだろうな…
インテリ補完するelisp無いかと思って探してたらそれっぽいのを見つけたんだけど、 それっぽいだけだった…。
強力な補完と強力なデバッガみたいなのって無いですかね。 自分で作れってことですかね。絶対やらんけど。
--
メタプログラミングの話
とりあえず、実装するかしないかは別にして色々書いていこうかと。 僕の考え自身完全にまとまってるわけではないので、文章は酷い。
簡単に言うと、パーサが作った構文木を色々いじって、 コンパイル時にプログラム自体を変更してしまおうという話。 C プリプロセッサとかの文字列置換とは違って、構文木をデータとする インタプリタにプログラムを通すわけ、more Cコンパイラ はインタプリタに食わせる言語とアセンブラに変換されるふたつの言語から 成り立ってる。話をわかりやすくするために、インタプリタに食わせる言語に NL(Neta Language…Meta Languageが既にあるので…)という名前を付けておくことにする。
ループのアンロールとか
nldef unroll_loop( n, stmt ) // nldef は NL 関数を定義する if n = 0 nil else stmt::unroll_loop( n-1, stmt ) // :: はconsを作る演算子だとして end var target_stmt = { puts("hello") }; // {} でくくった部分は文式(後述)になる unroll_loop( 3, target_stmt );
ていうコードが NL インタプリタを通すと、
{ puts("hello"); puts("hello"); puts("hello"); }
ていう風な感じになる(実際は構文木で、だけど)。
NL における値っていうのは全部、more Cプログラムの要素 (式、文、識別子、定数、スコープ、型等)で、そのうちの文(statement)を 表現する為の物が文式。
文式は NL インタプリタによって評価されると、 文を値とする式になる。 こうすることによって、文を持ち運ぶことが可能になる。
文を持ち運ぼうとしたら、Cだとインライン関数を書かないといけない。 けど、関数になってしまうので、型情報を晒すことになってしまう。 で、C++だとテンプレートがあるのでなんとかなるけど、 文を持ち運ぶのにしてはテンプレートは大袈裟だと思う。 書かないといけないコードの量が半端じゃない。
同様に式式(NL で評価すると式になる)というのも考える。実用性は薄いような気がするけど、実装は楽なので。
var expr = [ x++ ]; // [] で括った部分は 式式 int a = expr; int b = expr; int c = expr;
int a = x++; int b = x++; int c = x++;
文字列定数とか整数定数とか弄れるようにしておくと何か役立つかもしれない。
var mes = concat_string( "The CPU name is", cpu_name ); var f = fact( 3 ); int main() puts( mes ); return f; end
int main() puts( "The CPU name is Z80" ); return 6; end
一応続く。
IE でもエラー無しで見れるようになったはず
var arr = [ 1, 2, 3, ]; // Mozilla → arr = [ 1, 2, 3 ]; // IE → arr = [ 1, 2, 3, null ]; // ECMA Script 仕様 → arr = [ 1, 2, 3, 0 ]; alert( arr[ arr.length ] ); // → ???
まあ、JavaScript, JScript, ECMA Scriptは別物だし…
あー、コンパイルが遅過ぎる…
virtualaveが遅いような気がする。やっぱinfoseekかな…
ゲーセン行かなかった時間は睡眠とネットとテレビに費やされた。
あーエスプガルーダおもろい。ああ、今日は全然駄目だったんだけど。
--
GCCフロントエンド書く時は、とりあえず、libbackend.a作って、 それを適当にリンクさせるMakefileを書き直したほうが早い。 そうしないと、Make-lang.in変更するたびにMakefile再生成したりして、 それだけで2分かかる。あと、libbackend.a作る時はデバッグ情報消さないとやばい。 リンクするだけで2分かかる。新しいパソコンが欲しい。
--
何かソース(foo.c 拡張子は任意)をコンパイルしたとき、
import bar;
っていう文があったとして、ここで、bar.o が存在しなかったり、 依存関係の都合等で、bar.c をコンパイルする必要が出てくることがある。 で、その時にコンパイルしてしまうかどうか。 コンパイルしてしまうことにすれば、依存関係コンパイラが管理することになるので Makefile 書かなくてもいいっていう良さもあるんだけど(Javaみたいなの)、 それ以外にコンパイル速度の向上っていうのも考えられると思う。
foo.cに
import stdlib; import bar;
で、bar.c
import stdlib;
と、いう感じになってた場合、foo.cをコンパイルした後、bar.cをコンパイルすることになるんだけど、 この時、既に stdlib のモジュール情報はシンボルテーブルに読み込まれてて、 bar.c の import stdlib は無視してもよくなる。 それで、パースしなくていい分早くなるというわけである。 結構効果大きいんじゃないかと思う。
で、それは実装しようと思ってたんだけど、GCCの実装では 実際にコンパイルを行う部分(cc1.exe)とかは ひとつの.cソースをひとつの.sに変換するのしかいけないようで、どうしようか困ったのである。
案
多分 1. で。
遅かったらその時なんとかする。
あー、何も進まない…
--
ちょっと昨日の続きというか、書き忘れというか。
前、クラスの人と話してたんだけど、 極限まで依存関係を減らすならば、プリコンパイル(昨日の2.ね) のモジュール情報っていうのをもっと詳しくしておくようにして、 コンパイルが必要かどうかの判断をもっと高レベルなものにするっていううのが考えられると思う。
// A.h class A { public: int method1(); inline int method2() { return 3; } };
// func1.cpp #include "A.h" void func1() { A a; a.method1(); }
っていう状態があったとして、
// A.h class A { public: int method1(); inline int method2() { return 4; } // 変更 };
っていうふうに、A.h をちょっと変更したときに、 func1.cpp は本当は再コンパイルしなくていいはずなので、 そのへんを判定する仕組みを作っておくというわけだ。
けど、これ、ソースファイルが大量のヘッダをincludeしてたら、 それだけで、モジュール情報が膨大になるし、判定時間もそれなりに 大きくなってくるような気がしなくもない。
でも、import の仕組みが弱いせいでコンパイルが長くなってしまってる けど、言語仕様が決まっちゃってて、どうしようもない言語(というか、C++) なんかのコンパイル時間を減らすのには使えるかもしれない。 ただ、実装が面倒そうな気もするけど。
あと、コンパイル時間の問題っていうのは、CPUが速くなればなるほど (つまり、時代が進めば進むほど)どうでもいい問題になってくるので、 あんまり真面目に考える問題でもないのかもしれない。
--
寝ようと思ったはずなのに…
あー、二日空けたのは久し振りのような気がする。 いそがしいときは現実逃避する為に更新するので、 更新してない時はいそがしくない時。
ハイバネーションはACPI無くても動くみたい。 2.6.0-test11 コンパイル3回目。特に問題無く動いてる。 音が鳴らないのは仕方無いような気もするけど、ビープ音も鳴らなくなるのが謎。
個人利用の範囲だと2.6にする意味は無いような気がする… 2.4か、2.2+USB くらいで十分じゃないだろうか。
ガルーダ、3620万くらい。4000万くらいまでは続けるか…
--
Linux と Windows依存。
僕の場合、LinuxとWindowsで、使用時間で考えると、 圧倒的に Linux が長いと思う。多分。
でも、Linux のインストールされてないコンピュータに囲まれたとしても なんとかならないわけではない。 Emacs, Cygwin, 仮想デスクトップ, 窓使いの憂鬱とかを駆使すれば普通に使い易い環境を 構築できる。それにプラスWindowsの便利なツールも使えることを考えれば、 むしろこっちのほうがいいような気がしなくもない。
で、それに対して Windows の全く無い環境っていうのは少し苦しいかもしれない。 MS Office ファイルはやりとりしにくいし(OpenOfficeはフォントが謎)、 デバイスの名前わからんかったりするし。
まあ、「Linux入れればいいやー。」とか思って、安いOS無しパソコンを 買うのは止めたほうがいい。という自分への忠告。
--
衝撃の事実。gccのCプリプロセッサは cpp.exe を使っているわけではない。
なんか、cpplib っていう cpp みたいな振る舞いができるライブラリを作って、 それを直接使ってるみたいな感じだった。cpp.exe は cppmain.c と cpplib をくっつけただけだった。
でも、冷静に考えたら、別に普通かも。