RPN calculator in Rust
Since I’m now a Rust developer by day, I’m trying to flatten my learning curve by using Rust for pet/learning projects. Trying to pick a task small enough to learn and looking for a calculator that is usable directly from the command-line, I didn’t find anything I liked that was really simple. I’m aware of calculators like bc(1), dc(1), kalker and of course calc in Emacs. All of these are extraordinary in their own right. For any extensive calculations and/or statistics there is of course Julia and R. What I wanted was something simple that allows one to type simple expressions directly in the shell without the need for quoting or escaping of special characters. Hence rpn.
The start
Because Rust has support for ADT’s via enum
s see the Enums and Pattern Matching section in the Rust book for an introduction, handling a combination of integer and floating point arithmetic can be implemented with little effort. The first type written down looks like this:
|
|
The second value associated with the Float
variant, is used to track the number of signicant figures. The possibility of an item being either a number or an operator is represented by the Item
enum
:
|
|
Implementing the arithmetic was simply forwarding the hard work to the appropriate Rust standard library function. With a few macro’s to implement the necessary traits progress was nice. As an example here is the implementation of the binary operator trait:
|
|
Because the standard arithmetic operations are implemented is functions in the standard library these can be used here as the $native_op
function.
The middle
The parsing of the expressions, if one could call it that because all that is really happening is tokenizing. Is split into three FromStr
implementations. The first one of the Item
enum
:
|
|
The second one is the implementation for Num
:
|
|
Here we see the nice high-level method chains that can be written in Rust, in this case to calculate the number of significant figures eluded to earlier.
The FromStr
implementation, is a big switch statement looking something like:
|
|
The main
function takes in all arguments splits them on whitespace and call’s .parse::<Item>()
.
The evaluator takes the Item
’s and performs the operations by manipulating the stack as one would expect. Using the mentioned macro’s this calculator was implemented in 480 lines of code, excluding the tests.
Conclusion
It was fun to work on this tiny side-project, it learned me a great deal especially about traits and the differences between iter()
and into_iter()
.