duby 0.0.1

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.
Files changed (69) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +68 -0
  3. data/README.txt +31 -0
  4. data/Rakefile +37 -0
  5. data/bin/duby +9 -0
  6. data/bin/dubyc +9 -0
  7. data/bin/dubyp +9 -0
  8. data/examples/appengine/src/com/google/appengine/ext/duby/db/Model.duby +132 -0
  9. data/examples/appengine/src/com/ribrdb/DubyApp.duby +28 -0
  10. data/examples/bench_fractal.duby +57 -0
  11. data/examples/construction.duby +8 -0
  12. data/examples/edb.duby +3 -0
  13. data/examples/fib.duby +24 -0
  14. data/examples/fields.duby +22 -0
  15. data/examples/java_thing.duby +13 -0
  16. data/examples/simple_class.duby +12 -0
  17. data/examples/swing.duby +20 -0
  18. data/examples/tak.duby +15 -0
  19. data/javalib/JRubyParser.jar +0 -0
  20. data/lib/duby.rb +170 -0
  21. data/lib/duby/ast.rb +340 -0
  22. data/lib/duby/ast/call.rb +73 -0
  23. data/lib/duby/ast/class.rb +145 -0
  24. data/lib/duby/ast/flow.rb +328 -0
  25. data/lib/duby/ast/intrinsics.rb +46 -0
  26. data/lib/duby/ast/literal.rb +93 -0
  27. data/lib/duby/ast/local.rb +77 -0
  28. data/lib/duby/ast/method.rb +187 -0
  29. data/lib/duby/ast/structure.rb +44 -0
  30. data/lib/duby/ast/type.rb +93 -0
  31. data/lib/duby/c/compiler.rb +134 -0
  32. data/lib/duby/compiler.rb +261 -0
  33. data/lib/duby/jvm/compiler.rb +684 -0
  34. data/lib/duby/jvm/method_lookup.rb +185 -0
  35. data/lib/duby/jvm/source_compiler.rb +516 -0
  36. data/lib/duby/jvm/source_generator/builder.rb +368 -0
  37. data/lib/duby/jvm/source_generator/loops.rb +132 -0
  38. data/lib/duby/jvm/source_generator/precompile.rb +154 -0
  39. data/lib/duby/jvm/source_generator/typer.rb +11 -0
  40. data/lib/duby/jvm/typer.rb +118 -0
  41. data/lib/duby/jvm/types.rb +326 -0
  42. data/lib/duby/jvm/types/basic_types.rb +18 -0
  43. data/lib/duby/jvm/types/boolean.rb +11 -0
  44. data/lib/duby/jvm/types/factory.rb +151 -0
  45. data/lib/duby/jvm/types/floats.rb +70 -0
  46. data/lib/duby/jvm/types/integers.rb +110 -0
  47. data/lib/duby/jvm/types/intrinsics.rb +157 -0
  48. data/lib/duby/jvm/types/literals.rb +82 -0
  49. data/lib/duby/jvm/types/methods.rb +344 -0
  50. data/lib/duby/jvm/types/number.rb +92 -0
  51. data/lib/duby/nbcompiler.rb +29 -0
  52. data/lib/duby/old/compiler_old.rb +845 -0
  53. data/lib/duby/old/declaration.rb +72 -0
  54. data/lib/duby/old/mapper.rb +72 -0
  55. data/lib/duby/old/signature.rb +52 -0
  56. data/lib/duby/old/typer_old.rb +163 -0
  57. data/lib/duby/plugin/edb.rb +25 -0
  58. data/lib/duby/plugin/java.rb +42 -0
  59. data/lib/duby/plugin/math.rb +84 -0
  60. data/lib/duby/transform.rb +908 -0
  61. data/lib/duby/typer.rb +359 -0
  62. data/test/test_ast.rb +391 -0
  63. data/test/test_compilation.rb +98 -0
  64. data/test/test_java_typer.rb +199 -0
  65. data/test/test_javac_compiler.rb +57 -0
  66. data/test/test_jvm_compiler.rb +1459 -0
  67. data/test/test_math_plugin.rb +87 -0
  68. data/test/test_typer.rb +246 -0
  69. metadata +155 -0
