Skip to content

Instantly share code, notes, and snippets.

@passcod
Last active August 29, 2015 14:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save passcod/242bf44d78f7d99dccfc to your computer and use it in GitHub Desktop.
Save passcod/242bf44d78f7d99dccfc to your computer and use it in GitHub Desktop.
Implementation of GNU's dc (reverse-polish arbitrary-precision stack-based command-line calculator) in Ruby
#!/usr/bin/env ruby
require 'bigdecimal'
class Dc
def initialize
@stack = []
@registers = []
# TODO: support these
@precision = 0
@input_radix = 10
@output_radix = 10
end
def push(n)
@stack.push n
end
# Arithmetic
define_method '_+' do
a = @stack.pop
b = @stack.pop
@stack.push(a + b)
end
define_method '_-' do
a = @stack.pop
b = @stack.pop
@stack.push(b - a)
end
define_method '_*' do
a = @stack.pop
b = @stack.pop
@stack.push(a * b)
end
define_method '_/' do
a = @stack.pop
b = @stack.pop
@stack.push(b / a)
end
define_method '_%' do
a = @stack.pop
b = @stack.pop
@stack.push(b % a)
end
define_method '_~' do
a = @stack.pop
b = @stack.pop
@stack.push(b % a)
@stack.push(b / a)
end
define_method '_^' do
a = @stack.pop
b = @stack.pop
@stack.push(b ** a)
end
define_method '_|' do
a = @stack.pop
b = @stack.pop
c = @stack.pop
@stack.push((c ** b) % a)
end
def _v
a = @stack.pop
@stack.push a.sqrt @precision
end
# Output
def _p
puts @stack.last.to_s 'F'
end
def _n
print @stack.pop.to_s 'F'
end
def _P
# TODO
end
def _f
@stack.each do |v|
puts v.to_s 'F'
end
end
# Stack control
def _c
@stack = []
end
def _d
@stack.push @stack.last
end
def _r
a = @stack.pop
b = @stack.pop
@stack.push a
@stack.push b
end
# Registers
def _s(r)
@registers[r] ||= []
@registers[r][0] = @stack.pop
end
def _l(r)
@stack.push @registers[r].last
end
def _S(r)
@registers[r] ||= []
@registers[r].push @stack.pop
end
def _L(r)
@stack.push @registers[r].pop
end
# Parameters
def _i
@input_radix = @stack.pop
end
def _o
@output_radix = @stack.pop
end
def _k
@precision = @stack.pop
end
def _I
@stack.push @input_radix
end
def _O
@stack.push @output_radix
end
def _K
@stack.push @precision
end
# Status
def _Z
# Probably wrong, FIXME
a = @stack.pop
@stack.push BigDecimal.new((a.fix.to_i.to_s.length + a.frac.to_i.to_s.length).to_s)
end
def _X
@stack.push BigDecimal.new @stack.pop.frac.to_i.to_s.length.to_s
end
def _z
@stack.push BigDecimal.new @stack.length.to_s
end
end
cmd = ARGV.join(' ')
dc = Dc.new
rcmd = nil
fix = nil
frac = nil
cmd.each_char do |c|
if c =~ /[0-9]/ # TODO: support radix 11-16
if frac
frac += c
else
fix ||= ''
fix += c
end
elsif c == '.'
frac ||= ''
else
if fix
frac ||= '0'
dc.push BigDecimal.new "#{fix}.#{frac}"
fix = nil
frac = nil
end
if rcmd
dc.send "_#{rcmd}".to_sym, c[0].ord
rcmd = nil
elsif %w[s l S L].include? c
rcmd = c
elsif dc.methods.grep(/^_/).map{|m|m.to_s}.include? "_#{c}"
#puts "# send :_#{c}"
dc.send "_#{c}".to_sym
end
end
#puts "# c: #{c} fix: #{fix} frac: #{frac} rcmd: #{rcmd}"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment