letを変える

letは変数に値を束縛し次の式で使えるように出来る素晴らしい式です。
しかし、作ろうとしている言語では、Scalaのvarとvalとblockが必要です。
そこで、構文木の定義にblockとreturn, varを追加します。
blockは式のリストで、型は一番最後の式の型です。ただし、リターン式があればリターン式の型も合わせる必要があります。
リターン式は式の途中に記述出来る式で、リターン式があれば、そこで関数の計算は終了します。

package hm

sealed trait SyntaxNode
case class Lambda(v: String, body: SyntaxNode) extends SyntaxNode
case class Ident(name: String) extends SyntaxNode
case class Apply(fn: SyntaxNode, arg: SyntaxNode) extends SyntaxNode
//case class Let(v: String, defn: SyntaxNode, body: SyntaxNode) extends SyntaxNode
case class Var(v: String, defn: SyntaxNode) extends SyntaxNode
case class Block(body:List[SyntaxNode]) extends SyntaxNode
case class Return(arg:SyntaxNode) extends SyntaxNode

letでは変数の定義も出来るのでvarを作ります。varは再代入可能な変数を定義できる式です。

  def infer(ast:SyntaxNode, env:Map[String, Type], nongen:Set[Var]):Type = {
    ast match {
      case Ident(name) => gettype(name, env, nongen)
      case Apply(fn, arg) =>
        val funtype = infer(fn, env, nongen)
        val argtype = infer(arg, env, nongen)
        val resulttype = newVar()
        unify(Function(argtype, resulttype), funtype)
        resulttype
      case Lambda(arg, body) =>
        val argtype = newVar()
        val resulttype = infer(body, env + (arg -> argtype), nongen + argtype)
        Function(argtype, resulttype)
/*      case Let(v, defn, body) =>
        val defntype = infer(defn, env, nongen)
        val newenv = env + (v -> defntype)
        infer(body, newenv, nongen)*/
      case Var(v, defn) =>
        val defntype = infer(defn, env, nongen)
        env = env + (v -> defntype)
        defntype
      case Block(x::List()) => try {
          infer(x, env, nongen)
        } catch {
          case ReturnTypeException(t) => t
        }
      case Block(x::xs) =>
        infer(x, env, nongen)
        infer(Block(xs), env, nongen)
      case Return(x) => throw ReturnTypeException(infer(x, env, nongen))
    }
  }

と書いてみましたが、returnで例外投げちゃ駄目ですね。うーむ。