Hatena::ブログ(Diary)

Scala で TAPLを勉強しつつ LLVM コンパイラを作る日記

2014-04-08

[]名前書き換えスクリプト

TAPLのScala版のソースを呼んでいた訳ですが、変数名や型名を自分に合わせて書き換えたかった。

大量にある、ソースコードIDEリファクタリング機能を持ってしても手に余るので以下のようなコードがあると便利です。

実行する前にバックアップを着実に取ります

それと、書き換え中の履歴も出来るだけ多く取っておくと良いです。

要するに、何かしらsvnなりgitなりhgなりを使うと良い。

そして、1個ずつ範囲を限定しながら書き換えていきます

テストが沢山あればあるだけよいでしょう。

なくても、コンパイルが通るとか、ざっと動かしてみるとかでもよいです。

書き換えの作業が必要で大量にある場合は人がやってもミスは出ます

どちらにしても、ミスはあり得るので履歴を取っておいて、後でおかしくなっている事に気がついたら、履歴を遡って原因を究明します

原因が人為的ミスならその人に注意を促し、書き換えプログラムに問題があったのならその問題に対応したバージョンの書き換えプログラム更新ます

こういうスクリプトを使う時の心得としては、出来るだけ範囲を限定した書き換えをして書き換えようと思ったけど辞めたっていうものリストアップして表示して変更箇所を狭めていく事です。

そうすることで、間違えて書き換えてしまう事が減ります

こういうスクリプトによる書き換えで1週間コースでバグ有りな変更を3時間くらいで行う事が出来たりします

このノウハウを伝えるのはなかなか難しくて、良くわからないから手でやりますとか言う人が多くいたりします

例えば、TyをTに書き換えたいのだけど、Typeは書き換えたくないとかいろいろあります

書き換えたくないリストとしてignoresに書いておいたりすると便利な訳です。

ファイル名のリストディレクトリ検索して先に作っておき、範囲を限定するために、このソースだけとか選べるようにしておくと、便利なときは便利です。

あと、コマンドライン引数の有無で書き換えを実行するか、書き換え箇所の出力のみにするとか機能をいろいろ付けると便利です。

でも、正規表現を使っている場合には限界があるので出来れば字句解析くらいしてから書き換えしたいよねとかは思う訳です。例えば文字列のなかにdon'tとかあって、tをeに書き換えるとdon'eになってしまうとかあった訳ですけど。

それでも、作業効率的に言って圧倒的に正規表現で作業した方が良いことはままある訳です。


<?php

$files = array();
$files[] = "./p04arith/core.scala";
$files[] = "./p04arith/demo.scala";
:
$files[] = "./p27fullfomsubref/parser.scala";
$files[] = "./p27fullfomsubref/syntax.scala";
/**/

$datas = array();

