1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
#!/usr/bin/env ruby
require 'tins'
# A small Minsky (register) machine
module Minsky
class InterpreterError < StandardError; end
class ::Proc
attr_accessor :interpreter
attr_accessor :name
def execute
interpreter.display_registers(self)
call
end
end
class Registers
def initialize
@registers = Hash.new(0)
end
def [](name)
@registers[name]
end
def []=(name, value)
@registers[name] = value
end
def to_s
"[" + @registers.sort_by { |r,| r.to_s }.map { |r,v| "#{r}: #{v}" } *
"|" + "]"
end
def method_missing(name, value)
name = name.to_s
if name[-1] == ?=
name = name[0..-2].intern
value >= 0 or raise InterpreterError,
"only non-negative numbers can be stored in register #{name}"
@registers[name] = value
else
super
end
end
end
class Interpreter
include Tins::Interpreter
extend Tins::ConstantMaker
def initialize(source)
@source = source
@labels = []
@registers = Registers.new
end
attr_writer :stepping
def run
interpret_with_binding(@source, binding)
cont = @labels.first
while cont
cont = cont.execute
end
self
end
def display_registers(label)
@format ||= "%#{@labels.map { |l| l.name.to_s.size }.max}s"
STDOUT.puts "#{@format % label.name}: #{@registers}"
if @stepping
STDOUT.print "? "
STDOUT.flush
STDIN.gets
end
end
private
def label(name, &block)
@labels.find { |l| l.name == name } and
raise InterpreterError, "label named '#{name}' was already defined"
block.interpreter, block.name = self, name
@labels << block
end
def register_fetch(register)
@registers[register]
end
def register_decrement(register)
@registers[register] -= 1
end
def register_increment(register)
@registers[register] += 1
end
def label_fetch(name)
label = @labels.find { |l| l.name == name }
label or raise InterpreterError, "label named '#{name}' was not defined"
end
def increment(register, label)
label = label_fetch label
register_increment(register)
label
end
def decrement(register, zero_label, else_label)
register_value = register_fetch register
zero_label = label_fetch zero_label
else_label = label_fetch else_label
if register_value.zero?
zero_label
else
register_decrement(register)
else_label
end
end
def register
@registers
end
def halt
STDOUT.puts " *** machine halted"
nil
end
end
end
if $0 == __FILE__
if ARGV.empty?
Minsky::Interpreter.new(STDIN.read).run
else
interpreter = Minsky::Interpreter.new(File.read(ARGV.shift))
interpreter.stepping = !ARGV.empty?
interpreter.run
end
end
|