An RPN Interpreter in Go (Golang)

I built a non-Turing-complete interpreter in Go whose current incarnation is simply named “rpnrepl”. rpnrepl is a very tiny stack-based, postfix language whose syntax is similar to that of the Forth programming language.

rpnrepl can only perform four mathematical operations, display the topmost item on the stack, display a newline, and exit the running program.

Unlike Forth whose grammar can change dynamically, rpnrepl does have a simple grammar. Words are separated by spaces unless bound by single quotation-marks or double quotation-marks. If a word looks like an integer, it is treated as an integer. Otherwise a word is assumed to represent a function. rpnrepl tries to execute these functions immediately as it finds them.

The seven functions supported are +,-,*,/,.,cr, and bye.

+ pops the two integers at the top of the stack, adds them, and leaves the result on the stack. The subtraction, multiplication, and division words ( -, *, and / ) perform similar operations.

. displays the top stack item. Strings can be pushed onto the stack by just specifying a string in double-quotes or single quotes.

cr displays a newline on the console.

bye exits rpnrepl with return code 0.

Please refer to the source code for rpnrepl.go here:

https://github.com/jimlawless/rpnrepl

Lexical analysis of the input stream is performed by using regular expressions. Four regular-expressions are used to sift through the input:

var patDquote = "[\"][^\"]*[\"]"
var patSquote = "['][^']*[']"
var patNumber = "[-]?\\d+"
var patWord = "\\S+"

The regexes help me extract double-quote or single-quote strings or numbers. The final regex is a catch-all ( any contiguous non-whitespace characters. )

rpnrepl uses a stack of interface {} elements. This means that we can push any data-type onto our stack.

As the input is scanned, strings and numbers are immediately pushed onto the stack. Since we have a stack of interface {} entries, we can push instances of any data-type onto it. For now. we’ll stick to integers and strings.

As functions are encountered, they are executed. The map structure named “words” contains the rpnrepl commands that map to internal rpnrepl functions.

Here’s a sample that you might type into the rpnrepl console:

"Hey! 2 * 7 == " . 2 7 * . cr cr

The output you’d see is:

Hey! 2 * 7 == 14

If you create a script named test.rpn with the contents:

"Let's multiply 2 by 3" . cr cr
"2 times 3 == " . 2 3 * . cr cr
bye

You can then run the above script by using the command-line:

rpnrepl < test.rpn

The output you’ll see is:

Let's multiply 2 by 3
 
2 times 3 == 6

I have omitted any means to define subroutines or functions in rpnrepl. In a future post, we’ll define our own functions and will discuss intermediate compilation formats.