# OOP Calculator

This next example, where we won’t rely on `eval` to do our calculations for us, will be a bit more involved. In order to keep things as simple as possible and to avoid redundancy, we are going to create a few custom objects with clear-cut responsibilities — for instance, we will want to avoid the piece of code responsible for doing the actual calculations having to know anything about how the calculator buttons work.

Let’s see what we need:

1.) At the heart of each mathematical expression are mathematical operations. They are all very similar to each other — they each take one (like `sqr`) or two inputs (like `+`) and somehow transform them to a numerical value — so it makes sense to base them all on the same object that handles that transformation from inputs to a numerical result. When instantiating such an `Operation` object, we can pass it some configuration object, so it will know what mathematical operation it actually represents.

2.) If a chain of mathematical operations could just be evaluated from left to right, we’d already be pretty much done here — for each new operation the user enters, we could just create a new `Operation`, and feed it the last operation as input. For instance, the user enters `1 + 2`, that would result in an `addition` object with two inputs: `addition(1, 2)`. If the user then enters `* 3`, we create a `multiplication` object, which we feed the `addition` as the first input, and the entered number as the second: `multiplication(addition(1, 2), 3)`.

That’s not how it works, though, is it? Multiplication has a higher precedence than addition, which means that `*` forms a stronger bond with its surrounding numbers than `+` does. This means that the `2` that has been entered before doesn’t actually belong to that `addition`, but to the `multiplication`, so the result isn’t actually `multiplication(addition(1, 2), 3)` but `addition(1, multiplication(2, 3))`.

Different mathematical operations have different precedences, and we will need some sort of input stack that will hold all inputs for us and decide on its own, which has to be fed to which, so the precedences will be respected when the time comes for the `Operation`s to do their thing.

3.) If we want to have a working calculator with buttons and all, we will have to set that up in the DOM, and add some click handlers, so they interact with those other components. This is the easy part, and it doesn’t really have anything to do with the actual calculations.

## Operations

Alright, let’s get going. First of all, what exactly is different between various operations? All those differences, we are going to pack into neat little configuration objects.

The most obvious difference is the way they transform inputs into a result, meaning how they calculate. We can pack that information into a function, which takes the inputs and returns the result. Then, there’s the number of inputs needed to get a result, which can be 1 or 2. Let’s pick 2 as default, and mark the ones that only take 1. Quite importantly, each operation has a certain precedence value. That’s just an integer — the higher it is, the stronger it binds the surrounding numbers to itself. Each operation has a symbol we can print on the corresponding button, and a name we might need. Some operations will have certain validity restrictions — for instance, in a division, the second input can’t be `0`. We can make that a function that takes the inputs and returns an appropriate error if something’s wrong, and `false` if everything’s alright. Lastly, it would be nice to have a pretty representation of the calculation we can print out to the user, so they know what’s going on. For that, we can use a function that takes the inputs and returns a pretty string.

And that’s about all the differences there are. Let’s take addition, division and square as examples, since with those three, all the differences listed will be relevant. Our configuration object then will look like this:

 12345678910111213141516171819202122232425 var operationData = {   add: {     precedence: 1,     name: 'add',     operation: function (a, b) {return a + b;},     output: function (a, b) {return a + ' + ' + b;},     buttonHTML: '+'   },   divide: {     precedence: 2,     name: 'divide',     operation: function (a, b) {return a / b;},     isInvalidInput: function (a, b) {return b == 0 ? 'division by 0' : false;},     output: function (a, b) {return a + ' / ' + b;},     buttonHTML: '/'   },   square: {     precedence: 3,     singleInput: true,     name: 'square',     operation: function (a) {return Math.pow(a, 2);},     output: function (a) {return 'sqr(' + a + ')';},     buttonHTML: 'x2'   } };

Secondly, what do all operations have in common? That’s what we are going to pack into the instantiable `Operation` object.

Most importantly, we have to be able to add inputs and to execute the operation. In order to be able to execute, the operation needs to know whether it already has all the inputs it needs — let’s call this state being saturated. Before it can execute, it will also have to check the inputs’ validity, so it won’t trip up on an illegal operation. Also, we decided we wanted to be able to get a string representation of the calculation.

That’s all we need our operation objects to do. It might be helpful to be able to convert the `Operation` to a number (the calculation result) or a string (the pretty representation) using the `Number` and `String` factories, so let’s also add a `valueOf` and a `toString` method.

