メッセンジャー用電卓スクリプト

メッセンジャーでの会話で計算ができます。
http://homepage2.nifty.com/magicant/sjavascript/mt.html
ここのメルセンヌツイスタ乱数ライブラリを同じフォルダに置いてください。


使い方
自分と相手どちらからも使えます。
一番初めに=(イコール)、続けて式を書きます。
スペース・タブ・改行は無視されます。



=1+1
2
=-1+2
1
=1+2*3
7
=(1+2)*3
9
=1.5*2
3
=tan(pi/4)
1くらい


rnd 0.0以上1.0未満の乱数
rnd(n) 0以上n未満の整数乱数
pi 円周率
sin,cos,tan,asin,acos,atan

function OnEvent_Initialize(MessengerStart){
	mt = new MersenneTwister();
	Debug.ClearDebuggingWindow();
	Debug.Trace("Init");
	try{
		createTokenList("=rnd");
		Debug.Trace("> " + Program());
	}catch(e){
		Debug.Trace(e);
	}
}

function OnEvent_Uninitialize(MessengerExit){
	Debug.Trace("Uninit");
}

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]+/},
	{type: "EQUAL", expression: /=/},
	{type: "OP1", expression: /[\+\-]/},
	{type: "OP2", expression: /[\*\/]/},
	{type: "BRACE", expression: /[\(\)]/}
];

var FUNCTION = {
	pi : function(){
		return Math.PI;
	},
	rnd : function(n){
		if(n){
			return mt.nextInt(n);
		}
		else{
			return mt.next();
		}
	},
	sin : function(pi){
		return Math.sin(pi);
	},
	cos  : function(pi){
		return Math.cos(pi);
	},
	tan : function(pi){
		return Math.tan(pi);
	},
	asin: function(x){
		return Math.asin(x);
	},
	acos: function(x){
		return Math.acos(x);
	},
	atan: function(x){
		return Math.atan(x);
	}
};

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 = Unary();
	while(peekToken().type == "OP2"){
		var op = nextToken();
		var right = Unary();
		switch(op.image){
		case "*":
			val *= right;
			break;
		case "/":
			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) | Value
function Factor(){
	if(peekToken().image == "("){
		ensureNextToken("(");
		var value = Expression();
		ensureNextToken(")");
		return value;
	}
	else{
		return Value();
	}
}

function Value(){
	var token = nextToken();
	if(token.type == "NUMBER"){
		return parseFloat(token.image);
	}
	else if(token.type == "IDENT"){
		var func = FUNCTION[token.image];
		if(!func){
			throw "fucntion " + token.image + " not found";
		}
		var arg = null;
		if(peekToken().image == "("){
			ensureNextToken("(");
			arg = Expression();
			ensureNextToken(")");
		}
		return func(arg);
	}
	else{
		throw token.image;
	}
}

function OnEvent_ChatWndReceiveMessage(ChatWnd, Origin, Message, MsgKind){
	try{
		createTokenList(Message);
		ChatWnd.SendMessage("> " + Program());
	}catch(e){
		Debug.Trace(e);
	}
	return Message;
}


throw/try/catch使えるんですね。助かりました。
小数の正規表現が適当だとかValueってなんだよとか気にしてはいけません。なんだかんだで結構使えそうになったものの、結構その場しのぎが多いです。
string.matchの結果が配列くさくてまいりました。lengthで必ず1になるとか。
あと先頭マッチの^使ったらおかしくなった・・・。
おかげで非常に無駄が・・・。
だれかヘルプ。