Ada Hackathon

Ada Hackathonに行ってきました。
濃ゆいメンバーで楽しかったです。
適当に思いつきでAdaScriptという言語を作ってみました。
今までscalaで作ってた言語を適当にAdaっぽく改造してみた言語です。


?ストリームと正規表現パターンマッチで字句解析
?パターンマッチと演算子順位法で構文解析
?パターンマッチでインタプリタ
っていう構成と、キーワードは出来るだけ3文字にするというポリシーで作ってみました。
型がduck typingなのでAda Scriptっていう大それた名前をつけてみたりしました。
xadaとか、xadaxとかにしようかなぁとか、無駄に名前はこって考えてました。

prc a is 3 end で3が返る
fun add(a,b) is a + b end; add(1,2)で3が返る

a=1; cse whn 1|2 => 3 whn oth => 6 endで3
a=2; cse whn 1|2 => 3 whn oth => 6 endで3
a=3; cse whn 1|2 => 3 whn oth => 6 endで6
if 0 thn 1 elf 2 thn 3 els 4 end で 3
for i in 1 .. 10 lop Put(i) end で1,2,3,4,5,6,7,8,9,10
for i in reverse 1 .. 10 lop Put(i) end で10,9,8,7,6,5,4,3,2,1
whl a<10 lop Put(a); a++; end でwhileループ

とかいう機能がありますけど、エラーとか適当です。
元からついてる機能で、LISP風マクロも使える代物になっております。
テストしてないですけど。クロージャもいけるのかも。

if a then b elsif c then d else e endみたいな文を式にすると
(if, (a, then, (b, elsif, (c, then, (d, else, e)))), end)
のような感じのツリーになるようにするとうまくいったと。
if endが括弧的演算子
then, elsif, elseは右結合2項演算子としてしまうと。

あと、他のブロックっぽい文法はadablockっていう演算子にしてみました。
開始キーワード、式、キーワード2、式2、end
っていう並びになる演算子であらわすと。実はそうではなくて、括弧演算子とis2項演算子
lop2項演算子とであらわすという方法もあるだろうなぁとか思ったりしてました。
我ながら、何を書いてるのかわけわからないなw

(prc, a, is, b, end)がいいか、(prc,(a,is,b),end)がいいかということなんですけど。
(fun, a, is, c, end)
(cse, a, is, (whn, (a, =>, b), body), end)

k.inabaさんはさらっと、OMetaでAda作ってて、やっぱり凄いな思いました。
OMetaもk.inabaさんも。
shinhさんは、Adaをけなしてるけど愛のあるけなし方で面白かった。なんというか。

D言語の本に、YTさんや、shinhさんのサインをもらおうとしたけどもらえませんでした。

http://shinh.skr.jp/m/?date=20090215#p02



/*
 * Copyright (c) 2009 h_sakurai, freecluster.net. All rights reserved.
 *
 * AdaScript.scala
 * AdaScript Intepreter
 * This program is Tiny programming language Ada like expression processor.
 *
 * HISTORY:
 *  AdaScript.scala prc,fun, append closure, quote, quasiquote, expand, macro, symbol,void
 * TODO:
 * match,for,string,define operator,comment,array(vector)
 * void,
 */
package AdaScript
import scala.util.matching.Regex
import scala.collection.mutable.{HashMap,Stack}

object main {

