AmberVM 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +38 -0
- data/bin/ambervm +278 -0
- data/lib/amber/acts_as_rvm_type.rb +157 -0
- data/lib/amber/classes/association.rb +36 -0
- data/lib/amber/classes/block.rb +52 -0
- data/lib/amber/classes/boolean.rb +40 -0
- data/lib/amber/classes/class.rb +50 -0
- data/lib/amber/classes/error.rb +22 -0
- data/lib/amber/classes/list.rb +96 -0
- data/lib/amber/classes/null.rb +35 -0
- data/lib/amber/classes/number.rb +95 -0
- data/lib/amber/classes/object.rb +56 -0
- data/lib/amber/classes/string.rb +79 -0
- data/lib/amber/classes.rb +113 -0
- data/lib/amber/environment.rb +251 -0
- data/lib/amber/fukubukuro/ecma_core.rb +409 -0
- data/lib/amber/fukubukuro.rb +866 -0
- data/lib/amber/functions/all.rb +3 -0
- data/lib/amber/functions/array/append.rb +50 -0
- data/lib/amber/functions/array/at.rb +50 -0
- data/lib/amber/functions/array/set_at.rb +50 -0
- data/lib/amber/functions/array.rb +30 -0
- data/lib/amber/functions/association/assoc_get.rb +55 -0
- data/lib/amber/functions/association/assoc_set.rb +56 -0
- data/lib/amber/functions/bitwise/bitwise_and.rb +41 -0
- data/lib/amber/functions/bitwise/bitwise_not.rb +41 -0
- data/lib/amber/functions/bitwise/bitwise_or.rb +41 -0
- data/lib/amber/functions/bitwise/bitwise_xor.rb +41 -0
- data/lib/amber/functions/bitwise.rb +3 -0
- data/lib/amber/functions/collection/get.rb +66 -0
- data/lib/amber/functions/collection/set.rb +67 -0
- data/lib/amber/functions/collection/size.rb +54 -0
- data/lib/amber/functions/general/cmp.rb +43 -0
- data/lib/amber/functions/general/eq.rb +45 -0
- data/lib/amber/functions/general/gt.rb +45 -0
- data/lib/amber/functions/general/gte.rb +45 -0
- data/lib/amber/functions/general/lt.rb +45 -0
- data/lib/amber/functions/general/lte.rb +45 -0
- data/lib/amber/functions/general/neq.rb +45 -0
- data/lib/amber/functions/general/type.rb +43 -0
- data/lib/amber/functions/general.rb +3 -0
- data/lib/amber/functions/io/print.rb +45 -0
- data/lib/amber/functions/io.rb +3 -0
- data/lib/amber/functions/list/align.rb +73 -0
- data/lib/amber/functions/list/join.rb +45 -0
- data/lib/amber/functions/list/map.rb +58 -0
- data/lib/amber/functions/list/split.rb +55 -0
- data/lib/amber/functions/list.rb +3 -0
- data/lib/amber/functions/logic/and.rb +55 -0
- data/lib/amber/functions/logic/not.rb +40 -0
- data/lib/amber/functions/logic/or.rb +50 -0
- data/lib/amber/functions/logic.rb +3 -0
- data/lib/amber/functions/math/abs.rb +39 -0
- data/lib/amber/functions/math/acos.rb +39 -0
- data/lib/amber/functions/math/add.rb +40 -0
- data/lib/amber/functions/math/asin.rb +39 -0
- data/lib/amber/functions/math/atan.rb +39 -0
- data/lib/amber/functions/math/ceil.rb +39 -0
- data/lib/amber/functions/math/cos.rb +39 -0
- data/lib/amber/functions/math/dec.rb +39 -0
- data/lib/amber/functions/math/div.rb +44 -0
- data/lib/amber/functions/math/exp.rb +39 -0
- data/lib/amber/functions/math/floor.rb +39 -0
- data/lib/amber/functions/math/inc.rb +39 -0
- data/lib/amber/functions/math/log.rb +39 -0
- data/lib/amber/functions/math/mod.rb +41 -0
- data/lib/amber/functions/math/mul.rb +43 -0
- data/lib/amber/functions/math/neg.rb +43 -0
- data/lib/amber/functions/math/power.rb +43 -0
- data/lib/amber/functions/math/rand.rb +36 -0
- data/lib/amber/functions/math/round.rb +39 -0
- data/lib/amber/functions/math/shl.rb +41 -0
- data/lib/amber/functions/math/shr.rb +41 -0
- data/lib/amber/functions/math/sin.rb +39 -0
- data/lib/amber/functions/math/sub.rb +43 -0
- data/lib/amber/functions/math/tan.rb +39 -0
- data/lib/amber/functions/math.rb +3 -0
- data/lib/amber/functions/objects/send.rb +22 -0
- data/lib/amber/functions/rails/print.rb +44 -0
- data/lib/amber/functions/rails.rb +3 -0
- data/lib/amber/functions/string/ansi.rb +24 -0
- data/lib/amber/functions/string/capstr.rb +23 -0
- data/lib/amber/functions/string/center.rb +25 -0
- data/lib/amber/functions/string/chr.rb +16 -0
- data/lib/amber/functions/string/ljust.rb +26 -0
- data/lib/amber/functions/string/regmatch.rb +34 -0
- data/lib/amber/functions/string/rjust.rb +26 -0
- data/lib/amber/functions/string.rb +3 -0
- data/lib/amber/functions.rb +103 -0
- data/lib/amber/interpreter.rb +1380 -0
- data/lib/amber/languages/brainfuck.rb +153 -0
- data/lib/amber/languages/ecma/compiler.rb +1661 -0
- data/lib/amber/languages/ecma/core-math.js +67 -0
- data/lib/amber/languages/ecma/core-objects.js +57 -0
- data/lib/amber/languages/ecma.rb +9 -0
- data/lib/amber/languages/ecma_fuku/compiler.rb +1622 -0
- data/lib/amber/languages/ecma_fuku/core-math.js +67 -0
- data/lib/amber/languages/ecma_fuku/core-objects.js +56 -0
- data/lib/amber/languages/ecma_fuku.rb +13 -0
- data/lib/amber/languages/math/compiler.rb +70 -0
- data/lib/amber/languages/math/tokenizer.rb +69 -0
- data/lib/amber/languages/math/tree.rb +110 -0
- data/lib/amber/languages/math.rb +26 -0
- data/lib/amber/languages.rb +99 -0
- data/lib/amber/library.rb +79 -0
- data/lib/amber/optimisation.rb +299 -0
- data/lib/amber/plugin.rb +337 -0
- data/lib/amber/rails.rb +90 -0
- data/lib/amber.rb +106 -0
- data/spec/amber/class_spec.rb +27 -0
- data/spec/amber/enviroment_spec.rb +61 -0
- data/spec/amber/function_spec.rb +25 -0
- data/spec/amber/functions/association/assoc_get_spec.rb +41 -0
- data/spec/amber/functions/association/assoc_set_spec.rb +43 -0
- data/spec/amber/functions/collection/get_spec.rb +12 -0
- data/spec/amber/functions/collection/set_spec.rb +10 -0
- data/spec/amber/functions/collection/size_spec.rb +10 -0
- data/spec/amber/functions/list/split_spec.rb +47 -0
- data/spec/amber/functions/string/ansi_spec.rb +44 -0
- data/spec/amber/functions/string/capstr_spec.rb +42 -0
- data/spec/amber/functions/string/center_spec.rb +49 -0
- data/spec/amber/functions/string/ljust_spec.rb +49 -0
- data/spec/amber/functions/string/regmatch_spec.rb +52 -0
- data/spec/amber/functions/string/rjust_spec.rb +49 -0
- data/spec/amber/interpreter/assignment_spec.rb +22 -0
- data/spec/amber/interpreter/condition_spec.rb +103 -0
- data/spec/amber/interpreter/constant_spec.rb +31 -0
- data/spec/amber/interpreter/core_call_spec.rb +72 -0
- data/spec/amber/interpreter/interpreter_spec.rb +11 -0
- data/spec/amber/interpreter/parameter_spec.rb +24 -0
- data/spec/amber/interpreter/sequence_spec.rb +47 -0
- data/spec/amber/interpreter/variable_spec.rb +24 -0
- data/spec/amber/plugin_spec.rb +10 -0
- data/spec/classes/atom/association_spec.rb +39 -0
- data/spec/classes/atom/block_spec.rb +25 -0
- data/spec/classes/atom/boolean_spec.rb +67 -0
- data/spec/classes/atom/error_spec.rb +43 -0
- data/spec/classes/atom/list_spec.rb +68 -0
- data/spec/classes/atom/number_spec.rb +132 -0
- data/spec/classes/atom/string_spec.rb +175 -0
- data/spec/languages/ecma/ecma_array_spec.rb +79 -0
- data/spec/languages/ecma/ecma_closure_spec.rb +38 -0
- data/spec/languages/ecma/ecma_literals_spec.rb +71 -0
- data/spec/languages/ecma/ecma_objects_spec.rb +165 -0
- data/spec/languages/ecma/ecma_old_spec.rb +540 -0
- data/spec/languages/ecma/ecma_spec.rb +64 -0
- data/spec/languages/ecma_fuku/ecma_array_spec.rb +61 -0
- data/spec/languages/ecma_fuku/ecma_closure_spec.rb +33 -0
- data/spec/languages/ecma_fuku/ecma_function_spec.rb +84 -0
- data/spec/languages/ecma_fuku/ecma_literals_spec.rb +55 -0
- data/spec/languages/ecma_fuku/ecma_objects_spec.rb +133 -0
- data/spec/languages/ecma_fuku/ecma_old_spec.rb +415 -0
- data/spec/languages/ecma_fuku/ecma_operator_spec.rb +33 -0
- data/spec/languages/ecma_fuku/ecma_spec.rb +52 -0
- data/spec/languages/math/compiler_spec.rb +49 -0
- data/spec/languages/math/tokenizer_spec.rb +73 -0
- data/spec/languages/math/tree_spec.rb +153 -0
- metadata +225 -0
@@ -0,0 +1,866 @@
|
|
1
|
+
# Define true? and false? methods on Ruby classes that behaves like JavaScript's truthyness.
|
2
|
+
class Object
|
3
|
+
def false?; false; end
|
4
|
+
def true?; !false?; end
|
5
|
+
end
|
6
|
+
class TrueClass
|
7
|
+
def true?; true; end
|
8
|
+
def false?; false; end
|
9
|
+
end
|
10
|
+
class Symbol
|
11
|
+
def false?; self == :undefined; end
|
12
|
+
end
|
13
|
+
[FalseClass, NilClass].each do |klass|
|
14
|
+
klass.class_eval do
|
15
|
+
def false?; true; end
|
16
|
+
def true?; false; end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
[Fixnum, Bignum, Float].each do |klass|
|
20
|
+
klass.class_eval do
|
21
|
+
alias false? zero?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
[Array, Hash, String].each do |klass|
|
25
|
+
klass.class_eval do
|
26
|
+
alias false? empty?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Numeric
|
31
|
+
def inc; self + 1; end
|
32
|
+
def dec; self - 1; end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Integer
|
36
|
+
def logical_shift_right positions = 1, bit_size = 32
|
37
|
+
raise ArgumentError, 'negative argument for logical_shift_right' if positions < 0
|
38
|
+
positions = positions.to_i if positions.is_a? Float
|
39
|
+
positions = bit_size if positions > bit_size
|
40
|
+
bits = (0...bit_size).map { |i| self[i] }.reverse
|
41
|
+
bits = [0] * positions + bits[0, bit_size - positions]
|
42
|
+
bits.join.to_i(2)
|
43
|
+
end
|
44
|
+
alias lsr logical_shift_right
|
45
|
+
end
|
46
|
+
class Float
|
47
|
+
def logical_shift_right positions = 1, bit_size = 32
|
48
|
+
(to_i % 2 ** bit_size).logical_shift_right positions, bit_size
|
49
|
+
end
|
50
|
+
alias lsr logical_shift_right
|
51
|
+
end
|
52
|
+
|
53
|
+
# automatic type cast for JavaScript
|
54
|
+
class Float
|
55
|
+
def to_str
|
56
|
+
to_s.chomp('.0')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
unless defined? define_singleton_method()
|
61
|
+
module Kernel
|
62
|
+
def define_singleton_method name, &block
|
63
|
+
(class << self; self; end).send :define_method, name, &block
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module Fukubukuro
|
69
|
+
|
70
|
+
module VM
|
71
|
+
class << self
|
72
|
+
def print *args
|
73
|
+
$stdout.print(*args)
|
74
|
+
end
|
75
|
+
def new_array
|
76
|
+
[]
|
77
|
+
end
|
78
|
+
def sin a
|
79
|
+
Math.sin a
|
80
|
+
end
|
81
|
+
def cos a
|
82
|
+
Math.cos a
|
83
|
+
end
|
84
|
+
def power base, exp
|
85
|
+
base ** exp
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class Sequence < Array
|
91
|
+
def call
|
92
|
+
for statement in self
|
93
|
+
value = statement.call
|
94
|
+
end
|
95
|
+
value
|
96
|
+
end
|
97
|
+
alias value call
|
98
|
+
def inspect
|
99
|
+
self.class.inspect + super
|
100
|
+
end
|
101
|
+
def pretty_print q
|
102
|
+
q.text self.class.inspect
|
103
|
+
super
|
104
|
+
end
|
105
|
+
def self.inspect
|
106
|
+
'Sequence'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class Environment < Hash
|
111
|
+
def self.new
|
112
|
+
super do |h, k|
|
113
|
+
h[k] = Variable.new
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class Program < Sequence
|
119
|
+
attr_accessor :environment
|
120
|
+
def initialize statements = Sequence[], environment = Environment.new
|
121
|
+
super(statements)
|
122
|
+
self.environment = environment
|
123
|
+
end
|
124
|
+
def self.inspect
|
125
|
+
'Program'
|
126
|
+
end
|
127
|
+
def ruby_to_amber object
|
128
|
+
raise NotImplementedError
|
129
|
+
end
|
130
|
+
def amber_to_ruby container
|
131
|
+
if container.respond_to? :to_ruby
|
132
|
+
container.to_ruby
|
133
|
+
else
|
134
|
+
container
|
135
|
+
end
|
136
|
+
end
|
137
|
+
def execute env
|
138
|
+
if env.is_a? Hash
|
139
|
+
for name in env.keys
|
140
|
+
environment[name.to_sym].value = ruby_to_amber env[name]
|
141
|
+
end
|
142
|
+
else
|
143
|
+
raise 'Expected Hash as environment, got %p' % [env]
|
144
|
+
end
|
145
|
+
result = call
|
146
|
+
if env.is_a? Hash
|
147
|
+
for name, variable in environment
|
148
|
+
env[name] = env[name.to_s] = amber_to_ruby(variable.value)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
amber_to_ruby result
|
152
|
+
end
|
153
|
+
def optimize
|
154
|
+
self
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class Statement < Proc
|
159
|
+
alias value call
|
160
|
+
end
|
161
|
+
|
162
|
+
# class MathStatement < Statement
|
163
|
+
# def self.new method, *args
|
164
|
+
# if args.size == 1
|
165
|
+
# arg = args.first
|
166
|
+
# super() do
|
167
|
+
# ::Math.send method, arg.value
|
168
|
+
# end
|
169
|
+
# else
|
170
|
+
# super() do
|
171
|
+
# ::Math.send method, *args.map { |a| a.value }
|
172
|
+
# end
|
173
|
+
# end
|
174
|
+
# end
|
175
|
+
# end
|
176
|
+
|
177
|
+
# class DebugStatement < Statement
|
178
|
+
# def self.new *args
|
179
|
+
# super() do
|
180
|
+
# puts 'fuku-debug: ' + args.map { |a| a.value }.join(', ')
|
181
|
+
# end
|
182
|
+
# end
|
183
|
+
# end
|
184
|
+
|
185
|
+
# def debug *args
|
186
|
+
# DebugStatement.new(*args)
|
187
|
+
# end
|
188
|
+
|
189
|
+
class CoreCall < Statement
|
190
|
+
def self.new method, *args
|
191
|
+
super() do
|
192
|
+
ECMA.make_core_object_from VM.send(method, *args.map { |arg| arg.value.to_ruby })
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
class IfCondition < Statement
|
198
|
+
def self.new condition, true_block, false_block = nil
|
199
|
+
if true_block && false_block
|
200
|
+
super() do
|
201
|
+
if condition.call.true?
|
202
|
+
true_block.call
|
203
|
+
else
|
204
|
+
false_block.call
|
205
|
+
end
|
206
|
+
end
|
207
|
+
elsif true_block
|
208
|
+
super() do
|
209
|
+
true_block.call if condition.call.true?
|
210
|
+
end
|
211
|
+
elsif false_block
|
212
|
+
super() do
|
213
|
+
false_block.call if condition.call.false?
|
214
|
+
end
|
215
|
+
else
|
216
|
+
super() do
|
217
|
+
condition.call
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
class ShortCircuitAnd < Statement
|
224
|
+
def self.new first, second
|
225
|
+
super() do
|
226
|
+
if (first_value = first.value).true?
|
227
|
+
if (second_value = second.value).true?
|
228
|
+
second_value
|
229
|
+
else
|
230
|
+
first_value
|
231
|
+
end
|
232
|
+
else
|
233
|
+
first_value
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class ShortCircuitOr < Statement
|
240
|
+
def self.new first, second
|
241
|
+
super() do
|
242
|
+
if (first_value = first.value).true?
|
243
|
+
value
|
244
|
+
else
|
245
|
+
second.value
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
class ConditionalLoop < Statement
|
252
|
+
attr_accessor :condition, :block
|
253
|
+
def inspect
|
254
|
+
self.class.inspect + '(%p) {}' % [condition, block]
|
255
|
+
end
|
256
|
+
def pretty_print q
|
257
|
+
q.text self.class.inspect
|
258
|
+
q.group 2, '(', ')' do
|
259
|
+
q.pp condition
|
260
|
+
end
|
261
|
+
q.group 2, ' { ', ' }' do
|
262
|
+
q.pp block
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
class WhileLoop < ConditionalLoop
|
268
|
+
def self.new condition, block
|
269
|
+
if block.is_a? Sequence
|
270
|
+
case block.size
|
271
|
+
when 0
|
272
|
+
loop = super() do
|
273
|
+
until condition.call.false?
|
274
|
+
end
|
275
|
+
end
|
276
|
+
when 1
|
277
|
+
statement = block[0]
|
278
|
+
loop = super() do
|
279
|
+
until condition.call.false?
|
280
|
+
statement.call
|
281
|
+
end
|
282
|
+
end
|
283
|
+
when 2
|
284
|
+
statement1 = block[0]
|
285
|
+
statement2 = block[1]
|
286
|
+
loop = super() do
|
287
|
+
until condition.call.false?
|
288
|
+
statement1.call
|
289
|
+
statement2.call
|
290
|
+
end
|
291
|
+
end
|
292
|
+
else
|
293
|
+
loop = super() do
|
294
|
+
until condition.call.false?
|
295
|
+
for statement in block
|
296
|
+
statement.call
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
else
|
302
|
+
loop = super() do
|
303
|
+
until condition.call.false?
|
304
|
+
block.call
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
loop.condition = condition
|
309
|
+
loop.block = block
|
310
|
+
loop
|
311
|
+
end
|
312
|
+
def self.inspect
|
313
|
+
'WhileLoop'
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
class DoWhileLoop < ConditionalLoop
|
318
|
+
def self.new condition, block
|
319
|
+
loop = super() do
|
320
|
+
if block.is_a? Sequence
|
321
|
+
begin
|
322
|
+
for statement in block
|
323
|
+
statement.call
|
324
|
+
end
|
325
|
+
end until condition.call.false?
|
326
|
+
else
|
327
|
+
raise 'Expected Sequence, got %p' % block
|
328
|
+
begin
|
329
|
+
block.call
|
330
|
+
end until condition.call.false?
|
331
|
+
end
|
332
|
+
end
|
333
|
+
loop.condition = condition
|
334
|
+
loop.block = block
|
335
|
+
loop
|
336
|
+
end
|
337
|
+
def self.inspect
|
338
|
+
'DoWhileLoop'
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
# Just for convenience. Call like this:
|
343
|
+
#
|
344
|
+
# ForLoop.new(
|
345
|
+
# Assignment.new(i, 1),
|
346
|
+
# MethodCall.new(i, :<=, n),
|
347
|
+
# Assignment.new(i, MethodCall.new(i, :+, C(1)))),
|
348
|
+
# Sequence[
|
349
|
+
# ...
|
350
|
+
# ]
|
351
|
+
class ForLoop
|
352
|
+
def self.new start, condition, increment, block
|
353
|
+
Sequence[
|
354
|
+
start,
|
355
|
+
WhileLoop.new(condition,
|
356
|
+
Sequence[
|
357
|
+
block,
|
358
|
+
increment
|
359
|
+
]
|
360
|
+
)
|
361
|
+
]
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
class Assignment < Statement
|
366
|
+
attr_accessor :receiver, :statement
|
367
|
+
def self.new receiver, statement
|
368
|
+
if receiver.is_a? MethodCall
|
369
|
+
if receiver.meth == :[]
|
370
|
+
index = receiver.args.first
|
371
|
+
MethodCall.new(receiver.object, :[]=, index, statement)
|
372
|
+
elsif receiver.meth == :fuku_send
|
373
|
+
getter = receiver.args.first.value
|
374
|
+
setter = Constant.new(:"#{getter}=")
|
375
|
+
MethodCall.new(receiver.object, :fuku_send, setter, statement)
|
376
|
+
else
|
377
|
+
raise 'Cannot assign to method calls.'
|
378
|
+
end
|
379
|
+
elsif statement.is_a? MethodCall
|
380
|
+
MethodCallAssignment.new receiver, statement.object, statement.meth, *statement.args
|
381
|
+
else
|
382
|
+
assignment = super() do
|
383
|
+
receiver.value = statement.call
|
384
|
+
end
|
385
|
+
assignment.receiver = receiver
|
386
|
+
assignment.statement = statement
|
387
|
+
assignment
|
388
|
+
end
|
389
|
+
end
|
390
|
+
def inspect
|
391
|
+
'Assignment(%p = %p)' % [receiver, statement]
|
392
|
+
end
|
393
|
+
def pretty_print q
|
394
|
+
q.group 2, 'Assignment(', ')' do
|
395
|
+
q.pp receiver
|
396
|
+
q.text ' = '
|
397
|
+
q.pp statement
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
def ass *args
|
403
|
+
Assignment.new(*args)
|
404
|
+
end
|
405
|
+
|
406
|
+
VARIABLE_IMPLEMENTATION_METHOD = ENV.fetch 'fuku_container', 'object'
|
407
|
+
# puts "using #{VARIABLE_IMPLEMENTATION_METHOD} for variable containers"
|
408
|
+
|
409
|
+
case VARIABLE_IMPLEMENTATION_METHOD
|
410
|
+
when 'object'
|
411
|
+
class Variable
|
412
|
+
attr_accessor :value
|
413
|
+
def initialize value = nil
|
414
|
+
@value = value
|
415
|
+
end
|
416
|
+
end
|
417
|
+
when 'container'
|
418
|
+
class Variable < Container
|
419
|
+
end
|
420
|
+
when 'array'
|
421
|
+
class Variable < Array
|
422
|
+
class << self
|
423
|
+
alias new []
|
424
|
+
end
|
425
|
+
alias value first
|
426
|
+
if false && [].respond_to?(:first=)
|
427
|
+
puts 'using Array#first='
|
428
|
+
alias value= first=
|
429
|
+
else
|
430
|
+
def value= value
|
431
|
+
self[0] = value
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
else
|
436
|
+
raise
|
437
|
+
end
|
438
|
+
|
439
|
+
class Variable
|
440
|
+
class << self
|
441
|
+
attr_reader :running_numbers
|
442
|
+
end
|
443
|
+
running_number = 0
|
444
|
+
@running_numbers = Hash.new do |h, k|
|
445
|
+
h[k] = running_number += 1
|
446
|
+
end
|
447
|
+
alias call value
|
448
|
+
def inspect
|
449
|
+
'Variable%p(%p)' % [
|
450
|
+
name ? name.to_sym : Variable.running_numbers[self],
|
451
|
+
value
|
452
|
+
]
|
453
|
+
end
|
454
|
+
def pretty_print q
|
455
|
+
q.text inspect
|
456
|
+
end
|
457
|
+
attr_accessor :name
|
458
|
+
end
|
459
|
+
|
460
|
+
def Variables count
|
461
|
+
# Array.new(count) { StackVariable[nil] }
|
462
|
+
Array.new(count) { Variable.new nil }
|
463
|
+
end
|
464
|
+
|
465
|
+
def var
|
466
|
+
Variable.new
|
467
|
+
end
|
468
|
+
|
469
|
+
class Constant < Variable
|
470
|
+
def inspect
|
471
|
+
'Constant(' + value.inspect + ')'
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
class CastException < ::RuntimeError
|
476
|
+
attr_accessor :new_value, :result
|
477
|
+
def initialize new_value, result
|
478
|
+
self.new_value = new_value
|
479
|
+
self.result = result
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
class MethodCall < Statement
|
484
|
+
attr_accessor :object, :meth, :args
|
485
|
+
def self.new object, method, *args
|
486
|
+
if method == :fuku_call && object.is_a?(MethodCall) && object.meth == :fuku_send
|
487
|
+
return new(object.object, :fuku_method_call, object.args.first, *args)
|
488
|
+
end
|
489
|
+
method_call =
|
490
|
+
if args.empty?
|
491
|
+
super() do
|
492
|
+
begin
|
493
|
+
object.value.send method
|
494
|
+
rescue CastException => boom
|
495
|
+
object.value = boom.new_value
|
496
|
+
boom.result
|
497
|
+
end
|
498
|
+
end
|
499
|
+
elsif args.all? { |arg| arg.is_a? Constant }
|
500
|
+
if args.size == 1
|
501
|
+
arg = args.first.value
|
502
|
+
super() do
|
503
|
+
begin
|
504
|
+
object.value.send method, arg
|
505
|
+
rescue ::NoMethodError
|
506
|
+
if object.value == nil
|
507
|
+
raise 'Variable %s not defined' % object.name
|
508
|
+
else
|
509
|
+
raise
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
else
|
514
|
+
args = args.map { |a| a.value }
|
515
|
+
super() do
|
516
|
+
object.value.send method, *args
|
517
|
+
end
|
518
|
+
end
|
519
|
+
else
|
520
|
+
if args.size == 1
|
521
|
+
arg = args.first
|
522
|
+
super() do
|
523
|
+
object.value.send method, arg.value
|
524
|
+
end
|
525
|
+
else
|
526
|
+
super() do
|
527
|
+
object.value.send method, *args.map { |a| a.value }
|
528
|
+
end
|
529
|
+
end
|
530
|
+
end
|
531
|
+
method_call.object = object
|
532
|
+
method_call.meth = method
|
533
|
+
method_call.args = args
|
534
|
+
method_call
|
535
|
+
end
|
536
|
+
alias value call
|
537
|
+
def inspect
|
538
|
+
'MethodCall:%p.%p(%p)' % [object, meth, args.join(', ')]
|
539
|
+
end
|
540
|
+
def pretty_print q
|
541
|
+
q.text 'MethodCall:'
|
542
|
+
q.pp object
|
543
|
+
q.text '.'
|
544
|
+
q.pp meth
|
545
|
+
q.group 2, '(', ')' do
|
546
|
+
q.pp args
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
def meth *args
|
552
|
+
MethodCall.new(*args)
|
553
|
+
end
|
554
|
+
|
555
|
+
class MethodCallAssignment < Statement
|
556
|
+
attr_accessor :receiver, :object, :meth, :args
|
557
|
+
def self.new receiver, object, meth, *args
|
558
|
+
method_call_assignment = if args.empty?
|
559
|
+
super() do
|
560
|
+
receiver.value = object.value.send meth
|
561
|
+
end
|
562
|
+
elsif args.all? { |arg| arg.is_a? Constant }
|
563
|
+
if args.size == 1
|
564
|
+
value = args.first.value
|
565
|
+
super() do
|
566
|
+
receiver.value = object.value.send meth, value
|
567
|
+
end
|
568
|
+
else
|
569
|
+
raise
|
570
|
+
args = args.map(&:value)
|
571
|
+
super() do
|
572
|
+
receiver.value = object.value.send meth, *args
|
573
|
+
end
|
574
|
+
end
|
575
|
+
else
|
576
|
+
if args.size == 1
|
577
|
+
arg = args.first
|
578
|
+
super() do
|
579
|
+
receiver.value = object.value.send meth, arg.value
|
580
|
+
end
|
581
|
+
else
|
582
|
+
super() do
|
583
|
+
receiver.value = object.value.send meth, *args.map { |a| a.value }
|
584
|
+
end
|
585
|
+
end
|
586
|
+
end
|
587
|
+
method_call_assignment.receiver = receiver
|
588
|
+
method_call_assignment.object = object
|
589
|
+
method_call_assignment.meth = meth
|
590
|
+
method_call_assignment.args = args
|
591
|
+
method_call_assignment
|
592
|
+
end
|
593
|
+
alias value call
|
594
|
+
def inspect
|
595
|
+
'MethodCallAssignment(%p = %p.%p(%p))' % [receiver, object, meth, args]
|
596
|
+
end
|
597
|
+
def pretty_print q
|
598
|
+
q.group 2, 'MethodCallAssignment(', ')' do
|
599
|
+
q.pp receiver
|
600
|
+
q.text ' = '
|
601
|
+
q.pp object
|
602
|
+
q.text '.'
|
603
|
+
q.pp meth
|
604
|
+
q.group 2, '(', ')' do
|
605
|
+
q.pp args
|
606
|
+
end
|
607
|
+
end
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
class FunctionDefinition < Statement
|
612
|
+
attr_accessor :signature, :block
|
613
|
+
def self.new signature, block
|
614
|
+
function_definition = super() do
|
615
|
+
Function.new signature, block
|
616
|
+
end
|
617
|
+
function_definition.signature = signature
|
618
|
+
function_definition.block = block
|
619
|
+
function_definition
|
620
|
+
end
|
621
|
+
def inspect
|
622
|
+
'FunctionDefintion(%p) { %p }' % [signature, block]
|
623
|
+
end
|
624
|
+
def pretty_print q
|
625
|
+
q.group 2, 'FunctionDefintion(', ')' do
|
626
|
+
q.pp signature
|
627
|
+
end
|
628
|
+
q.group 2, ' { ', ' }' do
|
629
|
+
q.pp block
|
630
|
+
end
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
class Function < Statement
|
635
|
+
def self.new signature, block
|
636
|
+
case signature.size
|
637
|
+
when 0
|
638
|
+
super() do
|
639
|
+
catch :return do
|
640
|
+
block.call
|
641
|
+
raise 'no return'
|
642
|
+
end
|
643
|
+
end
|
644
|
+
when 1
|
645
|
+
param = signature.first
|
646
|
+
super() do |arg|
|
647
|
+
param.value = arg
|
648
|
+
catch :return do
|
649
|
+
block.call
|
650
|
+
raise 'no return'
|
651
|
+
end
|
652
|
+
end
|
653
|
+
else
|
654
|
+
super() do |*arguments|
|
655
|
+
for i in 0...signature.size
|
656
|
+
signature[i].value = arguments[i]
|
657
|
+
end
|
658
|
+
catch :return do
|
659
|
+
block.call
|
660
|
+
raise 'no return'
|
661
|
+
end
|
662
|
+
end
|
663
|
+
end
|
664
|
+
end
|
665
|
+
alias fuku_call call
|
666
|
+
def to_ruby
|
667
|
+
function = self
|
668
|
+
proc do |*args|
|
669
|
+
function.call(*args).to_ruby
|
670
|
+
end
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
# class CachingFunction < Statement
|
675
|
+
# def self.new signature, block
|
676
|
+
# raise
|
677
|
+
# case signature.size
|
678
|
+
# when 0
|
679
|
+
# cache = nil
|
680
|
+
# super() do
|
681
|
+
# cache ||= block.call
|
682
|
+
# end
|
683
|
+
# when 1
|
684
|
+
# signature_first = signature.first
|
685
|
+
# cache = {}
|
686
|
+
# super() do |value|
|
687
|
+
# cache.delete cache.first.first if cache.size >= 16
|
688
|
+
# cache[value] ||= begin
|
689
|
+
# signature_first.value = value
|
690
|
+
# block.call
|
691
|
+
# end
|
692
|
+
# end
|
693
|
+
# else
|
694
|
+
# cache = {}
|
695
|
+
# super() do |values|
|
696
|
+
# cache.delete cache.first.first if cache.size >= 16
|
697
|
+
# cache[values] ||= begin
|
698
|
+
# for i in 0...signature.size
|
699
|
+
# signature[i].value = values[i]
|
700
|
+
# end
|
701
|
+
# block.call
|
702
|
+
# end
|
703
|
+
# end
|
704
|
+
# end
|
705
|
+
# end
|
706
|
+
# end
|
707
|
+
|
708
|
+
# class StackVariable < Array
|
709
|
+
# alias value last
|
710
|
+
# def value= value
|
711
|
+
# self[-1] = value
|
712
|
+
# end
|
713
|
+
# alias call value
|
714
|
+
# end
|
715
|
+
|
716
|
+
# class RecursiveFunction
|
717
|
+
# attr_accessor :signature
|
718
|
+
# def initialize signature, block = nil
|
719
|
+
# @signature = signature
|
720
|
+
# self.block = block
|
721
|
+
# end
|
722
|
+
# def block= block
|
723
|
+
# case signature.size
|
724
|
+
# when 0
|
725
|
+
# define_singleton_method :call do
|
726
|
+
# block.call
|
727
|
+
# end
|
728
|
+
# when 1
|
729
|
+
# param = signature.first
|
730
|
+
# define_singleton_method :call do |value|
|
731
|
+
# param.push value
|
732
|
+
# result = block.call
|
733
|
+
# param.pop
|
734
|
+
# result
|
735
|
+
# end
|
736
|
+
# else
|
737
|
+
# raise
|
738
|
+
# define_singleton_method :call do |values|
|
739
|
+
# for i in 0...signature.size
|
740
|
+
# signature[i].push values[i]
|
741
|
+
# end
|
742
|
+
# result = block.call
|
743
|
+
# for var in signature
|
744
|
+
# var.pop
|
745
|
+
# end
|
746
|
+
# result
|
747
|
+
# end
|
748
|
+
# end
|
749
|
+
# end
|
750
|
+
# end
|
751
|
+
|
752
|
+
# class CachingRecursiveFunction
|
753
|
+
# attr_accessor :signature
|
754
|
+
# def initialize signature
|
755
|
+
# @signature = signature
|
756
|
+
# end
|
757
|
+
# def block= block
|
758
|
+
# case signature.size
|
759
|
+
# when 0
|
760
|
+
# raise
|
761
|
+
# cache = nil
|
762
|
+
# define_singleton_method :call do
|
763
|
+
# cache ||= block.call
|
764
|
+
# end
|
765
|
+
# when 1
|
766
|
+
# signature_first = signature.first
|
767
|
+
# cache = {}
|
768
|
+
# define_singleton_method :call do |value|
|
769
|
+
# value = cache[value] ||= begin
|
770
|
+
# signature_first.push value
|
771
|
+
# result = block.call
|
772
|
+
# signature_first.pop
|
773
|
+
# result
|
774
|
+
# end
|
775
|
+
# cache.delete cache.first.first if cache.size >= 8
|
776
|
+
# value
|
777
|
+
# end
|
778
|
+
# else
|
779
|
+
# cache = {}
|
780
|
+
# define_singleton_method :call do |values|
|
781
|
+
# values = cache[values] ||= begin
|
782
|
+
# for i in 0...signature.size
|
783
|
+
# signature[i].push values[i]
|
784
|
+
# end
|
785
|
+
# result = block.call
|
786
|
+
# for var in signature
|
787
|
+
# var.pop
|
788
|
+
# end
|
789
|
+
# result
|
790
|
+
# end
|
791
|
+
# cache.delete cache.first.first if cache.size >= 8
|
792
|
+
# values
|
793
|
+
# end
|
794
|
+
# end
|
795
|
+
# end
|
796
|
+
# end
|
797
|
+
|
798
|
+
# class FunctionCall < Statement
|
799
|
+
# def self.new function, *arguments
|
800
|
+
# if false # cacheable?
|
801
|
+
# case arguments.size
|
802
|
+
# when 0
|
803
|
+
# raise
|
804
|
+
# cache = nil
|
805
|
+
# super() do
|
806
|
+
# cache ||= function.call
|
807
|
+
# end
|
808
|
+
# when 1
|
809
|
+
# arg = arguments.first
|
810
|
+
# super() do
|
811
|
+
# function.call(arg.value)
|
812
|
+
# end
|
813
|
+
# else
|
814
|
+
# raise
|
815
|
+
# cache = {}
|
816
|
+
# super() do
|
817
|
+
# cache.delete cache.first.first if cache.size >= 16
|
818
|
+
# values = arguments.map { |a| a.value }
|
819
|
+
# cache[values] ||= function.call(values)
|
820
|
+
# end
|
821
|
+
# end
|
822
|
+
# else
|
823
|
+
# case arguments.size
|
824
|
+
# when 0
|
825
|
+
# super() do
|
826
|
+
# function.call
|
827
|
+
# end
|
828
|
+
# when 1
|
829
|
+
# arg = arguments.first
|
830
|
+
# super() do
|
831
|
+
# function.call(arg.value)
|
832
|
+
# end
|
833
|
+
# else
|
834
|
+
# super() do
|
835
|
+
# function.call(*arguments.map { |a| a.value })
|
836
|
+
# end
|
837
|
+
# end
|
838
|
+
# end
|
839
|
+
# end
|
840
|
+
# end
|
841
|
+
|
842
|
+
class ReturnStatement < Statement
|
843
|
+
def self.new result
|
844
|
+
super() do
|
845
|
+
throw :return, result.value
|
846
|
+
end
|
847
|
+
end
|
848
|
+
end
|
849
|
+
|
850
|
+
class << CurrentReceiver = Object.new
|
851
|
+
attr_accessor :value
|
852
|
+
def set value
|
853
|
+
# puts 'CurrentReceiver set to %p.' % [value]
|
854
|
+
self.value = value
|
855
|
+
end
|
856
|
+
def inspect
|
857
|
+
'CurrentReceiver(%p)' % [
|
858
|
+
value
|
859
|
+
]
|
860
|
+
end
|
861
|
+
def pretty_print q
|
862
|
+
q.text inspect
|
863
|
+
end
|
864
|
+
end
|
865
|
+
|
866
|
+
end
|