/
interpreter_class.rb
320 lines (239 loc) · 7.81 KB
/
interpreter_class.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
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
#encoding: utf-8
module Duck
def interpreter(args={})
Interpreter.new(args)
end
class Interpreter < Assembler
class << self; attr_accessor :global_ticks end
@global_ticks = 0
attr_accessor :script
attr_accessor :ticks
attr_accessor :max_ticks
attr_accessor :greedy_flag
attr_accessor :initial_script,:initial_contents,:initial_buffer,:initial_binder
attr_accessor :binder
attr_accessor :halted
attr_accessor :trace
attr_accessor :trace_string
attr_accessor :staged_item
def initialize(args = {})
default_args = {
script:"",
contents:[],
buffer:[],
binder:Binder.new,
halted:false,
ticks:0,
max_ticks:6000,
greedy_flag:true}
args = default_args.merge(args)
@script = Script.new(args[:script])
@initial_script = args[:script]
@contents = args[:contents]
@initial_contents = @contents.collect {|item| item.deep_copy}
@buffer = args[:buffer]
@initial_buffer = @buffer.collect {|item| item.deep_copy}
parsed_binder = args[:binder].kind_of?(Hash) ? Binder.from_key_value_hash(args[:binder]) : args[:binder]
@binder = parsed_binder
@initial_binder = @binder.deep_copy
replace_proxy_in_binder
@halted = args[:halted]
@ticks = args[:ticks]
@max_ticks = args[:max_ticks]
@greedy_flag = args[:greedy_flag]
@needs = [] # whew
@trace = false
@trace_string = StringIO.new
end
def replace_proxy_in_binder
@binder.contents = @binder.contents.delete_if {|item| item.kind_of?(Proxy)}
@binder.contents.unshift Proxy.new(self)
end
def deep_copy
result = Interpreter.new
result.script = @script.deep_copy
result.contents = @contents.collect {|i| i.deep_copy}
result.buffer = @buffer.collect {|i| i.deep_copy}
result.halted = @halted
result.replace_proxy_in_binder
result.initial_script = @initial_script
result.initial_contents = @initial_contents
result.initial_buffer = @initial_buffer
result.ticks = @ticks
result.max_ticks = @max_ticks
result.greedy_flag = @greedy_flag
result.trace = @trace
result
end
def to_s
rep = (@contents.inject("[") {|s,i| s+i.to_s+", "}).chomp(", ")
rep += @contents.empty? ? "::" : " ::"
rep += (@buffer.inject(" ") {|s,i| s+i.to_s+", "}).chomp(", ") unless @buffer.empty?
rep += " :: "
rep += @script.to_s
rep += "]"
end
def reset(opts = {})
default_opts = {
script: @initial_script,
contents: @initial_contents,
buffer: @initial_buffer,
binder: @initial_binder,
ticks: 0,
max_ticks: @max_ticks}
opts = default_opts.merge(opts)
@script = Script.new(opts[:script])
@initial_script = opts[:script]
@contents = opts[:contents]
@initial_contents = opts[:contents]
@buffer = opts[:buffer]
@initial_buffer = opts[:buffer]
parsed_binder = opts[:binder].kind_of?(Hash) ?
Binder.from_key_value_hash(opts[:binder]) :
opts[:binder]
@binder = parsed_binder
@initial_binder = parsed_binder
replace_proxy_in_binder
@halted = false
@greedy_flag = true
@ticks = opts[:ticks]
@max_ticks = opts[:max_ticks]
@trace_string = StringIO.new
self
end
def proxy
@binder.contents[0]
end
def next_token
@buffer.push(@script.next_token) unless @script.empty?
self.process_buffer unless @buffer.empty?
end
def unfinished?
@ticks < @max_ticks && (@script.value.strip.length > 0 || @buffer.length > 0)
end
def next_contents_index_wanted_by(some_item)
where_in_stack = @contents.rindex {|arg| some_item.can_use?(arg)}
if where_in_stack.nil? && @binder.contains_an_arg_for?(some_item)
where_in_stack = -1
end
where_in_stack
end
def rebuffer_result_when_staged_item_grabs(item)
curried_result = @staged_item.grab(item)
rebuffer_intermediate_result(curried_result)
end
def trace!
@trace = true
self
end
def write_to_trace(text)
puts text
@trace_string.write(text + "\n")
end
def cartoon_of_state
write_to_trace "Interpreter #{self.object_id} #{@ticks}/#{@max_ticks} : #{self.inspect}"
end
def trace_staging
write_to_trace " #{@ticks}: staging the #{@staged_item.class} #{@staged_item.inspect}" unless @staged_item.nil?
end
def trace_used_as_arg(position)
write_to_trace " ... grabbed by #{@contents[position].class} #{@contents[position].inspect}" unless position.nil?
end
def trace_uses_arg(position)
write_to_trace " ... grabbed the #{@contents[position].class} #{@contents[position].inspect}" unless position.nil?
end
def trace_pushed_item(item)
write_to_trace " ... #{item.class} #{item.inspect} was moved to contents"
end
def saved_trace
@trace_string.string || ""
end
def process_next_buffer_item
unless buffer.empty?
count_a_tick
@staged_item = @buffer.delete_at(0)
trace_staging if @trace
next_arg_position = next_contents_index_wanted_by(@staged_item)
if next_arg_position.nil?
wanted_by_position = next_contents_index_that_wants(@staged_item) if @greedy_flag
trace_used_as_arg(wanted_by_position) if @trace
if wanted_by_position.nil?
@contents.push @staged_item
trace_pushed_item(@staged_item) if @trace
else
rebuffer_result_when_staged_item_is_grabbed_by_contents_item_at wanted_by_position
end
elsif next_arg_position == -1
arg_from_binder = @binder.produce_respondent(@staged_item.needs[0])
rebuffer_result_when_staged_item_grabs arg_from_binder
else
trace_uses_arg(next_arg_position) if @trace
rebuffer_result_when_staged_item_grabs_contents_item_at next_arg_position
end
end
end
# DUCK METHODS
duck_handle :empty do
@contents = []
@buffer = []
self
end
duck_handle :flatten do
new_contents = @contents.inject([]) do |arr,item|
case
when item.kind_of?(Assembler)
arr + item.contents + item.buffer
when item.kind_of?(List)
arr + item.contents
else
arr << item
end
end
@contents = new_contents
self
end
duck_handle :reverse do
@contents = @contents.reverse
self
end
duck_handle :rotate do
@contents = @contents.rotate(1)
self
end
duck_handle :run do
@staged_item = nil
@halted = false
while unfinished?
cartoon_of_state if @trace
next_token if @buffer.empty?
process_buffer unless @buffer.empty?
end
cartoon_of_state if @trace
self
end
duck_handle :shift do
if @contents.empty?
self
else
item = @contents.shift
[self,item]
end
end
duck_handle :step do
process_next_buffer_item
self
end
duck_handle :to_assembler do
Assembler.new(contents:[@script] + @contents, buffer:@buffer)
end
duck_handle :to_interpreter do
self
end
duck_handle :to_binder do
Binder.new([@script] + @contents + @buffer)
end
duck_handle :to_list do
List.new([@script] + @contents + @buffer)
end
end
end