Skip to content

jessescool/RPN

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RPNCalc

3/5/23

I use this type of calculator when I do arithmetic. Popular with accountants, the OG was the HP-16C (iconic). HP still makes the HP Prime (which I used in high school). My favorite implementation is by PCalc on iPhone.

RPNCalc is an implementation of a postfix calculator, where a binary operator is placed after its two operands. Calculators of this type are said to use "Reverse Polish Notation". RPNCalc uses the standard input stream to take three types of input: integers, boolean values, and small "subprograms" in the format of an "rstring". Operations mirror those normally seen on common RPN calculators, like the classic HP12C. These include swap, drop, (you'll see these in PCalc) and the like. Reverse Polish notation and postfix notation eliminate the ambiguity of expressions, removing the need for perentheses, increasing clarity and efficiency.

Files

  • main.cpp - RPNCalc's executor, simply creates and runs an RPNCalc object

  • RPNCalc.h - defines methods for the RPNCalc class, outlining usable commands; defines the calculator stack and con/destructor

  • RPNCalc.cpp - implements the RPNCalc class, handling the majority of functionality within RPNCalc; starts a query loop to handle input, calls helper functions on a per-input basis

  • DatumStack.h - defines a stack structure for Datum objects, with usual methods like pop, push, etc. This class handles the Datum object exclusively, and is not a template

  • DatumStack.cpp - implements the DatumStack stack structure, using an std::list as a backbone; a very simple class

  • parser.h - a header file for the singleton parseRString function, which begins handling a user-inputted rstring as soon as the first {, finishing upon a symmetric }

  • parser.cpp - the implementaiton of parseRString, leveraging recursion for nested rstrings, returning std::string

  • Makefile - The GNU compiling and linking automator file, containing rules for each class and function within RPNCalc; also contains cleaning rules and critical testing automation, like diff_test and unit_test

  • unit_tests.h - Creates a seperate main file for each void function... for testing

Compilation and Execution

Compile this program by calling make in a CLI. You may also call make RPNCalc. This generates an executable called RPNCalc, which can be run in a CLI as ./RPNCalc. The program takes input from the standard input stream, but can handle redirected input.

Commands may be found in RPNCalc.h and their detailed actions in RPNCalc.cpp in corresponding function contracts, but I will also list them briefly here:

  • [n]: pushes n (an integer) to the calculator stack
  • #t: pushes #t (true) to the calculator stack
  • #f: pushes #f (false) to the calculator stack
  • not: reverses the boolean at the top of the stack
  • clear: clears the stack
  • drop: drops the top member of the stack
  • dup: duplicates the top member of the stack
  • swap: swaps the top and second-to-top members of the stack
  • print: prints the top member of the stack to the standard outstream
  • + - * / mod: valid arithmetic operators working in postfix notation
  • == > >= < <=: valid comparison operators working in postfix notation
  • [rstring]: pushes a not-yet-executed set of cmds to the stack (see rstring)
  • exec: executes the contents of the rstring at the top of the stack
  • file: reads the top rstring and execs the contents from its contained file
  • IF: pops: rstring1, rstring1, bool, executes 1 if false, 2 if true
  • quit: quits the calculator

Worth elaboring on is the file command. Small subprograms may be built via the .cyl interface, which Mark Sheldon can tell you more about. These files are translated into .cylc files, which can be read by the file command and executed within the RPNCalc program. They can be incredibly powerful.

Testing

I tested RPNCalc in two ways.

I tested the core data structure of RPNCalc, the DatumStack, using the unit_test framework. Because DatumStack has entirely public methods, unit_test proved easy to use without workarounds. Intended behavior was clear for the DatumStack class, even without reference implementation. This made unit_test (unable to compare inputs of two different programs) a perfect testing method. These tests can be run via the command unit_test, and were created before the creation of the RPNCalc class.

I tested the RPNCalc class (and the program as a whole) using a diff-automation framework included in my Makefile. In many cases of the RPNCalc class, without a reference implementation, the correct course of behavior would be up to judgement. Eg. in the reference implementation, when calling a command like exec, three members of the stack are popped before we check their types, discarding them if their types are faulty. Is this really the "right" way to do something? We couldn't test for something like this via the unit_test framework. We need to direcly compare every output of the RPNCalc class to the reference ./theRPNCalc program in order to understand our source of truth. I wrote these functions as I defined RPNCalc's methods but only tested them upon RPNCalc's completion.

These files come in two different forms. Files with the txt extension are redirected to the standard input stream. They are handled just like user input. Files with the cylc extension are read in via the <fstream> library and executed in accordance with the file command.

Diffing must be done in a bash shell, redirecting not only the standard output but also the standard error stream to the outfile. Type bash to enter one of these shells.

.txt files tests can be run via diff_test [input.txt]. These make up the majority of the tests.

About

An RPN calculator in C++

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published