-
Notifications
You must be signed in to change notification settings - Fork 0
/
bf.jl
213 lines (178 loc) · 6.29 KB
/
bf.jl
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
using ArgParse
# Simple wrapper to insert our translated brainfuck code into a Julia function
# that defines the heap (a), heap size (l) and the data pointer (dp).
function _construct_function(body_expr)
fun_expr = quote
function _bf()
# Heap
a = Char[0]
# Heap size
l = 1
# Data pointer
dp = 1
$body_expr
end
end
fun_expr
end
# Translate brainfuck operations into equivalent Julia code, taking advantage
# of Julia's decent metaprogramming support.
function _construct_body_expr(program)
body_expr = :(begin end)
skip_until = 0
for (n, op) in enumerate(program)
# Skip operations up to and including the requested operation.
if n <= skip_until
continue
end
if op == '+'
subprogram = program[n:end]
num_incs = _scan_num_ops(subprogram, '+')
skip_until = n + num_incs - 1
inc_expr = quote
a[dp] += $num_incs
end
# Append the expression to the body.
body_expr.args = [body_expr.args; inc_expr.args]
elseif op == '-'
subprogram = program[n:end]
num_decs = _scan_num_ops(subprogram, '-')
skip_until = n + num_decs - 1
dec_expr = quote
a[dp] -= $num_decs
end
# Append the expression to the body.
body_expr.args = [body_expr.args; dec_expr.args]
elseif op == '<'
subprogram = program[n:end]
num_dp_lefts = _scan_num_ops(subprogram, '<')
skip_until = n + num_dp_lefts - 1
dp_left_expr = quote
heap_exceeded = -(dp - $num_dp_lefts - 1)
if heap_exceeded > 0
for n in 1:heap_exceeded
unshift!(a, 0)
end
l += heap_exceeded
end
dp -= $num_dp_lefts
if dp < 1
dp = 1
end
end
# Append the expression to the body.
body_expr.args = [body_expr.args; dp_left_expr.args]
elseif op == '>'
subprogram = program[n:end]
num_dp_rights = _scan_num_ops(subprogram, '>')
skip_until = n + num_dp_rights - 1
dp_right_expr = quote
heap_exceeded = dp + $num_dp_rights - l
if heap_exceeded > 0
for x in 1:heap_exceeded
push!(a, 0)
end
l += heap_exceeded
end
dp += $num_dp_rights
end
# Append the expression to the body.
body_expr.args = [body_expr.args; dp_right_expr.args]
elseif op == '.'
out_expr = quote
write(STDOUT, a[dp])
end
# Append the expression to the body.
body_expr.args = [body_expr.args; out_expr.args]
elseif op == ','
in_expr = quote
c = read(STDIN, Char)
a[dp] = c
end
# Append the expression to the body.
body_expr.args = [body_expr.args; in_expr.args]
elseif op == '['
# Look for the matching closing bracket. Every time we see an
# opening bracket we increment a counter keeping track of how
# many closing brackets we need to read until we found 'our'
# closing bracket.
matching_bracket_pos = 0
depth = 0
for (m, mbr) in enumerate(program[n:end])
if mbr == '['
depth += 1
elseif mbr == ']'
depth -= 1
if depth == 0
matching_bracket_pos = m
break
elseif depth < 0
error("closing bracket found without matching opening bracket")
end
end
end
if matching_bracket_pos > 0
# Since the recursive call will translate the subprogram
# within the loop for us we don't want to do it in this call.
# Set skip_until to the position of the closing bracket so we
# skip all those ops.
skip_until = n + matching_bracket_pos - 1
# Get everything within (but not including) the opening and
# closing brackets.
subprogram = program[n + 1:n + matching_bracket_pos - 2]
subbody_expr = _construct_body_expr(subprogram)
# Insert the translated subprogram in a loop that is equivalent
# to the brainfuck [] semantics.
loop_expr = quote
while a[dp] > 0
$subbody_expr
end
end
# Append the expression to the body.
body_expr.args = [body_expr.args; loop_expr.args]
else
error("no closing bracket for opening bracket at pos $n")
end
end
end
body_expr
end
_all_ops = Set(['+', '-', '<', '>', '[', ']', '.', ','])
function _scan_num_ops(program, search_op)
count_ = 0
for (n, op) in enumerate(program)
if op == search_op
count_ += 1
elseif op in _all_ops
break
end
end
if count_ == 0
error("subprogram should start with the requested operation!")
end
count_
end
# Main function. Execution starts here.
function main()
# Parse arguments. No fancy options yet.
s = ArgParseSettings()
@add_arg_table s begin
"input"
help = "input file holding brainfuck source to run"
required = true
end
args = parse_args(ARGS, s)
# Read program from the input file.
input_file = open(args["input"])
program = readall(input_file)
close(input_file)
# Perform the brainfuck-to-julia translation.
body_expr = _construct_body_expr(program)
fun_expr = _construct_function(body_expr)
# Evaluate the function, resulting in its JIT compilation.
eval(fun_expr)
# Run the brainfuck program.
_bf()
end
# Start at the main function.
main()