Jumat, 19 Februari 2010

MathParser class

package
{
public class MathParser
{
static public const RAD:int = 0;
static public const DEG:int = 1;
static public const GRAD:int = 2;

static protected const _pi:Number = Math.PI;

static public var instance:MathParser = new MathParser(1);


protected var _val :Number;
protected var _fac :Number;
protected var _mode :int;
protected var _rgxs :Array;
protected var functionList:Array =
["abs", "acos", "asin", "atan", "ceil", "cos", "exp", "floor", "log", "sin", "sqrt", "tan"];

public function MathParser( md:int )
{
mode  = md;
_rgxs = new Array(10);
_rgxs[0] = /[ \n\r\t]/g;
_rgxs[1] = /(?<=[\d\)])(?=[a-df-z\(])|(?<=pi)(?=[^\+\-\*\/\\^!)])|(?<=\))(?=\d)|(?<=[^\/\*\+\-])(?=exp)/i;
_rgxs[2] = /([a-z]*)\(([^\(\)]+)\)(\^|!?)/i;
_rgxs[3] = /([a-z]{2,})([\+-]?\d+,*\d*[eE][\+-]*\d+|[\+-]?\d+,*\d*)/i;
_rgxs[4] = /\{(.+)\}!" )/;
_rgxs[5] = /([\d.]+,*[\d.]*[eE][\+-]?[\d.]+|[\d.]+,*[\d.]*)!/;
_rgxs[6] = /\{(.+)\}\^(-?[\d.]+,*[\d.]*[eE][\+-]?[\d.]+|-?[\d.]+,*[\d.]*)/;
_rgxs[7] = /([\d.]+,*[\d.]*e[\+-]?[\d.]+|[\d.]+,*[\d.]*)\^(-?[\d.]+,*[\d.]*[eE][\+-]?[\d.]+|-?[\d.]+,*[\d.]*)/;
_rgxs[8] = /([\+-]?[\d.]+,*[\d.]*[eE][\+-]?[\d.]+|[\-\+]?[\d.]+,*[\d.]*)([\/\*])(-?[\d.]+,*[\d.]*[eE][\+-]?[\d.]+|-?[\d.]+,*[\d.]*)/;
_rgxs[9] = /([\+-]?[\d.]+,*[\d.]*[eE][\+-]?[\d.]+|[\+-]?[\d.]+,*[\d.]*)([\+-])(-?[\d.]+,*[\d.]*[eE][\+-]?[\d.]+|-?[\d.]+,*)[\d.]*/;
}

// Getter & Setter
public function get Result():Number { return _val; }

public function get mode():int { return _mode; }
public function set mode(n:int):void
{
_mode = n;
switch( n )
{
case RAD : _fac = 1.0; break;
case DEG : _fac = 2.0 * _pi / 360.0; break;
case GRAD: _fac = 2.0 * _pi / 400.0; break;
}
}

public function parseFormula( str:String ):Number
{
try
{
var ss = str.replace(/[  \n\r\t]/g, " ");
//replace all constants

ss = ss.replace(/PI/g, Math.PI.toString());
ss = ss.replace(/E/g, Math.E.toString());
ss = ss.replace(/LN2/g, Math.LN2.toString());
ss = ss.replace(/LN10/g, Math.LN10.toString());
ss = ss.replace(/LOG2E/g, Math.LOG2E.toString());
ss = ss.replace(/LOG10E/g, Math.LOG10E.toString());
ss = ss.replace(/SQRT1_2/g, Math.SQRT1_2.toString());
ss = ss.replace(/SQRT2/g, Math.SQRT2.toString());

//remove blank spaces
ss = ss.replace( _rgxs[0], "" );

// remove others

ss = ss.replace(_rgxs[1],"*");
ss = ss.replace("pi", Math.PI.toString());

var o:Object = _rgxs[2].exec( ss );

while( o != null )
{
if( o[3].length > 0 )
{
ss = ss.replace( o[0], "{" + o[1] + evalString( o[2] ) + "}" + o[3] );
}
else
{
ss = ss.replace( o[0], o[1] + evalString( o[2] ) );
}
o = _rgxs[2].exec( ss );
}

_val = Number( evalString( ss ) );

return _val;
}
catch (e:Error)
{
trace(e);
return NaN;
}
return NaN;
}

protected function evalString( ss:String ):String
{
//trace(ss);
var oo:Object = _rgxs[3].exec( ss );

while( oo != null && functionList.indexOf(oo[1].toLowerCase() ) > -1  )
{
switch( oo[1].toLowerCase() )
{
case "abs" : ss = ss.replace( oo[0], Math.abs( Number( oo[2] ) ).toString() ); break;
case "acos" : ss = ss.replace( oo[0], Math.acos( _fac * Number( oo[2] ) ).toString() ); break;
case "asin" : ss = ss.replace( oo[0], Math.asin( _fac * Number( oo[2] ) ).toString() ); break;
case "atan" : ss = ss.replace( oo[0], Math.atan( _fac * Number( oo[2] ) ).toString() ); break;
case "cos" : ss = ss.replace( oo[0], Math.cos( _fac * Number( oo[2] ) ).toString() ); break;
case "ceil" : ss = ss.replace( oo[0], Math.ceil( Number( oo[2] ) ).toString() ); break;
case "exp" : ss = ss.replace( oo[0], Math.exp( Number( oo[2] ) ).toString() ); break;
case "floor": ss = ss.replace( oo[0], Math.floor( Number( oo[2] ) ).toString() ); break;
case "log" : ss = ss.replace( oo[0], Math.log( Number( oo[2] ) ).toString() ); break;
case "sin" : ss = ss.replace( oo[0], Math.sin( _fac * Number( oo[2] ) ).toString() ); break;
case "sqrt" : ss = ss.replace( oo[0], Math.sqrt( Number( oo[2] ) ).toString() ); break;
case "tan" : ss = ss.replace( oo[0], Math.tan( _fac * Number( oo[2] ) ).toString() ); break;
}
oo = _rgxs[3].exec( ss );
}

// {5}!
var n:Number;
oo = _rgxs[4].exec( ss );
while( oo != null )
{
n = Number( oo[1] );
if( (n < 0) && (n != Math.round(n)) ){trace("ERROR........");}
ss = ss.replace( _rgxs[4], checkNumber( Number( oo[1] ) ).toString() );
oo = _rgxs[4].exec( ss );
}

// 5!
oo = _rgxs[5].exec( ss );
while( oo != null )
{
n = Number( oo[1] );
if( (n < 0) && (n != Math.round(n)) ){trace("ERROR........");}
ss = ss.replace( _rgxs[5], checkNumber( Number( oo[1] ) ).toString() );
oo = _rgxs[5].exec( ss );
}

// {-2}^-1
oo = _rgxs[6].exec( ss );

while( oo != null )
{
ss = ss.replace( oo[0], Math.pow( Number( oo[1] ), Number( oo[2] ) ).toString() );
oo = _rgxs[6].exec( ss );
}

// 2^-1
oo = _rgxs[7].exec( ss );
while( oo != null )
{
ss = ss.replace( _rgxs[7], Math.pow( Number( oo[1] ), Number( oo[2] ) ).toString() );
oo = _rgxs[7].exec( ss );
}

oo = _rgxs[8].exec( ss );

var ret:Number;
while( oo != null )
{
if (oo[2] == "*")
{
ret = Number( oo[1] ) * Number( oo[3] );
if( (ret < 0) || (oo.index == 0) ) ss = ss.replace( _rgxs[8], ret.toString() );
else ss = ss.replace( oo[0], "+" + ret );
}
else if (oo[2] == "/")
{
ret = Number( oo[1] ) / Number( oo[3] );
if( (ret < 0) || (oo.index == 0) ) ss = ss.replace( _rgxs[8], ret.toString() );
else ss = ss.replace( _rgxs[8], "+" + ret );
}
oo = _rgxs[8].exec( ss );
}

oo = _rgxs[9].exec( ss );
while( oo != null )
{
if (oo[2] == "+")
{
ret = Number( oo[1] ) + Number( oo[3] );
if( (ret < 0) || (oo.index == 0) ) ss = ss.replace( _rgxs[9], ret.toString() );
else ss = ss.replace( _rgxs[9], "+" + ret );
}
else if (oo[2] == "-")
{
ret = Number( oo[1] ) - Number( oo[3] );
if( (ret < 0) || (oo.index == 0) ) ss = ss.replace( _rgxs[9], ret.toString() );
else ss = ss.replace( _rgxs[9], "+" + ret );
}
oo = _rgxs[9].exec( ss );
}

if( ss.substr(0,2) == "--" ) ss = ss.substr(2);

return ss;
}

protected function checkNumber( n:Number ):Number
{
return (n == 0.0) ? 1.0 : (n * checkNumber( n - 1.0 ));
}
}
}

1 komentar:

  1. Note: (regards to the original founder)
    The idea is not originally mine. I have watch at past (in a dMicrosoft dot net application) but not accurate enought but the idea always in my brain. So I remake it in AS3. And this one is accurate.

    BalasHapus