duby 0.0.2-java → 0.0.3-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 +7 -0
- data/README.txt +18 -7
- data/Rakefile +72 -0
- data/examples/ant/example-build.xml +7 -0
- data/examples/appengine/Rakefile +8 -67
- data/examples/appengine/Readme +4 -3
- data/examples/appengine/lib/duby/appengine_tasks.rb +173 -0
- data/examples/appengine/lib/duby/plugin/datastore.rb +92 -31
- data/examples/appengine/lib/duby_task.rb +61 -0
- data/examples/appengine/src/com/ribrdb/DubyApp.duby +32 -6
- data/examples/appengine/src/com/ribrdb/list.dhtml +2 -2
- data/examples/appengine/{config.ru → src/config.ru} +0 -0
- data/examples/bintrees.duby +66 -0
- data/examples/dynamic.duby +17 -0
- data/examples/fib.duby +3 -11
- data/examples/fields.duby +3 -3
- data/examples/fractal.duby +1 -3
- data/examples/sort_closure.duby +7 -0
- data/examples/swing.duby +11 -11
- data/javalib/duby-bootstrap.jar +0 -0
- data/javalib/dynalang-invoke-0.1.jar +0 -0
- data/lib/duby.rb +168 -35
- data/lib/duby/ast.rb +224 -27
- data/lib/duby/ast/call.rb +85 -25
- data/lib/duby/ast/class.rb +112 -28
- data/lib/duby/ast/flow.rb +65 -44
- data/lib/duby/ast/intrinsics.rb +223 -21
- data/lib/duby/ast/literal.rb +67 -16
- data/lib/duby/ast/local.rb +36 -40
- data/lib/duby/ast/method.rb +83 -67
- data/lib/duby/ast/structure.rb +105 -23
- data/lib/duby/compiler.rb +83 -28
- data/lib/duby/env.rb +33 -0
- data/lib/duby/jvm/base.rb +210 -0
- data/lib/duby/jvm/compiler.rb +293 -219
- data/lib/duby/jvm/method_lookup.rb +77 -67
- data/lib/duby/jvm/source_compiler.rb +250 -157
- data/lib/duby/jvm/source_generator/builder.rb +53 -49
- data/lib/duby/jvm/source_generator/loops.rb +9 -9
- data/lib/duby/jvm/source_generator/precompile.rb +35 -25
- data/lib/duby/jvm/typer.rb +19 -10
- data/lib/duby/jvm/types.rb +127 -68
- data/lib/duby/jvm/types/basic_types.rb +26 -13
- data/lib/duby/jvm/types/enumerable.rb +6 -4
- data/lib/duby/jvm/types/factory.rb +49 -13
- data/lib/duby/jvm/types/floats.rb +16 -0
- data/lib/duby/jvm/types/integers.rb +63 -2
- data/lib/duby/jvm/types/intrinsics.rb +43 -21
- data/lib/duby/jvm/types/methods.rb +326 -86
- data/lib/duby/jvm/types/number.rb +3 -0
- data/lib/duby/nbcompiler.rb +1 -1
- data/lib/duby/plugin/edb.rb +1 -1
- data/lib/duby/plugin/java.rb +10 -1
- data/lib/duby/transform.rb +134 -46
- data/lib/duby/typer.rb +75 -50
- data/test/test_ast.rb +106 -106
- data/test/test_compilation.rb +46 -32
- data/test/test_env.rb +42 -0
- data/test/test_java_typer.rb +35 -51
- data/test/test_javac_compiler.rb +4 -1
- data/test/test_jvm_compiler.rb +564 -133
- data/test/test_typer.rb +68 -92
- metadata +37 -21
- data/examples/README +0 -16
- data/lib/duby/c/compiler.rb +0 -134
- data/lib/duby/old/compiler_old.rb +0 -845
- data/lib/duby/old/declaration.rb +0 -72
- data/lib/duby/old/mapper.rb +0 -72
- data/lib/duby/old/signature.rb +0 -52
- data/lib/duby/old/typer_old.rb +0 -163
- data/lib/duby/plugin/math.rb +0 -84
- data/test/test_math_plugin.rb +0 -87
data/examples/README
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
Duby: A Type-Inferred Ruby-Like JVM Language
|
2
|
-
|
3
|
-
Duby is an experimental project to create a JVM bytecode compiler for a
|
4
|
-
Ruby-like language that infers static types from argument declarations
|
5
|
-
and called methods. It is currently under development, but may eventually
|
6
|
-
be used to implement portions of JRuby, since Duby combines the terse,
|
7
|
-
neat syntax of Ruby with the performance of statically-typed Java
|
8
|
-
bytecode.
|
9
|
-
|
10
|
-
To compile the samples, run the following lines:
|
11
|
-
|
12
|
-
jruby lib/ruby/site_ruby/1.8/compiler/duby/compiler.rb <filename>
|
13
|
-
|
14
|
-
And to load and call them, use JRuby's Java integration:
|
15
|
-
|
16
|
-
CLASSPATH=. jruby -rjava -e "p Java::Foo.new.fib(35)"
|
data/lib/duby/c/compiler.rb
DELETED
@@ -1,134 +0,0 @@
|
|
1
|
-
require 'duby'
|
2
|
-
require 'duby/compiler'
|
3
|
-
|
4
|
-
module Duby
|
5
|
-
module Compiler
|
6
|
-
class C
|
7
|
-
class MathCompiler
|
8
|
-
def call(compiler, call)
|
9
|
-
call.target.compile(compiler)
|
10
|
-
|
11
|
-
compiler.src << " #{call.name} "
|
12
|
-
|
13
|
-
call.parameters.each {|param| param.compile(compiler)}
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
attr_accessor :filename, :src
|
18
|
-
|
19
|
-
def initialize(filename)
|
20
|
-
@filename = filename
|
21
|
-
@src = ""
|
22
|
-
|
23
|
-
self.type_mapper[AST::TypeReference.new(:fixnum)] = "int"
|
24
|
-
|
25
|
-
self.call_compilers[AST::TypeReference.new(:fixnum)] = MathCompiler.new
|
26
|
-
end
|
27
|
-
|
28
|
-
def compile(ast)
|
29
|
-
ast.compile(self)
|
30
|
-
end
|
31
|
-
|
32
|
-
def define_main(body)
|
33
|
-
old_src, @src = @src, "int main(int argc, char **argv)\n{\n"
|
34
|
-
|
35
|
-
body.compile(self)
|
36
|
-
|
37
|
-
@src << "}\n\n"
|
38
|
-
|
39
|
-
@src = old_src + @src
|
40
|
-
end
|
41
|
-
|
42
|
-
def define_method(name, signature, args, body)
|
43
|
-
old_src, @src = @src, "#{type_mapper[signature[:return]]} #{name}("
|
44
|
-
|
45
|
-
args.compile(self)
|
46
|
-
|
47
|
-
@src << ")\n{\n"
|
48
|
-
|
49
|
-
body.compile(self)
|
50
|
-
|
51
|
-
@src << "\n}\n\n"
|
52
|
-
|
53
|
-
@src = old_src + @src
|
54
|
-
end
|
55
|
-
|
56
|
-
def declare_argument(name, type)
|
57
|
-
@src << "#{type_mapper[type]} #{name}"
|
58
|
-
end
|
59
|
-
|
60
|
-
def branch(iff)
|
61
|
-
@src << "if ("
|
62
|
-
|
63
|
-
iff.condition.compile(self)
|
64
|
-
|
65
|
-
@src << ") {"
|
66
|
-
|
67
|
-
iff.body.compile(self)
|
68
|
-
|
69
|
-
if iff.else
|
70
|
-
@src << "} else {"
|
71
|
-
|
72
|
-
iff.else.compile(self)
|
73
|
-
end
|
74
|
-
|
75
|
-
@src << "}"
|
76
|
-
end
|
77
|
-
|
78
|
-
def call(call)
|
79
|
-
call_compilers[call.target.inferred_type].call(self, call)
|
80
|
-
end
|
81
|
-
|
82
|
-
def call_compilers
|
83
|
-
@call_compilers ||= {}
|
84
|
-
end
|
85
|
-
|
86
|
-
def self_call(fcall)
|
87
|
-
@src << "#{fcall.name}("
|
88
|
-
|
89
|
-
fcall.parameters.each {|param| param.compile(self)}
|
90
|
-
|
91
|
-
@src << ")"
|
92
|
-
end
|
93
|
-
|
94
|
-
def local(name, type)
|
95
|
-
@src << name
|
96
|
-
end
|
97
|
-
|
98
|
-
def fixnum(value)
|
99
|
-
@src << value.to_s
|
100
|
-
end
|
101
|
-
|
102
|
-
def newline
|
103
|
-
@src << ";\n"
|
104
|
-
end
|
105
|
-
|
106
|
-
def ret
|
107
|
-
@src << "return "
|
108
|
-
|
109
|
-
yield
|
110
|
-
end
|
111
|
-
|
112
|
-
def generate
|
113
|
-
@src
|
114
|
-
end
|
115
|
-
|
116
|
-
def type_mapper
|
117
|
-
@type_mapper ||= {}
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
if __FILE__ == $0
|
124
|
-
ast = Duby::AST.parse(File.read(ARGV[0]))
|
125
|
-
|
126
|
-
typer = Duby::Typer::Simple.new(:script)
|
127
|
-
ast.infer(typer)
|
128
|
-
typer.resolve(true)
|
129
|
-
|
130
|
-
compiler = Duby::Compiler::C.new("#{ARGV[0]}.c")
|
131
|
-
ast.compile(compiler)
|
132
|
-
|
133
|
-
File.open(compiler.filename, "w") {|file| file.write(compiler.generate)}
|
134
|
-
end
|
@@ -1,845 +0,0 @@
|
|
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
|