topical media & game development
graphic-canvas-example-function-parse.js / js
********************************************************\
*
Created by Benjamin Joffe *
*
This is a function that takes a string such as: *
*
x^sin(x) *
*
And will turn it into a JavaScript evaluatable string: *
*
Math.pow(x, Math.sin(x)); *
*
*
\**********************************************************
Math.csc = function(x){
return 1 / Math.sin(x);
};
Math.sec = function(x){
return 1 / Math.cos(x);
};
Math.cot = function(x){
return 1 / Math.tan(x);
};
Math.acsc = function(x){
return Math.asin(1/x);
};
Math.asec = function(x){
return Math.acos(1/x);
};
Math.acot = function(x){
return Math.atan(1/x);
};
Math.sinh = function(x){
x = Math.exp(x);
return (x - 1/x) / 2;
};
Math.cosh = function(x){
x = Math.exp(x);
return (x + 1/x) / 2;
};
Math.tanh = function(x){
x = Math.exp(x);
return (x*x - 1) / (x*x + 1);
};
Math.asinh = function(x){
return Math.log(x + Math.sqrt(1+x*x));
};
Math.acosh = function(x){
return 2 * Math.log( Math.sqrt((x+1)/2) + Math.sqrt((x-1)/2) );
};
Math.atanh = function(x){
return (Math.log(1+x) - Math.log(1-x)) / 2;
};
Math.csch = function(x){
x = Math.exp(x);
return 2 / (x - 1/x);
};
Math.sech = function(x){
x = Math.exp(x);
return 2 / (x + 1/x);
};
Math.coth = function(x){
x = Math.exp(x);
return (x*x + 1) / (x*x - 1);
};
Math.acsch = function(x){
x = 1/x;
return Math.log(x + Math.sqrt(1+x*x));
};
Math.asech = function(x){
x = 1/x;
return 2 * Math.log( Math.sqrt((x+1)/2) + Math.sqrt((x-1)/2) );
};
Math.acoth = function(x){
x = 1/x;
return (Math.log(1+x) - Math.log(1-x)) / 2;
};
// Enough Trig!!!
Math.log10 = function(x){
return Math.log(x)/2.302585092994046;
};
Math.sqr = function(x){
return x*x;
};
Math.sign = function(x){
return x > 0 ? 1 : x < 0 ? -1 : x==0 ? 0 : undefined;
};
function parseMath(a){
a = a.split(' ').join('').toLowerCase().split('');
if (a.length==0) return [false, 'Error 000. Nothing entered'];
var c_alpha = "abcdefghijklmnopqrstuvwxyz";
var c_numero = "0.123456789";
var c_opera = ",+-*/%^";
var c_all = c_alpha + c_numero + c_opera + '()';
var i,j,k;
var level=0;
var word;
var decimal;
var level2;
var mode = 'ready';
i=0, j;
var functionList = '\
-sin-cos-tan-\
-asin-acos-atan-\
-csc-sec-cot-\
-acsc-asec-acot-\
-sinh-cosh-tanh-\
-asinh-acosh-atanh-\
-csch-sech-coth-\
-acsch-asech-acoth-\
';
functionList += '-sqrt-exp-abs-sign-sqr-log-';
while (i<a.length) switch (mode) {
case 'end' :
if (a[i]=='+' || a[i]=='-' || a[i]=='*' || a[i]=='/') {
i++;
mode = 'ready';
break;
}
if (a[i]=='^') {
mode = 'power';
break;
}
if (a[i]=='(') {
a.splice(i,0,'*'), i++;
mode = 'ready';
break;
}
if (a[i]==')') {
if (level<1) return [false, '<A href="http://Error 002. Grouping symbol error'];
level--;
i++;
break;
}
if (c_numero.indexOf(a[i])>=0) {
a.splice(i,0,'*'), i++;
mode = 'numero';
break;
}
if (c_alpha.indexOf(a[i])>=0) {
a.splice(i,0,'*'), i++;
mode = 'alpha';
break;
}
return [false, 'Error 001. Invalid character after a term'];
case 'ready' :
for (; i<a.length; i++) {
if (a[i]=='-') {
if (i>0) {
if (a[i-1]=='-') a.splice(i-1,2,'+'), i--;
else if (a[i-1]=='+') a.splice(i-1,1), i--;
}
continue;
}
if (a[i]=='+') {
if (i>0 && (a[i-1]=='-' || a[i-1]=='+')) a.splice(i,1), i--;
continue;
}
if (c_numero.indexOf(a[i])>=0) {
mode='numero';
break;
}
if (c_alpha.indexOf(a[i])>=0) {
mode='alpha';
break;
}
if (a[i]=='(') {
level++;
continue;
}
if (a[i]==')') return [false, 'Error 002. Grouping symbol error'];
return [false, 'Syntax Error (mode: ready)'];
}
break;
case 'numero' :
decimal = a[i++]=='.';
for (; i<a.length; i++) {
if (c_numero.indexOf(a[i])>=0) {
if (a[i]=='.') {
if (decimal) return [false, 'Error 003. Decimal place error'];
decimal=true;
}
continue;
}
mode = 'end';
break;
}
break;
case 'alpha' :
word = a[i++];
for (; i<a.length; i++) {
if (c_alpha.indexOf(a[i])>=0) word+=a[i];
else break;
}
if (i<a.length && c_numero.indexOf(a[i])>=0) return [false, 'Error 004. Numeral illegally following a pronumeral'];
mode = 'end';
if (word=='pi') a.splice(i-2, 2, '3.141592653589793'), i-=1;
else if (word=='e') a[i-1]='2.718281828459045';
else if (word=='phi') a.splice(i-3, 3, '1.618033988749895'), i-=2;
else if (word=='rand') a.splice(i-4, 4, '(','Math.random(',')',')');
else if (functionList.indexOf('-'+word+'-')>=0) {
if (i<a.length && a[i]=='(') {
a.splice(i-word.length, word.length+1, 'Math.'+word+'(');
i-=word.length-1;
level++;
mode = 'ready';
}
else return [false, 'Error 005. Function: <B>'+word+'</B> is being treated as a constant'];
}
else if (word!='x' && word!='y') {
return [false, 'Error 006. Unknown variable: <B>'+(word.length<13 ? word : word.substr(0,13)+'...')+'</B>'];
}
break;
case 'power' :
a[i]=',';
// look left...
level2=0;
for (j=i-1; j>=0; j--) {
if (a[j]==')') level2++;
else if (level2==0 && (a[j].indexOf('(')>=0 || a[j]==',' || a[j]=='+' || a[j]=='-' || a[j]=='*' || a[j]=='/')) {
break;
}
else if (a[j].indexOf('(')>=0) level2--;
}
a.splice(j+1, 0, 'Math.pow('), i++;
// look right...
decimal = false; // decimal means not a + or a -
level2 = 0;
for (j=i+1; j<a.length; j++) {
if (a[j]!='+' && a[j]!='-') decimal=true;
if (a[j]=='(') level2++;
else if (decimal && level2==0 && (a[j]==')' || a[j]=='+' || a[j]=='-' || a[j]=='*' || a[j]=='/')) {
break;
}
else if (a[j]==')') level2--;
}
a.splice(j, 0, ')'); // no need to increment i, this is after i,
level++;
i++
mode = 'ready';
break;
default : i++;
}
if (level!=0) return [false, 'Error 008. Grouping symbol error'];
if (mode=='start' || mode=='ready') return [false, 'Error 007. Invalid final character'];
return [true, a.join('')];
}
(C) Æliens
20/2/2008
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.