foreach($files as $file) {
  $src = file_get_contents($file);
  // $src = "abcdtt1T2 t t t t1 t1 (t3) (T1) (t1_)";

  $changes = array(
   // "t1"=>"e1", "t"=>"e", "t2"=>"e2", "t3"=>"e3",
/*
"ctx" => "c",
"ctx1" => "c1",
"ctx2" => "c2",
*/
/*
"Ty" => "T",
"TyVar" => "TVar",
"TyId" => "TId",
"TyTop" => "TTop",
"TyBot" => "TBot",
"TyArr" => "TArr",
"TyBool" => "TBool",
"TyRecord" => "TRecord",
"TyString" => "TString",
"TyUnit" => "TUnit",
"TyAll" => "TAll",
"TyNat" => "TNat",
"TySome" => "TSome",
"TyVariant" => "TVariant",
"TyVarBind" => "TVarBind",
"TyAbbBind" => "TAbbBind",
"TyRef" => "TRef",
"TySource" => "TSource",
"TySink" => "TSink",
"TyRec" => "TRec",
"TyApp" => "TApp",
"TyAbs" => "TAbs",
*/

"tyX" => "tX",
"tyX1" => "tX1",
"tyT" => "tT",
"tyTi" => "tTi",
"tyTi1" => "tTi1",
"tyTi2" => "tTi2",
"tyTiExpected" => "tTiExpected",
"tyS" => "tS",
"tySi" => "tSi",
"tyT1" => "tT1",
"tyT11" => "tT11",
"tyT12" => "tT12",
"tyT2" => "tT2",
"tyT3" => "tT3",
"tyS1" => "tS1",
"tyS2" => "tS2",
"ty" => "t",
"ty1" => "t1",
"ty2" => "t2",
"ty3" => "t3",
"tyi" => "ti",
"tyi1" => "ti1",
'tyI' => "tI",
'tyC2' => "tC2",
'tyY' => "tY",
'tyU' => "tU",
'tyU1' => "tU1",
"t11" => "e11",
"t21" => "e21",
"tyの型" => "Tの型",
    );
  // $changes = array("t"=>"e",);

$ignores = array(
  "type",
  "typeof",
  "tytermSubst",
  "typeShift",
  "typeSubst",
  "tyBody",
  "tyEqv",
  "tyMap",
  "tyBinder",
  "tyESubstTop",
  'types',
  'tyarith',
  'typeShiftAbove',
  'typeSubstTop',
  'typechecking',
  'typer',
  'typing',
  'tyAbbArgs',
  'tyBound',
);

  if($argv[1]){
    foreach($changes as $s=>$d) {
        $src = preg_replace("/\b(".$s.")\b/", $d, $src)."\n";
    }
  }

  $srcs = explode("\n", $src);
  $dst = "";
  foreach($srcs as $sr) {
    foreach($changes as $s=>$d) {
      
      if (preg_match("/\b".$s."\b/", $sr)>0) {
        $dst .= preg_replace("/\b(".$s.")\b/", "**\$1**", $sr)."\n";
      }
      foreach($ignores as $ss) {
        $sr = preg_replace("/\b(".$ss.")\b/", "", $sr);
      }
      if (preg_match("/\b(".$s."[a-zA-Z0-9_]*)/", $sr,$m)>0) {
        $datas[$m[1]] = 1;
        $rests .= preg_replace("/\b(".$s."[a-zA-Z0-9_]*)/", "**\$1**", $sr)."\n";
      }
    }
  }
  print $dst;

  print "\n======\n";
  print $rests;
  $src = preg_replace("/\n\n+\$/","\n", $src);
  // print $src;

  if($argv[1]=="save") {
    file_put_contents($file, $src);
  }
}

$dt2 = array();
foreach($datas as $k=>$v) {
  $dt2[] = $k;
}

var_export($dt2);

2014-03-31

[][]部分的型付け

多相型推論をする言語を作りたいのですが、その前に部分的型付けを勉強しておきたい所です。TAPLのrcdsubbot*1から型付け部分だけ抜き出して名前オレオレに変換してScalaで書いてみました。TopScalaでいうとAny型で、どんな型でもTopの子になります。Botは最小型というどの型の子にでもなり得る、IPS細胞みたいな型です。サンプルコード*2の変数定義以外が動きます。

このプログラムで実現しているサブタイピング構造的なサブタイピングといいます。C++等のオブジェクト指向継承は、更に順番も必要になるでしょうが、サブタイピング規則を書き換える事でいろいろなサブタイピングが出来るようになります。

package rcdsubbot

sealed trait T
case object TTop extends T // 最大の型
case object TBot extends T // 最小の型
case class TFun(t1: T, t2: T) extends T // 関数の型
case class TRecord(els: List[(String, T)]) extends T // レコードの型

sealed trait E
case class EVar(i: String) extends E // 変数
case class EFun(v: String, y: T, t: E) extends E // λ抽象
case class EApp(t1: E, t2: E) extends E // 関数適用
case class ERecord(fields: List[(String, E)]) extends E // レコード
case class EProj(t: E, proj: String) extends E // レコードのフィールド参照

object main extends App {
  // lambda x:Top.x
  println(typing(EFun("x",TTop,EVar("x"))))

  //((lambda x: Top -> Top. x) (lambda x: Top. x)): Top -> Top;
  println(typing(
    EApp(
      EFun("x",TFun(TTop,TTop),EVar("x")),
      EFun("x",TTop,EVar("x")))
  ))
  /*
  ((lambda r: {x: Top -> Top}. r.x (r.x))
      {x=lambda z: Top. z,
      y=lambda z: Top. z}):
    Top;
  */
  println(typing(
    EApp(
      EFun("r",TRecord(List("x"->TFun(TTop,TTop))),
        EApp(EProj(EVar("r"), "x"),EProj(EVar("r"), "x"))),
      ERecord(List(
        "x"->EFun("z",TTop,EVar("z")),
        "y"->EFun("z",TTop,EVar("z"))
      )))
  ))

  //(lambda x: Bot. x): Bot -> Bot;
  println(typing(EFun("x",TBot,EVar("x"))))


