duby 0.0.2-java
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 +8 -0
- data/README.txt +39 -0
- data/Rakefile +13 -0
- data/bin/duby +9 -0
- data/bin/dubyc +9 -0
- data/bin/dubyp +9 -0
- data/examples/README +16 -0
- data/examples/appengine/Rakefile +72 -0
- data/examples/appengine/Readme +27 -0
- data/examples/appengine/config.ru +7 -0
- data/examples/appengine/lib/duby/plugin/datastore.rb +171 -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/appengine/src/com/ribrdb/list.dhtml +15 -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/fractal.duby +57 -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/examples/test.edb +9 -0
- data/javalib/JRubyParser.jar +0 -0
- data/lib/duby.rb +168 -0
- data/lib/duby/ast.rb +386 -0
- data/lib/duby/ast/call.rb +145 -0
- data/lib/duby/ast/class.rb +154 -0
- data/lib/duby/ast/flow.rb +332 -0
- data/lib/duby/ast/intrinsics.rb +56 -0
- data/lib/duby/ast/literal.rb +97 -0
- data/lib/duby/ast/local.rb +92 -0
- data/lib/duby/ast/method.rb +244 -0
- data/lib/duby/ast/structure.rb +62 -0
- data/lib/duby/ast/type.rb +93 -0
- data/lib/duby/c/compiler.rb +134 -0
- data/lib/duby/compiler.rb +282 -0
- data/lib/duby/jvm/compiler.rb +766 -0
- data/lib/duby/jvm/method_lookup.rb +193 -0
- data/lib/duby/jvm/source_compiler.rb +605 -0
- data/lib/duby/jvm/source_generator/builder.rb +387 -0
- data/lib/duby/jvm/source_generator/loops.rb +110 -0
- data/lib/duby/jvm/source_generator/precompile.rb +170 -0
- data/lib/duby/jvm/source_generator/typer.rb +11 -0
- data/lib/duby/jvm/typer.rb +131 -0
- data/lib/duby/jvm/types.rb +331 -0
- data/lib/duby/jvm/types/basic_types.rb +19 -0
- data/lib/duby/jvm/types/boolean.rb +11 -0
- data/lib/duby/jvm/types/enumerable.rb +63 -0
- data/lib/duby/jvm/types/factory.rb +155 -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 +230 -0
- data/lib/duby/jvm/types/literals.rb +82 -0
- data/lib/duby/jvm/types/methods.rb +381 -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 +1028 -0
- data/lib/duby/typer.rb +369 -0
- data/test/TestUser.class +0 -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 +58 -0
- data/test/test_jvm_compiler.rb +1770 -0
- data/test/test_math_plugin.rb +87 -0
- data/test/test_typer.rb +246 -0
- metadata +156 -0
@@ -0,0 +1,193 @@
|
|
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
|
+
# exact args failed, do a deeper search
|
37
|
+
log "No exact match for #{mapped_type.name}.#{name}(#{mapped_params.map(&:name)})"
|
38
|
+
|
39
|
+
method = find_jls(mapped_type, name, mapped_params, meta, constructor)
|
40
|
+
|
41
|
+
unless method
|
42
|
+
log "Failed to locate method #{mapped_type.name}.#{name}(#{mapped_params.map(&:name)})"
|
43
|
+
return nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
log "Found method #{method.declaring_class.name}.#{name}(#{method.argument_types.map(&:name)}) from #{mapped_type.name}"
|
48
|
+
return method
|
49
|
+
end
|
50
|
+
|
51
|
+
def find_jls(mapped_type, name, mapped_params, meta, constructor)
|
52
|
+
# mapped_type = jvm_type(mapped_type)
|
53
|
+
# mapped_params = convert_params(mapped_params)
|
54
|
+
if constructor
|
55
|
+
all_methods = mapped_type.unmeta.declared_constructors
|
56
|
+
by_name = all_methods
|
57
|
+
elsif meta
|
58
|
+
all_methods = mapped_type.declared_class_methods
|
59
|
+
else
|
60
|
+
all_methods = []
|
61
|
+
cls = mapped_type
|
62
|
+
while cls
|
63
|
+
all_methods += cls.declared_instance_methods
|
64
|
+
cls = cls.superclass
|
65
|
+
end
|
66
|
+
end
|
67
|
+
# filter by name, if we haven't already got a by_name list
|
68
|
+
by_name ||= all_methods.select {|m| m.name == name && mapped_params.size <= m.argument_types.size}
|
69
|
+
# filter by arity
|
70
|
+
by_name_and_arity = by_name.select {|m| m.argument_types.size == mapped_params.size}
|
71
|
+
|
72
|
+
phase1_methods = phase1(mapped_params, by_name_and_arity)
|
73
|
+
|
74
|
+
if phase1_methods.size > 1
|
75
|
+
raise "Ambiguous targets invoking #{mapped_type}.#{name}:\n#{phase1_methods}"
|
76
|
+
end
|
77
|
+
|
78
|
+
phase1_methods[0] ||
|
79
|
+
phase2(mapped_params, by_name) ||
|
80
|
+
phase3(mapped_params, by_name)
|
81
|
+
end
|
82
|
+
|
83
|
+
def phase1(mapped_params, potentials)
|
84
|
+
log "Beginning JLS phase 1 search with params (#{mapped_params.map(&:name)})"
|
85
|
+
|
86
|
+
# cycle through methods looking for more specific matches; gather matches of equal specificity
|
87
|
+
methods = potentials.inject([]) do |currents, potential|
|
88
|
+
method_params = potential.argument_types
|
89
|
+
|
90
|
+
# exact match always wins; duplicates not possible
|
91
|
+
if each_is_exact(mapped_params, method_params)
|
92
|
+
return [potential]
|
93
|
+
end
|
94
|
+
|
95
|
+
# otherwise, check for potential match and compare to current
|
96
|
+
# TODO: missing ambiguity check; picks last method of equal specificity
|
97
|
+
if each_is_exact_or_subtype_or_convertible(mapped_params, method_params)
|
98
|
+
if currents.size > 0
|
99
|
+
if is_more_specific?(potential.argument_types, currents[0].argument_types)
|
100
|
+
# potential is better, dump all currents
|
101
|
+
currents = [potential]
|
102
|
+
elsif is_more_specific?(currents[0].argument_types, potential.argument_types)
|
103
|
+
# currents are better, try next potential
|
104
|
+
#next
|
105
|
+
else
|
106
|
+
# equal specificity, append to currents
|
107
|
+
currents << potential
|
108
|
+
end
|
109
|
+
else
|
110
|
+
# no previous matches, use potential
|
111
|
+
currents = [potential]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
currents
|
116
|
+
end
|
117
|
+
|
118
|
+
methods
|
119
|
+
end
|
120
|
+
|
121
|
+
def is_more_specific?(potential, current)
|
122
|
+
each_is_exact_or_subtype_or_convertible(potential, current)
|
123
|
+
end
|
124
|
+
|
125
|
+
def phase2(mapped_params, potentials)
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
|
129
|
+
def phase3(mapped_params, potentials)
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
|
133
|
+
def each_is_exact(incoming, target)
|
134
|
+
incoming.each_with_index do |in_type, i|
|
135
|
+
target_type = target[i]
|
136
|
+
|
137
|
+
# exact match
|
138
|
+
return false unless target_type == in_type
|
139
|
+
end
|
140
|
+
return true
|
141
|
+
end
|
142
|
+
|
143
|
+
def each_is_exact_or_subtype_or_convertible(incoming, target)
|
144
|
+
incoming.each_with_index do |in_type, i|
|
145
|
+
target_type = target[i]
|
146
|
+
|
147
|
+
# exact match
|
148
|
+
next if target_type == in_type
|
149
|
+
|
150
|
+
# primitive is safely convertible
|
151
|
+
if target_type.primitive?
|
152
|
+
if in_type.primitive?
|
153
|
+
next if primitive_convertible? in_type, target_type
|
154
|
+
end
|
155
|
+
return false
|
156
|
+
end
|
157
|
+
|
158
|
+
# object type is assignable
|
159
|
+
return false unless target_type.assignable_from? in_type
|
160
|
+
end
|
161
|
+
return true
|
162
|
+
end
|
163
|
+
|
164
|
+
BOOLEAN = Java::boolean.java_class
|
165
|
+
BYTE = Java::byte.java_class
|
166
|
+
SHORT = Java::short.java_class
|
167
|
+
CHAR = Java::char.java_class
|
168
|
+
INT = Java::int.java_class
|
169
|
+
LONG = Java::long.java_class
|
170
|
+
FLOAT = Java::float.java_class
|
171
|
+
DOUBLE = Java::double.java_class
|
172
|
+
|
173
|
+
PrimitiveConversions = {
|
174
|
+
BOOLEAN => [BOOLEAN],
|
175
|
+
BYTE => [BYTE, SHORT, CHAR, INT, LONG, FLOAT, DOUBLE],
|
176
|
+
SHORT => [SHORT, INT, LONG, FLOAT, DOUBLE],
|
177
|
+
CHAR => [CHAR, INT, LONG, FLOAT, DOUBLE],
|
178
|
+
INT => [INT, LONG, FLOAT, DOUBLE],
|
179
|
+
LONG => [LONG, DOUBLE],
|
180
|
+
FLOAT => [FLOAT, DOUBLE],
|
181
|
+
DOUBLE => [DOUBLE]
|
182
|
+
}
|
183
|
+
|
184
|
+
def primitive_convertible?(in_type, target_type)
|
185
|
+
if PrimitiveConversions.include? in_type
|
186
|
+
PrimitiveConversions[in_type].include?(target_type)
|
187
|
+
else
|
188
|
+
in_type.convertible_to?(target_type)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,605 @@
|
|
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
|
+
@type = AST::type(classname)
|
43
|
+
end
|
44
|
+
|
45
|
+
def toplevel_class
|
46
|
+
@class = @type.define(@file)
|
47
|
+
end
|
48
|
+
|
49
|
+
def generate(&block)
|
50
|
+
log "Generating source files..."
|
51
|
+
@file.generate do |filename, builder|
|
52
|
+
log " #{builder.class_name}"
|
53
|
+
if block_given?
|
54
|
+
yield filename, builder
|
55
|
+
else
|
56
|
+
File.open(filename, 'w') {|f| f.write(builder.generate)}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
log "...done!"
|
60
|
+
end
|
61
|
+
|
62
|
+
def define_main(body)
|
63
|
+
if body.class != AST::ClassDefinition
|
64
|
+
@class = @type.define(@file)
|
65
|
+
with :method => @class.main do
|
66
|
+
log "Starting main method"
|
67
|
+
|
68
|
+
@method.start
|
69
|
+
|
70
|
+
body.compile(self, false)
|
71
|
+
|
72
|
+
@method.stop
|
73
|
+
end
|
74
|
+
@class.stop
|
75
|
+
else
|
76
|
+
body.compile(self, false)
|
77
|
+
end
|
78
|
+
|
79
|
+
log "Main method complete!"
|
80
|
+
end
|
81
|
+
|
82
|
+
def define_method(node)
|
83
|
+
name, signature, args = node.name, node.signature, node.arguments.args
|
84
|
+
args ||= []
|
85
|
+
return_type = signature[:return]
|
86
|
+
exceptions = signature[:throws] || []
|
87
|
+
with :static => @static || node.static? do
|
88
|
+
if @static
|
89
|
+
method = @class.public_static_method(name.to_s, return_type, exceptions, *args)
|
90
|
+
else
|
91
|
+
method = @class.public_method(name.to_s, return_type, exceptions, *args)
|
92
|
+
end
|
93
|
+
|
94
|
+
with :method => method do
|
95
|
+
log "Starting new method #{name}"
|
96
|
+
@method.annotate(node.annotations)
|
97
|
+
@method.start
|
98
|
+
|
99
|
+
unless @method.type.nil? || @method.type.void?
|
100
|
+
self.return(ImplicitReturn.new(node.body))
|
101
|
+
else
|
102
|
+
node.body.compile(self, false) if node.body
|
103
|
+
end
|
104
|
+
|
105
|
+
log "Method #{name} complete!"
|
106
|
+
@method.stop
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def constructor(node)
|
112
|
+
args = node.arguments.args || []
|
113
|
+
exceptions = node.signature[:throws] || []
|
114
|
+
method = @class.public_constructor(exceptions, *args)
|
115
|
+
with :method => method do
|
116
|
+
@method.annotate(node.annotations)
|
117
|
+
@method.start
|
118
|
+
if node.delegate_args
|
119
|
+
delegate = if node.calls_super
|
120
|
+
"super"
|
121
|
+
else
|
122
|
+
"this"
|
123
|
+
end
|
124
|
+
method.print "#{delegate}("
|
125
|
+
node.delegate_args.each_with_index do |arg, index|
|
126
|
+
method.print ', ' unless index == 0
|
127
|
+
raise "Invalid constructor argument #{arg}" unless arg.expr?(self)
|
128
|
+
arg.compile(self, true)
|
129
|
+
end
|
130
|
+
method.puts ");"
|
131
|
+
end
|
132
|
+
|
133
|
+
node.body.compile(self, false) if node.body
|
134
|
+
method.stop
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def return(node)
|
139
|
+
if @method.type.nil? || @method.type.void?
|
140
|
+
@method.puts 'return;'
|
141
|
+
return
|
142
|
+
end
|
143
|
+
if node.value.expr?(self)
|
144
|
+
@method.print 'return '
|
145
|
+
node.value.compile(self, true)
|
146
|
+
@method.puts ';'
|
147
|
+
else
|
148
|
+
store_value('return ', node.value)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def _raise(node)
|
153
|
+
if node.expr?(self)
|
154
|
+
@method.print 'throw '
|
155
|
+
node.compile(self, true)
|
156
|
+
@method.puts ';'
|
157
|
+
else
|
158
|
+
store_value('throw ', node)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def rescue(node, expression)
|
163
|
+
@method.block 'try' do
|
164
|
+
maybe_store(node.body, expression)
|
165
|
+
end
|
166
|
+
node.clauses.each do |clause|
|
167
|
+
clause.types.each do |type|
|
168
|
+
name = clause.name || 'tmp$ex'
|
169
|
+
@method.block "catch (#{type.to_source} #{name})" do
|
170
|
+
maybe_store(clause.body, expression)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def ensure(node, expression)
|
177
|
+
@method.block 'try' do
|
178
|
+
maybe_store(node.body, expression)
|
179
|
+
end
|
180
|
+
@method.block 'finally' do
|
181
|
+
node.clause.compile(self, false)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def line(num)
|
186
|
+
end
|
187
|
+
|
188
|
+
def declare_local(name, type)
|
189
|
+
@method.declare_local(type, name)
|
190
|
+
end
|
191
|
+
|
192
|
+
def declare_field(name, type, annotations)
|
193
|
+
@class.declare_field(name, type, @static, annotations)
|
194
|
+
end
|
195
|
+
|
196
|
+
def local(name, type)
|
197
|
+
@method.print name
|
198
|
+
end
|
199
|
+
|
200
|
+
def field(name, type, annotations)
|
201
|
+
name = name[1..-1]
|
202
|
+
declare_field(name, type, annotations)
|
203
|
+
@method.print "#{this}.#{name}"
|
204
|
+
end
|
205
|
+
|
206
|
+
def this
|
207
|
+
@static ? @class.class_name : 'this'
|
208
|
+
end
|
209
|
+
|
210
|
+
def local_assign(name, type, expression, value)
|
211
|
+
value = value.precompile(self)
|
212
|
+
if method.local?(name)
|
213
|
+
@method.print @lvalue if expression
|
214
|
+
@method.print "#{name} = "
|
215
|
+
value.compile(self, true)
|
216
|
+
@method.puts ';'
|
217
|
+
else
|
218
|
+
@method.declare_local(type, name) do
|
219
|
+
value.compile(self, true)
|
220
|
+
end
|
221
|
+
if expression
|
222
|
+
@method.puts "#{@lvalue}#{name};"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def field_declare(name, type, annotations)
|
228
|
+
name = name[1..-1]
|
229
|
+
declare_field(name, type, annotations)
|
230
|
+
end
|
231
|
+
|
232
|
+
def local_declare(name, type)
|
233
|
+
declare_local(name, type)
|
234
|
+
end
|
235
|
+
|
236
|
+
def field_assign(name, type, expression, value, annotations)
|
237
|
+
name = name[1..-1]
|
238
|
+
declare_field(name, type, annotations)
|
239
|
+
lvalue = "#{@lvalue if expression}#{this}.#{name} = "
|
240
|
+
store_value(lvalue, value)
|
241
|
+
end
|
242
|
+
|
243
|
+
def store_value(lvalue, value)
|
244
|
+
if value.is_a? String
|
245
|
+
@method.puts "#{lvalue}#{value};"
|
246
|
+
elsif value.expr?(self)
|
247
|
+
@method.print lvalue
|
248
|
+
value.compile(self, true)
|
249
|
+
@method.puts ';'
|
250
|
+
else
|
251
|
+
with :lvalue => lvalue do
|
252
|
+
value.compile(self, true)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def assign(name, value)
|
258
|
+
store_value("#{name} = ", value)
|
259
|
+
name
|
260
|
+
end
|
261
|
+
|
262
|
+
def maybe_store(value, expression)
|
263
|
+
if expression
|
264
|
+
store_value(@lvalue, value)
|
265
|
+
else
|
266
|
+
value.compile(self, false)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def body(body, expression)
|
271
|
+
# all except the last element in a body of code is treated as a statement
|
272
|
+
i, last = 0, body.children.size - 1
|
273
|
+
while i < last
|
274
|
+
body.children[i].compile(self, false)
|
275
|
+
i += 1
|
276
|
+
end
|
277
|
+
# last element is an expression only if the body is an expression
|
278
|
+
maybe_store(body.children[last], expression)
|
279
|
+
end
|
280
|
+
|
281
|
+
def branch_expression(node)
|
282
|
+
node.condition.compile(self, true)
|
283
|
+
@method.print ' ? ('
|
284
|
+
if node.body
|
285
|
+
node.body.compile(self, true)
|
286
|
+
else
|
287
|
+
@method.print @method.init_value(node.inferred_type)
|
288
|
+
end
|
289
|
+
@method.print ') : ('
|
290
|
+
if node.else
|
291
|
+
node.else.compile(self, true)
|
292
|
+
else
|
293
|
+
@method.print @method.init_value(node.inferred_type)
|
294
|
+
end
|
295
|
+
@method.print ')'
|
296
|
+
end
|
297
|
+
|
298
|
+
def branch(node, expression)
|
299
|
+
if expression && node.expr?(self)
|
300
|
+
return branch_expression(node)
|
301
|
+
end
|
302
|
+
predicate = node.condition.predicate.precompile(self)
|
303
|
+
@method.print 'if ('
|
304
|
+
predicate.compile(self, true)
|
305
|
+
@method.block ")" do
|
306
|
+
if node.body
|
307
|
+
maybe_store(node.body, expression)
|
308
|
+
elsif expression
|
309
|
+
store_value(@lvalue, @method.init_value(node.inferred_type))
|
310
|
+
end
|
311
|
+
end
|
312
|
+
if node.else || expression
|
313
|
+
@method.block 'else' do
|
314
|
+
if node.else
|
315
|
+
maybe_store(node.else, expression)
|
316
|
+
else
|
317
|
+
store_value(@lvalue, @method.init_value(node.inferred_type))
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
def loop(loop, expression)
|
324
|
+
if loop.redo? || loop.post || !loop.condition.predicate.expr?(self)
|
325
|
+
loop = ComplexWhileLoop.new(loop, self)
|
326
|
+
else
|
327
|
+
loop = SimpleWhileLoop.new(loop, self)
|
328
|
+
end
|
329
|
+
with(:loop => loop) do
|
330
|
+
loop.compile(expression)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
def expr?(target, params)
|
335
|
+
!([target] + params).any? {|x| x.kind_of? Duby::AST::TempValue}
|
336
|
+
end
|
337
|
+
|
338
|
+
def operator(target, op, params, expression)
|
339
|
+
simple = expr?(target, params)
|
340
|
+
if expression && !simple
|
341
|
+
@method.print @lvalue
|
342
|
+
end
|
343
|
+
if params.size == 0
|
344
|
+
# unary operator
|
345
|
+
op = op[0,1]
|
346
|
+
@method.print op
|
347
|
+
target.compile(self, true)
|
348
|
+
else
|
349
|
+
@method.print '('
|
350
|
+
other = params[0]
|
351
|
+
target.compile(self, true)
|
352
|
+
@method.print " #{op} "
|
353
|
+
other.compile(self, true)
|
354
|
+
@method.print ')'
|
355
|
+
end
|
356
|
+
unless expression && simple
|
357
|
+
@method.puts ';'
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def compile_args(call)
|
362
|
+
call.parameters.map do |param|
|
363
|
+
param.precompile(self)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def self_type
|
368
|
+
type = AST::type(@class.name.tr('/', '.'))
|
369
|
+
type = type.meta if @static
|
370
|
+
type
|
371
|
+
end
|
372
|
+
|
373
|
+
def super_call(call, expression)
|
374
|
+
super_method_call(this, call, compile_args(call), expression)
|
375
|
+
end
|
376
|
+
|
377
|
+
def self_call(call, expression)
|
378
|
+
if call.cast?
|
379
|
+
args = compile_args(call)
|
380
|
+
simple = call.expr?(self)
|
381
|
+
@method.print @lvalue if expression && !simple
|
382
|
+
@method.print "(#{call.inferred_type.name})("
|
383
|
+
args.each{|arg| arg.compile(self, true)}
|
384
|
+
@method.print ")"
|
385
|
+
@method.puts ';' unless simple && expression
|
386
|
+
else
|
387
|
+
method_call(this, call, compile_args(call), expression)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
def call(call, expression)
|
392
|
+
if Duby::AST::Constant === call.target
|
393
|
+
target = call.target.inferred_type.name
|
394
|
+
else
|
395
|
+
target = call.target.precompile(self)
|
396
|
+
end
|
397
|
+
params = compile_args(call)
|
398
|
+
|
399
|
+
if Operators.include? call.name
|
400
|
+
operator(target, call.name, params, expression)
|
401
|
+
elsif call.target.inferred_type.array? && ArrayOps.include?(call.name)
|
402
|
+
array_op(target, call.name, params, expression)
|
403
|
+
elsif call.name == 'nil?'
|
404
|
+
operator(target, '==', ['null'], expression)
|
405
|
+
else
|
406
|
+
method_call(target, call, params, expression)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
def array_op(target, name, args, expression)
|
411
|
+
simple = expr?(target, args)
|
412
|
+
index, value = args
|
413
|
+
if expression && !simple
|
414
|
+
@method.print @lvalue
|
415
|
+
end
|
416
|
+
target.compile(self, true)
|
417
|
+
if name == 'length'
|
418
|
+
@method.print '.length'
|
419
|
+
else
|
420
|
+
@method.print '['
|
421
|
+
index.compile(self, true)
|
422
|
+
@method.print ']'
|
423
|
+
if name == '[]='
|
424
|
+
@method.print " = "
|
425
|
+
value.compile(self, true)
|
426
|
+
end
|
427
|
+
end
|
428
|
+
unless simple && expression
|
429
|
+
@method.puts ';'
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
def break(node)
|
434
|
+
@loop.break
|
435
|
+
end
|
436
|
+
|
437
|
+
def next(node)
|
438
|
+
@loop.next
|
439
|
+
end
|
440
|
+
|
441
|
+
def redo(node)
|
442
|
+
@loop.redo
|
443
|
+
end
|
444
|
+
|
445
|
+
# TODO: merge cleanly with method_call logic
|
446
|
+
def super_method_call(target, call, params, expression)
|
447
|
+
simple = call.expr?(self)
|
448
|
+
method = call.method(self)
|
449
|
+
unless simple || method.actual_return_type.void?
|
450
|
+
@method.print @lvalue if expression
|
451
|
+
end
|
452
|
+
if method.constructor?
|
453
|
+
@method.print "super("
|
454
|
+
else
|
455
|
+
@method.print "super.#{method.name}("
|
456
|
+
end
|
457
|
+
params.each_with_index do |param, index|
|
458
|
+
@method.print ', ' unless index == 0
|
459
|
+
param.compile(self, true)
|
460
|
+
end
|
461
|
+
if simple && expression
|
462
|
+
@method.print ')'
|
463
|
+
else
|
464
|
+
@method.puts ');'
|
465
|
+
end
|
466
|
+
if method.actual_return_type.void? && expression
|
467
|
+
@method.print @lvalue
|
468
|
+
if method.static?
|
469
|
+
@method.puts 'null;'
|
470
|
+
else
|
471
|
+
target.compile(self, true)
|
472
|
+
@method.puts ';'
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
end
|
477
|
+
|
478
|
+
def method_call(target, call, params, expression)
|
479
|
+
simple = call.expr?(self)
|
480
|
+
method = call.method(self)
|
481
|
+
unless simple || method.actual_return_type.void?
|
482
|
+
@method.print @lvalue if expression
|
483
|
+
end
|
484
|
+
if method.constructor?
|
485
|
+
@method.print "new "
|
486
|
+
target.compile(self, true)
|
487
|
+
@method.print '('
|
488
|
+
else
|
489
|
+
target.compile(self, true)
|
490
|
+
@method.print ".#{method.name}("
|
491
|
+
end
|
492
|
+
params.each_with_index do |param, index|
|
493
|
+
@method.print ', ' unless index == 0
|
494
|
+
param.compile(self, true)
|
495
|
+
end
|
496
|
+
if simple && expression
|
497
|
+
@method.print ')'
|
498
|
+
else
|
499
|
+
@method.puts ');'
|
500
|
+
end
|
501
|
+
if method.actual_return_type.void? && expression
|
502
|
+
@method.print @lvalue
|
503
|
+
if method.static?
|
504
|
+
@method.puts 'null;'
|
505
|
+
else
|
506
|
+
target.compile(self, true)
|
507
|
+
@method.puts ';'
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
end
|
512
|
+
|
513
|
+
def temp(expression, value=nil)
|
514
|
+
assign(@method.tmp(expression.inferred_type), value || expression)
|
515
|
+
end
|
516
|
+
|
517
|
+
def empty_array(type, size)
|
518
|
+
sizevar = size.precompile(self)
|
519
|
+
@method.print "#{@lvalue unless size.expr?(self)}new #{type.name}["
|
520
|
+
sizevar.compile(self, true)
|
521
|
+
@method.print ']'
|
522
|
+
end
|
523
|
+
|
524
|
+
def import(short, long)
|
525
|
+
end
|
526
|
+
|
527
|
+
def string(value)
|
528
|
+
@method.print value.inspect
|
529
|
+
end
|
530
|
+
|
531
|
+
def boolean(value)
|
532
|
+
@method.print value ? 'true' : 'false'
|
533
|
+
end
|
534
|
+
|
535
|
+
def array(node, expression)
|
536
|
+
if expression
|
537
|
+
# create unmodifiable list from array (simplest way to do this in Java source)
|
538
|
+
@method.print "java.util.Collections.unmodifiableList(java.util.Arrays.asList("
|
539
|
+
|
540
|
+
# elements, as expressions
|
541
|
+
boolean comma = false
|
542
|
+
node.children.each do |node|
|
543
|
+
@method.print ", "# if comma
|
544
|
+
node.compile(self, true)
|
545
|
+
comma = true
|
546
|
+
end
|
547
|
+
|
548
|
+
@method.print("))")
|
549
|
+
else
|
550
|
+
# elements, as non-expressions
|
551
|
+
# TODO: ensure they're all reference types!
|
552
|
+
node.children.each do |node|
|
553
|
+
node.compile(self, false)
|
554
|
+
end
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
def null
|
559
|
+
@method.print 'null'
|
560
|
+
end
|
561
|
+
|
562
|
+
def compile_self
|
563
|
+
@method.print 'this'
|
564
|
+
end
|
565
|
+
|
566
|
+
def print(node)
|
567
|
+
value = node.parameters[0]
|
568
|
+
value = value && value.precompile(self)
|
569
|
+
if node.println
|
570
|
+
@method.print "System.out.println("
|
571
|
+
else
|
572
|
+
@method.print "System.out.print("
|
573
|
+
end
|
574
|
+
value.compile(self, true) if value
|
575
|
+
@method.puts ');'
|
576
|
+
end
|
577
|
+
|
578
|
+
def define_class(class_def, expression)
|
579
|
+
with(:class => class_def.inferred_type.define(@file),
|
580
|
+
:static => false) do
|
581
|
+
@class.annotate(class_def.annotations)
|
582
|
+
class_def.body.compile(self, false) if class_def.body
|
583
|
+
|
584
|
+
@class.stop
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
def with(vars)
|
589
|
+
orig_values = {}
|
590
|
+
begin
|
591
|
+
vars.each do |name, new_value|
|
592
|
+
name = "@#{name}"
|
593
|
+
orig_values[name] = instance_variable_get name
|
594
|
+
instance_variable_set name, new_value
|
595
|
+
end
|
596
|
+
yield
|
597
|
+
ensure
|
598
|
+
orig_values.each do |name, value|
|
599
|
+
instance_variable_set name, value
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end
|
605
|
+
end
|