(E-Mail Removed) wrote:

>

> Hi folks, i've searched and searched and can't find any example of

> what i'm trying to do. Essentially (i think) i need to add a new

> operator to Number.

>

> I'm using eval to evaluate expressions that can be entered by users,

> these expressions are defined by another app so i can't change the

> format of the expressions. I've managed to support the majority of

> operators by supplying my own functions that wrap the equivalent Math

> functions but i'm struggling with a few.

>

> I need to support mod, div, shl and shr which will be in the format:

>

> x div y, x mod y, x shl y, x shr y

>

> How can i add an operator to Number to handle these? Is it even

> possible?

> Or is there some another way i can support them?

>

> I considered using regexp to replace the relevent bits but that got

> pretty ugly when i started to look at the possibility of nesting.

>

> Any help greatly appreciated.

> Cheers!
For what it's worth:

The following hasn't been extensively tested by any stretch, but

probably can give you ideas on how to proceed.

Example expressions (that can be evaluated):

x div y

12 mod 5

1020 div 33 mod 7 [resolves to 2 :: expression evaluated left to right]

5 * (((x div y) mod testz) shl 2) ^ 4

// this last one will resolve as 5 * Math.pow(subexpression, 4)

// all BASIC operators are resolved before the final string is eval'd

// so pay attention to parenthetic groupings!

Limitations:

1: variables to be resolved should be globally declared/defined.

otherwise, you need to find another way to make the translations

[i.e., variables are resolved before operations begin in function process]

2: complex expressions require an extra level of parsing:

e.g.:

// the formula for finding the day number in the year of a date

((275 * M) div 9) - K * ((M + 9) div 12) + D - 30

this will have to be "pre-processed" -- see details at end

3: as written, it will not handle extra spacing between parens

and variables/numbers

like: ( x div y ) -- the regex doesn't account for this condition

I tried to keep the regex's as simple as possible...

Notes:

you can skip the process function if numeric-only expressions are used

and use the evaluate function... just remember the "value"

returned will be a string and will need to be converted

e.g.: evaluate("122 div -7"); => returns "-18"

var res = +evaluate("122 div -7") results in type "number"

these routines have only been tested for very basic (no pun intended)

statements. Somebody better at regex could probably do a better job...

the "variable" extractor at the beginning of function process() should be

able to handle variable names with underscores and numerics as long

as

they begin with an alphabetic character (usual rules).

// main function to process expressions using

// div, mod, shl, shr, and ^

// add others to the regex and switch statements

function

process(p)

{

// used to extract var names

// won't allow a variable starting with a number

var re = /\b([A-Za-z]+[\w]*)\b/g;

var vars = p.match(re);

// "BASIC" operators will look like vars to JS -- so remove them

vars = vars.join(" ");

vars = vars.replace(/div|mod|shl|shr/g, "");

vars = vars.split(" ");

// grab and replace variable values from window object

// (hence the need for globals)

for(var i = 0; i< vars.length; i++)

{

if(vars[i]) // there will be blanks in the array - so test

p = p.replace(vars[i], window[vars[i]]);

// grabs the "current" variable value

// probably should have error checking here

}

// this section evals and removes parenthetical expressions

// only the most embedded will match (per pass)

// i.e.: (n op i) will match but (n op i) op i2 will not

// the routine replaces the parenthetical with a numeric value

// the the process is looped until all parentheticals are evaluated

var arr;

while(

arr = p.match(/([\d\w]+)\s+(div|mod|shl|shr|^)\s+([\d\w]+)/g)

)

{

var t = evaluate(arr[0]);

p = p.replace(arr[0], t);

var removeparen = /(\()(\d+)(\))/;

var newtest = p.match(removeparen);

if(newtest)

p = p.replace(new String(newtest[0]), newtest[2]);

else break; // this will stop infinite loops

// debugging statement -- in case of an infinite loop

// this process is interesting to watch...

// if(!confirm("current state = " + p)) break;

}

// after the parentheticals -- this section will resolve

// what's left -- from left to right...

while(

arr = p.match(/(-?\d+)\s+(div|mod|shl|shr|\^)\s+(-?\d+)/)

)

{

if(arr){

t = evaluate(arr[0]);

p = p.replace(new String(arr[0]), t);

}

}

// just a debugging statement

//alert("final state b4 return = " + p);

// should be nothing left but a JS expression to be evaluated

return eval(p);

}

