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.
- data/History.txt +4 -0
- data/Manifest.txt +68 -0
- data/README.txt +31 -0
- data/Rakefile +37 -0
- data/bin/duby +9 -0
- data/bin/dubyc +9 -0
- data/bin/dubyp +9 -0
- data/examples/appengine/src/com/google/appengine/ext/duby/db/Model.duby +132 -0
- data/examples/appengine/src/com/ribrdb/DubyApp.duby +28 -0
- data/examples/bench_fractal.duby +57 -0
- data/examples/construction.duby +8 -0
- data/examples/edb.duby +3 -0
- data/examples/fib.duby +24 -0
- data/examples/fields.duby +22 -0
- data/examples/java_thing.duby +13 -0
- data/examples/simple_class.duby +12 -0
- data/examples/swing.duby +20 -0
- data/examples/tak.duby +15 -0
- data/javalib/JRubyParser.jar +0 -0
- data/lib/duby.rb +170 -0
- data/lib/duby/ast.rb +340 -0
- data/lib/duby/ast/call.rb +73 -0
- data/lib/duby/ast/class.rb +145 -0
- data/lib/duby/ast/flow.rb +328 -0
- data/lib/duby/ast/intrinsics.rb +46 -0
- data/lib/duby/ast/literal.rb +93 -0
- data/lib/duby/ast/local.rb +77 -0
- data/lib/duby/ast/method.rb +187 -0
- data/lib/duby/ast/structure.rb +44 -0
- data/lib/duby/ast/type.rb +93 -0
- data/lib/duby/c/compiler.rb +134 -0
- data/lib/duby/compiler.rb +261 -0
- data/lib/duby/jvm/compiler.rb +684 -0
- data/lib/duby/jvm/method_lookup.rb +185 -0
- data/lib/duby/jvm/source_compiler.rb +516 -0
- data/lib/duby/jvm/source_generator/builder.rb +368 -0
- data/lib/duby/jvm/source_generator/loops.rb +132 -0
- data/lib/duby/jvm/source_generator/precompile.rb +154 -0
- data/lib/duby/jvm/source_generator/typer.rb +11 -0
- data/lib/duby/jvm/typer.rb +118 -0
- data/lib/duby/jvm/types.rb +326 -0
- data/lib/duby/jvm/types/basic_types.rb +18 -0
- data/lib/duby/jvm/types/boolean.rb +11 -0
- data/lib/duby/jvm/types/factory.rb +151 -0
- data/lib/duby/jvm/types/floats.rb +70 -0
- data/lib/duby/jvm/types/integers.rb +110 -0
- data/lib/duby/jvm/types/intrinsics.rb +157 -0
- data/lib/duby/jvm/types/literals.rb +82 -0
- data/lib/duby/jvm/types/methods.rb +344 -0
- data/lib/duby/jvm/types/number.rb +92 -0
- data/lib/duby/nbcompiler.rb +29 -0
- data/lib/duby/old/compiler_old.rb +845 -0
- data/lib/duby/old/declaration.rb +72 -0
- data/lib/duby/old/mapper.rb +72 -0
- data/lib/duby/old/signature.rb +52 -0
- data/lib/duby/old/typer_old.rb +163 -0
- data/lib/duby/plugin/edb.rb +25 -0
- data/lib/duby/plugin/java.rb +42 -0
- data/lib/duby/plugin/math.rb +84 -0
- data/lib/duby/transform.rb +908 -0
- data/lib/duby/typer.rb +359 -0
- data/test/test_ast.rb +391 -0
- data/test/test_compilation.rb +98 -0
- data/test/test_java_typer.rb +199 -0
- data/test/test_javac_compiler.rb +57 -0
- data/test/test_jvm_compiler.rb +1459 -0
- data/test/test_math_plugin.rb +87 -0
- data/test/test_typer.rb +246 -0
- metadata +155 -0
@@ -0,0 +1,684 @@
|
|
1
|
+
require 'duby'
|
2
|
+
require 'duby/jvm/method_lookup'
|
3
|
+
require 'duby/jvm/types'
|
4
|
+
require 'duby/typer'
|
5
|
+
require 'duby/plugin/java'
|
6
|
+
require 'bitescript'
|
7
|
+
|
8
|
+
module Duby
|
9
|
+
module AST
|
10
|
+
class FunctionalCall
|
11
|
+
attr_accessor :target
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Compiler
|
16
|
+
class JVM
|
17
|
+
java_import java.lang.System
|
18
|
+
java_import java.io.PrintStream
|
19
|
+
include Duby::JVM::MethodLookup
|
20
|
+
Types = Duby::JVM::Types
|
21
|
+
|
22
|
+
class << self
|
23
|
+
attr_accessor :verbose
|
24
|
+
|
25
|
+
def log(message)
|
26
|
+
puts "* [#{name}] #{message}" if JVM.verbose
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module JVMLogger
|
31
|
+
def log(message); JVM.log(message); end
|
32
|
+
end
|
33
|
+
include JVMLogger
|
34
|
+
|
35
|
+
class ImplicitSelf
|
36
|
+
attr_reader :inferred_type
|
37
|
+
|
38
|
+
def initialize(type)
|
39
|
+
@inferred_type = type
|
40
|
+
end
|
41
|
+
|
42
|
+
def compile(compiler, expression)
|
43
|
+
if expression
|
44
|
+
compiler.method.aload(0)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
attr_accessor :filename, :src, :method, :static, :class
|
50
|
+
|
51
|
+
def initialize(filename)
|
52
|
+
@filename = File.basename(filename)
|
53
|
+
@src = ""
|
54
|
+
@static = true
|
55
|
+
classname = File.basename(filename, '.duby')
|
56
|
+
|
57
|
+
@file = BiteScript::FileBuilder.new(@filename)
|
58
|
+
AST.type_factory.define_types(@file)
|
59
|
+
@type = AST::type(classname)
|
60
|
+
@class = @type.define(@file)
|
61
|
+
end
|
62
|
+
|
63
|
+
def compile(ast, expression = false)
|
64
|
+
ast.compile(self, expression)
|
65
|
+
log "Compilation successful!"
|
66
|
+
end
|
67
|
+
|
68
|
+
def define_main(body)
|
69
|
+
with :method => @class.main do
|
70
|
+
log "Starting main method"
|
71
|
+
|
72
|
+
@method.start
|
73
|
+
|
74
|
+
# declare argv variable
|
75
|
+
@method.local('argv', AST.type('string', true))
|
76
|
+
|
77
|
+
body.compile(self, false)
|
78
|
+
|
79
|
+
@method.returnvoid
|
80
|
+
@method.stop
|
81
|
+
end
|
82
|
+
|
83
|
+
log "Main method complete!"
|
84
|
+
end
|
85
|
+
|
86
|
+
def define_method(name, signature, args, body, force_static)
|
87
|
+
arg_types = if args.args
|
88
|
+
args.args.map { |arg| arg.inferred_type }
|
89
|
+
else
|
90
|
+
[]
|
91
|
+
end
|
92
|
+
return_type = signature[:return]
|
93
|
+
exceptions = signature[:throws]
|
94
|
+
started = false
|
95
|
+
if @static || force_static
|
96
|
+
method = @class.public_static_method(name.to_s, exceptions, return_type, *arg_types)
|
97
|
+
else
|
98
|
+
if name == "initialize"
|
99
|
+
method = @class.public_constructor(exceptions, *arg_types)
|
100
|
+
method.start
|
101
|
+
method.aload 0
|
102
|
+
method.invokespecial @class.superclass, "<init>", [@method.void]
|
103
|
+
started = true
|
104
|
+
else
|
105
|
+
method = @class.public_method(name.to_s, exceptions, return_type, *arg_types)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
return if @class.interface?
|
110
|
+
|
111
|
+
with :method => method, :static => @static || force_static do
|
112
|
+
log "Starting new method #{name}(#{arg_types})"
|
113
|
+
|
114
|
+
@method.start unless started
|
115
|
+
|
116
|
+
# declare all args so they get their values
|
117
|
+
if args.args
|
118
|
+
args.args.each {|arg| @method.local(arg.name, arg.inferred_type)}
|
119
|
+
end
|
120
|
+
|
121
|
+
expression = signature[:return] != Types::Void
|
122
|
+
body.compile(self, expression) if body
|
123
|
+
|
124
|
+
if name == "initialize"
|
125
|
+
@method.returnvoid
|
126
|
+
else
|
127
|
+
signature[:return].return(@method)
|
128
|
+
end
|
129
|
+
|
130
|
+
@method.stop
|
131
|
+
end
|
132
|
+
|
133
|
+
arg_types_for_opt = []
|
134
|
+
args_for_opt = []
|
135
|
+
if args.args
|
136
|
+
args.args.each do |arg|
|
137
|
+
if AST::OptionalArgument === arg
|
138
|
+
if @static || force_static
|
139
|
+
method = @class.public_static_method(name.to_s, exceptions, return_type, *arg_types_for_opt)
|
140
|
+
else
|
141
|
+
if name == "initialize"
|
142
|
+
method = @class.public_constructor(exceptions, *arg_types_for_opt)
|
143
|
+
method.start
|
144
|
+
method.aload 0
|
145
|
+
method.invokespecial @class.superclass, "<init>", [@method.void]
|
146
|
+
started = true
|
147
|
+
else
|
148
|
+
method = @class.public_method(name.to_s, exceptions, return_type, *arg_types_for_opt)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
with :method => method, :static => @static || force_static do
|
153
|
+
log "Starting new method #{name}(#{arg_types_for_opt})"
|
154
|
+
|
155
|
+
@method.start unless started
|
156
|
+
|
157
|
+
# declare all args so they get their values
|
158
|
+
|
159
|
+
expression = signature[:return] != Types::Void
|
160
|
+
|
161
|
+
@method.aload(0) unless @static
|
162
|
+
args_for_opt.each {|req_arg| @method.local(req_arg.name, req_arg.inferred_type)}
|
163
|
+
arg.children[0].compile(self, true)
|
164
|
+
|
165
|
+
# invoke the next one in the chain
|
166
|
+
if @static
|
167
|
+
@method.invokestatic(@class, name.to_s, [return_type] + arg_types_for_opt + [arg.inferred_type])
|
168
|
+
else
|
169
|
+
@method.invokevirtual(@class, name.to_s, [return_type] + arg_types_for_opt + [arg.inferred_type])
|
170
|
+
end
|
171
|
+
|
172
|
+
if name == "initialize"
|
173
|
+
@method.returnvoid
|
174
|
+
else
|
175
|
+
signature[:return].return(@method)
|
176
|
+
end
|
177
|
+
|
178
|
+
@method.stop
|
179
|
+
end
|
180
|
+
end
|
181
|
+
arg_types_for_opt << arg.inferred_type
|
182
|
+
args_for_opt << arg
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
log "Method #{name}(#{arg_types}) complete!"
|
187
|
+
end
|
188
|
+
|
189
|
+
def define_class(class_def, expression)
|
190
|
+
with(:type => class_def.inferred_type,
|
191
|
+
:class => class_def.inferred_type.define(@file),
|
192
|
+
:static => false) do
|
193
|
+
class_def.body.compile(self, false) if class_def.body
|
194
|
+
|
195
|
+
@class.stop
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def declare_argument(name, type)
|
200
|
+
# declare local vars for arguments here
|
201
|
+
end
|
202
|
+
|
203
|
+
def branch(iff, expression)
|
204
|
+
elselabel = @method.label
|
205
|
+
donelabel = @method.label
|
206
|
+
|
207
|
+
# this is ugly...need a better way to abstract the idea of compiling a
|
208
|
+
# conditional branch while still fitting into JVM opcodes
|
209
|
+
predicate = iff.condition.predicate
|
210
|
+
if iff.body || expression
|
211
|
+
jump_if_not(predicate, elselabel)
|
212
|
+
|
213
|
+
if iff.body
|
214
|
+
iff.body.compile(self, expression)
|
215
|
+
elsif expression
|
216
|
+
iff.inferred_type.init_value(@method)
|
217
|
+
end
|
218
|
+
|
219
|
+
@method.goto(donelabel)
|
220
|
+
else
|
221
|
+
jump_if(predicate, donelabel)
|
222
|
+
end
|
223
|
+
|
224
|
+
elselabel.set!
|
225
|
+
|
226
|
+
if iff.else
|
227
|
+
iff.else.compile(self, expression)
|
228
|
+
elsif expression
|
229
|
+
iff.inferred_type.init_value(@method)
|
230
|
+
end
|
231
|
+
|
232
|
+
donelabel.set!
|
233
|
+
end
|
234
|
+
|
235
|
+
def while_loop(loop, expression)
|
236
|
+
with(:break_label => @method.label,
|
237
|
+
:redo_label => @method.label,
|
238
|
+
:next_label => @method.label) do
|
239
|
+
# TODO: not checking "check first" or "negative"
|
240
|
+
predicate = loop.condition.predicate
|
241
|
+
|
242
|
+
if loop.check_first
|
243
|
+
@next_label.set!
|
244
|
+
if loop.negative
|
245
|
+
# if condition, exit
|
246
|
+
jump_if(predicate, @break_label)
|
247
|
+
else
|
248
|
+
# if not condition, exit
|
249
|
+
jump_if_not(predicate, @break_label)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
@redo_label.set!
|
254
|
+
loop.body.compile(self, false)
|
255
|
+
|
256
|
+
unless loop.check_first
|
257
|
+
@next_label.set!
|
258
|
+
if loop.negative
|
259
|
+
# if not condition, continue
|
260
|
+
jump_if_not(predicate, @redo_label)
|
261
|
+
else
|
262
|
+
# if condition, continue
|
263
|
+
jump_if(predicate, @redo_label)
|
264
|
+
end
|
265
|
+
else
|
266
|
+
@method.goto(@next_label)
|
267
|
+
end
|
268
|
+
|
269
|
+
@break_label.set!
|
270
|
+
|
271
|
+
# loops always evaluate to null
|
272
|
+
@method.aconst_null if expression
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def for_loop(loop, expression)
|
277
|
+
if loop.iter.inferred_type.array?
|
278
|
+
array_foreach(loop, expression)
|
279
|
+
else
|
280
|
+
iterator_foreach(loop, expression)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def array_foreach(loop, expression)
|
285
|
+
array = "tmp$#{loop.name}$array"
|
286
|
+
i = "tmp$#{loop.name}$i"
|
287
|
+
length = "tmp$#{loop.name}$length"
|
288
|
+
array_type = loop.iter.inferred_type
|
289
|
+
item_type = array_type.component_type
|
290
|
+
local_assign(array, array_type, true, loop.iter)
|
291
|
+
@method.arraylength
|
292
|
+
@method.istore(@method.local(length, Types::Int))
|
293
|
+
@method.push_int(0)
|
294
|
+
@method.istore(@method.local(i, Types::Int))
|
295
|
+
with(:break_label => @method.label,
|
296
|
+
:redo_label => @method.label,
|
297
|
+
:next_label => @method.label) do
|
298
|
+
@next_label.set!
|
299
|
+
local(i, Types::Int)
|
300
|
+
local(length, Types::Int)
|
301
|
+
@method.if_icmpge(@break_label)
|
302
|
+
local(array, array_type)
|
303
|
+
local(i, Types::Int)
|
304
|
+
item_type.aload(@method)
|
305
|
+
item_type.store(@method, @method.local(loop.name, item_type))
|
306
|
+
@method.iinc(@method.local(i, Types::Int), 1)
|
307
|
+
|
308
|
+
@redo_label.set!
|
309
|
+
loop.body.compile(self, false)
|
310
|
+
@method.goto @next_label
|
311
|
+
|
312
|
+
@break_label.set!
|
313
|
+
|
314
|
+
# loops always evaluate to null
|
315
|
+
@method.aconst_null if expression
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def break(node)
|
320
|
+
handle_ensures(node)
|
321
|
+
@method.goto(@break_label)
|
322
|
+
end
|
323
|
+
|
324
|
+
def next(node)
|
325
|
+
handle_ensures(node)
|
326
|
+
@method.goto(@next_label)
|
327
|
+
end
|
328
|
+
|
329
|
+
def redo(node)
|
330
|
+
handle_ensures(node)
|
331
|
+
@method.goto(@redo_label)
|
332
|
+
end
|
333
|
+
|
334
|
+
def jump_if(predicate, target)
|
335
|
+
raise "Expected boolean, found #{predicate.inferred_type}" unless predicate.inferred_type == Types::Boolean
|
336
|
+
predicate.compile(self, true)
|
337
|
+
@method.ifne(target)
|
338
|
+
end
|
339
|
+
|
340
|
+
def jump_if_not(predicate, target)
|
341
|
+
raise "Expected boolean, found #{predicate.inferred_type}" unless predicate.inferred_type == Types::Boolean
|
342
|
+
predicate.compile(self, true)
|
343
|
+
@method.ifeq(target)
|
344
|
+
end
|
345
|
+
|
346
|
+
def call(call, expression)
|
347
|
+
|
348
|
+
target = call.target.inferred_type
|
349
|
+
params = call.parameters.map do |param|
|
350
|
+
param.inferred_type
|
351
|
+
end
|
352
|
+
method = target.get_method(call.name, params)
|
353
|
+
if method
|
354
|
+
method.call(self, call, expression)
|
355
|
+
else
|
356
|
+
raise "Missing method #{target}.#{call.name}(#{params.join ', '})"
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def self_call(fcall, expression)
|
361
|
+
return cast(fcall, expression) if fcall.cast?
|
362
|
+
type = @type
|
363
|
+
type = type.meta if @static
|
364
|
+
fcall.target = ImplicitSelf.new(type)
|
365
|
+
|
366
|
+
params = fcall.parameters.map do |param|
|
367
|
+
param.inferred_type
|
368
|
+
end
|
369
|
+
method = type.get_method(fcall.name, params)
|
370
|
+
unless method
|
371
|
+
target = static ? @class.name : 'self'
|
372
|
+
|
373
|
+
raise NameError, "No method %s.%s(%s)" %
|
374
|
+
[target, fcall.name, params.join(', ')]
|
375
|
+
end
|
376
|
+
method.call(self, fcall, expression)
|
377
|
+
end
|
378
|
+
|
379
|
+
def cast(fcall, expression)
|
380
|
+
# casting operation, not a call
|
381
|
+
castee = fcall.parameters[0]
|
382
|
+
|
383
|
+
# TODO move errors to inference phase
|
384
|
+
source_type_name = castee.inferred_type.name
|
385
|
+
target_type_name = fcall.inferred_type.name
|
386
|
+
case source_type_name
|
387
|
+
when "byte", "short", "char", "int", "long", "float", "double"
|
388
|
+
case target_type_name
|
389
|
+
when "byte", "short", "char", "int", "long", "float", "double"
|
390
|
+
# ok
|
391
|
+
primitive = true
|
392
|
+
else
|
393
|
+
raise TypeError.new "not a reference type: #{castee.inferred_type}"
|
394
|
+
end
|
395
|
+
when "boolean"
|
396
|
+
if target_type_name != "boolean"
|
397
|
+
raise TypeError.new "not a boolean type: #{castee.inferred_type}"
|
398
|
+
end
|
399
|
+
primitive = true
|
400
|
+
else
|
401
|
+
case target_type_name
|
402
|
+
when "byte", "short", "char", "int", "long", "float", "double"
|
403
|
+
raise TypeError.new "not a primitive type: #{castee.inferred_type}"
|
404
|
+
else
|
405
|
+
# ok
|
406
|
+
primitive = false
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
castee.compile(self, expression)
|
411
|
+
if expression
|
412
|
+
if primitive
|
413
|
+
source_type_name = 'int' if %w[byte short char].include? source_type_name
|
414
|
+
if (source_type_name != 'int') && (%w[byte short char].include? target_type_name)
|
415
|
+
target_type_name = 'int'
|
416
|
+
end
|
417
|
+
|
418
|
+
if source_type_name != target_type_name
|
419
|
+
if RUBY_VERSION == "1.9"
|
420
|
+
@method.send "#{source_type_name[0]}2#{target_type_name[0]}"
|
421
|
+
else
|
422
|
+
@method.send "#{source_type_name[0].chr}2#{target_type_name[0].chr}"
|
423
|
+
end
|
424
|
+
end
|
425
|
+
else
|
426
|
+
if source_type_name != target_type_name
|
427
|
+
@method.checkcast fcall.inferred_type
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
def body(body, expression)
|
434
|
+
# all except the last element in a body of code is treated as a statement
|
435
|
+
i, last = 0, body.children.size - 1
|
436
|
+
while i < last
|
437
|
+
body.children[i].compile(self, false)
|
438
|
+
i += 1
|
439
|
+
end
|
440
|
+
# last element is an expression only if the body is an expression
|
441
|
+
body.children[last].compile(self, expression)
|
442
|
+
end
|
443
|
+
|
444
|
+
def local(name, type)
|
445
|
+
type.load(@method, @method.local(name, type))
|
446
|
+
end
|
447
|
+
|
448
|
+
def local_assign(name, type, expression, value)
|
449
|
+
declare_local(name, type)
|
450
|
+
|
451
|
+
value.compile(self, true)
|
452
|
+
|
453
|
+
# if expression, dup the value we're assigning
|
454
|
+
@method.dup if expression
|
455
|
+
|
456
|
+
type.store(@method, @method.local(name, type))
|
457
|
+
end
|
458
|
+
|
459
|
+
def declared_locals
|
460
|
+
@declared_locals ||= {}
|
461
|
+
end
|
462
|
+
|
463
|
+
def declare_local(name, type)
|
464
|
+
# TODO confirm types are compatible
|
465
|
+
unless declared_locals[name]
|
466
|
+
declared_locals[name] = type
|
467
|
+
index = @method.local(name, type)
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
def local_declare(name, type)
|
472
|
+
declare_local(name, type)
|
473
|
+
type.init_value(@method)
|
474
|
+
type.store(@method, @method.local(name, type))
|
475
|
+
end
|
476
|
+
|
477
|
+
def field(name, type)
|
478
|
+
name = name[1..-1]
|
479
|
+
|
480
|
+
# load self object unless static
|
481
|
+
method.aload 0 unless static
|
482
|
+
|
483
|
+
if static
|
484
|
+
@method.getstatic(@class, name, type)
|
485
|
+
else
|
486
|
+
@method.getfield(@class, name, type)
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
def declared_fields
|
491
|
+
@declared_fields ||= {}
|
492
|
+
end
|
493
|
+
|
494
|
+
def declare_field(name, type)
|
495
|
+
# TODO confirm types are compatible
|
496
|
+
unless declared_fields[name]
|
497
|
+
declared_fields[name] = type
|
498
|
+
if static
|
499
|
+
@class.private_static_field name, type
|
500
|
+
else
|
501
|
+
@class.private_field name, type
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
def field_declare(name, type)
|
507
|
+
name = name[1..-1]
|
508
|
+
declare_field(name, type)
|
509
|
+
end
|
510
|
+
|
511
|
+
def field_assign(name, type, expression, value)
|
512
|
+
name = name[1..-1]
|
513
|
+
|
514
|
+
real_type = declared_fields[name] || type
|
515
|
+
|
516
|
+
declare_field(name, real_type)
|
517
|
+
|
518
|
+
method.aload 0 unless static
|
519
|
+
value.compile(self, true)
|
520
|
+
if expression
|
521
|
+
instruction = 'dup'
|
522
|
+
instruction << '2' if type.wide?
|
523
|
+
instruction << '_x1' unless static
|
524
|
+
method.send instruction
|
525
|
+
end
|
526
|
+
|
527
|
+
if static
|
528
|
+
@method.putstatic(@class, name, real_type)
|
529
|
+
else
|
530
|
+
@method.putfield(@class, name, real_type)
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
def string(value)
|
535
|
+
@method.ldc(value)
|
536
|
+
end
|
537
|
+
|
538
|
+
def boolean(value)
|
539
|
+
value ? @method.iconst_1 : @method.iconst_0
|
540
|
+
end
|
541
|
+
|
542
|
+
def null
|
543
|
+
@method.aconst_null
|
544
|
+
end
|
545
|
+
|
546
|
+
def newline
|
547
|
+
# TODO: line numbering
|
548
|
+
end
|
549
|
+
|
550
|
+
def line(num)
|
551
|
+
@method.line(num) if @method
|
552
|
+
end
|
553
|
+
|
554
|
+
def generate
|
555
|
+
@class.stop
|
556
|
+
log "Generating classes..."
|
557
|
+
@file.generate do |filename, builder|
|
558
|
+
log " #{builder.class_name}"
|
559
|
+
if block_given?
|
560
|
+
yield filename, builder
|
561
|
+
else
|
562
|
+
File.open(filename, 'w') {|f| f.write(builder.generate)}
|
563
|
+
end
|
564
|
+
end
|
565
|
+
log "...done!"
|
566
|
+
end
|
567
|
+
|
568
|
+
def import(short, long)
|
569
|
+
end
|
570
|
+
|
571
|
+
def print(print_node)
|
572
|
+
@method.getstatic System, "out", PrintStream
|
573
|
+
print_node.parameters.each {|param| param.compile(self, true)}
|
574
|
+
params = print_node.parameters.map {|param| param.inferred_type.jvm_type}
|
575
|
+
method_name = print_node.println ? "println" : "print"
|
576
|
+
method = find_method(PrintStream.java_class, method_name, params, false)
|
577
|
+
if (method)
|
578
|
+
@method.invokevirtual(
|
579
|
+
PrintStream,
|
580
|
+
method_name,
|
581
|
+
[method.return_type, *method.parameter_types])
|
582
|
+
else
|
583
|
+
log "Could not find a match for #{PrintStream}.#{method_name}(#{params})"
|
584
|
+
fail "Could not compile"
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
def return(return_node)
|
589
|
+
return_node.value.compile(self, true)
|
590
|
+
handle_ensures(return_node)
|
591
|
+
return_node.inferred_type.return(@method)
|
592
|
+
end
|
593
|
+
|
594
|
+
def _raise(exception)
|
595
|
+
exception.compile(self, true)
|
596
|
+
@method.athrow
|
597
|
+
end
|
598
|
+
|
599
|
+
def rescue(rescue_node, expression)
|
600
|
+
start = @method.label.set!
|
601
|
+
body_end = @method.label
|
602
|
+
done = @method.label
|
603
|
+
rescue_node.body.compile(self, expression)
|
604
|
+
body_end.set!
|
605
|
+
@method.goto(done)
|
606
|
+
rescue_node.clauses.each do |clause|
|
607
|
+
target = @method.label.set!
|
608
|
+
if clause.name
|
609
|
+
@method.astore(@method.push_local(clause.name, clause.type))
|
610
|
+
else
|
611
|
+
@method.pop
|
612
|
+
end
|
613
|
+
clause.body.compile(self, expression)
|
614
|
+
@method.pop_local(clause.name) if clause.name
|
615
|
+
@method.goto(done)
|
616
|
+
clause.types.each do |type|
|
617
|
+
@method.trycatch(start, body_end, target, type)
|
618
|
+
end
|
619
|
+
end
|
620
|
+
done.set!
|
621
|
+
end
|
622
|
+
|
623
|
+
def handle_ensures(node)
|
624
|
+
return unless node.ensures
|
625
|
+
node.ensures.each do |ensure_node|
|
626
|
+
ensure_node.clause.compile(self, false)
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
def ensure(node, expression)
|
631
|
+
node.state = @method.label # Save the ensure target for JumpNodes
|
632
|
+
start = @method.label.set!
|
633
|
+
body_end = @method.label
|
634
|
+
done = @method.label
|
635
|
+
node.body.compile(self, expression) # First compile the body
|
636
|
+
body_end.set!
|
637
|
+
handle_ensures(node) # run the ensure clause
|
638
|
+
@method.goto(done) # and continue on after the exception handler
|
639
|
+
target = @method.label.set! # Finally, create the exception handler
|
640
|
+
@method.trycatch(start, body_end, target, nil)
|
641
|
+
handle_ensures(node)
|
642
|
+
@method.athrow
|
643
|
+
done.set!
|
644
|
+
end
|
645
|
+
|
646
|
+
def empty_array(type, size)
|
647
|
+
size.compile(self, true)
|
648
|
+
type.newarray(@method)
|
649
|
+
end
|
650
|
+
|
651
|
+
def with(vars)
|
652
|
+
orig_values = {}
|
653
|
+
begin
|
654
|
+
vars.each do |name, new_value|
|
655
|
+
name = "@#{name}"
|
656
|
+
orig_values[name] = instance_variable_get name
|
657
|
+
instance_variable_set name, new_value
|
658
|
+
end
|
659
|
+
yield
|
660
|
+
ensure
|
661
|
+
orig_values.each do |name, value|
|
662
|
+
instance_variable_set name, value
|
663
|
+
end
|
664
|
+
end
|
665
|
+
end
|
666
|
+
end
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
if __FILE__ == $0
|
671
|
+
Duby::Typer.verbose = true
|
672
|
+
Duby::AST.verbose = true
|
673
|
+
Duby::Compiler::JVM.verbose = true
|
674
|
+
ast = Duby::AST.parse(File.read(ARGV[0]))
|
675
|
+
|
676
|
+
typer = Duby::Typer::Simple.new(:script)
|
677
|
+
ast.infer(typer)
|
678
|
+
typer.resolve(true)
|
679
|
+
|
680
|
+
compiler = Duby::Compiler::JVM.new(ARGV[0])
|
681
|
+
compiler.compile(ast)
|
682
|
+
|
683
|
+
compiler.generate
|
684
|
+
end
|