All in all, the outline of the `Operation` object will be something like this:

 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 var Operation = function (options) {   // There's no need for external code to know about the   // operation's inputs, so let's make them local   var inputs = [];   // Make all the passed options accessible as object properties   for (var key in options) {     this[key] = options[key];   };     // Adding an input just puts it into the inputs array.   // Before that, check whether the operation even needs another input, though.   this.addInput = function (input) {     if (this.isSaturated()) return this;     inputs.push(input);     return this;   };   // Check whether all the inputs are valid.   // If no validation funtion has been passed, all inputs are valid.   this.isInvalidInput = this.isInvalidInput || function () {return false;};   // Check whether the operation already has all the inputs it needs   this.isSaturated = function () {     var inputCount = this.singleInput ? 1 : 2;     for (var i = 0; i < inputCount; ++i) {       if (inputs[i] == null || isNaN(inputs[i])) return false;     }     return true;   };   // Execute the operation, and put the result into the value property   this.execute = function () {     // Execution code   };   // Get a pretty representation of the calculation   this.getCalculationString = function () {     // Representation code   };   // Define the numerical value of the operation as its caclulation result   // If there isn't a result yet, execute the operation first   this.valueOf = function () {     if (this.value == null) {       this.execute();     }     return this.value;   };   // Define the string value of the operation as its pretty calculation string   // If it isn't set yet, execute the operation first   this.toString = function () {     if (this.calculationString == null) {       this.execute();     }     return this.getCalculationString();   }; };

Okay, after figuring out what all operations have in common, this has been easy enough. Notice that whenever a method doesn’t have any interesting return value, we are just returning the object itself, which will allow us to chain methods later on, like this:

You might ask yourself why we even bother with setting those inputs separately, instead of just passing them to the `execute` method. The reason is that because of precedence handling, not all inputs will be immediately available. For instance, if the user enters `1 + 2`, we can create an addition and set `1` as the first input, but we can’t set `2` as the second one yet, because the user might choose to enter `* 3` after that, which means that the `2` actually belongs to the multiplication, and not to the addition.

Alright, what’s still missing is the code for the `execute` and `getCalculationString` methods. Let’s have a look at `execute`:

First, it needs to check whether all the inputs are already there. If not, there’s no reason to continue. If all the inputs are there but invalid, let’s just throw an appropriate error for now — we are going to catch that later. If all is in order, the actual calculation can take place, and the pretty calculation string can be set. That will look something like this:

 12345678910111213141516171819202122 // Execute the operation, and put the result into the value property this.execute = function () {   // If execution has already failed once because of invalid inputs,   // there's no need to try again, since inputs can only be added, but not changed.   if (this.error) return this;   // If inputs are missing, or the operation has already been executed,   // there's no need to continue.   if ( ! this.isSaturated() || this.value != null) return this;   // Inputs don't have to be numbers — they can be other Operation objects too,   // so for calculation purposes, map the inputs to their numerical values.   var inputValues = inputs.map(function (input) {return Number(input);});   // If an input is invalid, throw an error.   // The error message is coming straight from the operation's configuration object   // and should explain well enough what went wrong (e.g. 'division by 0').   this.error = this.isInvalidInput.apply(this, inputValues);   if (this.error) {     throw new Error(this.error);   }   this.calculationString = this.getCalculationString();   this.value = this.operation.apply(this, inputValues);   return this; };

`this.operation.apply(this, inputValues)` is where the actual calculation happens, with `this.operation` being the calculation function we passed to the constructor as an option.

Notice that the inputs don’t have to be numbers — we will want to nest operations, so the inputs can in fact also be other `Operation` objects. Since we made sure to implement a `valueOf` method, we can just map the inputs to their numerical values, though, and be done with it. (`Array.map` isn’t implemented in older browsers, so we’ll just add a custom implementation to the `Array` prototype later on, for backwards compatibility.)

As for the `getCalculationString` method:

At the heart of it, there will certainly be the `output` function we passed in as an option. A bit of a problem, though, is this: We want to be able to get a pretty calculation string at all times during user input, but because of precedence handling, most of the time most operations won’t be saturated yet. An easy solution is to just set up a parameter, so we can pass a missing input to the method, or pass an empty string, if the input simply isn’t there yet. For calculation purposes, that would be no good, but since the `output` method only does a bit of string concatenation, that’s perfectly alright.

If one of the inputs isn’t a number but another `Operation`, we’ll just get its string representation, and work with that.

Another thing that would be nice is to be able to not only get a string representing the whole calculation that has been entered so far, but to somehow collapse that string, so it reflects the current progress of the calculation. For example, if the full calculation string so far is `4 * 3 + 2 *`, that first multiplication is already saturated and can be calculated, and it would be nice to get the collapsed string `12 + 2 *`, so the user can see a simplified representation of what they have entered so far. We can easily make that happen by trying to execute the operation, and if it has been successful, just return the result, instead of the calculation string.

All in all, the `getCalculationString` method can look like this:

 1234567891011121314151617181920 // Get a pretty representation of the calculation this.getCalculationString = function (lastInput, collapsed) {   // If collapsed string is requested, try to return the calculation result   if (collapsed) {     this.execute();     if (this.value != null) return this.value.toString();   }   // Map all inputs to their string representations   // regardless of whether they are numbers or Operation objects   var singleInput = this.singleInput;   var inputValues = inputs.map(function (input) {     var inputValue = input.getCalculationString ?       input.getCalculationString(lastInput, collapsed) :       input.toString();     // Single-input operations are already sporting parentheses     // in their output, so if the inputValue has a pair too, remove them.     return singleInput ? inputValue.replace(/^\((.*)\)\$/g, '\$1') : inputValue;   });   return options.output.apply(this, inputValues.concat([lastInput])); };

We’ve got all we need now to work with our operations. Let’s take that `4 * 3 + 2 *` calculation from before as an example — setting that incomplete calculation up, getting some output, adding the missing input, and getting the result will look like this:

Here’s a complete working example of the code we have so far. Click it to see the actual output:

Admittedly, what it’s able to do right now we could have gotten a lot cheaper, but a certain level of abstraction will pay off once we implement the precedence handling, which is what we’re going to do next.

## Input Stack

Here comes the interesting part — we’re creating a component that keeps track of the user’s inputs, understands precedence handling, and knows when it’s okay to calculate partial results and when it has to wait for more input.

The first thing that’s noteworthy is that every user input consists of a number and a command. That’s just how a calculator works: The user enters a number into the input field, but that number will never be sent alone, because there’s simply no way of doing that. For the calculator to know when the user has finished entering the number, a button has to be pressed, and that button represents a command. Likewise, no command will ever be sent without a number, because the calculator’s input field will never be empty. At the very beginning, it will contain `0`, and after each input it will be updated with a partial result. The only commands that are interesting for now are mathematical operations, which means that every input will consist of a number and the next operation.

What to do with the number? Whether it belongs to the new or the previous operation depends on which of those has the higher precedence. We just add it as input to the appropriate one. (If both have the same precedence, the left-to-right rule kicks in, and the number belongs to the previous operation.)

What to do with the operation? If the new operation’s precedence is equal to or lower than the previous one’s, the previous one is already saturated by now (we just filled its last input slot with a number), and we can just feed it as input to the new one — we’ve seen that before. Here’s an example:

 1234567 Current input: 1 * Input so far: 1 * Internal representation: multiply(1, X) Current input: 2 + Input so far: 1 * 2 + Internal representation: add(multiply(1, 2), X)

If the new operation’s precedence is higher than the previous one’s, then the previous one is still missing an input, so the best we can do is leave it for now, and add the new operation to the input stack:

 1234567 Current input: 3 * Input so far: 1 * 2 + 3 * Input stack: add(multiply(1, 2), X), multiply(3, X) Current input: 4 ^ Input so far: 1 * 2 + 3 * 4 ^ Input stack: add(multiply(1, 2), X), multiply(3, X), power(4, X)

So, if a new operation comes along, and we can’t yet resolve the previous one because it’s still missing something, we’re just going to push it into an array, in order to keep track of the whole calculation. The reason I’m calling this array a stack is the fact that we are only ever going to be interested in the topmost operation (the one that has been pushed on the stack last) — that’s the one whose precedence decides what to do with any new operations.

Alright, so, what happens if the next input is a lower-precedence operation? Let’s take care of the number first — like in the first example, it belongs to the previous operation:

 1234 Current input: 5 * Input so far: 1 * 2 + 3 * 4 ^ 5 * Input stack: add(multiply(1, 2), X), multiply(3, X), power(4, 5) (new operation not yet added to input stack)

What to do with the new operation, though? The thing is, whatever follows after `(1 * 2) + 3 * (4 ^ 5) *` (parentheses added for already saturated operations) can’t in any way influence `3 * (4 ^ 5)` any more, because `*` doesn’t have higher precedence than any of those other operations in there. That means that we might as well feed the last operation on the stack to the one before:

 1 Input stack: add(multiply(1, 2), X), multiply(3, power(4, 5))

We’ll call the thing we just did collapsing the stack. Can we do it once again? Let’s see: Can whatever follows after `(1 * 2) + [3 * (4 ^ 5)] *` (brackets added for the already collapsed part) in any way influence `(1 * 2) + [3 * (4 ^ 5)]`? Yes it can, because `*` binds `[3 * (4 ^ 5)]` stronger than `+` does. So, we’ve done all the collapsing we can do, and the only thing left is to feed the last operation on the stack to the new one, just as we did in the first example:

 1 Input stack: add(multiply(1, 2), X), multiply(multiply(3, power(4, 5), X)

If the user enters another number and hits `=`, that’s the end of the calculation, which means that we can just collapse the whole input stack and end up with a result:

 123 Current input: 6 = Input so far: 1 * 2 + 3 * 4 ^ 5 * 6 = Input stack: add(multiply(1, 2), multiply(multiply(3, power(4, 5), 6))

And that’s pretty much it.

One more thing is missing: We would like to be able use parentheses in order to override precedences. That’s no biggie, though, since opening a parenthesis just opens a new context, inside of which all precedences are handled as usual (and without any care about what’s going on outside of that new context). That means that it suffices to just open up a new input stack on top of the main one. If several parentheses are open at the same time, we’ll end up with several levels of input stacks. If one is closed, the input stack on the topmost level can be collapsed into a single operation, which can then be pushed onto the stack a level below.

We’ve got everything we need now to start coding. We want that input handling component to be just one object with a neat public interface we can use to push inputs to the stack — we don’t want any other parts of the code to have to care about how it does what it does — so it makes sense to make it a singleton using the module pattern (I’m explaining that concept in greater detail in the article about closures). And although the input handling component will actually have to manage several different stacks internally, I am just going to call it `InputStack`, because from the outside we’re just interested in what it does, and not in how it does it.

What do we need from the public interface? We need to be able to push inputs (consisting of a number and an operation), open and close contexts (which means using parentheses in the calculation), and evaluate what’s currently in there (which means calculating the actual result). Then we will need a way to get some output: First of all, we need to be able to get a partial result: if you play around with a calculator, you’ll notice that you won’t only get a result in the end — every time some part of your calculation already yields a result, you’ll get that. We won’t need another method for the end result — as soon as all contexts are closed and the remaining stack is fully collapsed, the partial result will automatically be the end result. Secondly, we will want to get the calculation string — as mentioned in the part about the `Operation` objects both the full string, and the collapsed one.

In order to make that work, what do we need to happen internally? That question is easily answered by looking at that example calculation above: We need to keep track of various levels of input stacks in some data structure. That’s basically an array of context levels, where each context level is an array of operations. Since we’re using those arrays as stacks (meaning we only ever manipulate the last entry), let’s make a custom `Stack` object that inherits from `Array`, and add a `peek` method that returns its last element. Then we need to be able to collapse the input stack. We will also need a way to feed the topmost operation in the stack to another operation, which basically means to wrap the topmost operation in the other one. If this input component is supposed to handle more than just one calculation in its lifetime, we will also have to be able to reset it after a calculation is finished.

So, the basic outline will look like this:

 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 var InputStack = (function () {     // Data structure to keep track of contexts and operations   var levels;   // If a context has just been closed, store its value here.   // On the next input, the input's number will be discarded   // and the closed context's value will be used instead.   var closedContext;   // Whenever something can be calculated already, it will end up here.   var partialResult;   // Whenever an Operation object throws an error, we are going to catch it and   // put it here, so we'll know that it doesn't make sense to continue the calculation   var error;   // Stack object. Just an array with a peek function.   var Stack = function () {     this.peek = function () {return this[this.length - 1];};   };   Stack.prototype = [];     // Initialize the stack for managing context levels   // and put in the first context level (which is a stack for managing operations).   // We're at the beginning of a new calculation now,   // so there are no open parentheses or errors yet.   var reset = function () {     levels = new Stack;     levels.push(new Stack);     closedContext = error = null;   };     // Feed the last operation to the new one, and put   // the new one into the last one's place on the stack   var wrapLastOperation = function (operation) {     var stack = levels.peek();     stack.push(operation.addInput(stack.pop()));     // Since there has been a change to the stack, try to collapse it     collapse(operation.precedence);   };   // Collapse the current context as far as possible.   // In order to figure out how far it can be collapsed,   // it needs to know the next operation's precedence.   var collapse = function (precedence) {     // Collapse code   };   // Initialize the data structure   reset();   return {     // Push a number and the next operation to the current stack     push: function (number, operation) {     },     // Open a new context (means: add an opening parenthesis to the calculation)     openContext: function () {     },     // Close the last context (means: add a closing parenthesis to the calculation)     closeContext: function (number) {     },     // Calculate the end result     evaluate: function (number) {     },     // Get a partial result for output in the calculator's number field.     // The "reset" function resets everything except the partial result — this is done here.     getPartialResult: function () {       var _partialResult = partialResult;       partialResult = 0;       return _partialResult;     },     // Get a pretty string representing the calculation so far     getCalculationString: function (collapsed) {     }   };   }());

Easy enough, so far. A note regarding that `closedContext` variable: Whenever a context is closed, that whole context level will be completely shut down, and the calculation will continue on the level below, where the value of that context that has just been closed and evaluated will be used instead of the user input’s number (as always, a number will be sent, but after a closing parenthesis, it doesn’t make any sense mathematically, so it will be discarded). Since we have to wait for the next user input in order to know where that closed context’s value will actually go (again, precedence handling), we’ll just store it in that local variable in the meantime. More on that later.

Alright, the code I’ve already put in there should be pretty clear. The code for the `collapse` function is still missing, and so is the code for pretty much all the public methods. Let’s try to flesh those out one by one:

The `collapse` function:

From the example above, we already know what to do: Pop the last operation off the stack, and check whether the operation that came before has lower precedence. If so, collapse, meaning put the popped off operation into the last one as input. Repeat as long as that precedence condition is satisfied. If the condition fails, just push the popped off operation back on the stack. During that process, we should also update the partial result, since collapsing parts of the stack means that new stuff can be calculated.

 1234567891011121314151617181920212223242526272829303132333435363738394041424344 var collapse = function (precedence) {     // Pop off the last operation, and fetch the previous one,   // so we can have a look at its precedence   var stack = levels.peek();   var currentOperation = stack.pop();   var previousOperation = stack.peek();     // If the stack was empty, nothing needs to be done   if ( ! currentOperation) return;   // If the popped off operation doesn't yet have all the inputs it needs,   // just push it back on the stack and return.   if ( ! currentOperation.isSaturated()) {     stack.push(currentOperation);     return;   }     // If code execution makes it here, that means we have a saturated   // operation on our hands, so let's try to fetch its result.   // If the operation throws an error because of invalid inputs,   // catch it and put the error message in both "partialResult" for output purposes,   // and in "error", so other methods will know not to continue this failed calculation.   try {     partialResult = Number(currentOperation);   }   catch (e) {     partialResult = error = 'Error: ' + e.message;   }   // If there is a previous operation on the stack, and it doesn't have   // lower precedence than the popped off one, feed the popped off one to it.   // That's the actual "collapsing" part right here.   if (previousOperation && previousOperation.precedence >= precedence) {     previousOperation.addInput(currentOperation);     // The stack has one less element now — let's try to do that again.     collapse(precedence);   }   // If not, just push the popped off operation back on the stack   else {     stack.push(currentOperation);   } };

As for the public methods: Let’s try to return `this` whenever possible, so we can chain them.

First, the `push` method:

There’s not much to say here — it does exactly as described in the examples: depending on precedence, it feeds the number to the appropriate operation, and then either pushes the operation to the stack, or wraps it around the last one. Here, the `closedContext` variable mentioned earlier comes into play: if it’s set, a context has just been closed before, so the number parameter is irrelevant (it doesn’t make any mathematical sense after the closing parenthesis) and the context’s value is used instead.

What’s noteworthy here is that calculators sometimes do it the other way around: If a number is added after a closing parenthesis, some calculators replace the expression inside the parentheses with the number, instead of discarding the number (my Windows calculator does that). My Android calculator, on the other hand, automatically places a multiplication sign between the closing parenthesis and the number. All those ways are okay too — it just comes down to how you want to handle inputs that don’t make immediate mathematical sense.

 12345678910111213141516171819202122232425262728293031323334353637383940414243 {   push: function (number, operation) {     // If the current calculation has already thrown an error, start from scratch.     error && reset();     // Fetch the last operation in the current context.     // That's the only one that's relevant for precedence handling.     var stack = levels.peek();     var lastOperation = stack.peek();     // If a context has just been closed, discard the number from the     // calculator's input field, and use the value of the closed context instead.     var input = closedContext || number;     closedContext = null;         // Set the partial result. If parts of the stack can be collapsed later,     // this will be overwritten with more interesting results.     partialResult = Number(input);         // If the stack is empty, or the new operation has higher precedence     // than the last one, the new operation binds the input to itself.     // So, just put it in there, and push the operation to the stack.     if ( ! lastOperation || operation.precedence > lastOperation.precedence) {       stack.push(operation.addInput(input));       // The new operation might already be saturated       // (in case it takes only one input), so try to collapse the stack.       collapse(operation.precedence);     }     // If not, the input belongs to the last operation.     else {       lastOperation.addInput(input);       // The last operation is now saturated, so try to collapse.       collapse(operation.precedence);       // Whatever turns out to be the topmost operation on the stack now       // needs to be fed to the new operation, which will take its place.       wrapLastOperation(operation);     }     return this;       } }

Let’s take the `openContext`, `closeContext` and `evaluate` functions on in one go:

I commented all the code, and it should be pretty self-explanatory what’s going on. What’s probably worth mentioning is that each `Operation` object is capable of providing its own pretty calculation string, but we don’t yet have a way of displaying parentheses around a closed context. That’s easily done, though, by introducing a new “context” operation that doesn’t do anything except adding parentheses to the output. The configuration object of that new operation will look like this:

 123456789 var operationData = {   context: {     precedence: 4,     singleInput: true,     name: 'context',     operation: function (a) {return a;},     output: function (a) {return '(' + a + ')';}   } }

I gave it the highest precedence value of all operations, because parentheses bind their contents stronger than any surrounding operations — but the way we will use that operation, it will always be saturated before landing on the stack, so its precedence is actually irrelevant.

And here are those three methods:

 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 {   openContext: function () {       // If the current calculation has already thrown an error, start from scratch.     error && reset();     var lastOperation = levels.peek().peek();         // If a context has just been closed, opening another one     // before putting in a mathematical operation first doesn't make any sense.     // Likewise, if the last operation already has all the inputs it needs,     // opening a new context won't do any good.     if (closedContext || lastOperation && lastOperation.isSaturated()) return;     // Opening a new context means pushing a new operations stack to the levels stack     levels.push(new Stack);     return this;   },     closeContext: function (number) {     // If the current calculation has already thrown an error, start from scratch.     error && reset();     // If there's only the main level left, there's no context to close     if (levels.length <= 1) return;     // If another context has just been closed, discard the inputted number     var input = closedContext || number;         // Try to fetch the last operation from the stack     var stack = levels.peek();     var lastOperation = stack.peek();         // Actually close the context and store the result away.     // Closing the context is done by collapsing the topmost stack     // into a single operation or number, and feeding that to a new "context" operation,     // which does nothing except adding parentheses to the output.     closedContext = new Operation(operationData.context).addInput(       lastOperation ?         // If the context's stack isn't empty, feed the input to the last operation, and collapse.         // After that, there will only be one operation left on the context's stack,         // which will be the input for the "context" operation.         (function () {           lastOperation.addInput(input);           // Passing 0 precedence makes sure that the whole stack will be collapsed.           collapse(0);           return stack.pop();         }())         // If the stack is empty, that means there's just a single number in the parentheses.         // In that case, that number is the input for the "context" operation.         : input     );     // The closed context has everything it needs, so fetch its result     partialResult = Number(closedContext);     // The context has been handled now, and its result stored away,     // so its input stack can be popped off the levels stack.     // The calculation will continue on the level below.     levels.pop();     return this;   },   evaluate: function (number) {     // If the current calculation has already thrown an error, start from scratch.     error && reset();     // If another context has just been closed, discard the inputted number     var input = closedContext || number;     // In case there are no operations on the stack at all, and the user just     // entered one number and hit "=", set the result to that number.     // If there's more, it will be overwritten later, while collapsing.     partialResult = Number(input);     // In case the user hit "=" whithout closing all the parentheses,     // close all open contexts now.     while (levels.length > 1) {       this.closeContext(input);     }     // If there actually is an operation on the stack, feed it     var lastOperation = levels.peek().peek();     lastOperation && lastOperation.addInput(input);     // Collapse the stack. Passing in the lowest possible precedence value     // makes sure that the whole stack will be collapsed.     collapse(0);     // By now, there's an end result stored away in partialResult.     // We don't need the calculation data that led to that result     // any more, so let's just reset all the stacks.     reset();         return this;   } }

All that’s missing is the `getCalculationString` method:

Getting the calculation string is very much like collapsing the stack: We start at the topmost operation in the topmost context, get its string, feed it to the previous one, and then do the same for that one. And so on. When we have reached the bottommost operation in the bottommost context, we end up with a string representing the full calculation.

Two things need to be mentioned:

First, that topmost operation we start with is probably still missing an input — that’s okay, though, because for display purposes we can just leave that empty. We just have to make sure not to forget that if a context has just been closed before, its result is supposed to be used as input on the level below, once the next operation kicks in, so when getting the string, we will also have to use that closed context as the missing input instead of leaving it empty.

Second, we’ve introduced that `context` operation for output purposes, so closed contexts come with their own sets of parentheses. As long as they are still open, though, they won’t, so let’s make sure to add an opening parenthesis to the calculation string for each active context level.

Here we go:

 123456789101112131415161718192021222324252627 {   getCalculationString: function (collapsed) {       // If a context has just been closed, that's the missing input.     // Else leave it empty.     var result = closedContext ? closedContext.getCalculationString('', collapsed) : '';     // Start with topmost context and work to the bottom     for (var j = levels.length - 1; j >= 0; --j) {             // Start with topmost operation and work to the bottom       for (var i = levels[j].length - 1; i >= 0; --i) {                 // Feed the string we already got to the next function, so it will be wrapped         result = levels[j][i].getCalculationString(result, collapsed);       }       // For each open context, add an opening parenthesis.       // (Except for the bottommost one — the whole calculation doesn't need parentheses.)       if (j > 0) {         result = '(' + result;       }     }         return result;   } }

Phew.

That’s it, we got it all working now. We still don’t have any UI, of course, but in order to test the calculator, we don’t need that — we can just use the public interface of the input handler.

As an example, let’s take the following calculation:

 1 (6 + 5 * 4 - 3) - sqr(3) * 2 =

A bit of precedence handling, a separate context, an operation that takes only one input — that should be enough to give us an idea. Using the interface we just wrote, this translates to the following pseudocode:

 123456789 openContext(); push(6, +); push(5, *); push(4, -) closeContext(3); push([number irrelevant], -); push(3, sqr); push([number irrelevant], *) evaluate(2);

Let’s see that in action. Click to actually see the output. You can edit the code, too — I’ve put in a few more operations to play with:

## The Finished Calculator

Setting up the user interface is a bit boring, really, so I won’t go into any detail on that. Just add input and output fields and a few buttons to the DOM. When the buttons are clicked, tell the input stack about it, and fetch the output.

It might be nice to also add some convenience stuff, like buttons for deleting (parts of) the input, or for storing away a value for later use. Being able to enter numbers and operations with the keyboard would be nice, too. I’ve added those things in the finished code below. Since all of this comes down to just a bit of DOM manipulation, I used jQuery for this part.

All we have to do to finish things up is wrap our code in a `Calculator` component, so it won’t pollute the global namespace and can just be dropped into any page. As long as there’s a `<div>` with the id “calculator” somewhere in the HTML, it will be transformed into the calculator we built.

Here’s the finished calculator — click to see it in action:

 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 OOP Calculator

It’s still not very interesting, in that it just does what any old calculator can do. Since we built it with maintainability and extensibility in mind, it should be pretty easy, though, to make it do cool things not every calculator does — more on that in the next part.