1

I have this simple calculator script, but it doesn't allow power ^.

function getValues() {
    var input = document.getElementById('value').value;

    document.getElementById('result').innerHTML = eval(input);

}
<label for="value">Enter: </label><input id="value">

  <div id="result">Results</div>

  <button onclick="getValues()">Get Results</button>

I tried using input = input.replace( '^', 'Math.pow(,)');

But I do not know how to get the values before '^' and after into the brackets.

Example: (1+2)^3^3 should give 7,625,597,484,987

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • 5
    The simple case where it's just `x^y` where `x,y` are integers is pretty doable with a simple regex. However, properly parsing something like `(2+34)^5` is obviously more complex. Could you clarify what you're looking to do? – CollinD Feb 09 '17 at 23:20
  • see: [basics how to write expression evaluation](http://stackoverflow.com/a/20919547/2521214) – Spektre Feb 10 '17 at 11:57

6 Answers6

2

Use a regular expression with capture groups:

input = '3 + 2 ^3';
input = input.replace(/(\d+)\s*\^\s*(\d+)/g, 'Math.pow($1, $2)');
console.log(input);

This will only work when the arguments are just numbers. It won't work with sub-expressions or when you repeat it, like

(1+2)^3^3

This will require writing a recursive-descent parser, and that's far more work than I'm willing to put into an answer here. Get a textbook on compiler design to learn how to do this.

Barmar
  • 741,623
  • 53
  • 500
  • 612
1

I don't think you'll be able to do this with simple replace.

If you want to parse infix operators, you build two stacks, one for symbols, other for numbers. Then sequentially walk the formula ignoring everything else than symbols, numbers and closing parenthesis. Put symbols and numbers into their stacks, but when you encounter closing paren, take last symbol and apply it to two last numbers. (was invented by Dijkstra, I think)

const formula = '(1+2)^3^3'


const symbols = []
const numbers = []

function apply(n2, n1, s) {
  if (s === '^') {
    return Math.pow(parseInt(n1, 10), parseInt(n2, 10))
  }
 
  return eval(`${n1} ${s} ${n2}`)
}

const applyLast = () => apply(numbers.pop(), numbers.pop(), symbols.pop())

const tokenize = formula => formula.split(/(\d+)|([\^\/\)\(+\-\*])/).filter(t => t !== undefined && t !== '')

const solver = (formula) => {
  const tf = tokenize(formula)

  for (let l of formula) {
    const parsedL = parseInt(l, 10)
    if (isNaN(parsedL)) {
      if (l === ')') {
        numbers.push(applyLast())
        continue
      } else {
        if (~['+', '-', '*', '/', '^'].indexOf(l))
          symbols.push(l)
        continue
      }
      
    } 
    numbers.push(l)
  }
  
  while (symbols.length > 0)
    numbers.push(applyLast())
  
  return numbers.pop()
}

console.log(solver(formula))
rdkn
  • 566
  • 7
  • 14
0

Get your input into a string and do...

var input = document.getElementById('value').value;
var values = input.split('^'); //will save an array with [value1, value 2]
var result = Math.pow(values[0], values[1]);
console.log(result);

This only if your only operation is a '^'

EDIT: Saw example after edit, this no longer works.

Alan
  • 361
  • 3
  • 22
0
function getValues() {
    var input = document.getElementById('value').value;

    // code to make ^ work like Math.pow
    input = input.replace( '^', '**');

    document.getElementById('result').innerHTML = eval(input);

}

The ** operator can replace the Math.pow function in most modern browsers. The next version of Safari (v10.1) coming out any day supports it.

at.
  • 50,922
  • 104
  • 292
  • 461
0

As said in other answers here, you need a real parser to solve this correctly. A regex will solve simple cases, but for nested statements you need a recursive parser. For Javascript one library that offers this is peg.js.

In your case, the example given in the online version can be quickly extended to handle powers:

Expression
  = head:Term tail:(_ ("+" / "-") _ Term)* {
      var result = head, i;

      for (i = 0; i < tail.length; i++) {
        if (tail[i][1] === "+") { result += tail[i][3]; }
        if (tail[i][1] === "-") { result -= tail[i][3]; }
      }

      return result;
    }

Term
  = head:Pow tail:(_ ("*" / "/") _ Pow)* { // Here I replaced Factor with Pow
      var result = head, i;

      for (i = 0; i < tail.length; i++) {
        if (tail[i][1] === "*") { result *= tail[i][3]; }
        if (tail[i][1] === "/") { result /= tail[i][3]; }
      }

      return result;
    }

// This is the new part I added
Pow
  = head:Factor tail:(_ "^" _ Factor)* {
      var result = 1;
      for (var i = tail.length - 1; 0 <= i; i--) {
          result = Math.pow(tail[i][3], result);
      }
      return Math.pow(head, result);
    }

Factor
  = "(" _ expr:Expression _ ")" { return expr; }
  / Integer

Integer "integer"
  = [0-9]+ { return parseInt(text(), 10); }

_ "whitespace"
  = [ \t\n\r]*

It returns the expected output 7625597484987 for the input string (1+2)^3^3.

fafl
  • 7,222
  • 3
  • 27
  • 50
0

Here is a Python-based version of this question, with solution using pyparsing: changing ** operator to power function using parsing?

Community
  • 1
  • 1
PaulMcG
  • 62,419
  • 16
  • 94
  • 130