# RPN Fun

**URL:** <https://rubytalk.org/t/rpn-fun/13890>\
**Category:** ruby-talk\
**Created:** [30 September 2004 22:23 UTC](https://rubytalk.org/t/rpn-fun/13890 "2004-09-30T22:23:57Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![James\_Edward\_Gray\_II](https://avatars.discourse-cdn.com/v4/letter/j/ea5d25/32.png) [@James\_Edward\_Gray\_II](https://rubytalk.org/u/James_Edward_Gray_II)\
**Post date:** [30 September 2004 22:23 UTC](https://rubytalk.org/t/rpn-fun/13890/1 "2004-09-30T22:23:57Z")

</div>

Perl's Quiz of the Week this time around was to make an RPN calculator. If you're one of those people who has fond memories of HP's RPN calculators, you might want to play with this code a little. Just thought I would share.

It understand postfix notation. You can do:

3 \<enter\>  
5 \<enter\>  
+ \<enter\>

or

3 \<space\> 5 \<space\> + \<enter\>

It'll break it down term by term. It understands Ruby numbers like -3 and 0b101\_010 and even allows commas in them. It supports most of Ruby's math routines and even better, lets you define your own with:

def \<space\> OPERATOR \<space\> PROC\_CODE \<enter\>

Those must be on their own line. Example:

def avg { |left, right| (left + right) / 2.0 }

It'll know that's a binary operator because it takes two args. Anything else is considered unary.

If anyone has any use for this, just tell me and I'll throw it on the RAA. I was just having a little flashback fun.

Enjoy.

James Edward Gray II

&nbsp;&nbsp;#!/usr/bin/env ruby

# class for generating RPN Calculator objects  
class RPNCalc  
&nbsp;&nbsp;attr\_accessor :mode # defines methods mode() and mode = ...  
&nbsp;&nbsp;  
&nbsp;&nbsp;# handles setup after constructor  
&nbsp;&nbsp;def initialize( mode = "dec", stack = [] )  
&nbsp;&nbsp;&nbsp;&nbsp;@mode = mode  
&nbsp;&nbsp;&nbsp;&nbsp;@stack = stack  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;@ops = { }  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;### Stack manipulation methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;# primary input method, adds something to stack if it's numerical  
&nbsp;&nbsp;def push( number )  
&nbsp;&nbsp;&nbsp;&nbsp;if number.kind\_of? Numeric # don't touch non-numbers  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# the following if converts floats to ints, when it doesn't matter  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if number.kind\_of?(Float) and number == number.to\_i  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@stack.unshift( number.to\_i )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@stack.unshift( number )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return top # return top value, what we just added  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return nil  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;# pop from stack  
&nbsp;&nbsp;def drop( ) return unary { nil } end  
&nbsp;&nbsp;# swap top two elements from stack  
&nbsp;&nbsp;def swap( )  
&nbsp;&nbsp;&nbsp;&nbsp;return binary do |l, r|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push( r )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;# empty stack  
&nbsp;&nbsp;def clear( ) return @stack = [] end  
&nbsp;&nbsp;def dup( )  
&nbsp;&nbsp;&nbsp;&nbsp;return unary do |num|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push( num )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;num  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;# arbitrary stack position swap (up)  
&nbsp;&nbsp;def roll( )  
&nbsp;&nbsp;&nbsp;&nbsp;if @stack.size \< top  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Insufficient elements on stack for operation."  
&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;return unary { |num| @stack.delete\_at(num) }  
&nbsp;&nbsp;end  
&nbsp;&nbsp;# arbitrary stack position swap (down)  
&nbsp;&nbsp;def rolld( )  
&nbsp;&nbsp;&nbsp;&nbsp;if @stack.size \< top  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Insufficient elements on stack for operation."  
&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;return binary do |num, n|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@stack.insert(n, num)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nil  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;### Basis math methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;def add( ) return binary { |l, r| l + r } end  
&nbsp;&nbsp;def sub( ) return binary { |l, r| l - r } end  
&nbsp;&nbsp;def mul( ) return binary { |l, r| l \* r } end  
&nbsp;&nbsp;def mod( ) return binary { |l, r| l % r } end  
&nbsp;&nbsp;def pow( ) return binary { |l, r| l \*\* r } end  
&nbsp;&nbsp;def div( )  
&nbsp;&nbsp;&nbsp;&nbsp;return binary do |l, r|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# defeat Ruby's integer division  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if l.integer? and r.integer? and l % r != 0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l / r.to\_f  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l / r  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end

&nbsp;&nbsp;### To int methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;def floor( ) return unary { |num| num.floor } end  
&nbsp;&nbsp;def ceil( ) return unary { |num| num.ceil } end  
&nbsp;&nbsp;def round( ) return unary { |num| num.round } end  
&nbsp;&nbsp;  
&nbsp;&nbsp;### Higher math methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;def abs( ) return unary { |num| num.abs } end  
&nbsp;&nbsp;def sqrt( ) return unary { |num| Math.sqrt( num ) } end  
&nbsp;&nbsp;def exp( ) return unary { |num| Math.exp( num ) } end  
&nbsp;&nbsp;def log( ) return unary { |num| Math.log( num ) } end  
&nbsp;&nbsp;def log10( ) return unary { |num| Math.log10( num ) } end  
&nbsp;&nbsp;def sin( ) return unary { |num| Math.sin( num ) } end  
&nbsp;&nbsp;def cos( ) return unary { |num| Math.cos( num ) } end  
&nbsp;&nbsp;def tan( ) return unary { |num| Math.tan( num ) } end  
&nbsp;&nbsp;def sinh( ) return unary { |num| Math.sinh( num ) } end  
&nbsp;&nbsp;def cosh( ) return unary { |num| Math.cosh( num ) } end  
&nbsp;&nbsp;def tanh( ) return unary { |num| Math.tanh( num ) } end  
&nbsp;&nbsp;def asin( ) return unary { |num| Math.asin( num ) } end  
&nbsp;&nbsp;def acos( ) return unary { |num| Math.acos( num ) } end  
&nbsp;&nbsp;def atan( ) return unary { |num| Math.atan( num ) } end  
&nbsp;&nbsp;def asinh( ) return unary { |num| Math.asinh( num ) } end  
&nbsp;&nbsp;def acosh( ) return unary { |num| Math.acosh( num ) } end  
&nbsp;&nbsp;def atanh( ) return unary { |num| Math.atanh( num ) } end  
&nbsp;&nbsp;def atan2( ) return binary { |l, r| Math.atan2( l, r ) } end

&nbsp;&nbsp;### Bitewise manipulation methods ###

&nbsp;&nbsp;# Warning: These methods convert their operands to ints before operation  
&nbsp;&nbsp;def bit\_and( ) return binary { |l, r| l.to\_i & r.to\_i } end  
&nbsp;&nbsp;def bit\_or( ) return binary { |l, r| l.to\_i | r.to\_i } end  
&nbsp;&nbsp;def bit\_xor( ) return binary { |l, r| l.to\_i ^ r.to\_i } end  
&nbsp;&nbsp;def bit\_neg( ) return unary { |num| ~num.to\_i } end  
&nbsp;&nbsp;  
&nbsp;&nbsp;### Operator definition methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;# adds Ruby code as new operator  
&nbsp;&nbsp;def define\_op( op, &code )  
&nbsp;&nbsp;&nbsp;&nbsp;@ops[op] = code  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;### Input/Output methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;# parses and executes expressions in postfix notation  
&nbsp;&nbsp;# also understands "def OP RUBY\_PROC\_CODE" on it's own line  
&nbsp;&nbsp;def calc( postfix\_exp )  
&nbsp;&nbsp;&nbsp;&nbsp;if postfix\_exp =~ /^\s\*def\s+(\S+)\s+(\{.+\})\s\*$/ # define new op  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;define\_op( $1.downcase, &eval( "proc #{$2}" ) )  
&nbsp;&nbsp;&nbsp;&nbsp;else # ... or process terms  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;terms = postfix\_exp.downcase.split(" ")  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;terms.each do |t|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if @ops.include? t # use custom op definition  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if @ops[t].arity == 2 # choose handler by proc args  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;binary &@ops[t]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unary &@ops[t]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;next  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case t # ... or hardcoded definition  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "+" then add  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "-" then sub  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "\*" then mul  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "%" then mod  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "\*\*" then pow  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "/" then div  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "&" then bit\_and  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "|" then bit\_or  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "^" then bit\_xor  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "~" then bit\_neg  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when /^-?(?:\d[,\_\d]\*|0[,\_0-7]+|0x[,\_0-9a-f]+|0b[,\_01]+)$/,  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/^-?\d[,\_\d]\*\.\d[,\_\d]\*(?:e-?[,\_\d]+)?$/  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push( eval( [t.tr](http://t.tr)(",", "") ) )  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "drop", "swap", "clear", "dup", "roll", "rolld",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"floor", "ceil", "round",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"abs", "sqrt",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"exp", "log", "log10",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"sin", "cos", "tan", "sinh", "cosh", "tanh",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"asin", "acos", "atan", "asinh", "acosh", "atanh",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"atan2"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;send( t.to\_sym )  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "bin", "dec", "hex", "oct"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@mode = t  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Invalid term: #{t}."  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;return top  
&nbsp;&nbsp;end  
&nbsp;&nbsp;# getter for top of stack  
&nbsp;&nbsp;def top( ) return @stack[0] end  
&nbsp;&nbsp;# primary output method, display stack with optional limit  
&nbsp;&nbsp;def to\_s( limit = @stack.size )  
&nbsp;&nbsp;&nbsp;&nbsp;if @stack.size == 0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return "Empty Stack\n"  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case @mode  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "bin"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (0...limit).to\_a.reverse.inject("") do |str, i|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str + "#{i}: #{'%b' % @stack[i]}\n"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "dec"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (0...limit).to\_a.reverse.inject("") do |str, i|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str + "#{i}: #{@stack[i]}\n"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "hex"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (0...limit).to\_a.reverse.inject("") do |str, i|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str + "#{i}: #{'%x' % @stack[i]}\n"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "oct"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (0...limit).to\_a.reverse.inject("") do |str, i|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str + "#{i}: #{'%o' % @stack[i]}\n"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;private  
&nbsp;&nbsp;  
&nbsp;&nbsp;### Operator processing methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;def unary( &op )  
&nbsp;&nbsp;&nbsp;&nbsp;if @stack.size \< 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Insufficient elements on stack for operation."  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;return push( op.call( @stack.shift ) )  
&nbsp;&nbsp;end  
&nbsp;&nbsp;def binary( &op )  
&nbsp;&nbsp;&nbsp;&nbsp;if @stack.size \< 2  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Insufficient elements on stack for operation."  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;right, left = @stack.slice!(0, 2)  
&nbsp;&nbsp;&nbsp;&nbsp;return push( op.call( left, right ) )  
&nbsp;&nbsp;end  
end

# basic stand-alone interface, just process lines and show the stack...  
if \_\_FILE\_\_ == $0  
&nbsp;&nbsp;rpn = RPNCalc.new

&nbsp;&nbsp;print "\n" + rpn.to\_s + "\n"  
&nbsp;&nbsp;print "\> " if STDIN.tty?  
&nbsp;&nbsp;while line = ARGF.gets  
&nbsp;&nbsp;&nbsp;&nbsp;line.chomp!  
&nbsp;&nbsp;&nbsp;&nbsp;if STDIN.tty? and line =~ /^q(?:uit)?|exit$/i  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;begin  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rpn.calc line  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rescue  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts "Error: " + $!  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print "\n" + rpn.to\_s + "\n"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print "\> " if STDIN.tty?  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
end

\_\_END\_\_

---

<div class="post-metadata">

**Author:** ![Andreas\_S2](https://avatars.discourse-cdn.com/v4/letter/a/f17d59/32.png) [@Andreas\_S2](https://rubytalk.org/u/Andreas_S2)\
**Post date:** [30 September 2004 22:55 UTC](https://rubytalk.org/t/rpn-fun/13890/2 "2004-09-30T22:55:11Z")

</div>

James Edward Gray II wrote:

> Perl's Quiz of the Week this time around was to make an RPN calculator. If you're one of those people who has fond memories of HP's RPN calculators, you might want to play with this code a little. Just thought I would share.

Hello,

it's really nice, however there is one problem: if a define contains a syntax error, the calculator quits instead of just showing the error message.

Andreas

> **···**
>
> --  
> [http://www.mikrocontroller.net](http://www.mikrocontroller.net) - Das Mikrocontroller-Forum

---

<div class="post-metadata">

**Author:** ![Mark\_Probert3](https://avatars.discourse-cdn.com/v4/letter/m/58f4c7/32.png) [@Mark\_Probert3](https://rubytalk.org/u/Mark_Probert3)\
**Post date:** [1 October 2004 02:10 UTC](https://rubytalk.org/t/rpn-fun/13890/3 "2004-10-01T02:10:00Z")

</div>

Thank you 🙂

Also, if you are interested in RPN, I have ported a FORTH to Ruby (just  
because ;-)). You can find it at

[http://raa.ruby-lang.org/project/ratlast/](http://raa.ruby-lang.org/project/ratlast/)

All the RPN features you could want, plus more (ie Full Ruby).

Trivial e.g.

&nbsp;&nbsp;require 'Atlast'

&nbsp;&nbsp;t = Atlast.new  
&nbsp;&nbsp;t.eval(": sqr ( n -- n\*n ) dup \* ;")  
&nbsp;&nbsp;n = t.run("4 sqr .")

&nbsp;&nbsp;puts "4\*4 = #{n}"

Regards,

> **···**
>
> James Edward Gray II \<james@grayproductions.net\> wrote:
> 
> > &nbsp;&nbsp;If you're one of those people who has fond memories of HP's RPN  
> > calculators, you might want to play with this code a little. Just  
> > thought I would share.
> 
> --  
> -mark. (probertm @ acm dot org)

---

<div class="post-metadata">

**Author:** ![James\_Edward\_Gray\_II](https://avatars.discourse-cdn.com/v4/letter/j/ea5d25/32.png) [@James\_Edward\_Gray\_II](https://rubytalk.org/u/James_Edward_Gray_II)\
**Post date:** [30 September 2004 23:42 UTC](https://rubytalk.org/t/rpn-fun/13890/4 "2004-09-30T23:42:37Z")

</div>

I noticed that about the same time you posted this. It's fixed in the version below.

Did I mention it has display modes? Type "bin", or "hex". 🙂

I know, it's a toy. A fun one though...

James Edward Gray II

#!/usr/bin/env ruby

# class for generating RPN Calculator objects  
class RPNCalc  
&nbsp;&nbsp;attr\_accessor :mode # defines methods mode() and mode = ...  
&nbsp;&nbsp;  
&nbsp;&nbsp;# handles setup after constructor  
&nbsp;&nbsp;def initialize( mode = "dec", stack = )  
&nbsp;&nbsp;&nbsp;&nbsp;@mode = mode  
&nbsp;&nbsp;&nbsp;&nbsp;@stack = stack  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;@ops = { }  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;### Stack manipulation methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;# primary input method, adds something to stack if it's numerical  
&nbsp;&nbsp;def push( number )  
&nbsp;&nbsp;&nbsp;&nbsp;if number.kind\_of? Numeric # don't touch non-numbers  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# the following if converts floats to ints, when it doesn't matter  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if number.kind\_of?(Float) and number == number.to\_i  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@stack.unshift( number.to\_i )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@stack.unshift( number )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return top # return top value, what we just added  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return nil  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;# pop from stack  
&nbsp;&nbsp;def drop( ) return unary { nil } end  
&nbsp;&nbsp;# swap top two elements from stack  
&nbsp;&nbsp;def swap( )  
&nbsp;&nbsp;&nbsp;&nbsp;return binary do |l, r|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push( r )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;# empty stack  
&nbsp;&nbsp;def clear( ) return @stack = end  
&nbsp;&nbsp;def dup( )  
&nbsp;&nbsp;&nbsp;&nbsp;return unary do |num|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push( num )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;num  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;# arbitrary stack position swap (up)  
&nbsp;&nbsp;def roll( )  
&nbsp;&nbsp;&nbsp;&nbsp;if @stack.size \< top  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Insufficient elements on stack for operation."  
&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;return unary { |num| @stack.delete\_at(num) }  
&nbsp;&nbsp;end  
&nbsp;&nbsp;# arbitrary stack position swap (down)  
&nbsp;&nbsp;def rolld( )  
&nbsp;&nbsp;&nbsp;&nbsp;if @stack.size \< top  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Insufficient elements on stack for operation."  
&nbsp;&nbsp;&nbsp;&nbsp;end

&nbsp;&nbsp;&nbsp;&nbsp;return binary do |num, n|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@stack.insert(n, num)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nil  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;### Basis math methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;def add( ) return binary { |l, r| l + r } end  
&nbsp;&nbsp;def sub( ) return binary { |l, r| l - r } end  
&nbsp;&nbsp;def mul( ) return binary { |l, r| l \* r } end  
&nbsp;&nbsp;def mod( ) return binary { |l, r| l % r } end  
&nbsp;&nbsp;def pow( ) return binary { |l, r| l \*\* r } end  
&nbsp;&nbsp;def div( )  
&nbsp;&nbsp;&nbsp;&nbsp;return binary do |l, r|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# defeat Ruby's integer division  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if l.integer? and r.integer? and l % r != 0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l / r.to\_f  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l / r  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end

&nbsp;&nbsp;### To int methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;def floor( ) return unary { |num| num.floor } end  
&nbsp;&nbsp;def ceil( ) return unary { |num| num.ceil } end  
&nbsp;&nbsp;def round( ) return unary { |num| num.round } end  
&nbsp;&nbsp;  
&nbsp;&nbsp;### Higher math methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;def abs( ) return unary { |num| num.abs } end  
&nbsp;&nbsp;def sqrt( ) return unary { |num| Math.sqrt( num ) } end  
&nbsp;&nbsp;def exp( ) return unary { |num| Math.exp( num ) } end  
&nbsp;&nbsp;def log( ) return unary { |num| Math.log( num ) } end  
&nbsp;&nbsp;def log10( ) return unary { |num| Math.log10( num ) } end  
&nbsp;&nbsp;def sin( ) return unary { |num| Math.sin( num ) } end  
&nbsp;&nbsp;def cos( ) return unary { |num| Math.cos( num ) } end  
&nbsp;&nbsp;def tan( ) return unary { |num| Math.tan( num ) } end  
&nbsp;&nbsp;def sinh( ) return unary { |num| Math.sinh( num ) } end  
&nbsp;&nbsp;def cosh( ) return unary { |num| Math.cosh( num ) } end  
&nbsp;&nbsp;def tanh( ) return unary { |num| Math.tanh( num ) } end  
&nbsp;&nbsp;def asin( ) return unary { |num| Math.asin( num ) } end  
&nbsp;&nbsp;def acos( ) return unary { |num| Math.acos( num ) } end  
&nbsp;&nbsp;def atan( ) return unary { |num| Math.atan( num ) } end  
&nbsp;&nbsp;def asinh( ) return unary { |num| Math.asinh( num ) } end  
&nbsp;&nbsp;def acosh( ) return unary { |num| Math.acosh( num ) } end  
&nbsp;&nbsp;def atanh( ) return unary { |num| Math.atanh( num ) } end  
&nbsp;&nbsp;def atan2( ) return binary { |l, r| Math.atan2( l, r ) } end

&nbsp;&nbsp;### Bitewise manipulation methods ###

&nbsp;&nbsp;# Warning: These methods convert their operands to ints before operation  
&nbsp;&nbsp;def bit\_and( ) return binary { |l, r| l.to\_i & r.to\_i } end  
&nbsp;&nbsp;def bit\_or( ) return binary { |l, r| l.to\_i | r.to\_i } end  
&nbsp;&nbsp;def bit\_xor( ) return binary { |l, r| l.to\_i ^ r.to\_i } end  
&nbsp;&nbsp;def bit\_neg( ) return unary { |num| ~num.to\_i } end  
&nbsp;&nbsp;  
&nbsp;&nbsp;### Operator definition methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;# adds Ruby code as new operator  
&nbsp;&nbsp;def define\_op( op, &code )  
&nbsp;&nbsp;&nbsp;&nbsp;@ops[op] = code  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;### Input/Output methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;# parses and executes expressions in postfix notation  
&nbsp;&nbsp;# also understands "def OP RUBY\_PROC\_CODE" on it's own line  
&nbsp;&nbsp;def calc( postfix\_exp )  
&nbsp;&nbsp;&nbsp;&nbsp;if postfix\_exp =~ /^\s\*def\s+(\S+)\s+(\{.+\})\s\*$/ # define new op  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;define\_op( $1.downcase, &eval( "proc #{$2}" ) )  
&nbsp;&nbsp;&nbsp;&nbsp;else # ... or process terms  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;terms = postfix\_exp.downcase.split(" ")  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;terms.each do |t|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if @ops.include? t # use custom op definition  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if @ops[t].arity == 2 # choose handler by proc args  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;binary &@ops[t]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unary &@ops[t]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;next  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case t # ... or hardcoded definition  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "+" then add  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "-" then sub  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "\*" then mul  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "%" then mod  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "\*\*" then pow  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "/" then div  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "&" then bit\_and  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "|" then bit\_or  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "^" then bit\_xor  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "~" then bit\_neg  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when /^-?(?:\d[,\_\d]\*|0[,\_0-7]+|0x[,\_0-9a-f]+|0b[,\_01]+)$/,  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/^-?\d[,\_\d]\*\.\d[,\_\d]\*(?:e-?[,\_\d]+)?$/  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;push( eval( t.tr(",", "") ) )  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "drop", "swap", "clear", "dup", "roll", "rolld",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"floor", "ceil", "round",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"abs", "sqrt",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"exp", "log", "log10",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"sin", "cos", "tan", "sinh", "cosh", "tanh",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"asin", "acos", "atan", "asinh", "acosh", "atanh",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"atan2"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;send( t.to\_sym )  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "bin", "dec", "hex", "oct"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@mode = t  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Invalid term: #{t}."  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;return top  
&nbsp;&nbsp;end  
&nbsp;&nbsp;# getter for top of stack  
&nbsp;&nbsp;def top( ) return @stack[0] end  
&nbsp;&nbsp;# primary output method, display stack with optional limit  
&nbsp;&nbsp;def to\_s( limit = @stack.size )  
&nbsp;&nbsp;&nbsp;&nbsp;if @stack.size == 0  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return "Empty Stack\n"  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case @mode # support four types of numerical display  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "bin"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (0...limit).to\_a.reverse.inject("") do |str, i|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str + "#{i}: #{'%b' % @stack[i]}\n"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "dec"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (0...limit).to\_a.reverse.inject("") do |str, i|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str + "#{i}: #{@stack[i]}\n"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "hex"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (0...limit).to\_a.reverse.inject("") do |str, i|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str + "#{i}: #{'%x' % @stack[i]}\n"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when "oct"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (0...limit).to\_a.reverse.inject("") do |str, i|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str + "#{i}: #{'%o' % @stack[i]}\n"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;private  
&nbsp;&nbsp;  
&nbsp;&nbsp;### Operator processing methods ###  
&nbsp;&nbsp;  
&nbsp;&nbsp;def unary( &op )  
&nbsp;&nbsp;&nbsp;&nbsp;if @stack.size \< 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Insufficient elements on stack for operation."  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;return push( op.call( @stack.shift ) )  
&nbsp;&nbsp;end  
&nbsp;&nbsp;def binary( &op )  
&nbsp;&nbsp;&nbsp;&nbsp;if @stack.size \< 2  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise "Insufficient elements on stack for operation."  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;right, left = @stack.slice!(0, 2)  
&nbsp;&nbsp;&nbsp;&nbsp;return push( op.call( left, right ) )  
&nbsp;&nbsp;end  
end

# basic stand-alone interface, just process lines and show the stack...  
if \_\_FILE\_\_ == $0  
&nbsp;&nbsp;rpn = RPNCalc.new

&nbsp;&nbsp;print "\n" + rpn.to\_s + "\n"  
&nbsp;&nbsp;print "\> " if STDIN.tty?  
&nbsp;&nbsp;while line = ARGF.gets  
&nbsp;&nbsp;&nbsp;&nbsp;line.chomp!  
&nbsp;&nbsp;&nbsp;&nbsp;if STDIN.tty? and line =~ /^q(?:uit)?|exit$/i  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;begin  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rpn.calc line  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rescue SyntaxError  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts "Syntax Error: " + $!  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rescue  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts "Error: " + $!  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print "\n" + rpn.to\_s + "\n"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print "\> " if STDIN.tty?  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
end

\_\_END\_\_

> **···**
>
> On Sep 30, 2004, at 5:55 PM, Andreas Schwarz wrote:
> 
> > Hello,
> > 
> > it's really nice, however there is one problem: if a define contains a syntax error, the calculator quits instead of just showing the error message.
> > 
> > Andreas
