C2Eを変えてみる

パーサ追加して、簡単な評価器を付けてみましたよ。
これをどんどん拡張していけば、バックエンドはmincamlとかと同じ物を使ったり出来て楽しいのではないかと思います。

package C2E5

import util.parsing.combinator._

object parser 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] = factor~rep("*"~>factor) ^^ {
    case a ~ b => b.foldLeft[E](a){case(a, b) => EMul(a,b)}
  }

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

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

  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 parse(str:String) = {
    parseAll(expr, str) match {
      case Success(tree,_) => tree
      case e => throw new Exception(""+e)
    }
  }
}

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
object EUnit extends E

object Main {
  def main(argv:Array[String]) {

    test(Map(),"1",EInt(1))
    test(Map("a"->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"->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}", 5)

  }
  def test(env:Map[String,Int],s:String,e:E) {
    val e2 = parser.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:Int) {
    val e2 = parser.parse(s)
    val e3 = eval(Map(), e2)
    if (e3 != e) throw new Exception("error "+s+"  expected "+e+" but found "+e3)
    println(s+"="+e3)
  }
  def eval(env:Map[String,Int], e:E):Int = e match {
    case EInt(a) => a
    case EAdd(a, b) => eval(env,a) + eval(env, b)
    case EMul(a, b) => eval(env,a) * eval(env, b)
    case ELet(a, b, c) => eval(env+(a->eval(env,b)),c)
    case EId(a) => env(a)
    case EUnit => 0
  }
}