duby 0.0.2-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/History.txt +8 -0
  2. data/README.txt +39 -0
  3. data/Rakefile +13 -0
  4. data/bin/duby +9 -0
  5. data/bin/dubyc +9 -0
  6. data/bin/dubyp +9 -0
  7. data/examples/README +16 -0
  8. data/examples/appengine/Rakefile +72 -0
  9. data/examples/appengine/Readme +27 -0
  10. data/examples/appengine/config.ru +7 -0
  11. data/examples/appengine/lib/duby/plugin/datastore.rb +171 -0
  12. data/examples/appengine/src/com/google/appengine/ext/duby/db/Model.duby +132 -0
  13. data/examples/appengine/src/com/ribrdb/DubyApp.duby +28 -0
  14. data/examples/appengine/src/com/ribrdb/list.dhtml +15 -0
  15. data/examples/construction.duby +8 -0
  16. data/examples/edb.duby +3 -0
  17. data/examples/fib.duby +24 -0
  18. data/examples/fields.duby +22 -0
  19. data/examples/fractal.duby +57 -0
  20. data/examples/java_thing.duby +13 -0
  21. data/examples/simple_class.duby +12 -0
  22. data/examples/swing.duby +20 -0
  23. data/examples/tak.duby +15 -0
  24. data/examples/test.edb +9 -0
  25. data/javalib/JRubyParser.jar +0 -0
  26. data/lib/duby.rb +168 -0
  27. data/lib/duby/ast.rb +386 -0
  28. data/lib/duby/ast/call.rb +145 -0
  29. data/lib/duby/ast/class.rb +154 -0
  30. data/lib/duby/ast/flow.rb +332 -0
  31. data/lib/duby/ast/intrinsics.rb +56 -0
  32. data/lib/duby/ast/literal.rb +97 -0
  33. data/lib/duby/ast/local.rb +92 -0
  34. data/lib/duby/ast/method.rb +244 -0
  35. data/lib/duby/ast/structure.rb +62 -0
  36. data/lib/duby/ast/type.rb +93 -0
  37. data/lib/duby/c/compiler.rb +134 -0
  38. data/lib/duby/compiler.rb +282 -0
  39. data/lib/duby/jvm/compiler.rb +766 -0
  40. data/lib/duby/jvm/method_lookup.rb +193 -0
  41. data/lib/duby/jvm/source_compiler.rb +605 -0
  42. data/lib/duby/jvm/source_generator/builder.rb +387 -0
  43. data/lib/duby/jvm/source_generator/loops.rb +110 -0
  44. data/lib/duby/jvm/source_generator/precompile.rb +170 -0
  45. data/lib/duby/jvm/source_generator/typer.rb +11 -0
  46. data/lib/duby/jvm/typer.rb +131 -0
  47. data/lib/duby/jvm/types.rb +331 -0
  48. data/lib/duby/jvm/types/basic_types.rb +19 -0
  49. data/lib/duby/jvm/types/boolean.rb +11 -0
  50. data/lib/duby/jvm/types/enumerable.rb +63 -0
  51. data/lib/duby/jvm/types/factory.rb +155 -0
  52. data/lib/duby/jvm/types/floats.rb +70 -0
  53. data/lib/duby/jvm/types/integers.rb +110 -0
  54. data/lib/duby/jvm/types/intrinsics.rb +230 -0
  55. data/lib/duby/jvm/types/literals.rb +82 -0
  56. data/lib/duby/jvm/types/methods.rb +381 -0
  57. data/lib/duby/jvm/types/number.rb +92 -0
  58. data/lib/duby/nbcompiler.rb +29 -0
  59. data/lib/duby/old/compiler_old.rb +845 -0
  60. data/lib/duby/old/declaration.rb +72 -0
  61. data/lib/duby/old/mapper.rb +72 -0
  62. data/lib/duby/old/signature.rb +52 -0
  63. data/lib/duby/old/typer_old.rb +163 -0
  64. data/lib/duby/plugin/edb.rb +25 -0
  65. data/lib/duby/plugin/java.rb +42 -0
  66. data/lib/duby/plugin/math.rb +84 -0
  67. data/lib/duby/transform.rb +1028 -0
  68. data/lib/duby/typer.rb +369 -0
  69. data/test/TestUser.class +0 -0
  70. data/test/test_ast.rb +391 -0
  71. data/test/test_compilation.rb +98 -0
  72. data/test/test_java_typer.rb +199 -0
  73. data/test/test_javac_compiler.rb +58 -0
  74. data/test/test_jvm_compiler.rb +1770 -0
  75. data/test/test_math_plugin.rb +87 -0
  76. data/test/test_typer.rb +246 -0
  77. metadata +156 -0
