0

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.

5
  • it would be also possible to have something like (2^3)^(4^5) ? Commented Sep 3, 2012 at 15:19
  • 5
    Regex does become very difficult to manage once you start trying to deal with nestable elements. For things like this, a parser is usually a better solution. A quick google brought up this one: jsfromhell.com/classes/math-parser (but I haven't tried it, and others may exist) Commented Sep 3, 2012 at 15:21
  • @FabrizioCalderan : Yep, and it becomes even harder Commented Sep 3, 2012 at 15:38
  • @SDC: Thank you, I'm gonna check on this side Commented Sep 3, 2012 at 15:39
  • Have a look at this answer: stackoverflow.com/a/4414453/1548853 Commented Sep 3, 2012 at 16:21

2 Answers 2

3

Use regex pattern:

/((?:(?=[^()]*\d)\d*(?:\.\d*)?)|(?:\((?:(?:[^()]+)|(?:[^()]*\([^()]*\)[^()]*)+)\)))\^((?:(?=[^()]*\d)\-?\d*(?:\.\d*)?)|(?:\((?:(?:[^()]+)|(?:[^()]*\([^()]*\)[^()]*)+)\)))/

Check this fiddle.


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

[^()]*
\(
[^()]*
\)
[^()]*
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you ! But it won't work if there are more than two nested parenthesis. I think I shouldn't use regex for this problem but parser like SDC suggested.
2

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
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.