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,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
|