haXeで作るプログラミング言語(8)

今回は関数の定義を追加してa=fun(c)c+1 a(2)で、3が返るようにします。
そのために、文演算子というものを用意します。
演算子?はぁ???なにそれ?って思うと思いますが、
要するに

symbol ( param1 ) param2

というようにかける物を文演算子と呼ぶことにします。

この文演算子を使って

fun(a,b) a+b

というような関数定義を行うことができます。
というわけで、パーサを作ります。これはほとんど前回の後置括弧演算子と一緒ですが、
シンボルのチェックが入るところが変わります。括弧演算子でかつ、シンボルだったらパラメータを後ろに強制的に
つけなければならないというようなことをします。


また、関数の実行も今回出来るように書き加えます。
関数の実行はまず、関数を変数に入っていれば、取り出す。
そして、M()演算子であれば、左辺が関数ならば、まず、関数の引数を変数にbindして結合する。
次にその関数の本体を実行する。
値を保存し、また環境を元に戻すということをするとできます。
さて、これで関数を実行することが出来ます。


それ以外にも、値としてExpを返すようにしたり、いろいろ変更加わりまくってます。
わかりにくくて申し訳ない感がありますけど、あとはまぁ、以下を参考にしてください。<なげやり

以下ソースです。

enum Exp {
	vid;
	nil;
	num(d:Int);
	sym(d:String);
	op(l:Exp, tag:String, r:Exp);
}

class Calc8 {
	static function main() {
		var c = new Calc8();
		trace(c.eval("a=fun(b)b+1 a(3)"));
	}
	var opLs:Hash<Int>;
	var opRs:Hash<Int>;
	var opPs:Hash<String>;
	var opMs:Hash<Int>;
	var opSs:Hash<String>;
	var endPs:Hash<Int>;
	function new(){
		opLs = new Hash<Int>();
		opRs = new Hash<Int>();
		opPs = new Hash<String>();
		opMs = new Hash<Int>();
		endPs = new Hash<Int>();
		opSs = new Hash<String>();
		opLs.set("+", 10);
		opLs.set("-", 10);
		opLs.set("*", 20);
		opLs.set("/", 20);
		opRs.set("=", 5);
		opPs.set("(",")");
		opPs.set("[","]");
		opPs.set("{","}");
		opMs.set("(", 0);
		opMs.set("{", 0);
		opMs.set("[", 0);
		endPs.set(")", 0);
		endPs.set("]", 0);
		endPs.set("}", 0);
		opSs.set("fun","(");
		opSs.set("if","(");
	}

	var src:String;
	var token:String;
	var token2:String;

	function lex():String {
		var r : EReg = ~/^[\r\n\t ]*([0-9]+|[+*\-\/\[\]{}()=]|[a-zA-Z_][a-zA-Z0-9]*)/;
		token2 = token;
		if(r.match(src)){
			token = r.matched(1);
			src = src.substr(r.matched(0).length);
			return token2;
		}
		token = "";
		return token2;
	}

	function eval(str:String):Exp {
		var exp = parse(str);
		trace(exp);
		env = new Hash<Exp>();
		return execute(exp);
	}

	function parse(str:String):Exp {
		src = str;
		lex();
		var rc = expn(0);
		while (token != "") {
			rc = op(rc, "@", expn(0));
		}
		return rc;
	}
	function eat(e:String):String {
		var t = lex();
		if(t != e) throw ("expect error " + e);
		return t;
	}

	function expn(p:Int):Exp {
		if(endPs.exists(token)) return vid;
		var tk = lex();
		var numReg : EReg = ~/[0-9]+/;
		var t:Exp;

		if (opSs.exists(tk) && opSs.get(tk) == token) {
			var p1 = lex();
			var e = expn(0);
			var op2 = eat(opPs.get(p1));
			t = op(e, "S"+tk+p1+op2, expn(p));
		} else
		if(opPs.exists(tk + "")) {
			var t2 = expn(0);
			if(lex() != opPs.get(tk)) throw ("error");
			t = op(nil, "P()", t2);
		} else if(numReg.match(tk)) {
			t = num(Std.parseInt(tk));
		} else {
			t = sym(tk);
		}

		var tagp:Int;
		while(true) {
			if(opMs.exists(token) && (tagp = opMs.get(token)) >= p) {
				var tag = lex();
				var e = expn(tagp);
				var tag2 = eat(opPs.get(tag));
				t = op(t, "M"+tag+tag2, e);
				continue;
			}
			if(opLs.exists(token) && (tagp = opLs.get(token)) > p) {
				var tag = lex();
				t = op(t, "L"+tag, expn(tagp));
				continue;
			}
			if(opRs.exists(token) && (tagp = opRs.get(token)) >= p) {
				var tag = lex();
				t = op(t, "R"+tag, expn(tagp));
				continue;
			}
			break;
		}
		return t;
	}

	var env:Hash<Exp>;
	
	function bind(env, prm, l):Void{
		switch(prm) {
		case sym(p): env.set(p,l); return;
		case op(p,t,ps):
			switch(l){
			case op(lp, lt, lps):
				bind(env, p, l);
				bind(env, ps, lps);
			default: throw "error";
			}
		default: throw "error";
		}
	}

	function execute(exp:Exp):Exp {
		switch(exp) {
		case vid: return num(0);
		case nil: return num(0);
		case num(d): return num(d);
		case sym(d): return env.get(d);
		case op(l, tag, r):
			switch(tag){
			case "Sfun()": return exp;
			case "P()": return execute(r);
			default:
			}
			switch(l) {
			case sym(d):
				switch(tag){
				case "R=":
					var rc = execute(r);
					env.set(d, rc);
					return rc;
				case "M()":
					if(d=="println"){
						var r = execute(r);
						return r;
					}
				default:
				}
			default:
			}
			// 左辺を評価
			var a = execute(l);
			switch (a) {
			case num(a):
				var b = execute(r);
				switch(b){
				case num(b):
					switch (tag) {
					case "L+": return num(a + b);
					case "L-": return num(a - b);
					case "L*": return num(a * b);
					case "L/": return num(cast(a / b, Int));
					}
					default:
				}
			default:
			}
			switch(tag) {
			case "@": return execute(r);
			case "M()":
				switch(a){
				case op(prm,fun,body):
					if(fun=="Sfun()"){
						var back = env;
						env = new Hash<Exp>();
						bind(env, prm, r);
						a = execute(body);
						env = back;
						return a;
					}
				default:
				}
			default:
			}
			return a;
		}
	}
}