Scripting the Equation grapher by variable
ericlin@ms1.hinet.net

For programing language, when we write script in script editor, it will compile the script in two way. One is for handling the constants. If we write script x=3+5-7; It will parse "3+5-7" and get the result as 1 and then compile it into codes similar to x=1; But, if we write script of math expression including variables such as x=3-a+b; It will generate a procedures that handles a and b. It does not "parse" the string each time when the value of a or b changes.

From the previous session, we will see that, if we extend our function to support many, many operators, the parsing procedures will scan the string for operators from the top way down to the bottom. Once specific operator is found, we use substr to break them apart. It is a long script thus a long way to go for each existing operator. If we want to make an equation grapher, we will make x=0 to 150; The same parsing process will be done 150 times. In fact, we can imagine that, the resulting operators and substring are the same. How wasteful it is. Why dont we "save" the operators that we found ?

If we can make "3+5*(x-4)/6" into something as real world calculation:

x =>  -4   =>  /6  => *5 => +3  =>result;

Can we have a way to store each "process code" like above ?

Programming language compiler in fact turn "+" into an internal function. For example, it may turn "+" into a function "sum";sum=function(num1,num2){return num1+num2;}

Now we are going to create an object representing a basic unit of math expression:

function myVar(fun, param0, param1) {
this.fun = fun;
this.param0 = param0;
this.param1 = param1;
}

This function is an object constructor. It stores an expression.  It stores a function name and two parameters.

For example, I construct one instance of this object:

myVar2=new myVar("sum",3,5);

myVar2 stores an expression of "sum(3,5)"; The magic of this object is that, now, I create an instance of this object with our myVar2 in parameters:

myVar3=new myVar("sum",myVar2,myVar4);

myVar3 will store an expression of "sum(myVar2,myVar4)"; Here myVar2 represents "sum(3,5)"; So, if we see it deeply, the "value" of myVar3 will be sum((sum(3,5),myVar4); Well I dont know what myVar4 is, maybe it represent sum(7,8); or it prepresent sum(multiply(6,4),2); or even more complex expression.

The logic is break an operator command into Object;

A string "3+5+8" will be broken by operator "+" into an object:

y=new myVar("sum","5+8",3);

Then "5+8" will be turn to myVar20=new ("sum",5,8); And the original y will be replaced by ("sum",myVar20,3);  After the first parse, the inital string is converted into many myVar objects. If we set x to new value, to get the value of this expression, we just chain up and trace these myVar objects to get the value. No need to parse the original string any more.

function parseParam(paramStr) {
//this function turn a raw string into myVar object name
//-----------------------
//for "+" operator;
var k = paramStr.lastIndexOf("+");
if (k>0) {
var substr1 = paramStr.substr(0, k);
var substr2 = paramStr.substr(k+1, paramStr.length-k-1);

varIndex++;
_root["myVar"+varIndex] = new myVar("sum", substr1, substr2);
var objectName = ("myVar"+varIndex);
return objectName;
}
//----------------------
return NaN;
//---------------------------------
}

At present that function handles only "+". To extend the support for mulitply, we need to parse "*" character and we need to write a function "mulitply" to handle the calculation.

I create a new function: "setValue", that does nothing but returning the param1. This function is needed to handle "parenthesis" where no calculation is done by this "operator"; We add these function to Flash Math object. Just for convenience.

Math.sum = function(a1, a2) {
return (a1+a2);
};
Math.setValue = function(a1) {
return a1;
};

Ok, now here is the recursive function to chain these myVar objects: It is different from parseParam function in that the argument is an object, while parseParm accept a string; Basically, it is for retrieving the value of an expression object;

function getValue(varObject) {
// ------------check params
var paramStr = varObject.param0;
var num0 = Number(paramStr);
if (paramStr == NaN) {
num0 = NaN;
} else if (typeof (_root[paramStr]) == "object") {
num0 = getValue(_root[paramStr]);
} else if (isNaN(num0)) {
varObject.param0 = parseParam(paramStr);
num0 = getValue(_root[varObject.param0]);
}
// ---------------param2:
var paramStr = varObject.param1;
trace("paramStr="+paramStr);
var num1 = Number(paramStr);
if (paramStr == NaN) {
num1 = NaN;
} else if (typeof (_root[paramStr]) == "object") {
num1 = getValue(_root[paramStr]);
} else if (isNaN(num1)) {
varObject.param1 = parseParam(paramStr);
num1 = getValue(_root[varObject.param1]);
}
// return value
var v = (Math[varObject.fun](num0, num1));
return v;
};

Note that, the codes that num1 get the value from param1 is nearly the same as num2 get the value from param2;

OK, we can test them now. Put all these codes to frame1 of the _root;

varIndex = 10;
y = new myVar("setValue", "3+5+7+X", 0);
For (var k=0;k<10;k++){
X=new myVar("setValue",k,null);
trace("when x="+k+",3+5+7+X="+getValue(y);
}

Now, we need to extend our function to support "-","*","/" etc. Take a look at the function parseParam; To support "*", we copy that "+" block and paste beneath it. Change var k = paramStr.lastIndexOf("+");  to var k = paramStr.lastIndexOf("*");  And then change  _root["myVar"+varIndex] = new myVar("sum", substr1, substr2);  into   _root["myVar"+varIndex] = new myVar("multiply", substr1, substr2);   We need to write a function Math.multiply(a,b);

That is all we need.

To support for parenthesis is more complex. Anyway, the principle is the same: Add this block to the top before "+" block:

//for parenthesis
var k = paramStr.lastIndexOf("(");
var m = paramStr.indexOf(")", k);
if (k>0) {

var substr1 = paramStr.substr(0, k);
var substr2 = paramStr.substr(k+1, m-k-1);
var substr3 = paramStr.substr(m+1, paramStr.length-m-1);

varIndex++;
_root["myVar"+varIndex] = new myVar("setValue", substr2, null);
var objectName = ("myVar"+varIndex);
var newStr = substr1+objectName+substr3;

varIndex++;
_root["myVar"+varIndex] = new myVar("setValue", newStr, null);
var objectName = ("myVar"+varIndex);
return objectName;
}

Note that, we dont need to change the script about getValue function.

Here you can download the fla. It support only "+" and parenthesis.

You can test it and see the output result of trace. The parse procedurs are done only once.

I am not going to discuss the support for function. And it is not difficult to figure out how to trap error during parsing. It would be better to give a message to user that, the equation is not valid.