言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

tcl/tk で遊ぶ

Xilinx の開発ツールの下の方には Tcl Shell というウィンドウがあって、GUI が気に入らない人はそこにコマンドを打ち込めばコマンドベースでツールが使える。こういうのがかっこいい!と思ったので、tcl/tk について調べる事にした。とりあえず Hello world

$ echo 'button .m -text {Hello World}; pack .m' | wish

これ一行でボタンが出来る。なかなかかっこいい。残念ながら Xilinx のツールでは言語の部分(tcl)だけ使っていて、GUI 部品(tk)は使われていないので、Xilinx のコンソールからいきなりボタンを作ったりは出来なかった。

wish というのが tcl/tk 用のシェルで、これだけでもコマンドラインシェルとして使える。というのも wish は tcl に理解出来ないコマンドがあると、それを外部コマンドとして扱うという怪しくで便利な約束になっているから。この怪しくて便利というのは tcl の設計思想のようで、他にもコマンドの引き数は全て文字列と見なすとか、制御構造ブロックも文字列とか、背中が痒くなる設計になっている。例えば。

$ echo 'pack [message .m -text [exec ping -c 1 hatena.ne.jp]]' | wish

ping を実行して結果をウィンドウに表示するんだけど、このコールバックっぽい [] も仕様では単なる文字列クォートだ。クォートには次のような種類がある。

  • ダブルクォート: 変数とコマンド置換
  • 中括弧: 置換無し
  • 大括弧: コマンドの標準出力を文字列に変換

上の例は次のように解釈される。

  • exec ping -c 1 hatena.ne.jp は外部コマンド ping を呼び出し、結果を標準出力へ
  • [exec ping -c 1 hatena.ne.jp] は標準出力を文字列にして返す。
  • message .m -text [exec ping -c 1 hatena.ne.jp] はテキストウィジェットを作ってパス名 .m を返す
  • pack [message .m ... ] は message からパス名 .m を受取り、pack で表示。

このうち message や pack コマンドは tk のコマンド。この tk だけど、歴史と人気があるツールキットだけあってなかなか面白い DSL っぽい特徴を持っているので、さらに調べてみた。tcl/tk のコマンドは

部品名 パス オプション...

という構成になっている。上の例では .m がパス名で、この後で部品を参照する時に使う名前。ドットがルートウィンドウを示しているからドットから始まるらしい。パス名はとても上手い考え方だと思う。というのはこれだけで

を同時に実現しているから。tcl にも変数があるのにそれを使わずパス名仕組みを導入する所はセンスがあると思う。GUI 部品の操作にはパターンがあって、インスタンス生成と木構造への登録、名前づけというのはセットで、変数を使うと同じような文の繰り返しになってしまう。パス名を使えばこれが簡潔になる。

さらにウィジェットコマンドというのが面白くて、メッセージ送信風の文法で、作った部品を後で操作出来る。

$ wish
% button .button -text "What time is it now?" ; pack .button # ボタン作成
% .button configure -command { tk_messageBox -message [exec date] } # ボタンを押した処理

こうかんじで、.button を作ってからそれに configure という「メッセージ」を送ってさらに設定して行くという使い方が出来る。つまり button コマンドがコンストラクタで .button がインスタンスだと考える事も出来るし、見ようによっては .button がコマンドで、configure がオプションと見る事も出来る。ドキュメントではオブジェクトではなく widget command という遠慮がちな言い方をしている所に、なにか時代の流れ感じる。