0

I have a string like this:

"2 + 3 ^ 4"

That I want to be replaced with:

"2 + Math.pow(3, 4)"

So simply, I want the caret "^" sign to operate as the Math.pow function in my math expressions.

Then I could evaluate it:

eval("2 + Math.pow(3, 4)") -> 83

Here are a few more examples:

"4 ^ 3 ^ 2" -> "Math.pow(4, Math.pow(3, 2))"
"4 ^ (3 ^ 2) ^ 2 ^ 1" -> "Math.pow(4, Math.pow(Math.pow(3, 2), Math.pow(2, 1)))"
"4. ^ Math.sqrt(2) ^ 3" -> "Math.pow(4., Math.pow(Math.sqrt(2), 3))"
"2 ^ 3e-2" -> "Math.pow(2, 3e-2)"
"-5 ^ .2" -> "-Math.pow(5, .2)"
"(-5) ^ 2" -> "Math.pow(-5, 2)"
".2 ^ -Infinity" -> "Math.pow(.2, -Infinity)"
"2.34 ^ ((3 + 2) * Math.sin(3))" -> "Math.pow(2.34, (3 + 2) * Math.sin(3))"
"Math.cos(2) ^ (3 + 2)" -> "Math.pow(Math.cos(2), 3 + 2)"

What I have tried:

    String.prototype.replaceAt = function(index, character) {
        return this.substr(0, index) + character + this.substr(index+character.length);
    }

    str = str.replace(/ /g, "");

    str = str.replace(/((?:(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?|Infinity))/g, "($1)")

    for (var i = str.length - 1; i >= 0; i--) {
        var m = str.lastIndexOf("^");

        var c = 0;
        for (var j = m + 1; j < str.length; j++) {
            if (str[j] == "(") c++;
            if (str[j] == ")") c--;

            if (c == 0) {
                str = str.replaceAt(j + 1, str[j + 1] + ")")
                break
            }
        }

        c = 0;
        for (var j = m - 1; j >= 0; j--) {
            if (str[j] == "(") c++;
            if (str[j] == ")") c--;

            if (c == 0) {
                str = str.replaceAt(j - 1, "Math.pow(" + str[j - 1])
                break
            }
        }

        str = str.replaceAt(m, ",")
    }

Doesn't work at all and very messy.

  • Sounds like you need to learn about parsers. – Barmar Oct 25 '14 at 08:59
  • @Barmar So you know how to do this? –  Oct 25 '14 at 09:03
  • Nope, it's a bit too complicated for me. Most people use tools like `yacc` or `bison` to write parsers. I'm not sure how I would do it in Javascript. – Barmar Oct 25 '14 at 09:05
  • Try ditching all those string-replaces and push all of your numbers and operators onto a stack. Then unwind the stack and build a new string from scratch. (When used properly, this will also give you a first try at "validating".) – Jongware Oct 25 '14 at 09:10
  • The basic idea is to tokenize you're formula. Get every "X ^ Y" in an expression tree. (Where X and/or Y can be formula's or values). A recursive RegEx like "str.replace(/(.*)\^(.*)/g, "Math.pow($1,$2)")" would do (but AFAIK: JavaScript doesn't support the recursive regex http://stackoverflow.com/questions/4414339/recursive-matching-with-regular-expressions-in-javascript) – Marvin Smit Oct 25 '14 at 09:10
  • @MarvinSmit Can you give a JSFiddle example? –  Oct 25 '14 at 09:11

2 Answers2

0

Use http://mathjs.org/

It allows you to calculate expressions like

print(math.eval('sqrt(3^2 + 4^2)')); 

Using Stacks you can create a class which can convert a mathematical expression into JS understandable expression, but the possibility of achieving all the test cases correctly requires a lot of testing. SO its better to use libraries (they have good docs and example available online).

void
  • 36,090
  • 8
  • 62
  • 107
  • I know that math.js exists and I have checked it out, it is good, but I want to understand my code so no thanks. –  Oct 25 '14 at 09:07
-1

This is an example of how you could parse your strings and format them with the Math.pow function.

First i would like to point out this is on request of the OP (custom code) instead of using a proven library. 2nd, this is example code and none optimized in any way. It does not "modify () or moves signs +-" like the OP posted in 2 of the given examples

//
// resolves the formula (^ into Math.pow)
//
function resolve(formula) {
    var result = "";

    // search for {operand} ^ {operatand}
    var match = formula.match(/(.[^^]*)\^(.*)/);

    // if matched, we have 3 entries.
    if (match && match.length == 3) {
        result += "Math.pow(" + resolve(match[1]) + "," + resolve(match[2]) + ")";
    } else {
        result += formula;
    }
    return result;
};

Here's a working fiddle to see it in action: http://jsfiddle.net/httb9945/

Marvin Smit
  • 4,088
  • 1
  • 22
  • 21
  • Nice, but 4 ^ ((3 ^ 2) ^ 2) ^ 1 isn't working, can you fix that aswell? –  Oct 25 '14 at 14:27