User:Stewbasic/calc.js

var sp="[ \t\r\n]*"; var varname="[a-zA-Z][a-zA-Z0-9_]*";

function warn(s){ var err=getvar("error"); err.let(err.eval+s); }

function Variable(s){ this.v=0; this.a=0; this.name=s; } Variable.prototype.eval=function{ if(this.jcInput){ var s=this.jcInput.val; if(this.type=='int') s=parseInt(s); if(this.type=='float') s=parseFloat(s); if(s==NaN) s=0; if(this.range){ var parts=this.range.split('-'); if(parts[0]!=''&&sparseFloat(parts[1])) s=parseFloat(parts[1]); }		this.let(s); }else if(this.jcRadio){ this.v=this.jcRadio.filter(':checked').val; }else if(this.jcCheck){ this.v=this.jcCheck.is(':checked')?1:0; }	return this.v; } Variable.prototype.let=function(v){ this.v=v; if(this.jcOutput) this.jcOutput.text(v); if(this.jcInput) this.jcInput.val(v); if(this.jcRadio){ v=parseInt(v); this.jcRadio.filter('[value='+v+']').attr('checked', true); }	if(this.jcCheck) this.jcCheck.attr('checked', v!=0); if(this.jcToggle){ this.v=(this.v)?1:0; this.jcToggle.attr('bgcolor',(this.v)?'#009900':'#FFFFFF'); } }

var varlist=new Object; function getvar(s){ if(!s.match(new RegExp(varname))){ warn("Invalid variable name "+s); s.replace(/[^a-zA-Z0-9_]/g,""); s='x'+s; }	if(varlist[s]) return varlist[s]; return varlist[s]=new Variable(s); }

function Expression{ this.ls=[]; this.args=1;	// used to check the expression is balanced while parsing } Expression.prototype.ops=[ {s:'+',a:2,eval:function(y,x){return x+y}}, {s:'-',a:2,eval:function(y,x){return x-y}}, {s:'*',a:2,eval:function(y,x){return x*y}}, {s:'/',a:2,eval:function(y,x){return x/y}}, {s:'=',a:2,eval:function(y,x){return (x==y)?1:0}}, {s:'!=',a:2,eval:function(y,x){return (x!=y)?1:0}}, {s:'<',a:2,eval:function(y,x){return (x',a:2,eval:function(y,x){return (x>y)?1:0}}, {s:'%',a:2,eval:function(y,x){return x%y}}, {s:'!',a:1,eval:function(x){return x?0:1}}, {s:'floor',a:1,eval:Math.floor}, {s:'ceil',a:1,eval:Math.ceil} ]; Expression.prototype.evaluate=function{ var stack=[]; for(i in this.ls){ var token=this.ls[i]; if(typeof(token)=='number'||typeof(token)=='string') stack.push(token); else if(typeof(token)=='object') switch(token.a){ case 0: stack.push(token.eval); break; case 1: stack.push(token.eval(stack.pop)); break; case 2: stack.push(token.eval(stack.pop,stack.pop)); break; }	}	return stack[0]; } Expression.prototype.push=function(t){ var m;	if(t.match(new RegExp("^"+sp+"$"))!=null) return true; for(i in this.ops){ var op=this.ops[i]; if(t==op.s){ this.ls.push(op); if((this.args+=op.a)>this.ls.length) return "Unbalanced"; return true; }	}	if(m=t.match(new RegExp("^"+sp+"("+varname+")"+sp+"$"))){ this.ls.push(getvar(m[1])); return true; }	var n=parseFloat(t); if(n==NaN) return "Unrecognised token"; this.ls.push(n); return true; } function parseExpression(s){ var outp=new Expression; var quotes=s.split('"');	for(i in quotes) if((i%2)==0){		var tokens=quotes[i].split(" ");		for(j in tokens){			var res=outp.push(tokens[j]);			if(res!=true){				warn(res+" in expression ("+s+") at token "+outp.ls.length);				return parseExpression('0');			}		}	}else outp.ls.push(quotes[i]);	if(outp.args!=outp.ls.length){		warn("Too few operators in expression ("+s+")"+outp.args+"!="+outp.ls.length);		return parseExpression('0');	}	return outp; } function If(e,s1,s2){	this.e=e;	this.s1=s1;	this.s2=s2;	this.next=null; } If.prototype.run=function{	if(this.e.evaluate==0){		if(this.s2) this.s2.run;	}else{		if(this.s1) this.s1.run;	}	if(this.next) this.next.run; } If.prototype.push=function(e){	if(this.s1==null){s1=e; return false;}	if(this.s2==null) s2=e;	return true; } function Let(s,e){	this.v=getvar(s);	this.e=e;	this.next=null; } Let.prototype.run=function{	this.v.let(this.e.evaluate); if(this.next) this.next.run; } function parseStatement(s){ if(!s) return null; var ifre=new RegExp("^"+sp+"if"+sp+"\\(([^)]+)\\)"+sp+"{");	var letre=new RegExp("^"+sp+"let"+sp+"\\("+sp+"("+varname+")"+sp+","+"([^)]+)\\)"); function parseOneStatement{ var m,outp=null; if(m=s.match(ifre)){ s=s.substring(m[0].length); var e=parseExpression(m[1]); var s1=parseOneStatement; if(m=s.match(new RegExp("^"+sp+"{"))){ s=s.substring(m[0].length); s2=parseOneStatement; }else s2=null; outp=new If(e,s1,s2); }else if(m=s.match(letre)){ s=s.substring(m[0].length); outp=new Let(m[1],parseExpression(m[2])); }else if(m=s.match(new RegExp("^"+sp+"}"))) s=s.substring(m[0].length); if(outp) outp.next=parseOneStatement; return outp; }	return parseOneStatement; } $(document).ready(function{	$('.jcInput').each(function { var options=$(this).text.split('|',5); var $input = $(' '); $(this).empty.append($input); var v=getvar(options[0]); v.jcInput=$input; v.type=options[1]; $input.val(options[2]); v.range=options[3]; var e=parseStatement(options[4]); if(e) $input.change(function{return e.run}); });	$('.jcRadio').each(function { var options=$(this).text.split('|'); $(this).empty; var e=parseStatement(options[options.length-1]); var v=getvar(options[0]); for(i=1;i'); $(this).append($input); $(this).append(options[i]+' '); if(e) $input.click(function{return e.run}); }		v.jcRadio=$('input:radio[name='+v.name+']'); });	$('.jcCheck').each(function { var options=$(this).text.split('|',2); var $input = $(' '); $(this).empty.append($input); var v=getvar(options[0]); v.jcCheck=$input; var e=parseStatement(options[1]); if(e) $input.click(function{return e.run}); });	$('.jcToggle').each(function { var v=getvar($(this).attr('id')); v.jcToggle=$(this); $(this).click(function{return v.let(v.v==0)}); });	$('.jcButton').each(function { var options=$(this).text.split('|',2); var $input = $(' ').attr({type:'submit'}).val(options[0]); $(this).empty.append($input); var e=parseStatement(options[1]); if(e) $input.click(function{return e.run}); });	$('.jcOutput').each(function { var options=$(this).text.split('|',2); var v=getvar(options[0]); v.jcOutput=$(this); if(options[1]) v.let(options[1]); else $(this).empty; }); });