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; } } }