-
Notifications
You must be signed in to change notification settings - Fork 255
/
bf.cr
144 lines (123 loc) · 2.38 KB
/
bf.cr
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
require "socket"
module Op
record Inc, val : Int32
record Move, val : Int32
record Print
alias T = Inc | Move | Print | Array(Op::T)
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
while @pos >= @tape.size
@tape << 0
end
end
end
class Printer
getter quiet
def initialize(quiet : Bool)
@sum1 = 0
@sum2 = 0
@quiet = quiet
end
def print(n : Int32)
if @quiet
@sum1 = (@sum1 + n) % 255
@sum2 = (@sum2 + @sum1) % 255
else
print(n.chr)
end
end
def checksum
(@sum2 << 8) | @sum1
end
end
class Program
@ops : Array(Op::T)
def initialize(code : String, p : Printer)
@ops = parse(code.each_char)
@p = p
end
def run
_run @ops, Tape.new
end
private def _run(program, tape)
program.each do |op|
case op
when Op::Inc
tape.inc(op.val)
when Op::Move
tape.move(op.val)
when Array(Op::T)
while tape.get > 0
_run(op, tape)
end
when Op::Print
@p.print(tape.get)
else
# pass
end
end
end
private def parse(iterator)
res = [] of Op::T
iterator.each do |c|
op = case c
when '+'; Op::Inc.new(1)
when '-'; Op::Inc.new(-1)
when '>'; Op::Move.new(1)
when '<'; Op::Move.new(-1)
when '.'; Op::Print.new
when '['; parse(iterator)
when ']'; break
else; # pass
end
res << op if op
end
res
end
end
def notify(msg)
begin
TCPSocket.open("localhost", 9001) { |s|
s.puts msg
}
rescue
# standalone usage
end
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
if left != right
STDERR.puts "#{left} != #{right}"
exit(1)
end
end
class EntryPoint
verify
text = File.read(ARGV[0])
p = Printer.new(ENV.has_key?("QUIET"))
notify("Crystal\t#{Process.pid}")
Program.new(text, p).run
notify("stop")
if p.quiet
puts "Output checksum: #{p.checksum}"
end
end