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

前回は括弧演算子を作りました。今回は後置括弧演算子を作ります。
後置括弧演算子とは、以下のような形のものとします。

  • 関数呼び出し
a()
  • 配列アクセス
a[]
  • ブロック
a{}

C言語だと、関数呼び出しと配列呼び出し等にあたるものです。
{}はrubyのブロックのようなものに使うとよいでしょう。
まずは、2項演算子と同じように作ります。

	var opMs:Hash<Int>;

まずはこんな風にハッシュを用意

		opMs.set("(", 0);
		opMs.set("{", 0);
		opMs.set("[", 0);

でもって、テーブルに括弧を定義

			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, "opM"+tag+tag2, e);
				continue;
			}

expn関数内の2項演算子の箇所をコピペして、予測される括弧をeat関数でチェック&読み込みを追加しておしまい。


さて今回はこの後置括弧演算子を使ってprint文のような組み込み関数を実装します。

execute関数内でこのようにします。switch文が3つ重なってますが、要するに
op(sym("println"),"opM()", ...);だった場合の処理となっています。

			case "opM()":
				switch(l) {
				case sym(n):
					switch(n) {
					case "println":
						var r = execute(r);
						trace(r+"");
						return r;
					default:
						return throw "error";
					}
				default: throw "error";
				}

次回は関数を追加して、a=fun(c)c+1 a(2)で、3が返る。というようなことをしようと思います。

以下ソースです。

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

class Calc7 {
	static function main() {
		var c = new Calc7();
		trace(c.eval("a=3 println(a) b=a+2 println(b) "));
	}
	var opLs:Hash<Int>;
	var opRs:Hash<Int>;
	var opPs:Hash<String>;
	var opMs:Hash<Int>;
	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>();
		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);
	}

	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):Int {
		var exp = parse(str);
		trace(exp);
		env = new Hash<Int>();
		return execute(exp);
	}

	function parse(str:String):Exp {
		src = str;
		lex();
		return expn(0);
	}
	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 t = fact();
		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, "opM"+tag+tag2, e);
				continue;
			}
			if(opLs.exists(token) && (tagp = opLs.get(token)) > p) {
				var tag = lex();
				t = op(t, "opL"+tag, expn(tagp));
				continue;
			}
			if(opRs.exists(token) && (tagp = opRs.get(token)) >= p) {
				var tag = lex();
				t = op(t, "opR"+tag, expn(tagp));
				continue;
			}
			break;
		}
		return t;
	}

	function fact():Exp {
		var t = lex();
		if(opPs.exists(t+"")) {

			var t2 = expn(0);
			if(lex() != opPs.get(t)) throw ("error");
			return op(nil, "opP()", t2);
		}
		var numReg : EReg = ~/[0-9]+/;
		if(numReg.match(t)) {
			return num(Std.parseInt(t));
		}
		return sym(t);
	}
	var env:Hash<Int>;
	
	function execute(exp:Exp):Int {
		switch(exp) {
		case vid: return 0;
		case nil: return 0;
		case num(d): return d;
		case op(l, tag, r):
			switch (tag) {
			case "@": execute(l); return execute(r);
			case "opM()":
				switch(l) {
				case sym(n):
					switch(n) {
					case "println":
						var r = execute(r);
						trace(r+"");
						return r;
					default:
						return throw "error";
					}
				default: throw "error";
				}
			case "opP()": return execute(r);
			case "opL+": return execute(l) + execute(r);
			case "opL-": return execute(l) - execute(r);
			case "opL*": return execute(l) * execute(r);
			case "opL/": return cast(execute(l) / execute(r), Int);
			case "opR=":
				var rc = execute(r);
				switch(l){
				case sym(d):
					env.set(cast(d, String), rc);
					return rc;
				default: throw "error";
				}
			default: return 0;
			}
		case sym(d): return env.get(d);
		}
	}
}

Calc7.hxml

-swf Calc7.swf
-main Calc7