Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript, RegExp - replace with evaluated expression involving tagged expressions?

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?

like image 725
Radagast the Brown Avatar asked Dec 17 '22 16:12

Radagast the Brown


2 Answers

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)
like image 179
Igor Avatar answered Dec 19 '22 05:12

Igor


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.

like image 21
alex Avatar answered Dec 19 '22 05:12

alex