PLACE
spec
solutions
JavaScript

RPN Calculator in JavaScript

Unlike all other implementations of the calculator, in JavaScript there is no notion of ‘standard input’ to read from or ‘standard output’ to write to. Any means of communication with the outside world have to be provided by the host environment of the script. But before coming to that, let us concentrate on the essential part of the calculator.

The function evalrpn below takes a string s and produces one of the following:
 — a number, the result of computing a correctly formed expression;
 — the string 'error', when s does not hold a correct expression;
 — the empty string '', when s is empty or contains only blanks.

The first thing evalrpn does is obtain an array of tokens from s. For this it calls the built-in function split, sending it a regular expression saying that all whitespace characters are token delimiters. Before calling split, however, possible leading and trailing blanks are removed from s by means of calling replace. If that is omitted, and there are indeed leading and/or trailing blanks, there will be empty-string token(s) in the resulting array.

The for loop iterates over all tokens tk, maintaining a stack (array) st. Each token is first checked if it represents a number: the call of test with a regular expression describing the syntax of a number; if it is a number, Number is applied to obtain its value. Otherwise, if tk is a character within '+-*/' and there are at least two entries currently in st, the corresponding arithmetic operation is performed through evaluating a properly composed string. A space (' ') is inserted between the operation and the second argument solely to prevent a possible ‘--’ (occuring when tk is '-' and y is negative), which the interpreter would treat as ‘decrement by 1’.

If a syntax error is found in the expression, i.e. tk is neither a number nor an operation, or there are no arguments available in st for an operation, the loop is prematurely exited. Thus, once having got out of for, one can tell if there was an error by the value of the index variable i. Of course, having more than one item left in st at that point also indicates a syntax error, so the values of both i and st are checked.

In case the list of tokens is empty (and in that case only), st remains empty as well, so then evalrpn returns ''.

function evalrpn(s) {
  var st,tk,i,x,y,z
  s = s.replace(/^\s*|\s*$/g,'')
  s = s.length>0 ? s.split(/\s+/) : []
  st = []
  for (i=0; i<s.length; ++i) {
    tk = s[i]
    if (/^[+-]?(\.\d+|\d+(\.\d*)?)$/.test(tk))
      z = Number(tk)
    else {
      if (tk.length>1 || '+-*/'.indexOf(tk)==-1 || st.length<2) break
      y = st.pop();  x = st.pop()
      z = eval(x+tk+' '+y)
    }
    st.push(z)
  }
  return i<s.length || st.length>1 ? 'error'
       : st.length==1 ? st.pop() : ''
}

Now that we have a function to compute an RPN expression from a string, where do we get those strings from, and where do we send the results? One possibility is to use a stand-alone ECMAScript / JavaScript interpreter, such as DMDScript. A stand-alone interpreter implements the language with extensions that enable it to do simple I/O.

In the typical case of running a JavaScript interpreter within a web browser, one has to resort to HTML as a means of interacting with the RPN expression evaluator. Normally, browsers provide a DOM interface to the HTML document structure: a set of functions extending JavaScript with the ability to interrogate and manipulate the content of the document seen in the browser. In order to make use of this, we first put the text of evalrpn in a script element within the header of an HTML file, placing it between an opening and a closing tag, as shown below:

<script type="text/javascript">
  evalrpn's definition goes here
</script>

Then, at some place in the same file, we can put the following text. It defines two fields of type ‘input’ and labels them accordingly.

<input id="RPNinput" type="input" size="50"
onfocus="document.getElementById('RPNresult').value=this.value=''"
onchange="document.getElementById('RPNresult').value=evalrpn(this.value)"
> RPN expression
<br>
<input id="RPNresult" size="20" readonly> result

The first field is for entering the text that goes to evalrpn for evaluation, and the second one, below, displays the result (so it is declared ‘readonly’). Whenever the first field receives focus from the user, both fields get cleaned-up and a new expression can be entered. Once the focus leaves that field, whatever evalrpn returns from evaluating its contents is displayed in the result field.

(In some browsers, pressing Enter on the keyboard has the same effect of refreshing the result as moving out of the entry field. This is convenient, as it makes possible to write an expression, evaluate it, add some more terms, evaluate again etc.)

You can try yourself how the RPN calculator works right here:

RPN expression
result

There are other possibilities for organizing a dialogue with the calculator within a browser. For example, one can place the text fields in a form and submit it over HTTP. These possibilities, however, have more to do with HTML, DOM etc. than with the language proper, so there is no need to discuss them at any length. The one presented here is farely simple, yet it conveys the idea of how a JavaScript program can be run in a browser.

boykobbatgmaildotcom