ftdop2.scala

次のscala東北勉強会で読む奴です。
いろいろ、未完成ですけど、このプログラムの発展形がC言語風言語で仕様が小さいながらパワフルな言語
のあるべき姿だと考えています。
C言語風の式言語がまずある。
で、その式言語を使う処理系はまた別個に用意されている。
そう、LISPとS式は別個で考えられるように。。。

/*
 * Copyright (c) 2008 h_sakurai, freecluster.net. All rights reserved.
 *
 * ftdop2.scala
 * Functional Top Down Operator Precedence
 * This program is Tiny programming language C Like eXpression Processor (TCLXP).
 *
 */
package ftdop2
import scala.util.matching.Regex
import scala.collection.mutable.HashMap
object main {

	def main(argv:Array[String]) {
		println(parse(argv(0)))
		println(eval(argv(0)))
	}
	// c like expression reader
	def parse(src:String) = {
		val symReg = """^[ \r\n\t]*([a-zA-Z_][a-zA-Z_0-9]*)(.*)""".r
		val numReg = """^[ \r\n\t]*([0-9]+)(.*)""".r
		val opReg  = """^[ \r\n\t]*([+\-*/=]+|[a-zA-Z_][a-zA-Z_0-9]*)(.*)""".r
		val prnReg = """^[ \r\n\t]*([({\[])(.*)""".r
		val eoxReg = """^[ \r\n\t]*([)}\]].*|$)""".r

		val infixs:(Any => Int) = {
			case "*" => 20
			case "/" => 20
			case "-" => 10
			case "+" => 10
			case "else" => 100
			case _	 => -1
		}

		val infixrs:(Any => Int) = {
			case "=" => 5
			case _	 => -1
		}

		val prefixs:(Any => Int) = {
			case "-" => 1
			case _   => -1
		}

		val postfixs:(Any => Int) = {
			case "++" => 1
			case _   => -1
		}

		val parens:(Any => Int) = {
			case "(" => 100
			case "{" => 100
			case "[" => 100
			case _	 => -1
		}

		val endParens:(Any => Regex) = {
			case "(" => """^[ \r\n\t]*(\))(.*)""".r
			case "{" => """^[ \r\n\t]*(\})(.*)""".r
			case "[" => """^[ \r\n\t]*(\])(.*)""".r
		}

		val sts:( (Any,String) => Int ) = {
			case (Symbol("if"),"(") => 100
			case _ => -1
		}

		sealed case class AS(a:Any, s:String)

		def eat(t:Regex)(as:AS):AS = t.unapplySeq(as.s) match {
			case Some(List(s,s2)) => AS(s, s2)
			case _ => throw new Error("error")
		}

		def exp(p:Int)(as:AS):AS = {
			//println("exp("+p+")("+as+")");
			as match {
			case AS(null, numReg( x, xs)) =>
				exp(p)(AS(x.toInt, xs))
			case AS(null, symReg( x, xs)) =>
				exp(p)(AS(Symbol(x), xs))
			case AS(null, prnReg(p1, xs)) if (p < parens(p1)) =>
				val AS(y, ys)  = exp(0)(AS(null, xs))
				val AS(p2, zs) = eat(endParens(p1))(AS(null, ys))
				exp(p)(AS((p1,y,p2),zs))
			case AS(null, opReg(op, xs)) if (p < prefixs(op)) =>
				val AS(y, ys) = exp(prefixs(op))(AS(null, xs))
				AS((op,y),ys)
			case AS(x, prnReg(p1, xs)) if (0 < sts(x,p1)) =>
				val AS(y, ys)  = exp(0)(AS(null, xs))
				val AS(p2, zs) = eat(endParens(p1))(AS(null, ys))
				val AS(w, ws) = exp(0)(AS(null, zs))
				exp(p)(AS((x,p1,y,p2,w), ws))
			case AS(x, prnReg(p1, xs)) if (sts(x,p1) < 0 && p < parens(p1)) =>
				val AS(y, ys)  = exp(0)(AS(null, xs))
				val AS(p2, zs) = eat(endParens(p1))(AS(null, ys))
				exp(p)(AS((x,p1,y,p2), zs))
			case AS(x, opReg(op, xs)) if (p <= postfixs(op)) =>
				AS((x,op),xs)
			case AS(x,	opReg(op, xs)) if (p < infixs(op)) =>
				val AS(y, ys) = exp(infixs(op))(AS(null, xs))
				exp(p)(AS((x, op, y), ys))
			case AS(x, opReg(op, xs)) if (p <=infixrs(op))=>
				val AS(y, ys) = exp(infixrs(op))(AS(null, xs))
				exp(p)(AS((x, op, y), ys))
			case AS(_, eoxReg(_)) => as

			case AS(x, xs) if (p <= 0) =>
				val AS(y, ys) = exp(0)(AS(null, xs))
				exp(0)(AS((x, "@", y), ys))
			case as => as
			}
		}
		exp(0)(AS(null, src)).a
	}

	def eval(s:String):Int = eval(parse(s),new HashMap[String, Int])

	// c like expression evaluator
	def eval(a:Any, e:HashMap[String,Int]):Int = a match {
		case Symbol(a) => e(a)
		case (Symbol(a),"=",b) => val r = eval(b,e); e += a -> r; r
		case (Symbol(a),"++") => val r = e(a); e += a -> (r + 1); r
		case (a,"@", b) => eval(a, e); eval(b, e)
		case ('print,"(", a,")") => val r = eval(a, e); println(r);r
		case (a,"(",b,")") => eval(a, e) + eval(b, e)
		case (a,"{",b,"}") => eval(a, e) * eval(b, e)
		case (a,"[",b,"]") => eval(a, e) - eval(b, e)
		case ("(",a,")") => eval(a, e)
		case (a,"+",b) => eval(a, e) + eval(b, e)
		case (a,"*",b) => eval(a, e) * eval(b, e)
		case (a,"-",b) => eval(a, e) - eval(b, e)
		case (a,"/",b) => eval(a, e) / eval(b, e)
		case ("-",a)   => -eval(a, e)
		case ('if,"(",a,")",(b,"else",c)) => if(a != 0) eval(b, e) else eval(c, e)
		case ('if,"(",a,")", b) => if(a != 0) eval(b, e) else 0
		case a:Int => a
		case a => throw new Error("runtime error " + a)
	}
}