メッセンジャー用電卓スクリプト2
電卓ってさあ・・・
コンパイラでいうHello, World!だよね・・・。
ほんと何やってんだろ・・・。
caper
http://naoyuki.hirayama.googlepages.com/caper.html
っていうJavaScriptコードも吐けるLALR(1)パーザジェネレータも見つけたしこれ使ってもいいんじゃねーの?逆に面倒かね。
id:yappy_t:20090208の改良でございます(これってもっときれいな書き方ないのか?)。
文法(表現方法と演算子優先順位はフィーリングで)
剰余演算子%と累乗演算子^と2つ以上の引数を持つ関数に対応しました。小数に%を使ってどうなるかは知りません。
atan2で
Program := "=" Expr Expr := ["+""-"]Expr | Expr ["+""-""*""/""%""^"] Expr | Term Term :=| ( "(" Expr ("," Expr)* ")" )?
BNFと正規表現とJavaCCの混ざった気持ち悪い表現だこと!!
そういえばこれfunc()っていう書き方に対応してないね・・・。○pi×pi()。どうでもいいや。
複数引数対応もあり、使用できる関数が増えました。
min/max関数は任意の個数の引数をとれます。
logは底を省略するとeになります。
引数の個数はあんまりチェックしてません。めんどい。
e max(x1, x2, x3, ...) min(x1, x2, x3, ...) log(x) log(a, b) atan2(y, x)
その他もろもろ
ソースを見ればいいと思うよ!!
function OnEvent_Initialize(MessengerStart){ mt = new MersenneTwister(); Debug.ClearDebuggingWindow(); Debug.Trace("Init"); /* try{ createTokenList("=atan2(1,1)"); Debug.Trace("> " + Program()); }catch(e){ Debug.Trace(e); } */ } function OnEvent_Uninitialize(MessengerExit){ Debug.Trace("Uninit"); } function OnEvent_ChatWndReceiveMessage(ChatWnd, Origin, Message, MsgKind){ try{ createTokenList(Message); ChatWnd.SendMessage("> " + Program()); }catch(e){ Debug.Trace(e); } return Message; } var mt; var ind; var tokenList; function nextToken(){ if(ind >= tokenList.length) throw "No token"; return tokenList[ind++]; } function ensureNextToken(image){ var token = nextToken(); if(token.image != image) throw image + " requred but " + token.image; } function peekToken(){ if(ind >= tokenList.length) return {}; return tokenList[ind]; } var SKIP = /\s+/; var TOKEN = [ {type: "NUMBER", expression: /\d+(?:\.\d+)?/}, {type: "IDENT", expression: /[a-zA-Z][a-zA-z0-9]*/}, {type: "EQUAL", expression: /=/}, {type: "OP1", expression: /[\+\-]/}, {type: "OP2", expression: /[\*\/\%]/}, {type: "OP3", expression: /\^/}, {type: "BRACE", expression: /[\(\)]/}, {type: "COMMA", expression: /\,/} ]; var FUNCTION = { min : function(args){ var result = args[0]; for(var i=1; i<args.length; i++){ result = Math.min(result, args[i]); } return result; }, max : function(args){ var result = args[0]; for(var i=1; i<args.length; i++){ result = Math.max(result, args[i]); } return result; }, pi : function(){ return Math.PI; }, e: function(){ return Math.E; }, rand : function(args){ if(args.length >= 1){ return mt.nextInt(args[0]); } else{ return mt.next(); } }, abs : function(args){ return Math.abs(args[0]); }, exp : function(args){ return Math.exp(args[0]); }, log : function(args){ if(args.length >= 2) return Math.log(args[1]) / Math.log(args[0]); else return Math.log(args[0]); }, sqrt : function(args){ return Math.sqrt(args[0]); }, rad : function(args){ return args[0] * Math.PI / 180; }, deg : function(args){ return args[0] * 180 / Math.PI; }, sin : function(args){ return Math.sin(args[0]); }, cos : function(args){ return Math.cos(args[0]); }, tan : function(args){ return Math.tan(args[0]); }, asin: function(args){ return Math.asin(args[0]); }, acos: function(args){ return Math.acos(args[0]); }, atan: function(args){ return Math.atan(args[0]); }, atan2: function(args){ return Math.atan2(args[0], args[1]); }, sinh: function(args){ return (Math.exp(args[0]) - Math.exp(-args[0])) / 2; }, cosh: function(args){ return (Math.exp(args[0]) + Math.exp(-args[0])) / 2; }, tanh: function(args){ return (Math.exp(args[0]) - Math.exp(-args[0])) / (Math.exp(args[0]) + Math.exp(-args[0])); } }; function createTokenList(str){ ind = 0; tokenList = []; LOOP: while(str.length != 0){ //Debug.Trace(str); { var mstr = str.match(SKIP); if(mstr && RegExp.index==0){ str = str.substr(mstr[0].length); } //Debug.Trace("skip: " + mstr); } for(var i=0; i<TOKEN.length; i++){ var mstr = str.match(TOKEN[i].expression); if(mstr && RegExp.index==0){ str = str.substring(mstr[0].length); var token = {}; token.type = TOKEN[i].type; token.image = mstr[0]; tokenList.push(token); //Debug.Trace("token: " + mstr); continue LOOP; } } throw "Did not match"; } } // =Expression function Program(){ ensureNextToken("="); return Expression(); } function Expression(){ return AddSub(); } function AddSub(){ var val = MulDiv(); while(peekToken().type == "OP1"){ var op = nextToken(); var right = MulDiv(); switch(op.image){ case "+": val += right; break; case "-": val -= right; break; default: throw "Internal error: " + op.image; } } return val; } function MulDiv(){ var val = Pow(); while(peekToken().type == "OP2"){ var op = nextToken(); var right = Pow(); switch(op.image){ case "*": val *= right; break; case "/": val /= right; break; case "%": val %= right; break; default: throw "Internal error: " + op.image; } } return val; } function Pow(){ var val = Unary(); while(peekToken().type == "OP3"){ var op = nextToken(); var right = Unary(); switch(op.image){ case "^": val = Math.pow(val, right); break; default: throw "Internal error: " + op.image; } } return val; } function Unary(){ var val = 1; while(peekToken().type == "OP1"){ var op = nextToken(); switch(op.image){ case "+": break; case "-": val *= -1; break; default: throw "Internal error: " + op.image; } } return val * Factor(); } // (Expression) | Term function Factor(){ if(peekToken().image == "("){ ensureNextToken("("); var value = Expression(); ensureNextToken(")"); return value; } else{ return Term(); } } function Term(){ var token = nextToken(); // Number Literal if(token.type == "NUMBER"){ return parseFloat(token.image); } // Function // IDENT ("(" Expr ("," Expr)* ")")? else if(token.type == "IDENT"){ var func = FUNCTION[token.image]; if(!func){ throw "fucntion " + token.image + " not found"; } var args = []; if(peekToken().image == "("){ ensureNextToken("("); args.push(Expression()); while(peekToken().image == ","){ ensureNextToken(","); args.push(Expression()); } ensureNextToken(")"); } return func(args); } else{ throw token.image; } }