Reputation: 867
I'm coding a calculator in Javascript.
I want to translate : [expression1]^[expression2]
into : Math.pow([expression1], [expression2])
When I have simple expressions like 5^-3
it's quite easy to do with a regex, but when it comes with more complex expressions like :
(-5*(6+3)-5)^((2*2)-2)+(4*5)
I don't see how to deal with it with regex because of the nested parenthesis...
Any advice? Thanks.
Upvotes: 0
Views: 5250
Reputation: 4569
browser javascript, as it is now. does not support recursive regular expressions.
my solution for this was to match a prefix with regular expressions. then to search for its parenthesis using a tokenizer approach. then if the parenthesis are balanced then return the chunk that is inside the parenthesis.
// replace_tokenizer_paranthesis by shimon doodkin
// this function is searching for a regexp prefix
// then searching for a chunk inside balanced parantheseis
//
// s="s( s(gdfgs)fasd)" - searched string
// prefix=/s\(/ - searched regex for prefix
// onmatch: - replace callback function, arguments:
// a= prefix,
// b= inside data,
// c = closing paranthesis
// to return same , return [a,b+c]
//
// example:
// replace_tokenizer_paranthesis(s,prefix,function (a,b,c){ return [ "CONVERT(",b+",DATE)" ] }) // return [ newprefix , all rest ]
function replace_tokenizer_paranthesis(s,prefix,onmatch)
{
var m,start=0;
var input;
while(m=(input=s.substr(start)).match(prefix))
{
var offset_at_end=m.index+m[0].length;
var counter = 1;
var skip_until="";
var i = offset_at_end;
for(; i < input.length; i++)
{
//var pc = i-1>=0?input[i-1]:"";
var c = input[i];
if( counter===0 )
{
break;
}
//else if(c===skip_until&& pc!=='\\') //maybe backslash queoted
else if(c===skip_until)
{
skip_until='';
}
else if(c==='"')
{
skip_until='"';
}
else if(c==="'")
{
skip_until="'";
}
else if(c==='(')
{
counter++;
}
else if(c===')')
{
counter--;
}
}
if( counter===0 )
{
var result=onmatch(m[0], input.substring(offset_at_end,i-1),")")
s=s.substring(0,start+m.index)+result[0]+result[1]+s.substr(start+i);
start+=result[0].length;
}
else
start+=offset_at_end;
}
return s
}
Upvotes: 2
Reputation: 43663
Use regex pattern:
/((?:(?=[^()]*\d)\d*(?:\.\d*)?)|(?:\((?:(?:[^()]+)|(?:[^()]*\([^()]*\)[^()]*)+)\)))\^((?:(?=[^()]*\d)\-?\d*(?:\.\d*)?)|(?:\((?:(?:[^()]+)|(?:[^()]*\([^()]*\)[^()]*)+)\)))/
Explanation:
[Step 1] :: Regex patter contains two almost same sub-patterns linked with ^
sign between
((?:(?=[^()]*\d)\d*(?:\.\d*)?)|(?:\((?:(?:[^()]+)|(?:[^()]*\([^()]*\)[^()]*)+)\)))
\^
((?:(?=[^()]*\d)\-?\d*(?:\.\d*)?)|(?:\((?:(?:[^()]+)|(?:[^()]*\([^()]*\)[^()]*)+)\)))
The only difference is that second one (behind ^
) allows negative number as a simple parameter (\-?
)
[Step 2] :: Sub-pattern from Step 1 has two alternatives:
(?:(?=[^()]*\d)\-?\d*(?:\.\d*)?)
|
(?:\((?:(?:[^()]+)|(?:[^()]*\([^()]*\)[^()]*)+)\))
[Step 3] :: First alternative is a number - for example: 1234
or 12.34
or 1234.
or .1234
(?=[^()]*\d)
\-?\d*
(?:\.\d*)?
[Step 4] :: Second alternative is a nested parenthesis formula
\(
(?:(?:[^()]+)|(?:[^()]*\([^()]*\)[^()]*)+)
\)
[Step 5] :: which might be simple or complex (with other parenthesis inside)
(?:[^()]+)
|
(?:[^()]*\([^()]*\)[^()]*)+
[Step 6] :: and if is complex and have some other parenthesis inside, we ensure there are nested
[^()]*
\(
[^()]*
\)
[^()]*
Upvotes: 3