custom Number operator

Discussion in 'Javascript' started by netclectic@hotmail.com, Nov 27, 2003.

  1. Guest

    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!
     
    , Nov 27, 2003
    #1
    1. Advertising

  2. wrote on 27 Nov 2003:

    > 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.


    JavaScript does not support operator overloading. However, you could
    add named methods, using the prototype property, to the Math, Number,
    or your own objects. An alternative would be to write a generic
    parsing method that takes a string and evaluates it using your own
    operator set, but I think that's more work than you'd be willing to
    do.

    Mike

    --
    Michael Winter
    d (remove ".invalid" to reply)
     
    Michael Winter, Nov 27, 2003
    #2
    1. Advertising

  3. writes:

    > 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.


    Number is a function (although it can be used to represent the class
    of objects it creates too). You can add *methods* to the number objects,
    but not operators.

    > 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?


    You can't create new operators in Javascript (that would be changing the
    syntax of the language), so you can't just evaluate the string above.

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


    What you can do, is to use a string replace to change the operators
    to the corresponding Javascript operator. They are:

    div -> / (not precisly, though. Javascript doesn't have integer division)
    mod -> %
    shl -> >> (or >>> depending on how the most siginificant bit is set)
    shr -> <<

    input = input.replace(/\bdiv\b/g,"/").
    replace(/\bmod\b/g,"%").
    replace(/\bshl\b/g,">>").
    replace(/\bshr\b/g,"<<");

    > I considered using regexp to replace the relevent bits but that got
    > pretty ugly when i started to look at the possibility of nesting.


    If you replace infix operators with other infix operators, there shouldn't
    be any problems (knock wood).

    Your problem is that there is no integer division in Javascript. To replace
    expr1 div expr2
    with the corresponding
    Math.floor(expr1 / expr2)
    would require parsing the string. Possible, but hard(er) work. Same
    for:
    expr1 .myDiv(expr2)

    /L
    --
    Lasse Reichstein Nielsen -
    DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
    'Faith without judgement merely degrades the spirit divine.'
     
    Lasse Reichstein Nielsen, Nov 27, 2003
    #3
  4. Guest

    >
    > What you can do, is to use a string replace to change the operators
    > to the corresponding Javascript operator. They are:
    >
    > div -> / (not precisly, though. Javascript doesn't have integer division)
    > mod -> %
    > shl -> >> (or >>> depending on how the most siginificant bit is set)
    > shr -> <<
    >
    > input = input.replace(/\bdiv\b/g,"/").
    > replace(/\bmod\b/g,"%").
    > replace(/\bshl\b/g,">>").
    > replace(/\bshr\b/g,"<<");
    >

    --
    >
    > If you replace infix operators with other infix operators, there shouldn't
    > be any problems (knock wood).
    >

    cheers, i realise now i was trying to make my regexp to complicated
    for these.

    >
    > Your problem is that there is no integer division in Javascript. To replace


    > expr1 div expr2
    > with the corresponding
    > Math.floor(expr1 / expr2)
    > would require parsing the string. Possible, but hard(er) work. Same
    > for:
    > expr1 .myDiv(expr2)
    >
    > /L

    yeah, i came up with this initially but it's far from ideal:

    expr = expr.replace(/([-+]?\d+(\.\d+)?)\W?div\W?([-+]?\d+(\.\d+)?)/gi,
    'Math.floor($1/$3)');

    I also have the same problem with suppproting ^ as a power operator,
    e.g. x ^ y = Math.pow(x,y)


    I was trying to avoid parsing the expression if i possibly could but
    it's looking likley that i might have to do just that.
     
    , Nov 27, 2003
    #4
  5. Guest

    >
    > you could add named methods, using the prototype property, to the Math, Number,
    > or your own objects.
    >

    yeah, i looked at that but i couldn't see a way of supporting 'infix
    operators' like mod, div, etc. can this be done?
     
    , Nov 27, 2003
    #5
  6. writes:

    > yeah, i came up with this initially but it's far from ideal:
    >
    > expr = expr.replace(/([-+]?\d+(\.\d+)?)\W?div\W?([-+]?\d+(\.\d+)?)/gi,
    > 'Math.floor($1/$3)');


    It might suffice for the expressions you have. It doesn't work for
    general expressions (no single regular expression can do that).
    Example:
    (8 div 4) div 2
    becomes:
    (Math.floor(8/4)) div 2
    and
    (8 mod 2) div 2
    isn't matched at all.

    The \W looks curious. Is it supposed to match whitespace? In that case,
    \s is better. \W matches any non-word character, which is quite a lot.

    > I was trying to avoid parsing the expression if i possibly could but
    > it's looking likley that i might have to do just that.


    Most likely. You are changing infix to prefix. That requires parsing
    if you have arbitrarily nested expressions.

    /L
    --
    Lasse Reichstein Nielsen -
    DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
    'Faith without judgement merely degrades the spirit divine.'
     
    Lasse Reichstein Nielsen, Nov 27, 2003
    #6
  7. JRS: In article <>, seen
    in news:comp.lang.javascript, posted at Thu, 27
    Nov 2003 03:49:50 :-
    >
    >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


    In addition to what others have said, check that the behaviour of your
    code agrees with expectation for all nine combinations of x<=>0, y<=>0.

    --
    © John Stockton, Surrey, UK. ?@merlyn.demon.co.uk Turnpike v4.00 IE 4 ©
    <URL:http://jibbering.com/faq/> Jim Ley's FAQ for news:comp.lang.javascript
    <URL:http://www.merlyn.demon.co.uk/js-index.htm> JS maths, dates, sources.
    <URL:http://www.merlyn.demon.co.uk/> TP/BP/Delphi/JS/&c., FAQ topics, links.
     
    Dr John Stockton, Nov 27, 2003
    #7
  8. Fox Guest

    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) // there will be blanks in the array - so test
    p = p.replace(vars, window[vars]);
    // 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.replace(/[\(]/, ""));

    //inner parens removed, so replace open paren

    pat = pat.replace(myarray, "(" + 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.
     
    Fox, Nov 29, 2003
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jakob Bieling

    Q: operator void* or operator bool?

    Jakob Bieling, Mar 5, 2004, in forum: C++
    Replies:
    2
    Views:
    611
    Rob Williscroft
    Mar 5, 2004
  2. John Smith
    Replies:
    2
    Views:
    440
    Ivan Vecerina
    Oct 6, 2004
  3. Alex Vinokur
    Replies:
    4
    Views:
    3,070
    Peter Koch Larsen
    Nov 26, 2004
  4. Alex Vinokur
    Replies:
    3
    Views:
    5,056
    Jeff Schwab
    Mar 20, 2005
  5. Tim Clacy
    Replies:
    15
    Views:
    2,736
    Kanenas
    May 30, 2005
Loading...

Share This Page