// generic subroutine...takes exactly 1 operation: a op b

// evaluate expects a string value containing a "simple expression"

// e.g.: 12 mod 5 --

// NO variables!

function

evaluate(exp)

{

var re = /(-?\d+)\s+(div|mod|shl|shr|\^)\s+(-?\d+)/;

var t;

var tarr = exp.match(re);

switch(tarr[2])

{

case "div":

t = Math.floor(+tarr[1]/+tarr[3]);

break;

case "mod":

t = +tarr[1] % +tarr[3];

break;

case "shl":

t = +tarr[1] << +tarr[3];

break;

case "shr":

t = +tarr[1] >> +tarr[3];

break;

case "^":

t = Math.pow(+tarr[1] , +tarr[3]);

break;

break;

default:

// kick error if detected -- if required

break;

}

return t;

}

//////////////////////// Examples \\\\\\\\\\\\\\\\\\\\\\\\\\

// sample global var declarations used in expressions to be evaluated

var x = 30;

var y = 5;

var testz = 4;

var pat = "5 * (((x div y) mod testz) shl 2) ^ 4";

alert("process = " + process(pat)); // result = 20480

pat = "x div y";

alert( "process2 = " + process(pat));// result = 6

pat = "12 mod 5"; // result = 2

alert("process3 = " + process(pat));

alert("evaluate1 = " + evaluate("122 div -7");

//********************************************

more complex example:

var M = 11; // november month number (not JS oriented)

var D = 28; // today's date

var K = 2; // used for non-leapyear -- otherwise 1

var pat = "((275 * M) div 9) - K * ((M + 9) div 12) + D - 30";

var re = /(\([^)]+\))/g;

// this regex is a little unusual...

// it finds ((275 * M) and ((M + 9) only

// after evaluating, it replaces 1 left paren + eval'd value

var myarray = pat.match(re);

for(var i = 0; i < myarray.length; i++)

{

var t = eval(myarray[i].replace(/[\(]/, ""));

//inner parens removed, so replace open paren

pat = pat.replace(myarray[i], "(" + t);

}

// after this, pat becomes:

// (3025 div 9) - K * (20 div 12) + D - 30 [simplified enough for process()]

then

alert("pat string = " + pat + "\n" +

"day of year = " + process(pat)); // =>result 332

good luck...

Personally, I prefer something like:

Number.prototype.div = function(denom) { return Math.floor(this/denom); }

// e.g.: (122).div(7) or someVar.div(divisor); etc...

of course, there's no way to implement ^ as a method.

process() could be extended to handle logical operators as well, like

AND, OR, NOT, etc...

While I was putting this together, I made quite a number of

modifications to the original source. I've tried to make sure that all

the mods were properly updated in this post... if something doesn't work

as I've written it would, let me know -- I'll email you the working demo.

As written, the regex's only allow integer values -- if you need to

accept decimal values, change the lines:

arr = p.match(/(-?\d+)\s+(div|mod|shl|shr|\^)\s+(-?\d+)/) in process to

arr = p.match(/(-?[\d\.]+)\s+(div|mod|shl|shr|\^)\s+(-?[\d\.]+)/)

and

var re = /(-?\d+)\s+(div|mod|shl|shr|\^)\s+(-?\d+)/; in evaluate to

var re = /(-?[\d\.]+)\s+(div|mod|shl|shr|\^)\s+(-?[\d\.]+)/;

this will allow the use of decimals - however, strings like: 23.45.455

will break (but that's not something a careful programmer need worry

about -- right?).

I'm sure there are numerous other problems with this script -- off the

top of my head: 1) object properties will fail (e.g.: myObj.myVar mod

whatever) as will array notation (myArray[index])... and such... This is

just an algorithm -- not a complete solution. It should suit for simple

BASIC syntax statements.