  //(lambda x: Bot. x x): Bot -> Bot;
  println(typing(EFun("x",TBot,EApp(EVar("x"),EVar("x")))))
}

object typing {

  def apply(e:E):T = {
    visit(Map(), e)
  }

  def subtype(cT:T, pT:T): Boolean = {
    cT == pT || // 
    ((cT, pT) match {
      case (_, TTop) => true // SA-Top
      case (TBot, _) => true // SA-Bot
      case (TFun(ct1,ct2), TFun(pt1,pt2)) => // SA-Arrow
        subtype(ct1, pt1) &&
        subtype(ct2, pt2)
      case (TRecord(css), TRecord(pts)) => // SA-Rcd
        pts.forall {
          case (s,pt) =>
            css.find{case (s1,_) => s1 == s} match {
              case Some((_,ct)) => subtype(ct, pt)
              case None => false
            }
        } 
      case _ => false
    })
  }

  def visit(env:Map[String,T], e:E):T = {
    e match {
    case EVar(v) => env(v) // TA-Var
    case EFun(v, t, e) => // TA-Abs
      val t2 = visit(env+(v->t), e)
      TFun(t, t2)
    case EApp(e1, e2) => // TA-App
      val t1 = visit(env, e1)
      val t2 = visit(env, e2)
      t1 match {
        case TFun(t11, t12) =>
          if (!subtype(t2, t11))
            throw new Exception("type error "+t2 + "!="+t12)
          t12
        case TBot => TBot // TA-AppBot
        case _ => throw new Exception("type error")
      }
    case ERecord(fs) => // TA-Rcd
      val fts = fs.map {
        case(s, e) => (s, visit(env, e))
      }
      TRecord(fts)
    case EProj(e1, s) => // TA-Proj
      visit(env, e1) match {
        case TRecord(fts) =>
          fts.find{case (s1, _) => s1 == s} match {
            case Some((_,t)) => t
            case None => throw new Exception("type error")
          }
        case TBot => TBot // TA-ProjBot
        case _ => throw new Exception("type error")
      }
    }
  }
}

2014-03-02 texを覚えた

texを覚えた

から本を書いてみたいなぁとは思っていたのですが、書ける気がしないなぁと思ってました。

大学のときの卒業論文はワードか一太郎で書いたような覚えがあります

先輩達はlatexがどうのと言ってた気はするんだけど、まぁ、いいやと。

でも、texで書いている同人誌とか書くという話を見ててやれば出来るんじゃないかと思い始めました。

markdownからtexに変換出来るツール(pandoc)とかもあるようだし。

最初markdownで書けば良いかなと軽い気持で初めて見ました。

気がついたら大分覚えてmarkdown介するのがめんどくさくなるくらい使い慣れてきました。

はいえ知らない事をいきなり書くのも難しい事なので、分かっている事を書いてみようと思いました。

というわけで、練習用にLLVMで作るコンパイラノウハウを書いてみてます

https://bitbucket.org/h_sakurai/lll/src/tip/main.pdf

日記と違って、思った事をガーッと書いて終わりというよりも、校正校正を重ねてみたいな作業が必要になるので大変です。

でも慣れれば、無駄な文章を書かずに一発で終わらせる事も出来るのかなとも思います

コンパイラ技術的な所は、必要な知識は分かったので後は実戦あるのみです。

1つ1つ潰していけば、作りたいものは作れそうです。

2014-02-22 λで前方参照

[]λで前方参照

Cっぽいλ計算にトポロジカルソートを利用した前方参照を付けてみました。

トップレベルだけは、前方参照できます

main=mul(add(1)(2))(3)
add=(a)=>(b)=>a+b
mul=(a)=>(b)=>a*b

プログラムがちゃんと動きます

以下ソースです。

package C2E7

import util.parsing.combinator._

object tsort extends App {

  // 参照してない物から取り除く
  def apply[A](toPreds: Map[A, Set[A]], done: List[A]): Iterable[A] = {

    // 参照していないものと参照しているものを分ける
    val (noPreds, hasPreds) = toPreds.foldLeft((Map[A,Set[A]](),Map[A,Set[A]]())) {
      case ((no,has),(a1,a2)) =>
        if(a2.isEmpty) (no+(a1->a2),has) else (no,has+(a1->a2))
    }

    // 参照していない集合が空
    if (noPreds.isEmpty) {
      // 参照している集合も空なら終わり
      if (hasPreds.isEmpty) done
      // 参照をもっている物があれば循環参照があるのでエラー
      else sys.error(hasPreds.toString)
    } else {
      // 参照していない物を取り出す
      val found = noPreds.map { case (a1, a2) => a1 }
      // 参照している集合から、参照していない名前を取り除く
      val nextPreds = hasPreds.map{ case (p, a) => (p, a -- found) }
      // 再帰呼び出しする
      tsort(nextPreds, done ++ found)    
    }
  }
}

