rpn-calc-cli
v0.1.7
Published
A Reverse Polish notation calculator for the command line.
Downloads
2
Readme
rpn-calc-cli
An interactive command line Reverse Polar notation calculator built in Node.js.
What is RPN?
Reverse Polish notation, also known as postfix notation, is a mathematical notation where operators follow their operands. This is in contrast to Polish notation (prefix notation), wherein operands follow their operators. RPN found favor in the computer science community for how much specificity it added to calculating expressions and its ease with stack-oriented programming. Because expressions do not need to be parenthisized, the user can be more confident their intention will be reflected in the calculation.
Calculations are made by pushing numbers onto a stack. Everytime a mathematical operator is called, the last two numbers of the stack are popped off, evaluated, and their result is pushed back on to the stack.
Prerequisites
Before you continue, make sure you meet the following requirements:
- Node.js 16.11.1
- npm 8.1.1
Install
npm i -g rpn-calc-cli
Usage
calculate <flags>
Put a space after each operator and/or operand. The calculator will accept as many or as few elments as you wish to input at once.
>> 3 3 +
> 6
//also valid:
>> 3
> 3
>> 3
> 3
>> +
> 6
You can also input 'ac' to clear the stack and 'q' to exit the program.
Calling calculate with a --showStack flag will show you the stack as you push numbers and evaluations onto it. Even if you don't call the flag, the app will let you know if the stack is too short to be evaluated.
Contributing to rpn-calc-cli
To contribute to rpn-calc-cli, follow these steps:
- Fork this repository.
- Create a branch: git checkout -b <branch_name>.
- Make your changes and commit them: git commit -m '<commit_message>'
- Push to the original branch: git push origin rpn-calc-cli
- Create the pull request.
- Alternatively see the GitHub documentation on creating a pull request.
Design
In the spirit of not reinventing the wheel, I opted to scaffold the project with Ahmad Awais create-node-cli. This provided basic strucuture to the project, as well as some design configurations, automatic argument parsing and app documentation. I also used Inquirer.js for my user prompts. This provided an elegant looking user interface out of the box, with filtering and validation built into the prompt object. Lastly, I used Jest for testing.
For the actual RPN logic, I opted for a functional programming approach with a modular design pattern. My interface, calc.js, consists of a recursive function called parseUserInput. This recursive design allows for each element of the userInputArray to be dynamically handled without the eyesore of a massive loop. parseUserInput accepts an array made up of the user's input, and a current stack that defaults to an empty array. Each element of the user's input is evaluated and handled depending on whether it's a number or an operator, with error handling throughout. Its base case, the userInputArray being empty, returns an object with the currentStack and the currentAnswer (the last element of the stack) for the CLI to show. The currentStack is then passed back into the CLI function so that it can be paired with the userInputArray for the next recursion. In this way, the state of the application is passed back and forth and only mutated when the integrity of the element has been confirmed.
The tradeoff to this approach is that the inputs and the outputs of both the CLI and parseUserInput has to remain consistent, as they are passing arguments back and forth throughout the lifecycle of the app. To handle this, beyond testing, I would build an interpreter to handle different types of interfaces beyond my CLI, some of which may not provide a ready made array or stack for parseUserInput. With this, the core logic of the app could be used with different types of protocols and frontends.
Finally, my modules are structured to separate number and operator logic. While the number modules are self-contained, as much of what they do is fairly generic, the operator logic is contained in one public module with a couple of private ones to support it. rpn-operator-helper.js exports out isOperator, a boolean that checks the element against "valid" operators (which are contained in valid-math-operators.js) so that only vetted operators will trigger actions, as well as pushCalculationToStack, which actually evaluates the user's expression and pushes the new value back to the stack. Since pushCalculationToStack, along with its cousin pushNumberToStack, directly mutates the stack, I have both functions returning the stack explictly to avoid side-effects.
Roadmap
While I am proud of the project, there are several steps to make it even better. Implementing more operators would be at the top of my list, as the current crop only covers basic addition, subtraction, division, and multiplication. In addition, while I was able to unit test all the logic and modules in the lib folder, I was unable to figure out how to do integration testing with the CLI frontend itself. Simulating user input proved to be difficult and, unfortunately, the solutions coming up online weren't effective. I emailed some developer friends for help as well as stubbed out a fake test that I look forward to filling out with actual logic.
I had a great time doing a more functional programming approach, but it would be fun to break down the logic with object-oriented concepts, like a static all object in a class to maintain state within the backend. As I was learning as I went, functional programming allowed me to stay with the flow of the overall program, which kept me grounded while sifting through the weeds.
Contact
If you would like to contact me, please email [email protected]
License
This project uses the following license: MIT License