@@ -0,0 +1,185 @@
1
+ module Duby
2
+ module JVM
3
+ module MethodLookup
4
+ # dummy log; it's expected the inclusion target will have it
5
+ def log(msg); end
6
+
7
+ # def jvm_type(type)
8
+ # return type if type.kind_of? Java::JavaClass
9
+ # return type.jvm_type
10
+ # end
11
+ #
12
+ # def convert_params(params)
13
+ # params.map {|param| jvm_type(param)}
14
+ # end
15
+
16
+ def find_method(mapped_type, name, mapped_params, meta)
17
+ # mapped_type = jvm_type(mapped_type)
18
+ # mapped_params = convert_params(mapped_params)
19
+ raise ArgumentError if mapped_params.any? {|p| p.nil?}
20
+ if name == 'new'
21
+ if meta
22
+ name = "<init>"
23
+ constructor = true
24
+ else
25
+ constructor = false
26
+ end
27
+ end
28
+
29
+ begin
30
+ if constructor
31
+ method = mapped_type.constructor(*mapped_params)
32
+ else
33
+ method = mapped_type.java_method(name, *mapped_params)
34
+ end
35
+ rescue NameError
36
+ unless constructor
37
+ # exact args failed, do a deeper search
38
+ log "Failed to locate method #{mapped_type}.#{name}(#{mapped_params})"
39
+
40
+ method = find_jls(mapped_type, name, mapped_params, meta)
41
+ end
42
+ unless method
43
+ log "Failed to locate method #{name}(#{mapped_params}) on #{mapped_type}"
44
+ return nil
45
+ end
46
+ end
47
+
48
+ log "Found method #{method.declaring_class}.#{name}(#{method.argument_types}) from #{mapped_type}"
49
+ return method
50
+ end
51
+
52
+ def find_jls(mapped_type, name, mapped_params, meta)
53
+ # mapped_type = jvm_type(mapped_type)
54
+ # mapped_params = convert_params(mapped_params)
55
+ if meta
56
+ all_methods = mapped_type.declared_class_methods
57
+ else
58
+ all_methods = []
59
+ cls = mapped_type
60
+ while cls
61
+ all_methods += cls.declared_instance_methods
62
+ cls = cls.superclass
63
+ end
64
+ end
65
+ by_name = all_methods.select {|m| m.name == name && mapped_params.size <= m.argument_types.size}
66
+ by_name_and_arity = by_name.select {|m| m.argument_types.size == mapped_params.size}
67
+
68
+ phase1_methods = phase1(mapped_params, by_name_and_arity)
69
+
70
+ if phase1_methods.size > 1
71
+ raise "Ambiguous targets invoking #{mapped_type}.#{name}:\n#{phase1_methods}"
72
+ end
73
+
74
+ phase1_methods[0] ||
75
+ phase2(mapped_params, by_name) ||
76
+ phase3(mapped_params, by_name)
77
+ end
78
+
79
+ def phase1(mapped_params, potentials)
80
+ # cycle through methods looking for more specific matches; gather matches of equal specificity
81
+ methods = potentials.inject([]) do |currents, potential|
82
+ method_params = potential.argument_types
83
+
84
+ # exact match always wins; duplicates not possible
85
+ return [potential] if each_is_exact(mapped_params, method_params)
86
+
87
+ # otherwise, check for potential match and compare to current
88
+ # TODO: missing ambiguity check; picks last method of equal specificity
89
+ if each_is_exact_or_subtype_or_convertible(mapped_params, method_params)
90
+ if currents.size > 0
91
+ if is_more_specific?(potential.argument_types, currents[0].argument_types)
92
+ # potential is better, dump all currents
93
+ currents = [potential]
94
+ elsif is_more_specific?(currents[0].argument_types, potential.argument_types)
95
+ # currents are better, try next potential
96
+ #next
97
+ else
98
+ # equal specificity, append to currents
99
+ currents << potential
100
+ end
101
+ else
102
+ # no previous matches, use potential
103
+ currents = [potential]
104
+ end
105
+ end
106
+
107
+ currents
108
+ end
109
+
110
+ methods
111
+ end
112
+
113
+ def is_more_specific?(potential, current)
114
+ each_is_exact_or_subtype_or_convertible(potential, current)
115
+ end
116
+
117
+ def phase2(mapped_params, potentials)
118
+ nil
119
+ end
120
+
121
+ def phase3(mapped_params, potentials)
122
+ nil
123
+ end
124
+
125
+ def each_is_exact(incoming, target)
126
+ incoming.each_with_index do |in_type, i|
127
+ target_type = target[i]
128
+
129
+ # exact match
130
+ return false unless target_type == in_type
131
+ end
132
+ return true
133
+ end
134
+
135
+ def each_is_exact_or_subtype_or_convertible(incoming, target)
136
+ incoming.each_with_index do |in_type, i|
137
+ target_type = target[i]
138
+
139
+ # exact match
140
+ next if target_type == in_type
141
+
142
+ # primitive is safely convertible
143
+ if target_type.primitive?
144
+ if in_type.primitive?
145
+ next if primitive_convertible? in_type, target_type
146
+ end
147
+ return false
148
+ end
149
+
150
+ # object type is assignable
151
+ return false unless target_type.assignable_from? in_type
152
+ end
153
+ return true
154
+ end
155
+
156
+ BOOLEAN = Java::boolean.java_class
157
+ BYTE = Java::byte.java_class
158
+ SHORT = Java::short.java_class
159
+ CHAR = Java::char.java_class
160
+ INT = Java::int.java_class
161
+ LONG = Java::long.java_class
162
+ FLOAT = Java::float.java_class
163
+ DOUBLE = Java::double.java_class
164
+
165
+ PrimitiveConversions = {
166
+ BOOLEAN => [BOOLEAN],
167
+ BYTE => [BYTE, SHORT, CHAR, INT, LONG, FLOAT, DOUBLE],
168
+ SHORT => [SHORT, INT, LONG, FLOAT, DOUBLE],
169
+ CHAR => [CHAR, INT, LONG, FLOAT, DOUBLE],
170
+ INT => [INT, LONG, FLOAT, DOUBLE],
171
+ LONG => [LONG, DOUBLE],
172
+ FLOAT => [FLOAT, DOUBLE],
173
+ DOUBLE => [DOUBLE]
174
+ }
175
+
176
+ def primitive_convertible?(in_type, target_type)
177
+ if PrimitiveConversions.include? in_type
178
+ PrimitiveConversions[in_type].include?(target_type)
179
+ else
180
+ in_type.convertible_to?(target_type)
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,516 @@
1
+ require 'duby'
2
+ require 'duby/ast'
3
+ require 'duby/jvm/types'
4
+ require 'duby/jvm/compiler'
5
+ require 'duby/jvm/source_generator/builder'
6
+ require 'duby/jvm/source_generator/precompile'
7
+ require 'duby/jvm/source_generator/loops'
8
+
9
+ class String
10
+ def compile(compiler, expression)
11
+ compiler.method.print self if expression
12
+ end
13
+ end
14
+
15
+ module Duby
16
+ module Compiler
17
+ class JavaSource
18
+ JVMTypes = Duby::JVM::Types
19
+ include Duby::Compiler::JVM::JVMLogger
20
+ attr_accessor :filename, :method, :static, :class, :lvalue
21
+
22
+ Operators = [
23
+ '+', '-', '+@', '-@', '/', '%', '*', '<',
24
+ '<=', '==', '!=', '>=', '>',
25
+ '<<', '>>', '>>>', '|', '&', '^', '~'
26
+ ]
27
+ ArrayOps = [
28
+ '[]', '[]=', 'length'
29
+ ]
30
+
31
+ ImplicitReturn = Struct.new(:value)
32
+
33
+ def initialize(filename)
34
+ @filename = File.basename(filename)
35
+ @static = true
36
+ parts = filename.split '/'
37
+ classname = parts.pop.sub /[.].+/, ''
38
+ package = parts.join('.') unless parts.empty?
39
+
40
+ @file = Duby::JavaSource::Builder.new(filename, self)
41
+ #@file.package = package
42
+ @class = @file.public_class(classname)
43
+ end
44
+
45
+ def generate(&block)
46
+ @class.stop
47
+ log "Generating source files..."
48
+ @file.generate do |filename, builder|
49
+ log " #{builder.class_name}"
50
+ if block_given?
51
+ yield filename, builder
52
+ else
53
+ File.open(filename, 'w') {|f| f.write(builder.generate)}
54
+ end
55
+ end
56
+ log "...done!"
57
+ end
58
+
59
+ def define_main(body)
60
+ with :method => @class.main do
61
+ log "Starting main method"
62
+
63
+ @method.start
64
+
65
+ body.compile(self, false)
66
+
67
+ @method.stop
68
+ end
69
+
70
+ log "Main method complete!"
71
+ end
72
+
73
+ def define_method(name, signature, args, body, force_static)
74
+ args = args.args || []
75
+ return_type = signature[:return]
76
+ exceptions = signature[:throws] || []
77
+ if @static || force_static
78
+ method = @class.public_static_method(name.to_s, return_type, exceptions, *args)
79
+ else
80
+ if name == "initialize"
81
+ method = @class.public_constructor(exceptions, *args)
82
+ else
83
+ method = @class.public_method(name.to_s, return_type, exceptions, *args)
84
+ end
85
+ end
86
+
87
+ with :method => method, :static => @static || force_static do
88
+ log "Starting new method #{name}"
89
+
90
+ @method.start
91
+
92
+ unless @method.type.nil? || @method.type.void?
93
+ self.return(ImplicitReturn.new(body))
94
+ else
95
+ body.compile(self, false) if body
96
+ end
97
+
98
+ log "Method #{name} complete!"
99
+ @method.stop
100
+ end
101
+ end
102
+
103
+ def return(node)
104
+ if @method.type.nil? || @method.type.void?
105
+ @method.puts 'return;'
106
+ return
107
+ end
108
+ if node.value.expr?(self)
109
+ @method.print 'return '
110
+ node.value.compile(self, true)
111
+ @method.puts ';'
112
+ else
113
+ store_value('return ', node.value)
114
+ end
115
+ end
116
+
117
+ def _raise(node)
118
+ if node.expr?(self)
119
+ @method.print 'throw '
120
+ node.compile(self, true)
121
+ @method.puts ';'
122
+ else
123
+ store_value('throw ', node)
124
+ end
125
+ end
126
+
127
+ def rescue(node, expression)
128
+ @method.block 'try' do
129
+ maybe_store(node.body, expression)
130
+ end
131
+ node.clauses.each do |clause|
132
+ clause.types.each do |type|
133
+ name = clause.name || 'tmp$ex'
134
+ @method.block "catch (#{type.to_source} #{name})" do
135
+ maybe_store(clause.body, expression)
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ def ensure(node, expression)
142
+ @method.block 'try' do
143
+ maybe_store(node.body, expression)
144
+ end
145
+ @method.block 'finally' do
146
+ node.clause.compile(self, false)
147
+ end
148
+ end
149
+
150
+ def line(num)
151
+ end
152
+
153
+ def declare_local(name, type)
154
+ @method.declare_local(type, name)
155
+ end
156
+
157
+ def declare_field(name, type)
158
+ @class.declare_field(name, type, @static)
159
+ end
160
+
161
+ def local(name, type)
162
+ @method.print name
163
+ end
164
+
165
+ def field(name, type)
166
+ name = name[1..-1]
167
+ declare_field(name, type)
168
+ @method.print "#{this}.#{name}"
169
+ end
170
+
171
+ def this
172
+ @static ? @class.class_name : 'this'
173
+ end
174
+
175
+ def local_assign(name, type, expression, value)
176
+ value = value.precompile(self)
177
+ if method.local?(name)
178
+ @method.print @lvalue if expression
179
+ @method.print "#{name} = "
180
+ value.compile(self, true)
181
+ @method.puts ';'
182
+ else
183
+ @method.declare_local(type, name) do
184
+ value.compile(self, true)
185
+ end
186
+ if expression
187
+ @method.puts "#{@lvalue}#{name};"
188
+ end
189
+ end
190
+ end
191
+
192
+ def field_declare(name, type)
193
+ name = name[1..-1]
194
+ declare_field(name, type)
195
+ end
196
+
197
+ def local_declare(name, type)
198
+ declare_local(name, type)
199
+ end
200
+
201
+ def field_assign(name, type, expression, value)
202
+ name = name[1..-1]
203
+ declare_field(name, type)
204
+ lvalue = "#{@lvalue if expression}#{this}.#{name} = "
205
+ store_value(lvalue, value)
206
+ end
207
+
208
+ def store_value(lvalue, value)
209
+ if value.is_a? String
210
+ @method.puts "#{lvalue}#{value};"
211
+ elsif value.expr?(self)
212
+ @method.print lvalue
213
+ value.compile(self, true)
214
+ @method.puts ';'
215
+ else
216
+ with :lvalue => lvalue do
217
+ value.compile(self, true)
218
+ end
219
+ end
220
+ end
221
+
222
+ def assign(name, value)
223
+ store_value("#{name} = ", value)
224
+ name
225
+ end
226
+
227
+ def maybe_store(value, expression)
228
+ if expression
229
+ store_value(@lvalue, value)
230
+ else
231
+ value.compile(self, false)
232
+ end
233
+ end
234
+
235
+ def body(body, expression)
236
+ # all except the last element in a body of code is treated as a statement
237
+ i, last = 0, body.children.size - 1
238
+ while i < last
239
+ body.children[i].compile(self, false)
240
+ i += 1
241
+ end
242
+ # last element is an expression only if the body is an expression
243
+ maybe_store(body.children[last], expression)
244
+ end
245
+
246
+ def branch_expression(node)
247
+ node.condition.compile(self, true)
248
+ @method.print ' ? ('
249
+ if node.body
250
+ node.body.compile(self, true)
251
+ else
252
+ @method.print @method.init_value(node.inferred_type)
253
+ end
254
+ @method.print ') : ('
255
+ if node.else
256
+ node.else.compile(self, true)
257
+ else
258
+ @method.print @method.init_value(node.inferred_type)
259
+ end
260
+ @method.print ')'
261
+ end
262
+
263
+ def branch(node, expression)
264
+ if expression && node.expr?(self)
265
+ return branch_expression(node)
266
+ end
267
+ predicate = node.condition.predicate.precompile(self)
268
+ @method.print 'if ('
269
+ predicate.compile(self, true)
270
+ @method.block ")" do
271
+ if node.body
272
+ maybe_store(node.body, expression)
273
+ elsif expression
274
+ store_value(@lvalue, @method.init_value(node.inferred_type))
275
+ end
276
+ end
277
+ if node.else || expression
278
+ @method.block 'else' do
279
+ if node.else
280
+ maybe_store(node.else, expression)
281
+ else
282
+ store_value(@lvalue, @method.init_value(node.inferred_type))
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ def while_loop(loop, expression)
289
+ if loop.redo || !loop.condition.predicate.expr?(self)
290
+ loop = ComplexWhileLoop.new(loop, self)
291
+ else
292
+ loop = SimpleWhileLoop.new(loop, self)
293
+ end
294
+ with(:loop => loop) do
295
+ loop.compile(expression)
296
+ end
297
+ end
298
+
299
+ def for_loop(loop, expression)
300
+ if loop.redo
301
+ loop = RedoableForLoop.new(loop, self)
302
+ else
303
+ loop = SimpleForLoop.new(loop, self)
304
+ end
305
+ with(:loop => loop) do
306
+ loop.compile(expression)
307
+ end
308
+ end
309
+
310
+ def expr?(target, params)
311
+ !([target] + params).any? {|x| x.kind_of? Duby::AST::TempValue}
312
+ end
313
+
314
+ def operator(target, op, params, expression)
315
+ simple = expr?(target, params)
316
+ if expression && !simple
317
+ @method.print @lvalue
318
+ end
319
+ if params.size == 0
320
+ # unary operator
321
+ op = op[0,1]
322
+ @method.print op
323
+ target.compile(self, true)
324
+ else
325
+ @method.print '('
326
+ other = params[0]
327
+ target.compile(self, true)
328
+ @method.print " #{op} "
329
+ other.compile(self, true)
330
+ @method.print ')'
331
+ end
332
+ unless expression && simple
333
+ @method.puts ';'
334
+ end
335
+ end
336
+
337
+ def compile_args(call)
338
+ call.parameters.map do |param|
339
+ param.precompile(self)
340
+ end
341
+ end
342
+
343
+ def self_type
344
+ type = AST::type(@class.name.tr('/', '.'))
345
+ type = type.meta if @static
346
+ type
347
+ end
348
+
349
+ def self_call(call, expression)
350
+ if call.cast?
351
+ args = compile_args(call)
352
+ simple = call.expr?(self)
353
+ @method.print @lvalue if expression && !simple
354
+ @method.print "(#{call.inferred_type.name})("
355
+ args.each{|arg| arg.compile(self, true)}
356
+ @method.print ")"
357
+ @method.puts ';' unless simple && expression
358
+ else
359
+ method_call(this, call, compile_args(call), expression)
360
+ end
361
+ end
362
+
363
+ def call(call, expression)
364
+ if Duby::AST::Constant === call.target
365
+ target = call.target.inferred_type.name
366
+ else
367
+ target = call.target.precompile(self)
368
+ end
369
+ params = compile_args(call)
370
+
371
+ if Operators.include? call.name
372
+ operator(target, call.name, params, expression)
373
+ elsif call.target.inferred_type.array? && ArrayOps.include?(call.name)
374
+ array_op(target, call.name, params, expression)
375
+ elsif call.name == 'nil?'
376
+ operator(target, '==', ['null'], expression)
377
+ else
378
+ method_call(target, call, params, expression)
379
+ end
380
+ end
381
+
382
+ def array_op(target, name, args, expression)
383
+ simple = expr?(target, args)
384
+ index, value = args
385
+ if expression && !simple
386
+ @method.print @lvalue
387
+ end
388
+ target.compile(self, true)
389
+ if name == 'length'
390
+ @method.print '.length'
391
+ else
392
+ @method.print '['
393
+ index.compile(self, true)
394
+ @method.print ']'
395
+ if name == '[]='
396
+ @method.print " = "
397
+ value.compile(self, true)
398
+ end
399
+ end
400
+ unless simple && expression
401
+ @method.puts ';'
402
+ end
403
+ end
404
+
405
+ def break(node)
406
+ @loop.break
407
+ end
408
+
409
+ def next(node)
410
+ @loop.next
411
+ end
412
+
413
+ def redo(node)
414
+ @loop.redo
415
+ end
416
+
417
+ def method_call(target, call, params, expression)
418
+ simple = call.expr?(self)
419
+ method = call.method(self)
420
+ unless simple || method.actual_return_type.void?
421
+ @method.print @lvalue if expression
422
+ end
423
+ if method.constructor?
424
+ @method.print "new "
425
+ target.compile(self, true)
426
+ @method.print '('
427
+ else
428
+ target.compile(self, true)
429
+ @method.print ".#{method.name}("
430
+ end
431
+ params.each_with_index do |param, index|
432
+ @method.print ', ' unless index == 0
433
+ param.compile(self, true)
434
+ end
435
+ if simple && expression
436
+ @method.print ')'
437
+ else
438
+ @method.puts ');'
439
+ end
440
+ if method.actual_return_type.void? && expression
441
+ @method.print @lvalue
442
+ if method.static?
443
+ @method.puts 'null;'
444
+ else
445
+ target.compile(self, true)
446
+ @method.puts ';'
447
+ end
448
+ end
449
+
450
+ end
451
+
452
+ def temp(expression, value=nil)
453
+ assign(@method.tmp(expression.inferred_type), value || expression)
454
+ end
455
+
456
+ def empty_array(type, size)
457
+ sizevar = size.precompile(self)
458
+ @method.print "#{@lvalue unless size.expr?(self)}new #{type.name}["
459
+ sizevar.compile(self, true)
460
+ @method.print ']'
461
+ end
462
+
463
+ def import(short, long)
464
+ end
465
+
466
+ def string(value)
467
+ @method.print value.inspect
468
+ end
469
+
470
+ def boolean(value)
471
+ @method.print value ? 'true' : 'false'
472
+ end
473
+
474
+ def null
475
+ @method.print 'null'
476
+ end
477
+
478
+ def print(node)
479
+ value = node.parameters[0]
480
+ value = value && value.precompile(self)
481
+ if node.println
482
+ @method.print "System.out.println("
483
+ else
484
+ @method.print "System.out.print("
485
+ end
486
+ value.compile(self, true) if value
487
+ @method.puts ');'
488
+ end
489
+
490
+ def define_class(class_def, expression)
491
+ with(:class => class_def.inferred_type.define(@file),
492
+ :static => false) do
493
+ class_def.body.compile(self, false) if class_def.body
494
+
495
+ @class.stop
496
+ end
497
+ end
498
+
499
+ def with(vars)
500
+ orig_values = {}
501
+ begin
502
+ vars.each do |name, new_value|
503
+ name = "@#{name}"
504
+ orig_values[name] = instance_variable_get name
505
+ instance_variable_set name, new_value
506
+ end
507
+ yield
508
+ ensure
509
+ orig_values.each do |name, value|
510
+ instance_variable_set name, value
511
+ end
512
+ end
513
+ end
514
+ end
515
+ end
516
+ end