sealed trait E
case class EInt(a:Int) extends E
case class EAdd(a:E,b:E) extends E
case class EMul(a:E,b:E) extends E
case class ELet(a:String, b:E, c:E) extends E
case class EId(a:String) extends E
case class EFun(m:Map[String,E], a:String, b:E) extends E
case class EApp(a:E, b:E) extends E
case class EAssign(id:String,e:E) extends E
case object EUnit extends E


object graph {


  // 依存グラフ取得
  def getDep(e:E): Set[String] = {
    e match {
      case EAssign(id,e)=> Set(id) ++ getDep(e)
      case EApp(e1,e2)=> getDep(e1)++getDep(e2)
      case EAdd(e1,e2)=> getDep(e1)++getDep(e2)
      case EMul(e1,e2)=> getDep(e1)++getDep(e2)
      case EId(id)=> Set(id)
      case EFun(m,id,e) => getDep(e)--Set(id)
      case ELet(id,e1,e2) => getDep(e1)++(getDep(e2)--Set(id))
      case EInt(_) => Set()
      case EUnit => Set()
    }
  }

  // 依存グラフ取得
  def getDep(p:List[E]):Map[String,Set[String]] = {
    p.foldLeft(Map[String,Set[String]]()){
      case (map,EAssign(id,e)) => map +(id->(getDep(e)--Set(id)))
      case _ => throw new Exception("error")
    }
  }

  case class FoundExprException(e:E) extends Exception

  // a = bという式の連続から1つの式を取得する
  def list2let(p:List[E]):E = {
    println("list "+p)
    val e = try {
      val map = p.foldLeft(Map[String,E]()){
        case (map,e@EAssign(id,_))=>map+(id->e)
        case (_,e) => throw FoundExprException(e)
      }
      val datas = tsort(getDep(p),List())
      datas.foldRight(null:E) {
        case (s,n)=> map(s) match {
          case EAssign("main",b)=>b
          case EAssign(a,b)=>ELet(a,b,n)
          case _ => throw new Exception("error")
        }
      }
    } catch {
      case FoundExprException(e) => e
    }
    println("e="+e)
    e
  }
}

// パーサ
object parse extends RegexParsers{

  def expr: Parser[E] = term~rep("+"~>term) ^^ {
    case a ~ b => b.foldLeft[E](a){case (a, b)=> EAdd(a,b)}
  }

  def term : Parser[E] = app~rep("*"~>app) ^^ {
    case a ~ b => b.foldLeft[E](a){case(a, b) => EMul(a,b)}
  }

  def app : Parser[E] = factor~rep("("~>expr<~")") ^^ {
    case a ~ b => b.foldLeft[E](a){case(a, b) => EApp(a,b)}
  }

  def factor: Parser[E] = intLiteral | valExpr | id | fun | "("~>expr<~")" | block | unit

  def fun: Parser[E] = ("("~>id<~")") ~ ("=>"~> expr) ^^ {
    case (EId(a)~b) => EFun(Map(),a,b)
  }

  def unit: Parser[E] = ";" ^^ {
    a => EUnit
  }

  def top = rep(assign|expr)

  def assign = id ~ ("=" ~> expr) ^^ {
    case EId(a)~b => EAssign(a,b)
  }

  def block: Parser[E] = "{" ~> rep(expr) <~ "}" ^^ {
    a =>
      def c2e(l:List[E]):E = {
        l match {
          case List() => EUnit
          case List(a) => a
          case ELet(a,b,EUnit)::c => ELet(a, b, c2e(c))
          case a::b => ELet(null, a, c2e(b))
        }
      }
      ELet(null,EUnit,c2e(a.filter{a=>a != EUnit}))
  }

  def valExpr : Parser[E] = ("val"~>id)~("="~>expr) ^^ {
    case(EId(a) ~ b) => ELet(a, b, EUnit)
  }

  def intLiteral : Parser[E] = """-?[1-9][0-9]*|0""".r ^^ {
    a => EInt(a.toInt)
  }

