-
Notifications
You must be signed in to change notification settings - Fork 256
/
bf.rb
144 lines (118 loc) · 2.58 KB
/
bf.rb
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
# frozen_string_literal: true
require 'socket'
class Op
attr_accessor :op, :val
def initialize(op, val)
@op = op
@val = val
end
end
class Tape
def initialize
@tape = [0]
@pos = 0
end
def get
@tape[@pos]
end
def inc(x)
@tape[@pos] += x
end
def move(x)
@pos += x
@tape << 0 while @pos >= @tape.size
end
end
class Printer
attr_reader :quiet
def initialize(quiet)
@sum1 = 0
@sum2 = 0
@quiet = quiet
end
def print(n)
if @quiet
@sum1 = (@sum1 + n) % 255
@sum2 = (@sum2 + @sum1) % 255
else
$stdout.print(n.chr)
end
end
def checksum
(@sum2 << 8) | @sum1
end
end
class Program
def initialize(code, p)
@ops = parse code.chars.each
@p = p
end
def run
_run @ops, Tape.new
end
private
def _run(program, tape)
program.each do |op|
case op.op
when :inc then tape.inc(op.val)
when :move then tape.move(op.val)
when :loop then _run(op.val, tape) while tape.get.positive?
when :print then @p.print(tape.get)
end
end
end
def parse(iterator)
res = []
while (c = iterator.next rescue nil)
op = case c
when '+' then Op.new(:inc, 1)
when '-' then Op.new(:inc, -1)
when '>' then Op.new(:move, 1)
when '<' then Op.new(:move, -1)
when '.' then Op.new(:print, 0)
when '[' then Op.new(:loop, parse(iterator))
when ']' then break
end
res << op if op
end
res
end
end
def notify(msg)
Socket.tcp('localhost', 9001) { |s| s.puts msg }
rescue SystemCallError
# standalone usage
end
def verify
text = '++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>' \
'---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.'
p_left = Printer.new(true)
Program.new(text, p_left).run
left = p_left.checksum
p_right = Printer.new(true)
"Hello World!\n".each_char { |c| p_right.print(c.ord) }
right = p_right.checksum
return unless left != right
warn "#{left} != #{right}"
exit(1)
end
if __FILE__ == $PROGRAM_NAME
verify
engine = RUBY_ENGINE
if engine == 'truffleruby'
desc = RUBY_DESCRIPTION
if desc.include?('Native')
engine = 'Ruby/truffleruby'
elsif desc.include?('JVM')
engine = 'Ruby/truffleruby (JVM)'
end
elsif engine == 'ruby' && RubyVM::RJIT.enabled?
engine = 'Ruby (--jit)'
end
text = File.read(ARGV[0])
p = Printer.new(ENV.key?('QUIET'))
notify("#{engine}\t#{Process.pid}")
Program.new(text, p).run
notify('stop')
puts "Output checksum: #{p.checksum}" if p.quiet
end