@@ -0,0 +1,92 @@
1
+ module Duby::JVM::Types
2
+ class Number < PrimitiveType
3
+ # The type returned by arithmetic operations with this type.
4
+ def math_type
5
+ self
6
+ end
7
+
8
+ def suffix
9
+ ''
10
+ end
11
+
12
+ # Adds an intrinsic that delegates to an intrinsic in another primitive
13
+ # type. That type must support promoting the "this" argument.
14
+ def delegate_intrinsic(name, type, return_type)
15
+ args = [type]
16
+ delegate = type.intrinsics[name][args]
17
+ add_method(name, args, return_type) do |compiler, call, expression|
18
+ if expression
19
+ delegate.call(compiler, call, expression)
20
+ end
21
+ end
22
+ end
23
+
24
+ def add_delegates(name, return_type = nil)
25
+ index = TYPE_ORDERING.index(math_type)
26
+ larger_types = TYPE_ORDERING[index + 1, TYPE_ORDERING.size]
27
+ larger_types.each do |type|
28
+ delegate_intrinsic(name, type, return_type || type)
29
+ end
30
+ end
31
+
32
+ # if_cmpxx for non-ints
33
+ def jump_if(builder, op, label)
34
+ builder.send "#{prefix}cmp#{suffix}"
35
+ builder.send "if#{op}", label
36
+ end
37
+
38
+ def boolean_operator(name, op)
39
+ add_method(name, [math_type], Boolean) do |compiler, call, expression|
40
+ if expression
41
+ # Promote the target or the argument if necessary
42
+ convert_args(compiler,
43
+ [call.target, *call.parameters],
44
+ [math_type, math_type])
45
+ compiler.method.op_to_bool do |label|
46
+ jump_if(compiler.method, op, label)
47
+ end
48
+ end
49
+ end
50
+ add_delegates(name, Boolean)
51
+ end
52
+
53
+ def math_operator(name, op)
54
+ add_method(name, [math_type], math_type) do |compiler, call, expression|
55
+ if expression
56
+ # Promote the target or the argument if necessary
57
+ convert_args(compiler,
58
+ [call.target, *call.parameters],
59
+ [math_type, math_type])
60
+ compiler.method.send "#{prefix}#{op}"
61
+ end
62
+ end
63
+ add_delegates(name)
64
+ end
65
+
66
+ def unary_operator(name, op)
67
+ add_method(name, [], math_type) do |compiler, call, expression|
68
+ if expression
69
+ call.target.compile(compiler, true)
70
+ compiler.method.send("#{prefix}#{op}") if op
71
+ end
72
+ end
73
+ end
74
+
75
+ def add_intrinsics
76
+ boolean_operator('<', :lt)
77
+ boolean_operator('<=', :le)
78
+ boolean_operator('==', :eq)
79
+ boolean_operator('!=', :ne)
80
+ boolean_operator('>=', :ge)
81
+ boolean_operator('>', :gt)
82
+ math_operator('+', :add)
83
+ math_operator('-', :sub)
84
+ math_operator('*', :mul)
85
+ math_operator('/', :div)
86
+ math_operator('%', :rem)
87
+ unary_operator('-@', :neg)
88
+ unary_operator('+@', nil)
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,29 @@
1
+ require 'duby'
2
+ module Duby
3
+ class NbCompiler
4
+ include org.jruby.duby.DubyCompiler
5
+
6
+ class ParseResult
7
+ ParseError = org.jruby.duby.ParseError
8
+
9
+ include org.jruby.duby.ParseResult
10
+
11
+ attr_reader :ast, :errors
12
+ def initialize(ast, errors)
13
+ @ast = ast
14
+ parse_errors = errors.map do |error|
15
+ ParseError.new(error.message, error.position)
16
+ end
17
+ @errors = parse_errors.to_java(ParseError)
18
+ end
19
+ end
20
+
21
+ def parse(text)
22
+ Duby::AST.type_factory = Duby::JVM::Types::TypeFactory.new
23
+ ast = Duby::AST.parse_ruby(text)
24
+ transformer = Duby::Transform::Transformer.new
25
+ return ParseResult.new(
26
+ transformer.transform(ast, nil), transformer.errors)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,845 @@
1
+ require 'compiler/builder'
2
+ require 'duby/typer'
3
+ require 'duby/signature'
4
+ require 'duby/mapper'
5
+ require 'duby/declaration'
6
+ require 'jruby'
7
+
8
+ # I don't like these at top-level, but reopened Java classes have trouble with const lookup
9
+ def log(str)
10
+ puts str if $VERBOSE
11
+ end
12
+
13
+ class CompileError < Exception
14
+ def initialize(position, message)
15
+ full_message = "Compile error at #{position.file}:#{position.start_line}: #{message}"
16
+ super(full_message)
17
+ end
18
+ end
19
+
20
+ module Compiler
21
+ module PrimitiveRuby
22
+ JObject = java.lang.Object.java_class
23
+ JClass = java.lang.Class.java_class
24
+ JString = java.lang.String.java_class
25
+ Void = java.lang.Void::TYPE
26
+ System = java.lang.System.java_class
27
+ PrintStream = java.io.PrintStream.java_class
28
+ JInteger = java.lang.Integer.java_class
29
+ Jbyte = Java::byte.java_class
30
+ Jchar = Java::char.java_class
31
+ Jshort = Java::short.java_class
32
+ Jint = Java::int.java_class
33
+ Jlong = Java::long.java_class
34
+ Jfloat = Java::float.java_class
35
+ Jdouble = Java::double.java_class
36
+ Jboolean = Java::boolean.java_class
37
+ JavaClass = Java::JavaClass
38
+
39
+ # reload
40
+ module Java::OrgJrubyAst
41
+ class Node
42
+ def compile(builder)
43
+ # default behavior is to raise, to expose missing nodes
44
+ raise CompileError.new(position, "Unsupported syntax: #{self}")
45
+ end
46
+ end
47
+
48
+ class ArgsNode
49
+ def compile(builder)
50
+ raise("PRuby only supports normal args") if opt_args || rest_arg != -1 || block_arg_node
51
+ return unless args
52
+ args.child_nodes.each do |arg|
53
+ builder.local(arg.name)
54
+ end
55
+ end
56
+ end
57
+
58
+ class ArrayNode
59
+ def compile(builder)
60
+ # not implemented
61
+ raise
62
+ end
63
+ end
64
+
65
+ class BeginNode
66
+ def compile(builder)
67
+ body_node.compile(builder)
68
+ end
69
+ end
70
+
71
+ class BlockNode
72
+ def compile(builder)
73
+ size = child_nodes.size
74
+ if size == 0
75
+ case type
76
+ when Jint
77
+ builder.iconst_0
78
+ else
79
+ builder.aconst_null
80
+ end
81
+ else
82
+ i = 0
83
+ while i < size
84
+ node = child_nodes.get(i)
85
+ node = node.next_node while NewlineNode === node
86
+ next unless node
87
+
88
+ builder.line node.position.start_line + 1
89
+
90
+ node.compile(builder)
91
+
92
+ if i + 1 < size
93
+ builder.pop if builder.method?
94
+ end
95
+
96
+ i += 1
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ class ClassNode
103
+ def compile(builder)
104
+ cb = builder.public_class(cpath.name)
105
+ body_node.compile(cb)
106
+ end
107
+ end
108
+
109
+ class CallNode
110
+ def compile(builder)
111
+ receiver_type = receiver_node.type(builder)
112
+
113
+ if receiver_type.primitive?
114
+ # we're performing an operation against a primitive, map it accordingly
115
+ log "Compiling #{name} at #{position.start_line} as primitive op"
116
+ compile_primitive(receiver_type, builder)
117
+ elsif receiver_type.array?
118
+ log "Compiling #{name} at #{position.start_line} as array op"
119
+ compile_array(receiver_type, builder)
120
+ else
121
+ case name
122
+ when "new"
123
+ log "Compiling #{name} at #{position.start_line} as object instantiation"
124
+ compile_new(receiver_type, builder)
125
+ else
126
+ log "Compiling #{name} at #{position.start_line} as call"
127
+ compile_call(receiver_type, builder)
128
+ end
129
+ end
130
+ end
131
+
132
+ def compile_call(receiver_type, builder)
133
+ case receiver_node
134
+ when ConstNode
135
+ # static call
136
+ static = true
137
+ else
138
+ receiver_node.compile(builder)
139
+ end
140
+
141
+ # I removed this because inference is working...but will it be needed under some circumstances?
142
+ # # inefficient to cast every time; better inference will help
143
+ # builder.checkcast(receiver_type)
144
+
145
+ compile_args(builder)
146
+
147
+ if static
148
+ builder.invokestatic receiver_type, mapped_name(builder), signature(builder)
149
+ else
150
+ if (receiver_type.interface?)
151
+ builder.invokeinterface receiver_type, mapped_name(builder), signature(builder)
152
+ else
153
+ builder.invokevirtual receiver_type, mapped_name(builder), signature(builder)
154
+ end
155
+ end
156
+ end
157
+
158
+ def compile_args(builder)
159
+ args_list = args_node.child_nodes.to_a
160
+ args_list.each_index do |idx|
161
+ node = args_list[idx]
162
+ node.compile(builder)
163
+ end
164
+ end
165
+
166
+ def compile_primitive(type, builder)
167
+ receiver_node.compile(builder)
168
+
169
+ if !args_node
170
+ case type
171
+ when Jboolean, Jbyte, Jshort, Jchar
172
+ # TODO: cast and do the same as int
173
+ raise CompileError.new(position, "Unary primitive operations on #{type} not supported")
174
+ when Jint
175
+ case name
176
+ when "-@"
177
+ builder.ineg
178
+ when "+@"
179
+ # do nothing
180
+ else
181
+ raise CompileError.new(position, "Primitive int operation #{name} not supported")
182
+ end
183
+ when Jlong
184
+ case name
185
+ when "-@"
186
+ builder.lneg
187
+ when "+@"
188
+ # do nothing
189
+ else
190
+ raise CompileError.new(position, "Primitive long operation #{name} not supported")
191
+ end
192
+ when Jfloat
193
+ case name
194
+ when "-@"
195
+ builder.fneg
196
+ when "+@"
197
+ # do nothing
198
+ else
199
+ raise CompileError.new(position, "Primitive float operation #{name} not supported")
200
+ end
201
+ when Jdouble
202
+ case name
203
+ when "-@"
204
+ builder.dneg
205
+ when "+@"
206
+ # do nothing
207
+ else
208
+ raise CompileError.new(position, "Primitive double operation #{name} not supported")
209
+ end
210
+ else
211
+ raise CompileError.new(position, "Unary primitive operations on #{type} not supported")
212
+ end
213
+ elsif args_node.size != 1
214
+ raise CompileError.new(position, "Binary primitive operations require exactly one argument")
215
+ else
216
+ node = args_node.get(0)
217
+ # TODO: check or cast types according to receiver's type
218
+ node.compile(builder)
219
+
220
+ case type
221
+ when Jboolean, Jbyte, Jshort, Jchar
222
+ # TODO: cast and do the same as int
223
+ raise CompileError.new(position, "Binary primitive operations on #{type} not supported")
224
+ when Jint
225
+ case name
226
+ when "+"
227
+ builder.iadd
228
+ when "-"
229
+ builder.isub
230
+ when "/"
231
+ builder.idiv
232
+ when "*"
233
+ builder.imul
234
+ when "&"
235
+ builder.iand
236
+ when "|"
237
+ builder.ior
238
+ when "^"
239
+ builder.ixor
240
+ else
241
+ raise CompileError.new(position, "Primitive int operation #{name} not supported")
242
+ end
243
+ when Jlong
244
+ case name
245
+ when "+"
246
+ builder.ladd
247
+ when "-"
248
+ builder.lsub
249
+ when "/"
250
+ builder.ldiv
251
+ when "*"
252
+ builder.lmul
253
+ when "&"
254
+ builder.land
255
+ when "|"
256
+ builder.lor
257
+ when "^"
258
+ builder.lxor
259
+ else
260
+ raise CompileError.new(position, "Primitive long operation #{name} not supported")
261
+ end
262
+ when Jfloat
263
+ case name
264
+ when "+"
265
+ builder.fadd
266
+ when "-"
267
+ builder.fsub
268
+ when "/"
269
+ builder.fdiv
270
+ when "*"
271
+ builder.fmul
272
+ else
273
+ raise CompileError.new(position, "Primitive float operation #{name} not supported")
274
+ end
275
+ when Jdouble
276
+ case name
277
+ when "+"
278
+ builder.dadd
279
+ when "-"
280
+ builder.dsub
281
+ when "/"
282
+ builder.ddiv
283
+ when "*"
284
+ builder.dmul
285
+ else
286
+ raise CompileError.new(position, "Primitive double operation #{name} not supported")
287
+ end
288
+ else
289
+ raise CompileError.new(position, "Primitive #{type} operations not supported")
290
+ end
291
+ end
292
+ end
293
+
294
+ def compile_array(type, builder)
295
+ receiver_node.compile(builder)
296
+
297
+ case name
298
+ when "length"
299
+ if args_node
300
+ raise CompileError.new(position, "Array length does not take an argument")
301
+ end
302
+
303
+ builder.arraylength
304
+ when "[]"
305
+ if !args_node || args_node.size != 1
306
+ raise CompileError.new(position, "Array accessignment must have exactly one argument")
307
+ end
308
+
309
+ node = args_node.get(0)
310
+ # TODO: check or cast to int for indexing
311
+ node.compile(builder)
312
+
313
+ if type.component_type.primitive?
314
+ case type.component_type
315
+ when Jboolean, Jbyte
316
+ builder.baload
317
+ when Jchar
318
+ builder.caload
319
+ when Jshort
320
+ builder.saload
321
+ when Jint
322
+ builder.iaload
323
+ when Jlong
324
+ builder.laload
325
+ when Jfloat
326
+ builder.faload
327
+ when Jdouble
328
+ builder.daload
329
+ end
330
+ else
331
+ builder.aaload
332
+ end
333
+ when "[]="
334
+ if !args_node || args_node.size != 2
335
+ raise CompileError.new(position, "Array assignment must have exactly two arguments")
336
+ end
337
+
338
+ # TODO: check or cast to int for indexing
339
+ args_node.get(0).compile(builder)
340
+ # TODO: check type matches?
341
+ args_node.get(1).compile(builder)
342
+
343
+ builder.aastore
344
+ else
345
+ raise CompileError.new(position, "Array operation #{name} not supported")
346
+ end
347
+ end
348
+
349
+ def compile_new(type, builder)
350
+ builder.new type
351
+ builder.dup
352
+
353
+ compile_args(builder)
354
+
355
+ builder.invokespecial type, mapped_name(builder), signature(builder)
356
+ end
357
+ end
358
+
359
+ class Colon2Node
360
+ end
361
+
362
+ class ConstNode
363
+ end
364
+
365
+ class DefnNode
366
+ def compile(builder)
367
+ first_real_node = body_node
368
+ first_real_node = body_node.child_nodes[0] if BlockNode === body_node
369
+ while NewlineNode === first_real_node
370
+ first_real_node = first_real_node.next_node
371
+ end
372
+
373
+ # determine signature from declaration line
374
+ signature = first_real_node.signature(builder) if HashNode === first_real_node
375
+
376
+ signature ||= [Void]
377
+
378
+ log "Compiling instance method for #{name} as #{signature.join(',')}"
379
+ builder.method(mapped_name(builder), *signature) do |method|
380
+ # Run through any type declarations first
381
+ first_real_node.declare_types(method) if HashNode === first_real_node
382
+
383
+ # declare args that may not have been declared already
384
+ args_node.compile(method)
385
+
386
+ body_node.compile(method) if body_node
387
+
388
+ # Expectation is that last element leaves the right type on stack
389
+ if signature[0].primitive?
390
+ case signature[0]
391
+ when Void
392
+ method.returnvoid
393
+ when Jboolean, Jbyte
394
+ method.breturn
395
+ when Jchar
396
+ method.creturn
397
+ when Jshort
398
+ method.sreturn
399
+ when Jint
400
+ method.ireturn
401
+ when Jlong
402
+ method.lreturn
403
+ when Jfloat
404
+ method.freturn
405
+ when Jdouble
406
+ method.dreturn
407
+ else
408
+ raise CompileError.new(position, "Unknown return type: #{signature[0]}")
409
+ end
410
+ else
411
+ method.areturn
412
+ end
413
+ end
414
+ end
415
+ end
416
+
417
+ class DefsNode
418
+ def compile(builder)
419
+ first_real_node = body_node
420
+ first_real_node = body_node.child_nodes[0] if BlockNode === body_node
421
+ while NewlineNode === first_real_node
422
+ first_real_node = first_real_node.next_node
423
+ end
424
+
425
+ # determine signature from declaration line
426
+ signature = first_real_node.signature(builder) if HashNode === first_real_node
427
+
428
+ signature ||= [Void]
429
+
430
+ log "Compiling static method for #{name} as #{signature.join(',')}"
431
+ builder.static_method(name, *signature) do |method|
432
+ # Run through any type declarations first
433
+ first_real_node.declare_types(method) if HashNode === first_real_node
434
+
435
+ # declare args that may not have been declared already
436
+ args_node.compile(method)
437
+
438
+ body_node.compile(method) if body_node
439
+
440
+ # Expectation is that last element leaves the right type on stack
441
+ if signature[0].primitive?
442
+ case signature[0]
443
+ when Void
444
+ method.returnvoid
445
+ when Jboolean, Jbyte
446
+ method.breturn
447
+ when Jchar
448
+ method.creturn
449
+ when Jshort
450
+ method.sreturn
451
+ when Jint
452
+ method.ireturn
453
+ when Jlong
454
+ method.lreturn
455
+ when Jfloat
456
+ method.freturn
457
+ when Jdouble
458
+ method.dreturn
459
+ else
460
+ raise CompileError.new(position, "Unknown return type: #{signature[0]}")
461
+ end
462
+ else
463
+ method.areturn
464
+ end
465
+ end
466
+ end
467
+ end
468
+
469
+ class FCallNode
470
+ def compile(builder)
471
+ case name
472
+ when "puts"
473
+ compile_puts(builder)
474
+ when "import"
475
+ compile_import(builder)
476
+ else
477
+ if (builder.static)
478
+ arg_types = []
479
+ args_node.child_nodes.each do |node|
480
+ node.compile(builder)
481
+ arg_types << node.type(builder)
482
+ end
483
+
484
+ builder.invokestatic builder.this, name, builder.static_signature(name, arg_types)
485
+ else
486
+ builder.aload 0
487
+ arg_types = []
488
+ args_node.child_nodes.each do |node|
489
+ node.compile(builder)
490
+ arg_types << node.type(builder)
491
+ end
492
+
493
+ builder.invokevirtual builder.this, name, builder.instance_signature(name, arg_types)
494
+ end
495
+ end
496
+ end
497
+
498
+ def compile_puts(builder)
499
+ log "Compiling special #{name} at #{position.start_line}"
500
+ builder.getstatic System, "out", [PrintStream]
501
+
502
+ arg_types = []
503
+ args_node.child_nodes.each do |node|
504
+ node.compile(builder)
505
+ arg_types << node.type(builder)
506
+ end
507
+
508
+ builder.invokevirtual PrintStream, "println", special_signature(PrintStream, builder)
509
+ builder.aconst_null
510
+ end
511
+
512
+ def compile_import(builder)
513
+ log "Compiling import at #{position.start_line}"
514
+ args_node.child_nodes.each do |node|
515
+ case node
516
+ when StrNode
517
+ builder.import(node.value)
518
+ else
519
+ raise CompileError.new(position, "Imports only allow strings right now")
520
+ end
521
+ end
522
+ end
523
+ end
524
+
525
+ class FixnumNode
526
+ def compile(builder)
527
+ builder.ldc_int(value)
528
+ end
529
+ end
530
+
531
+ class FloatNode
532
+ def compile(builder)
533
+ builder.ldc_float(value)
534
+ end
535
+ end
536
+
537
+ class HashNode
538
+ def compile(builder)
539
+ @declared ||= false
540
+
541
+ if @declared
542
+ # hash was used for type declaration, so we just push a null to skip it
543
+ # TODO: it would be nice if we could just skip the null too, but BlockNode wants to pop it
544
+ builder.aconst_null
545
+ else
546
+ raise CompileError.new(position, "Literal hash syntax not yet supported")
547
+ end
548
+ end
549
+ end
550
+
551
+ class IfNode
552
+ def compile(builder)
553
+ else_lbl = builder.label
554
+ done = builder.label
555
+ condition = self.condition
556
+ condition = condition.next_node while NewlineNode === condition
557
+
558
+ case condition
559
+ when CallNode
560
+ args = condition.args_node
561
+ receiver_type = condition.receiver_node.type(builder)
562
+
563
+ if receiver_type.primitive?
564
+ case condition.name
565
+ when "<"
566
+ raise CompileError.new(position, "Primitive < must have exactly one argument") if !args || args.size != 1
567
+
568
+ condition.receiver_node.compile(builder)
569
+ args.get(0).compile(builder)
570
+
571
+ # >= is else for <
572
+ case receiver_type
573
+ when Jint
574
+ builder.if_icmpge(else_lbl)
575
+ else
576
+ raise CompileError.new(position, "Primitive < is only supported for int")
577
+ end
578
+ when ">"
579
+ raise CompileError.new(position, "Primitive > must have exactly one argument") if !args || args.size != 1
580
+
581
+ condition.receiver_node.compile(builder)
582
+ args.get(0).compile(builder)
583
+
584
+ # <= is else for >
585
+ case receiver_type
586
+ when Jint
587
+ builder.if_icmple(else_lbl)
588
+ else
589
+ raise CompileError.new(position, "Primitive > is only supported for int")
590
+ end
591
+ when "=="
592
+ raise CompileError.new(position, "Primitive == must have exactly one argument") if !args || args.size != 1
593
+
594
+ condition.receiver_node.compile(builder)
595
+ args.get(0).compile(builder)
596
+
597
+ # ne is else for ==
598
+ case receiver_type
599
+ when Jint
600
+ builder.if_icmpne(else_lbl)
601
+ else
602
+ raise CompileError.new(position, "Primitive == is only supported for int")
603
+ end
604
+ else
605
+ raise CompileError.new(position, "Conditional not supported: #{condition.inspect}")
606
+ end
607
+
608
+ then_body.compile(builder)
609
+ builder.goto(done)
610
+
611
+ else_lbl.set!
612
+ else_body.compile(builder)
613
+
614
+ done.set!
615
+ else
616
+ raise CompileError.new(position, "Conditional on non-primitives not supported: #{condition.inspect}")
617
+ end
618
+ else
619
+ raise CompileError.new(position, "Non-call conditional not supported: #{condition.inspect}")
620
+ end
621
+ end
622
+ end
623
+
624
+ class InstAsgnNode
625
+ def compile(builder)
626
+ builder.field(mapped_name(builder), value_node.type(builder))
627
+
628
+ # assignment consumes the value, so we dup it
629
+ # TODO inefficient if we don't need the result
630
+ value_node.compile(builder)
631
+ builder.dup
632
+
633
+ builder.putfield(mapped_name(builder))
634
+ end
635
+ end
636
+
637
+ class InstVarNode
638
+ def compile(builder)
639
+ builder.getfield(mapped_name(builder))
640
+ end
641
+ end
642
+
643
+ class LocalAsgnNode
644
+ def compile(builder)
645
+ local_index = builder.local(name, value_node.type(builder))
646
+
647
+ # assignment consumes a value, so we dup it
648
+ # TODO: inefficient if we don't actually need the result
649
+ value_node.compile(builder)
650
+ builder.dup
651
+
652
+ case type(builder)
653
+ when Jboolean
654
+ builder.bistore(local_index)
655
+ when Jint
656
+ builder.istore(local_index)
657
+ when Jlong
658
+ builder.lstore(local_index)
659
+ when Jfloat
660
+ builder.fstore(local_index)
661
+ when Jdouble
662
+ builder.dstore(local_index)
663
+ else
664
+ builder.astore(local_index)
665
+ end
666
+ end
667
+ end
668
+
669
+ class LocalVarNode
670
+ def compile(builder)
671
+ local_index = builder.local(name)
672
+ case type(builder)
673
+ when Jboolean
674
+ builder.biload(local_index)
675
+ when Jint
676
+ builder.iload(local_index)
677
+ when Jlong
678
+ builder.lload(local_index)
679
+ when Jfloat
680
+ builder.fload(local_index)
681
+ when Jdouble
682
+ builder.dload(local_index)
683
+ else
684
+ builder.aload(local_index)
685
+ end
686
+ end
687
+ end
688
+
689
+ class ModuleNode
690
+ def compile(builder)
691
+ builder.package(cpath.name) {
692
+ body_node.compile(builder)
693
+ }
694
+ end
695
+ end
696
+
697
+ class NewlineNode
698
+ def compile(builder)
699
+ builder.line position.start_line
700
+ next_node.compile(builder)
701
+ end
702
+ end
703
+
704
+ class ReturnNode
705
+ def compile(builder)
706
+ value_node.compile(builder)
707
+ builder.areturn
708
+ end
709
+ end
710
+
711
+ class RootNode
712
+ def compile(builder)
713
+ # builder is class builder
714
+
715
+ if body_node
716
+ body_node.compile(builder)
717
+ end
718
+ end
719
+ end
720
+
721
+ class SelfNode
722
+ def compile(builder)
723
+ builder.local("this")
724
+ end
725
+ end
726
+
727
+ class StrNode
728
+ def compile(builder)
729
+ builder.ldc value
730
+ end
731
+ end
732
+
733
+ class SymbolNode
734
+ end
735
+
736
+ class VCallNode
737
+ def compile(builder)
738
+ if builder.static
739
+ builder.invokestatic builder.this, name, builder.static_signature(name, [])
740
+ else
741
+ builder.aload 0
742
+
743
+ builder.invokevirtual builder.this, name, builder.instance_signature(name, [])
744
+ end
745
+ end
746
+ end
747
+
748
+ class WhileNode
749
+ def compile(builder)
750
+ begin_lbl = builder.label
751
+ end_lbl = builder.label
752
+ cond_lbl = builder.label
753
+
754
+ case body_node.type(builder)
755
+ when Jint
756
+ builder.iconst_0
757
+ else
758
+ builder.aconst_null
759
+ end
760
+
761
+ if evaluate_at_start
762
+ builder.goto cond_lbl
763
+ end
764
+
765
+ begin_lbl.set!
766
+ builder.pop
767
+ body_node.compile(builder)
768
+
769
+ cond_lbl.set!
770
+ compile_condition(builder, begin_lbl)
771
+ end_lbl.set!
772
+ end
773
+
774
+ def compile_condition(builder, begin_lbl)
775
+ condition = condition_node
776
+ condition = condition.next_node while NewlineNode === condition
777
+
778
+ case condition
779
+ when CallNode
780
+ args = condition.args_node
781
+ receiver_type = condition.receiver_node.type(builder)
782
+
783
+ if receiver_type.primitive?
784
+ case condition.name
785
+ when "<"
786
+ raise CompileError.new(position, "Primitive < must have exactly one argument") if !args || args.size != 1
787
+
788
+ condition.receiver_node.compile(builder)
789
+ args.get(0).compile(builder)
790
+
791
+ case receiver_type
792
+ when Jint
793
+ builder.if_icmplt(begin_lbl)
794
+ else
795
+ raise CompileError.new(position, "Primitive < is only supported for int")
796
+ end
797
+ when ">"
798
+ raise CompileError.new(position, "Primitive > must have exactly one argument") if !args || args.size != 1
799
+
800
+ condition.receiver_node.compile(builder)
801
+ args.get(0).compile(builder)
802
+
803
+ case receiver_type
804
+ when Jint
805
+ builder.if_icmpgt(begin_lbl)
806
+ else
807
+ raise CompileError.new(position, "Primitive < is only supported for int")
808
+ end
809
+ else
810
+ raise CompileError.new(position, "Conditional not supported: #{condition.inspect}")
811
+ end
812
+ else
813
+ raise CompileError.new(position, "Conditional on non-primitives not supported: #{condition.inspect}")
814
+ end
815
+ else
816
+ raise CompileError.new(position, "Non-call conditional not supported: #{condition.inspect}")
817
+ end
818
+ end
819
+ end
820
+ end
821
+ end
822
+ end
823
+
824
+ if $0 == __FILE__
825
+ n = JRuby.parse(File.read(ARGV[0]), ARGV[0])
826
+ compiler = JVMScript::FileBuilder.new(ARGV[0])
827
+ begin
828
+ n.compile(compiler)
829
+
830
+ compiler.generate do |filename, builder|
831
+ puts "Compiling #{builder.class_name.gsub('/', '.')}.class"
832
+
833
+ class_name = builder.class_name
834
+ if class_name.rindex('/')
835
+ dir = class_name[0..class_name.rindex('/')]
836
+ FileUtils.mkdir_p(dir)
837
+ end
838
+
839
+ File.open(filename, 'w') {|file| file.write(builder.generate)}
840
+ end
841
+ rescue CompileError => e
842
+ puts e
843
+ puts e.backtrace
844
+ end
845
+ end