  def id : Parser[EId] = """[_a-zA-Z][_a-zA-Z0-9]*""".r ^^ {
    a => EId(a)
  }


  def apply(str:String) = {
    parseAll(top, str) match {
      case Success(tree,_) => graph.list2let(tree)
      case e => throw new Exception(""+e)
    }
  }
}

object eval {
  def apply(env:Map[String,E], e:E):E = {
    println("eval "+env+" |- "+e)
    e match {
    case EInt(a) => EInt(a)
    case EAdd(a, b) =>
      (eval(env,a),eval(env, b)) match {
        case (EInt(a1), EInt(b1)) => EInt(a1 + b1)
        case _ => throw new Exception("error "+e)
      }
    case EMul(a, b) =>
      (eval(env,a),eval(env, b)) match {
        case (EInt(a1), EInt(b1)) => EInt(a1 * b1)
        case _ => throw new Exception("error "+e)
      }
    case ELet(null, b, c) => eval(env,b); eval(env,c)
    case ELet(a, b, c) => eval(env+(a->eval(env,b)),c)
    case EId(a) => env(a)
    case EFun(m,a,b) => EFun(env,a,b) 
    case EApp(a, b) =>
      (eval(env,a),eval(env, b)) match {
        case (EFun(m,name,body), b1:E) => eval(m+(name->b1), body)
        case _ => throw new Exception("error "+e)
      }
    case EUnit => EUnit
    case EAssign(a,b) => throw new Exception("error")

    }
  }
}

object main extends App {

  def test(env:Map[String,E],s:String,e:E) {
    val e2 = parse(s)
    if (e2 != e) throw new Exception("error "+s+"  expected "+e+" but found "+e2)
    println(s+"="+eval(env,e2))
  }

  def test(s:String,e:E) {
    val e2 = parse(s)
    println("test "+s)
    val e3 = eval(Map(), e2)
    if (e3 != e) throw new Exception("error "+s+"  expected "+e+" but found "+e3)
    println(s+"="+e3)
  }


  val progs = List(
    EAssign("main",EApp(EApp(EId("sub"), EApp(EApp(EId("add"), EInt(1)),EInt(2))),EInt(2))),
    EAssign("add",EFun(Map(),"a",EFun(Map(),"b",EAdd(EId("a"),EId("b"))))),
    EAssign("sub",EFun(Map(),"a",EFun(Map(),"b",EMul(EId("a"),EId("b")))))
  )

  println(graph.list2let(progs))

  test(Map(),"1",EInt(1))
  test(Map("a"->EInt(1)),"a", EId("a"))
  test(Map(),"1+2", EAdd(EInt(1),EInt(2)))
  test(Map(),"val a = 1", ELet("a",EInt(1),EUnit))
  test(Map(),"{}", ELet(null, EUnit, EUnit))

  test(Map(),"{1}",ELet(null,EUnit,EInt(1)))
  test(Map("a"->EInt(1)),"{a}", ELet(null,EUnit,EId("a")))
  test(Map(),"{1+2}", ELet(null,EUnit,EAdd(EInt(1),EInt(2))))
  test(Map(),"{val a = 1}", ELet(null,EUnit,ELet("a",EInt(1),EUnit)))
  test(Map(),"{{}}", ELet(null, EUnit,ELet(null, EUnit, EUnit)))

  test(Map(),"{1 1}",ELet(null, EUnit,ELet(null,EInt(1),EInt(1))))
  test(Map(),"{1 2 3}",ELet(null, EUnit, ELet(null,EInt(1),ELet(null, EInt(2), EInt(3)))))
  test(Map(),"{val a = 1 a}", ELet(null,EUnit,ELet("a",EInt(1),EId("a"))))
  test(Map(),"{val a = 1 val b = 2}",ELet(null,EUnit, ELet("a",EInt(1),ELet("b",EInt(2),EUnit))))
  test(Map(),"{{ val a = 1} val b = 2}",ELet(null,EUnit, ELet(null,ELet(null,EUnit, ELet("a",EInt(1),EUnit)),ELet("b",EInt(2),EUnit))))
  test("{val a = 5 { val a = 1 {}} val b = 2 a}", EInt(5))
  test(Map(),"(a)=>a", EFun(Map(),"a", EId("a")))
  test(Map(),"((a)=>a)(2)", EApp(EFun(Map(), "a", EId("a")),EInt(2)))
  test("((a)=>a+a)(4)", EInt(8))
  test("((a)=>(b)=>a+b)(4)(5)", EInt(9))
  test("{val f4=((a)=>(b)=>a+b)(4) val f2=((a)=>(b)=>a+b)(2) f4(5)+f2(8)}", EInt(19))
  test("{(a)=>(b)=>(c)=>a+b*c}(4)(5)(6)", EInt(34))
  test("{val a = 1 val f=(b)=>a+b f(2)}", EInt(3))
  test("{val a = 1 a}", EInt(1))
  test("{1}", EInt(1))
  test("""
{
  val add = (a)=>(b)=>a+b
  val mul = (a)=>(b)=>a*b
  mul(add(1)(2))(3)
}
""",EInt(9))

