4

RegExp can replace matched patterns with replacements involving what is known as Tagged Expressions

Example:

var s = "... some string with full things...";
s = s.replace(/(some|full)/gi, "\"aw$1\");

which will result in

'... "awsome" string with "awfull" things...'

And life are cool, because some and full are matched, and $1 in the replaced string reflects the matched Tagged Expression in the braces, in this case - exactly only some or full.

Now, that we got the idea - I'm looking for an idea to do the following:

String before:

"{timeOfEffect: 3*24*60*60 }"

String after

"{timeOfEffect: 259200}"

The values are represented like that because they are edited by humans to graspable terms like (60 sec * 60 min * 24 hours)*3 => 3 days (don't ask. Client's request), but read in computer terms like 259200 in seconds, and could contain many occuring of that pattern.

I was thinking to try to create a replacement expression that multiplies $1 and $2, or even pass $1 and $2 to a function, or pass $1 * $2 to an evaluation context, but I have to create a function for it and do it manually.

The closest I got is

var x = /([0-9]*)\s\*\s([0-9]*)/g
  , r = function(m){
           return m[1] * m[2];
        }
while (m = x.exec(s))
  s = s.replace( x, r(m));

That sucks a little because exec returns only the first match. After handling it in the replace statement - next search starts again from the start of the string - which is a string of 60K length...

A good solution will be one of the following: a) perform the match starting from an index (without creating a new substring for that) b) provide a replace expression that allows evaluation

The alternative approach will be to tokenize the string, and process it in bits - which is a total alternative to the RegExp that will take a lot of code and effort, in which case I'll just live with the performance penalty or give a better fight on a better alternative for this requirement...

Help anybody?

Radagast the Brown
  • 3,156
  • 3
  • 27
  • 40

2 Answers2

7
var s = "timeOfEffect: 3*24*60*60 var: 6*8 ";
var r = new RegExp("([0-9*+-/]{0,}[0-9])","g");
s = s.replace(r,function(match){ return eval(match); });
alert(s)
Igor
  • 1,476
  • 10
  • 6
  • wow. this one is cool! with a little adjustment to the reg-exp - it covers all basic arithmethics (assuming they are syntactically correct)! – Radagast the Brown 0 secs ago edit – Radagast the Brown Mar 07 '11 at 11:53
  • 1
    Ok, here's the final solution. It can easily extended to support braces too, but I don't need any. The problem with the original offer was that it matched simple numbers too - which don't need replacement. `var r = new RegExp("([0-9\\.]+)([\\*+-\\/].)([0-9\\*+-\\/]+)([0-9])","g");` `r = function(m,m1,m2,m3,m4){ ` `try{` `return ":" + eval(m1+m2+m3+m4)` `}catch(ex){ ` `return ":\"@@EVAL_ERROR:" + m1+m2+m3+m4 + "\""` `}` `}` `}` `s = s.replace(x,r)` The output obviously is checked against "@@EVAL_ERROR" and sends mails about broken configurations... Thanks :) – Radagast the Brown Mar 07 '11 at 12:40
  • Hey kewwwwwlll! the pre-last argument to the match function is the match-index in the searched string, and the last argument is the entire searched string itself!! and it's damn fast! I can use it to emit accurate parse errors to the editors!! :) – Radagast the Brown Mar 07 '11 at 13:14
5
var str = '{timeOfEffect: 3*24*60*60}, {timeOfEffect: 1+7}, {timeOfEffect: 20-3}, {timeOfEffect: 3 / 0}';

var result = str.replace(/\s([\d\/\*\-\+\s]+?)\}/g, function(all, match) {
    return eval(match) + '}';
});

document.body.innerHTML = result;
// {timeOfEffect:259200}, {timeOfEffect:8}, {timeOfEffect:17}, {timeOfEffect:Infinity}

jsFiddle.

eval() is safe to use because we have ensured the string only contains 0-9, , \n, \t, /, *, - and +. I was hoping there may be something like Math.parse(), but there isn't.

If you need more complex math that requires parenthesis, simply add escaped ( and ) to the regex character range.

alex
  • 479,566
  • 201
  • 878
  • 984