片方のファイルにしか含まれない行の抽出

30分プログラム、その443。AとBというファイルを与えたときに、Aにだけ含まれる行を抽出するプログラム。
TODO一覧と、完了したタスク一覧を渡して、未完了のタスク一覧を出力したかった。要するに集合的な意味での差なんだから、ファイルのdiffといったらこっちを指すべきじゃね、とちょっと思ったりしてる。

使い方

$ cat a
foo
bar
baz

$ cat b
bar

$ ya-diff a b
foo
baz

ソースコード

import System.Environment
import Data.List

main = do [x,y] <- getArgs >>= mapM readFile
          putStr $ unlines $ (lines x) \\ (lines y)

ABCのSlotがさっぱりなので、コードを読もう

ABCのSlotがさっぱりなので、Tamarinのコードを読もう。

目的

(define foo (new Foo))

というコードのfooにslotを割り当てたら、

TypeError: Error #1034: Type Coercion failed: cannot convert Foo@11177c9 to class Foo.
        at global$init()

と怒られた。でも、どこが悪いのかさっぱりなので、コードを読もう。

対象

読むソースコードは、Tamarinの最新版。

あと、上記の例外を投げているのはToplevel::coerceなので、そこを中心に。

ドキュメント生成

まずはDoxygenを使って、メソッド一覧とかを作ってやる。
http://howdyworld.org/junk/tamarin/namespaceavmplus.html

ホントは、EAを使ってクラス図を生成するつもりだったけど、うまくいかなかった。foo()->bar()のパースに失敗するような気がする。

XCodeデバッグ

ブレイクポイントとかが使えると便利なので、XCodeのプロジェクトを開いてやる。
プロジェクトのファイルはplatform/mac/shell/shell.xcodeprojに置いてある。
実行するABCコマンドラインで指定するにはXcodeでコマンドライン引数を指定する: シン石丸の電脳芸事ニッキを参考にする。

Toplevel.cpp

エラーになるのは、Toplevel.cppの761行目付近。

if (actual->containsInterface(expected))
{
  return atom;
}
else
{
  // failed
#ifdef AVMPLUS_VERBOSE
  //core->console << "checktype failed " << expected << " <- " << atom << "\n";
#endif
  throwTypeError(kCheckTypeFailedError, core->atomToErrorString(atom), core->toErrorString(expected));
  return atom;//unreachable
}

Traits::containsInterfaceは次のようになっている。

inline bool containsInterface(Traitsp t) { 
  return this == t || this->getTraitsBindings()->containsInterface(t); 
}

で、本体のTratsBinding::containsInterfaceはこんな感じ。

bool TraitsBindings::containsInterface(Traitsp t) const
{
        for (TraitsBindingsp self = this; self; self = self->base)
        {
                if (self->owner == t || self->findInterfaceAddr(t)->t != NULL)
                        return true;
                }
        return false;
}