  test("""
main=mul(add(1)(2))(3)
add=(a)=>(b)=>a+b
mul=(a)=>(b)=>a*b
""",EInt(9))

  test("""
main = {
  val a = add(1)(2)
  val a = mul(a)(3)
  a
}
add = (a)=>(b)=>a+b
mul = (a)=>(b)=>a*b
""",EInt(9))

}

2014-02-20 トポロジカルソート

[]トポロジカルソート

前方参照できる型推論をするには依存関係を調べて順番に並べる必要があります。

どう調べたら良いのかと調べてたらトポロジカルソートをするのが基本みたいでした。

https://gist.github.com/ThiporKong/4399695

tsort.scala検索したら凄く短いけど、分からないので普段使わない関数は書き換えていつも使っている関数に書き直してみました。

それでもうまく説明が出来ないので、検索してみると以下のページが分かりやすかったです。

http://ameblo.jp/mingw/entry-10389158344.html

参照されていない物から取り除いて無くなるまで繰り返し、参照されていない物がないのに、まだ、残っていれば循環参照があるのでエラーにすればよいのです。

もしくは、逆に参照していない物から取り除けばよいのです。githubにあったソースは参照していない物から取り除く物でした。

以下ソースです。

余計なprintlnも書いてありますが、動きを把握するのに入れたので邪魔であれば消すと良いかと思います。

package tsort

object main extends App {

  // 参照してない物から取り除く
  def tsort[A](toPreds: Map[A, Set[A]], done: List[A]): Iterable[A] = {
    println("tsort "+toPreds+" done "+done)

    // 参照していないものと参照しているものを分ける
    val (noPreds, hasPreds) = toPreds.foldLeft((Map[A,Set[A]](),Map[A,Set[A]]())) {
      case ((no,has),(a1,a2)) =>
        if(a2.isEmpty) (no+(a1->a2),has) else (no,has+(a1->a2))
    }

    println("no  = "+noPreds)
    println("has = "+hasPreds)

    // 参照していない集合が空
    if (noPreds.isEmpty) {
      // 参照している集合も空なら終わり
      if (hasPreds.isEmpty) done
      // 参照をもっている物があれば循環参照があるのでエラー
      else sys.error(hasPreds.toString)
    } else {
      // 参照していない物を取り出す
      val found = noPreds.map { case (a1, a2) => a1 }
      println("found=" + found)
      // 参照している集合から、参照していない名前を取り除く
      val nextPreds = hasPreds.map{ case (p, a) => (p, a -- found) }
      // 再帰呼び出しする
      tsort(nextPreds, done ++ found)
    }
  }

  // 参照されてない人から取り除く
  def tsort2(src:Map[String,Set[String]], done: List[String]):List[String] = {
    // 参照されていないひとを探す

    // 参照されている人の集合を作る
    val finds = src.foldLeft(Set[String]()){
      case (s,(a1,a2)) => s ++ a2
    }
    println("finds="+finds)
    // 参照されてない人と、参照されている人を分ける
    val (no,has) = src.foldLeft(List[String](), Map[String,Set[String]]()) {
      case ((no,has),(a1,a2)) => if (finds.contains(a1)) (no,has+(a1->a2)) else (a1::no,has)
    }
    println("no ="+no)
    println("has="+has)
    // 参照されていない人がいない
    if(no.isEmpty) {
      // 参照されている人もいない
      if(has.isEmpty) {
        return done
      } else {
        throw new Exception("error "+has)
      }
    }
    // 参照されていない人がいる
    tsort2(has,no:::done)
  }

  val graph = Map(
   "ast" -> Set[String](),
   "ast1" -> Set[String](),
   "main" -> Set("parse", "type"),
   "parse" -> Set("ast", "type"),
   "type" -> Set("ast"))
  println(tsort(graph, List()))

  println(tsort2(graph,List()))
}