My own Postfix Calculator

Job hunt time and that means writing some code to do random stuff to things!

Given the requirements that the code to do this be ‘readable, easy to extend, and understand’ I wrote a Reverse Polish Notation calculator.

//reminder: 'operand' == some kinda number, 'operator' == a math symbol
var operandCount = 0 //for checking if we have > 2 operands to do stuff to
var stack = [] //list of everything entered, with operands as floats and operators as strings
var validCharacters = ['1', '2', '3', '4', '3', '4', '5', '6', '7', '8', '9', '10',
' ', '*', '+', '/', '-' ]
function handleInput (datum){
  if (datum == 'q'){
    console.log("recieved 'q,' quitting")
    process.exit(0);
  }
  var response = ''
  if (!checkInvalid(datum)){
    console.log ('Sorry, you can only enter '+validCharacters)
  } else { //the input wasn't invalid characters
    datum = datum.replace(/^\s*|\s*$/, '') //Pattern's just removing initial or terminal whitespace
    //if more thn one value was given at once, grab each
    //TODO: if you enter more than one value the 'operandCount' checker will
    //become inaccurate. Basically the entering of multiple values isn't fully
    //supported in this version.
    datum.split(' ').forEach(function (value){stack.unshift(value)})
    var lastVal = stack.shift()
    if (isNumeric(lastVal)){
      //console.log('oh my, that's a number')
      operandCount++
      stack.unshift (parseFloat(lastVal))
    } else{
      if (operandCount < 2){
        console.log ('please enter '+(2-operandCount)+' more value!')
      } else {
        operandCount -- //one operand is destroyed in any function
        switch (lastVal){
          case '+' : performAddition() ; break
          case '/' : performDivision() ; break
          case '-' : performSubtraction() ; break
          case '*' : performMultiplication() ; break
        }

      }
    }


  }
  response = stack[0]
  console.log (response)
}

//this function could be written on one line with functional JS but it would be harder to read
function checkInvalid(datum){
  var reply = true
  datum.trim().split('').forEach(function(character){
    if (validCharacters.indexOf(character) < 0){
      reply = false
    }
  })
  return reply
}

function isNumeric(n){
  n = parseFloat(n) //everything's coming in as a string from stdin.on('data') :shrug emoji:
  return (typeof n == 'number' && !isNaN(n))
}
//are you at all worried I'm shift()ing the operator here? nope! I shifted it already
function performAddition (){
  stack.unshift(stack.shift() + stack.shift())
}

function performSubtraction (){
  stack.unshift(stack.shift() - stack.shift())
}

function performMultiplication (){
  stack.unshift(stack.shift() * stack.shift())
}

function performDivision (){
  stack.unshift(stack.shift() / stack.shift())
}





process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdout.write('\033[32m> \033[0m'); //input prompt
process.stdin.on('data', function(datum) {
  handleInput(datum.trim())
  process.stdout.write('\033[32m> \033[0m');
});

Install

  1. Make Node work on your system
  2. download this script to TFPostfixCalc.js
  3. run node TFPostFixCalc.js
  4. enter numbers or operators into the prompt q or EOF should both quit

TODO

In order to seem like a real human being I left one of the issues as a TODO in the code, because if you enter 10 42 the code will sorta work, but to improve it I’d also have to handle 10 * where I’m not totally clear on for the standard… I think it’s just an error?

Other stuff I don’t love:

  • all those separate functions for the operators could at least be written more densely
  • several checks could be rewritten with chaining and Functional patterns to be way more dense, but this is the way I write it first and I think this ‘fluffy’ version is easier to read
  • I feel it’s correct on bad input to just ignore the last entered line and maintain the state otherwise. Wasn’t in the spec, just said ‘won’t accept junk’ so :shrug emoji:. The other option would be to quit at that point.
  • Might want to color the output? IDK hardly major…

Leave a Reply

Your email address will not be published.