Ever since the creation of the ENIAC [link] and its bretheren, people have been quite fond of using machines to speed up math. The benefit therein is twofold. First, it decreases effort dramatically. What once took legions of people to compute can now be done by a single automaton in a fraction of the time. Second, it decreases expenses. People take time to solve problems, and they can make mistakes. In critical situations, waiting for a calculation to complete can cost lives. Having that calculation come back incorrect can be even worse. Using a well-designed machine to perform mathematical computations solves this problem by gauranteeing that mathematical computations are performed accurately and punctually.
It might not be the 1940s anymore, but math is still very important. It's also still very annoying to compute by hand.
So I made my own calculator.
The goal
The idea behind this calculator was to create a tool that would let me do basic math quickly from my command line. If I just want to calculate how many blocks I need to construct something in Minecraft, I shouldn't need to wait for Desmos to load. I therefore wanted this tool to load and execute very quickly by being very simple.
I also wanted it to be easy to extend. A tool with many hard-coded features can be annoying to update. I wanted my calculator to be as easy to extend as possible, so that useful niche features could be added as built-in functions.
The implementation
To create a calculator, I needed to make four parts. Essentially, I needed an input, a parser, an evaluator, and an output.
Since this is a command-line tool, the input and output were easy. All I needed to do was to use C's standard IO library.
For the calculator to understand the input, I'd need to select a parsing model. There are many to choose from, such as infix, postfix, s-expressions, ALGOL-style syntax, and many more. I elected to go with an s-expression based system for two reasons. First, s-expressions are far easier to parse than most other systems. Second, they allow for a great deal of expressability and readability, which is valuable in a fast calculator. You don't need to mentally juggle the order of items on a stack when handling sequences of elements, as with postfixes, and you don't need to implement special array operators or repeat an infix many times. Instead, s-expressions can take as many or as few arguments as needed, and can be composed in a manner that is often very easy to make sense of.
Finally, I created the evaluator. It works recursively by first evaluating all the arguments, then looking up a C implementation of a function in a table, and finally executing that function.
Of course, different functions take different amounts of arguments. To handle this, I implemented many different kinds of functions. When a label is identified within the lookup routine, it corresponds to both a function and an arity. The arity indicates the kind of execution that the function desires.
For functions like addition, the C implementation takes only 2 arguments. To account for sums of more than 2 values, the function is given the label "fold" as it's arity. Folding functions [link] allows foldable functions to handle an unbounded number of arguments.
Other functions might have a 1-arity or 2-arity. These functions require a fixed quantity of arguments, and can not be made to work with any more or any fewer. When these functions recieve the wrong number of arguments, an error is thrown.
By recursively evaluating functions according to their arities, the calculator is able to solve whatever problem you give it in a very efficient manner.
The features
This calculator supports the basic four functions, like any other calculator. It also supports a variety of more advanced features.
For instance, it can be used as an actuarial calculator. It is capable of finding the present or future value of an investment when compounded at a constant rate. This includes support for lump sums that are accruing interest as well as annuities with a fixed payout.
The calculator has both an immediate and REPL mode. If you pass in a problem as a string, it'll execute that in place and print the result. However, if you don't give it any arguments, it'll just give you access to a REPL. This means that it can be used as either a regular calculator or as a component inside of other programs.
It's also quite easy to install. Check out the github below.
The github repo at hand.
Let's go!