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