	def main(argv:Array[String]) {
		println(parse(argv(0)))
		println(eval(argv(0)))
	}
	// c like expression reader
	def parse(src:String) = {
		var str = src
		var connectf = true
		val tokens = for(i <- Stream.from(0)) yield {
			def t ():Any = {
				val num = """^([0-9]+)(.*$)""".r
				val sym = """^'([^-\+*\/\(\)\[\]\{\}\s\=\;\:<,]+)(.*$)""".r
				val ptn = """^([-\+*\/\=<>|:.]+|[\:\(\)\[\]\{\},]|[^-\+*\/\(\)\[\]\{\}\s\=\;\:<>|,.]+|$)(.*$)""".r
				val eol = """^(;)(.*$)""".r
				val spc1 = """^[\s]*[\r\n]+[\s]*(.*$)""".r
				val spc = """^[\s]+(.*$)""".r
				val eof = """^[\s]*($)""".r
				str match {
				case eol(a,e) => str = e; connectf = false; a
				case eof(e) => str = ""; Nil
				case sym(a,e) => str = e; Symbol(a)
				case num(a,e) => str = e; a.toInt
				case ptn(a,e) => str = e; a
				case spc1(e) => str = e; connectf = false; t()
				case spc(e) => str = e; t()
				case _ => throw new Error("lexer error")
				}
			}
			connectf = true
			t()
		}

		val eox:(Any => Int) = {
			case ")" => 1
			case "}" => 1
			case "]" => 1
			case "is" => 1
			case "thn" => 1
			case "end" => 1
			case "lop" => 1
			case Nil => 1
			case _   => -1
		}

		val infixs:(Any => Int) = {
			case "*" => 20
			case "/" => 20
			case "-" => 10
			case "+" => 10
			case "<=" => 6
			case ">=" => 6
			case "<" => 6
			case ">" => 6
			case "==" => 5
			case ".." => 4
			case "|" => 3
			case "=>" => 2
			case "in" => 2
			case _	 => -1
		}

		val infixrs:(Any => Int) = {
			case "," => 1
			case ":=" => 5
			case "thn" => 3
			case "els" => 3
			case "elf" => 3
			case _	 => -1
		}
		val infix2s:(Any => Int) = {
			case _	 => -1
		}

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

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

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

		val endParens:(Any => Any) = {
			case "(" => ")"
			case "{" => "}"
			case "[" => "]"
			case "if" => "end"
			case _ => Nil
		}

		val sts:( (Any, Any) => Int ) = {
			case ("fun","(") => 2
			case ("mac","(") => 2
			case _ => -1
		}

		val adablock:((Any) => Int) = {
			case ("fun") => 2
			case ("prc") => 2
			case ("cas") => 2
			case ("for") => 2
			case ("whl") => 2
			case _ => -1
		}
		val adablock2:((Any) => String) = {
			case "prc" => "is"
			case "fun" => "is"
			case "cas" => "is"
			case "for" => "lop"
			case "whl" => "lop"
			case a => throw new Error("undefined adablock "+ a)
		}
		sealed case class AS(a:Any, s:Stream[Any])
		def eat(t:Any)(as:AS):AS = as.s match {
			case cons(x,xs) if(x==t) => AS(x, xs)
			case _ => throw new Error("error expected "+t+" but found "+as.s )
		}

		val cons = Stream.cons
		def exp(p:Int)(as:AS):AS = {
			//println("exp("+p+")("+as+")");
			as match {
			case AS(null, cons(x, xs))  if(eox(x)>0) => AS("void",cons(x,xs))
			case AS(null, cons(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, cons(x, xs)) if (p < adablock(x)) =>
				val AS(y, ys) = exp(0)(AS(null, xs))
				val AS(z, zs) = eat(adablock2(x))( AS(null, ys) )
				val AS(a, as) = exp(0)(AS(null, zs))
				val AS(b, bs) = eat("end")( AS(null, as) )
				exp(p)(AS((x, y, z, a, b), bs))

			case AS(null, cons(op, xs)) if (p < prefixs(op)) =>
				val AS(y, ys) = exp(prefixs(op))(AS(null, xs))
				exp(p)(AS((op,y),ys))
			case AS(null, cons(x, xs)) => exp(p)(AS(x, xs))
			case AS(x, cons(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(sts(x,p1))(AS(null, zs))
				exp(p)(AS((x,p1,y,p2,w), ws))
			case AS(x, cons(p1, xs))
					if (sts(x,p1) < 0 && p < parens(p1) && connectf) =>
				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, cons(op, xs)) if (p <= postfixs(op) && postfixs(op) == 0) =>
				val AS(y, ys) = exp(0)(AS(null, xs))
				if(y==null)AS((x,op),xs)
				else exp(0)(AS(((x,op), "@", y), ys))
			case AS(x, cons(op, xs)) if (p <= postfixs(op)) =>
				AS((x,op),xs)
			case AS(x, cons(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, cons(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(x, cons(op, xs)) if (p <=infix2s(op))=>
				val AS(y, ys) = exp(infixrs(op))(AS(null, xs))
				exp(p)(AS((x, op, y), ys))

			case AS(_, cons(x,xs)) if(eox(x)>0) => as
			case AS(null, xs) => as
			case AS(x, xs) if(xs == Nil) => as
			case AS(x, xs) if (p <= 0) =>
				val AS(y, ys) = exp(0)(AS(null, xs))
				if(y != Nil) exp(0)(AS((x, "@", y), ys))
				else                 AS(x, xs)
			case as => as
			}
		}
		exp(0)(AS(null, tokens)).a
	}

	def eval(s:String):Any = try {
		var env = new Env(null)
		env + ("macros" -> new Stack[(Any,Any)]())
		var exp = Macro.expand(parse(s),env)
		eval(exp,env)
	} catch {
	case e => println(e);-1
	}

	class Env (val parent:Env) {
		var e:HashMap[Any, Any] = new HashMap
		def apply(a:Any):Any = {
			if (e.contains(a)) e(a)
			else if(parent != null)parent(a)
			else null
		}
		def contains(a:Any) : Boolean = {
			if(e.contains(a)) true
			else if(parent == null) false
			else parent.contains(a)
		}
		def +(kv : (Any, Any)) : Env = {
			def add(env:Env, kv:(Any, Any)) : Boolean = {
				kv match {
				case (a, b) =>
					if(env.e.contains(a)) { env.e + kv; true }
					else (env.parent != null && add(env.parent, kv))
				}
			}
			if(!add(this, kv)) e + kv
			this
		}
		override def toString():String = {
			e.toString()+"\nparent:"+parent
		}
	}

	case class Fun(prms:Any,body:Any,e:Env) {
		override def  toString():String = {
			return "Fun("+prms+","+body+")"
		}
	}
	object Macro {
		def qq(a:Any, e:Env):Any = {
			a match {
			case (a,b) => (qq(a,e),qq(b,e))
			case (a,b,c) => (qq(a,e),qq(b,e),qq(c,e))
			case ("qe","{",b,"}") => b
			case ("q","{",b,"}") => a
			case ("uq","{",b,"}") => eval(b,e)
			case (a,b,c,d) => (qq(a,e),qq(b,e),qq(c,e),qq(d,e))
			case (a,b,c,d,f) => (qq(a,e),qq(b,e),qq(c,e),qq(d,e),qq(f,e))
			case a => a
			}
		}

		def expand(a:Any, e:Env):Any = {
			(a,e) match {
			case Macro(a,e) => a
			case ((a,b),e) => (expand(a,e),expand(b,e))
			case ((a,b,c),e) => (expand(a,e),expand(b,e),expand(c,e))
			case ((a,b,c,d),e) => (expand(a,e),expand(b,e),expand(c,e),expand(d,e))
			case (("mac","(",b,")",c),e) => e("macros").asInstanceOf[Stack[(Any,Any)]].push((b,c));0
			case ((a,b,c,d,f),e) => (expand(a,e),expand(b,e),expand(c,e),expand(d,e),expand(f,e))
			case (a,e) => a
			}
		}

		def unapply(arg:(Any,Env)):Option[(Any,Env)]=arg match {
//		case (("add","(",(b,",",c),")"),env) => Some((b,"+",c),env)
		case (a,e) =>
			for((prms,body) <- (e("macros").asInstanceOf[Stack[(Any,Any)]])) {
				var env = match5(prms,a,e)
				if(env != null) return Some(eval(body, env),e)
			}
			None
		}

		def match5(a:Any,b:Any,e:Env):Env = {
			var m = new Env(e)
			def mat(a:Any,b:Any):Boolean = {
				(a,b) match {
				case (Symbol(n),b) => m + (n -> b);true
				case ((a1,a2),(b1,b2)) => mat(a1,b1)&&mat(a2,b2)
				case ((a1,a2,a3),(b1,b2,b3)) => mat(a1,b1)&&mat(a2,b2)&&mat(a3,b3)
				case ((a1,a2,a3,a4),(b1,b2,b3,b4)) => mat(a1,b1)&&mat(a2,b2)&&mat(a3,b3)&&mat(a4,b4)
				case ((a1,a2,a3,a4,a5),(b1,b2,b3,b4,b5)) => mat(a1,b1)&&mat(a2,b2)&&mat(a3,b3)&&mat(a4,b4)&&mat(a5,b5)
				case (a,b) if(a==b)=> true
				case _ => false
				}
			}
			if(mat(a,b)) m
			else null
		}
	}

	def append(a:Any,op:String, b:Any):Any = {
		a match {
		case (a, `op` ,as) => (a,op,append(as,op,b))
		case a => (a,op,b)
		}
	}

	// TODO: Return type change to Option.
	case class Exit extends Exception
	
	// ada expression evaluator
	def eval(a:Any, e:Env):Any = {
		//println("eval("+a+")");
		a match {
		case (a, ":=", b) => val r = eval(b,e);  e + (a -> r); r
		case (a, "=", b) => if(eval(a,e) == eval(b,e))-1 else 0
		case (a, "/=", b) => if(eval(a,e) != eval(b,e))-1 else 0
		
		case (a, ">", b) => (eval(a,e),eval(b,e)) match {
			case (a:Int, b:Int) => if(a > b) -1 else 0
			case (a1, b1) => throw new Error("unknown "+a +" > "+ b +" -> " + a1 + " > "+ b1)
			}

		case (a, "<", b) => (eval(a,e), eval(b,e)) match {
			case (a:Int, b:Int) => if(a < b) -1 else 0
			case (a1, b1) => throw new Error("unknown "+a +" < "+ b +" -> " + a1 + " < "+ b1)
			}

		case (a, "<=", b) => (eval(a,e),eval(b,e)) match {
			case (a:Int, b:Int) => if(a <= b) -1 else 0
			case (a1, b1) => throw new Error("unknown "+a +" <= "+ b +" -> " + a1 + " <= "+ b1)
			}
		case (a, ">=", b) => (eval(a,e),eval(b,e)) match {
			case (a:Int, b:Int) => if(a >= b) -1 else 0
			case (a1, b1) => throw new Error("unknown "+a +" >= "+ b +" -> " + a1 + " >= "+ b1)
			}
		case (a, "++") => val r = e(a); e + (a -> (r.asInstanceOf[Int] + 1)); r
		case (a, "@", b) => eval(a, e); eval(b, e)
		case ("Put", "(", a, ")") => val r = eval(a, e); println(r); r
		case ("evl", "(", b, ")") => eval(eval(Macro.expand(b,e),e),e)
		case (a, "(", b, ")") =>
			(eval(a, e), eval(b, e)) match {
			case (a:Int, b:Int) => a + b
			case (Fun(a, body, e), b) =>
				// arguments bind
				var e2 = new Env(e)
				def bind(a:Any, b:Any) {
					(a, b) match {
					case ((a,",",as),(b,",",bs)) => e2.e + (a -> b); bind(as, bs)
					case (a,b) => e2.e + (a -> b)
					}
				}
				bind(a, b)
				eval(body, e2)
			case (_,_) => throw new Error("unknown function:"+a)
			}
		case ("prc", a, "is", b, "end") => eval((a,"=",b), e)
		case ((a,"(",b,")"),"{",c,"}") => eval((a,"(",append(b,",",Fun("void",c,e)),")"), e)
		case ("q","{",b,"}") => b
		case ("qq","{",b,"}") => Macro.qq(b, e)
		case ("qqq","{",b,"}") => Macro.qq(eval(b,e), e)
		case (a,"{",b,"}") => eval(a, e).asInstanceOf[Int] * eval(b, e).asInstanceOf[Int]
		case (a,"[",b,"]") => eval(a, e).asInstanceOf[Int] - eval(b, e).asInstanceOf[Int]
		case ("(",a,")") => eval(a, e)
		case ("{",a,"}") => eval(a, e)
		case (a,"+",b) => eval(a, e).asInstanceOf[Int] + eval(b, e).asInstanceOf[Int]
		case (a,"*",b) => eval(a, e).asInstanceOf[Int] * eval(b, e).asInstanceOf[Int]
		case (a,"-",b) => eval(a, e).asInstanceOf[Int] - eval(b, e).asInstanceOf[Int]
		case (a,"/",b) => eval(a, e).asInstanceOf[Int] / eval(b, e).asInstanceOf[Int]
		case (a,",",b) => (eval(a, e),",",eval(b,e))
		case ("-",a)   => -(eval(a, e).asInstanceOf[Int])
		case (a, ";")  => eval(a, e)
		case ("if", b, "end") => eval(b, e)
		case ("cas", a, "is", b, "end") => val (ra, rb) = when_ (eval(a, e), b, e); rb
		case (a,"thn", (b,"els",c)) => if(eval(a,e) != 0) eval(b, e) else eval(c, e)
		case (a,"thn", (b,"elf",c)) => if(eval(a,e) != 0) eval(b, e) else eval(c, e)
		case (a,"thn", b) => if(eval(a,e) != 0) eval(b, e) else 0
		case ("fun", (a,  "(", p, ")" ) ,"is", b, "end") =>  val r = Fun(p,b,e);  e + (a -> r); r
		case ("fun", "(", p, ")",  b) =>  val r = Fun(p,b,e);  e + (a -> r); r
		case (a,"..",b) => (eval(a,e),"..",eval(b,e))
		case ("for", (name,"in", range), "lop", body, "end") if(eval(range, e)match{case (a,"..",b)=>true case _ => false}) =>
			var (a,_,b)=eval(range,e)

			var rc:Any = 0
			try {
				var min = eval(a,e).toString.toInt
				var max = eval(b,e).toString.toInt
				if(min > max) {
					min = -min
					max = -max
					for(i <- min to max ) {
						e + (name -> -i)
						rc = eval(body,e)
					}
				} else {
					for(i <- min to max ) {
						e + (name -> i)
						rc = eval(body,e)
					}
				}
			} catch {
			case Exit() =>
			}
			rc
		case ("reverse",(a,b,c)) => (c, b, a)
		case "ext" => throw Exit()
		case ("whl", a, "lop", body, "end") =>
			var rc:Any = 0
			try {
				while(eval(a ,e) != 0) {
					rc=eval(body, e)
				}
			} catch {
			case Exit() =>
			}
			rc
		case a:Fun => a
		case a:String => e(a)
		case a:Symbol => a
		case a:Int => a
		case a => throw new Error("runtime error " + a)
	}}
	def dbg(a:Any):Any = {
		println("dbg "+a)
		a
	}

	def when_(a:Any, b:Any, e:Env):(Boolean, Any) = {
		def whenOr(a:Any, b:Any, e:Env):Boolean = {
			b match {
			case (xs,"|",x) => whenOr(a,xs,e)|| (x==a)
			case x          => (x==a)
			}
		}
		b match {
		case ("whn", ("oth", "=>", y)) => (true, y)
		case ("whn", (x, "=>", y)) => if(whenOr(a,x,e))(true,eval(y,e)) else (false,null)
		case (x, "@", y) => val(ra, rb) = when_(a, x, e); if(ra) (ra,rb) else when_(a, y, e)
		case _ => throw new Error("error expected when but found "+b)
		}
	}

}