mirah 0.0.4-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.
Files changed (141) hide show
  1. data/History.txt +15 -0
  2. data/README.txt +51 -0
  3. data/Rakefile +86 -0
  4. data/bin/duby +10 -0
  5. data/bin/dubyc +10 -0
  6. data/bin/dubyp +10 -0
  7. data/bin/jrubyp +36 -0
  8. data/bin/mirah +9 -0
  9. data/bin/mirah.cmd +1 -0
  10. data/bin/mirahc +9 -0
  11. data/bin/mirahc.cmd +1 -0
  12. data/bin/mirahp +9 -0
  13. data/bin/mirahp.cmd +1 -0
  14. data/examples/ant/example-build.xml +7 -0
  15. data/examples/appengine/Rakefile +19 -0
  16. data/examples/appengine/Readme +29 -0
  17. data/examples/appengine/src/org/mirah/MirahApp.mirah +57 -0
  18. data/examples/appengine/src/org/mirah/list.dhtml +15 -0
  19. data/examples/appengine/war/WEB-INF/lib/dubydatastore.jar +0 -0
  20. data/examples/bintrees.mirah +66 -0
  21. data/examples/construction.mirah +8 -0
  22. data/examples/dynamic.mirah +17 -0
  23. data/examples/edb.mirah +3 -0
  24. data/examples/fib.mirah +16 -0
  25. data/examples/fields.mirah +22 -0
  26. data/examples/fractal.mirah +55 -0
  27. data/examples/java_thing.mirah +13 -0
  28. data/examples/plugins/appengine/Rakefile +55 -0
  29. data/examples/plugins/appengine/lib/com/google/appengine/ext/duby/db/datastore.rb +375 -0
  30. data/examples/plugins/appengine/src/com/google/appengine/ext/duby/db/Model.duby +336 -0
  31. data/examples/plugins/appengine/test/com/google/appengine/ext/duby/db/ModelTest.duby +113 -0
  32. data/examples/simple_class.mirah +12 -0
  33. data/examples/sort_closure.mirah +7 -0
  34. data/examples/swing.mirah +20 -0
  35. data/examples/tak.mirah +15 -0
  36. data/examples/test.edb +9 -0
  37. data/examples/wiki/Rakefile +18 -0
  38. data/examples/wiki/src/org/mirah/wiki/MirahWiki.duby +324 -0
  39. data/examples/wiki/src/org/mirah/wiki/edit.eduby.html +42 -0
  40. data/examples/wiki/src/org/mirah/wiki/error.eduby.html +2 -0
  41. data/examples/wiki/src/org/mirah/wiki/layout.eduby.html +69 -0
  42. data/examples/wiki/src/org/mirah/wiki/parser.eduby.html +7 -0
  43. data/examples/wiki/src/org/mirah/wiki/view.eduby.html +15 -0
  44. data/examples/wiki/war/WEB-INF/classes/test/HeredocContext.class +0 -0
  45. data/examples/wiki/war/WEB-INF/classes/test/MirahParser.class +0 -0
  46. data/examples/wiki/war/WEB-INF/lib/appengine-api.jar +0 -0
  47. data/examples/wiki/war/WEB-INF/lib/dubydatastore.jar +0 -0
  48. data/examples/wiki/war/WEB-INF/lib/jmeta-runtime.jar +0 -0
  49. data/examples/wiki/war/WEB-INF/lib/pegdown-stubs.jar +0 -0
  50. data/examples/wiki/war/WEB-INF/pegdown.jar +0 -0
  51. data/examples/wiki/war/app.yaml +21 -0
  52. data/examples/wiki/war/public/favicon.ico +0 -0
  53. data/examples/wiki/war/public/images/appengine_duby.png +0 -0
  54. data/examples/wiki/war/public/images/back.gif +0 -0
  55. data/examples/wiki/war/public/images/dir.gif +0 -0
  56. data/examples/wiki/war/public/images/file.gif +0 -0
  57. data/examples/wiki/war/public/javascripts/prettify.js +61 -0
  58. data/examples/wiki/war/public/robots.txt +0 -0
  59. data/examples/wiki/war/public/stylesheets/main.css +156 -0
  60. data/examples/wiki/war/public/stylesheets/prettify.css +1 -0
  61. data/examples/wiki/war/public/stylesheets/sh_style.css +66 -0
  62. data/examples/wiki/war/public/stylesheets/source.css +21 -0
  63. data/examples/wiki/war/public/wmd/images/bg-fill.png +0 -0
  64. data/examples/wiki/war/public/wmd/images/bg.png +0 -0
  65. data/examples/wiki/war/public/wmd/images/blockquote.png +0 -0
  66. data/examples/wiki/war/public/wmd/images/bold.png +0 -0
  67. data/examples/wiki/war/public/wmd/images/code.png +0 -0
  68. data/examples/wiki/war/public/wmd/images/h1.png +0 -0
  69. data/examples/wiki/war/public/wmd/images/hr.png +0 -0
  70. data/examples/wiki/war/public/wmd/images/img.png +0 -0
  71. data/examples/wiki/war/public/wmd/images/italic.png +0 -0
  72. data/examples/wiki/war/public/wmd/images/link.png +0 -0
  73. data/examples/wiki/war/public/wmd/images/ol.png +0 -0
  74. data/examples/wiki/war/public/wmd/images/redo.png +0 -0
  75. data/examples/wiki/war/public/wmd/images/separator.png +0 -0
  76. data/examples/wiki/war/public/wmd/images/ul.png +0 -0
  77. data/examples/wiki/war/public/wmd/images/undo.png +0 -0
  78. data/examples/wiki/war/public/wmd/images/wmd-on.png +0 -0
  79. data/examples/wiki/war/public/wmd/images/wmd.png +0 -0
  80. data/examples/wiki/war/public/wmd/showdown.js +421 -0
  81. data/examples/wiki/war/public/wmd/wmd-base.js +1799 -0
  82. data/examples/wiki/war/public/wmd/wmd-plus.js +311 -0
  83. data/examples/wiki/war/public/wmd/wmd.js +73 -0
  84. data/javalib/JRubyParser.jar +0 -0
  85. data/javalib/dynalang-invoke-0.1.jar +0 -0
  86. data/javalib/mirah-bootstrap.jar +0 -0
  87. data/javalib/mirah-parser.jar +0 -0
  88. data/lib/duby.rb +2 -0
  89. data/lib/mirah.rb +338 -0
  90. data/lib/mirah/appengine_tasks.rb +146 -0
  91. data/lib/mirah/ast.rb +615 -0
  92. data/lib/mirah/ast/call.rb +307 -0
  93. data/lib/mirah/ast/class.rb +311 -0
  94. data/lib/mirah/ast/flow.rb +364 -0
  95. data/lib/mirah/ast/intrinsics.rb +470 -0
  96. data/lib/mirah/ast/literal.rb +154 -0
  97. data/lib/mirah/ast/local.rb +89 -0
  98. data/lib/mirah/ast/method.rb +360 -0
  99. data/lib/mirah/ast/scope.rb +208 -0
  100. data/lib/mirah/ast/structure.rb +226 -0
  101. data/lib/mirah/ast/type.rb +130 -0
  102. data/lib/mirah/compiler.rb +341 -0
  103. data/lib/mirah/env.rb +33 -0
  104. data/lib/mirah/jvm/base.rb +258 -0
  105. data/lib/mirah/jvm/compiler.rb +885 -0
  106. data/lib/mirah/jvm/method_lookup.rb +203 -0
  107. data/lib/mirah/jvm/source_compiler.rb +737 -0
  108. data/lib/mirah/jvm/source_generator/builder.rb +444 -0
  109. data/lib/mirah/jvm/source_generator/loops.rb +110 -0
  110. data/lib/mirah/jvm/source_generator/precompile.rb +188 -0
  111. data/lib/mirah/jvm/source_generator/typer.rb +11 -0
  112. data/lib/mirah/jvm/typer.rb +151 -0
  113. data/lib/mirah/jvm/types.rb +416 -0
  114. data/lib/mirah/jvm/types/basic_types.rb +33 -0
  115. data/lib/mirah/jvm/types/boolean.rb +17 -0
  116. data/lib/mirah/jvm/types/enumerable.rb +65 -0
  117. data/lib/mirah/jvm/types/extensions.rb +86 -0
  118. data/lib/mirah/jvm/types/factory.rb +186 -0
  119. data/lib/mirah/jvm/types/floats.rb +86 -0
  120. data/lib/mirah/jvm/types/integers.rb +171 -0
  121. data/lib/mirah/jvm/types/intrinsics.rb +376 -0
  122. data/lib/mirah/jvm/types/literals.rb +74 -0
  123. data/lib/mirah/jvm/types/methods.rb +614 -0
  124. data/lib/mirah/jvm/types/number.rb +143 -0
  125. data/lib/mirah/nbcompiler.rb +29 -0
  126. data/lib/mirah/plugin/edb.rb +29 -0
  127. data/lib/mirah/plugin/gwt.rb +173 -0
  128. data/lib/mirah/plugin/java.rb +55 -0
  129. data/lib/mirah/transform.rb +266 -0
  130. data/lib/mirah/transform2.rb +728 -0
  131. data/lib/mirah/typer.rb +407 -0
  132. data/lib/mirah_task.rb +107 -0
  133. data/test/test_ast.rb +359 -0
  134. data/test/test_compilation.rb +112 -0
  135. data/test/test_env.rb +42 -0
  136. data/test/test_gwt.rb +58 -0
  137. data/test/test_java_typer.rb +183 -0
  138. data/test/test_javac_compiler.rb +63 -0
  139. data/test/test_jvm_compiler.rb +2607 -0
  140. data/test/test_typer.rb +221 -0
  141. metadata +235 -0
@@ -0,0 +1,364 @@
1
+ module Duby
2
+ module AST
3
+ class Condition < Node
4
+ child :predicate
5
+
6
+ def initialize(parent, line_number, &block)
7
+ super(parent, line_number, &block)
8
+ end
9
+
10
+ def infer(typer)
11
+ unless resolved?
12
+ @inferred_type = typer.infer(predicate)
13
+ if @inferred_type && !@inferred_type.primitive?
14
+ call = Call.new(parent, position, '!=') do |call|
15
+ predicate.parent = call
16
+ [predicate, [Null.new(call, position)]]
17
+ end
18
+ self.predicate = call
19
+ @inferred_type = typer.infer(predicate)
20
+ end
21
+
22
+ @inferred_type ? resolved! : typer.defer(self)
23
+ end
24
+
25
+ @inferred_type
26
+ end
27
+ end
28
+
29
+ class If < Node
30
+ child :condition
31
+ child :body
32
+ child :else
33
+
34
+ def initialize(parent, line_number, &block)
35
+ super(parent, line_number, &block)
36
+ end
37
+
38
+ def infer(typer)
39
+ unless resolved?
40
+ condition_type = typer.infer(condition)
41
+ unless condition_type
42
+ typer.defer(condition)
43
+ end
44
+
45
+ # condition type is unrelated to body types, so we proceed with bodies
46
+ then_type = typer.infer(body) if body
47
+
48
+ if !then_type
49
+ # attempt to determine else branch
50
+ if self.else
51
+ else_type = typer.infer(self.else)
52
+
53
+ if !else_type
54
+ # we have neither type, defer until later
55
+ typer.defer(self)
56
+ else
57
+ # we have else but not then, defer only then and use else type for now
58
+ @inferred_type = else_type
59
+ if body
60
+ typer.defer(self)
61
+ else
62
+ resolved! if condition_type
63
+ end
64
+ end
65
+ else
66
+ # no then type could be inferred and no else body, defer for now
67
+ typer.defer(self)
68
+ end
69
+ else
70
+ if self.else
71
+ else_type = typer.infer(self.else)
72
+
73
+ if !else_type
74
+ # we determined a then type, so we use that and defer the else body
75
+ @inferred_type = then_type
76
+ typer.defer(self)
77
+ else
78
+ # both then and else inferred, ensure they're compatible
79
+ if then_type.compatible?(else_type)
80
+ # types are compatible...if condition is resolved, we're done
81
+ @inferred_type = then_type.narrow(else_type)
82
+ resolved! if condition_type
83
+ else
84
+ raise Typer::InferenceError.new("if statement with incompatible result types #{then_type} and #{else_type}")
85
+ end
86
+ end
87
+ else
88
+ # only then and type inferred, we're 100% resolved
89
+ @inferred_type = then_type
90
+ resolved! if condition_type
91
+ end
92
+ end
93
+ end
94
+
95
+ @inferred_type
96
+ end
97
+ end
98
+
99
+ class Loop < Node
100
+ child :init
101
+ child :condition
102
+ child :pre
103
+ child :body
104
+ child :post
105
+ attr_accessor :check_first, :negative, :redo
106
+
107
+ def initialize(parent, position, check_first, negative, &block)
108
+ @check_first = check_first
109
+ @negative = negative
110
+
111
+ @children = [
112
+ Body.new(self, position),
113
+ nil,
114
+ Body.new(self, position),
115
+ nil,
116
+ Body.new(self, position),
117
+ ]
118
+ super(parent, position) do |l|
119
+ condition, body = yield(l)
120
+ [self.init, condition, self.pre, body, self.post]
121
+ end
122
+ end
123
+
124
+ def infer(typer)
125
+ unless resolved?
126
+ child_types = children.map do |c|
127
+ if c.nil? || (Body === c && c.empty?)
128
+ typer.no_type
129
+ else
130
+ typer.infer(c)
131
+ end
132
+ end
133
+ if child_types.any? {|t| t.nil?}
134
+ typer.defer(self)
135
+ else
136
+ resolved!
137
+ @inferred_type = typer.null_type
138
+ end
139
+ end
140
+
141
+ @inferred_type
142
+ end
143
+
144
+ def check_first?; @check_first; end
145
+ def negative?; @negative; end
146
+
147
+ def redo?
148
+ if @redo.nil?
149
+ nodes = @children.dup
150
+ until nodes.empty?
151
+ node = nodes.shift
152
+ while node.respond_to?(:inlined) && node.inlined
153
+ node = node.inlined
154
+ end
155
+ next if node.nil? || Loop === node
156
+ if Redo === node
157
+ return @redo = true
158
+ end
159
+ nodes.insert(-1, *node.children.flatten)
160
+ end
161
+ return @redo = false
162
+ else
163
+ @redo
164
+ end
165
+ end
166
+
167
+ def init?
168
+ init && !(init.kind_of?(Body) && init.empty?)
169
+ end
170
+
171
+ def pre?
172
+ pre && !(pre.kind_of?(Body) && pre.empty?)
173
+ end
174
+
175
+ def post?
176
+ post && !(post.kind_of?(Body) && post.empty?)
177
+ end
178
+
179
+ def to_s
180
+ "Loop(check_first = #{check_first?}, negative = #{negative?})"
181
+ end
182
+ end
183
+
184
+ class Not < Node
185
+ def initialize(parent, line_number, &block)
186
+ super(parent, line_number, &block)
187
+ end
188
+ end
189
+
190
+ class Return < Node
191
+ include Valued
192
+
193
+ child :value
194
+
195
+ def initialize(parent, line_number, &block)
196
+ super(parent, line_number, &block)
197
+ end
198
+
199
+ def infer(typer)
200
+ resolve_if(typer) do
201
+ if value
202
+ typer.infer(value)
203
+ else
204
+ typer.no_type
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ class Break < Node;
211
+ def infer(typer)
212
+ unless resolved?
213
+ resolved!
214
+ @inferred_type = typer.null_type
215
+ end
216
+ @inferred_type
217
+ end
218
+ end
219
+
220
+ class Next < Break; end
221
+
222
+ class Redo < Break; end
223
+
224
+ class Raise < Node
225
+ include Valued
226
+
227
+ child :exception
228
+
229
+ def initialize(parent, line_number, &block)
230
+ super(parent, line_number, &block)
231
+ end
232
+
233
+ def infer(typer)
234
+ unless resolved?
235
+ @inferred_type = AST.unreachable_type
236
+ throwable = AST.type(nil, 'java.lang.Throwable')
237
+ if children.size == 1
238
+ arg_type = typer.infer(self.exception)
239
+ unless arg_type
240
+ typer.defer(self)
241
+ return
242
+ end
243
+ if throwable.assignable_from?(arg_type) && !arg_type.meta?
244
+ resolved!
245
+ return @inferred_type
246
+ end
247
+ end
248
+
249
+ arg_types = children.map {|c| typer.infer(c)}
250
+ if arg_types.any? {|c| c.nil?}
251
+ typer.defer(self)
252
+ else
253
+ if arg_types[0] && throwable.assignable_from?(arg_types[0])
254
+ klass = children.shift
255
+ else
256
+ klass = Constant.new(self, position, 'RuntimeException')
257
+ end
258
+ exception = Call.new(self, position, 'new') do
259
+ [klass, children, nil]
260
+ end
261
+ resolved!
262
+ @children = [exception]
263
+ typer.infer(exception)
264
+ end
265
+ end
266
+ @inferred_type
267
+ end
268
+ end
269
+
270
+ defmacro('raise') do |transformer, fcall, parent|
271
+ Raise.new(parent, fcall.position) do |raise_node|
272
+ fcall.parameters
273
+ end
274
+ end
275
+
276
+ class RescueClause < Node
277
+ include Scope
278
+ include Scoped
279
+ attr_accessor :name, :type, :types
280
+ child :type_nodes
281
+ child :body
282
+
283
+ def initialize(parent, position)
284
+ super(parent, position) do
285
+ static_scope.parent = scope.static_scope
286
+ yield(self) if block_given?
287
+ end
288
+ end
289
+
290
+ def infer(typer)
291
+ unless resolved?
292
+ @types ||= type_nodes.map {|n| n.type_reference(typer)}
293
+ if name
294
+ static_scope.shadow(name)
295
+ # TODO find the common parent Throwable
296
+ @type = types.size == 1 ? types[0] : AST.type(nil, 'java.lang.Throwable')
297
+ typer.learn_local_type(static_scope, name, @type)
298
+ end
299
+ @inferred_type = typer.infer(body)
300
+
301
+ if (@inferred_type && !body.resolved?)
302
+ puts "#{body} not resolved"
303
+ end
304
+
305
+ (@inferred_type && body.resolved?) ? resolved! : typer.defer(self)
306
+ end
307
+
308
+ @inferred_type
309
+ end
310
+
311
+ def binding_type(duby=nil)
312
+ static_scope.parent.binding_type(defining_class, duby)
313
+ end
314
+
315
+ def binding_type=(type)
316
+ static_scope.parent.binding_type = type
317
+ end
318
+
319
+ def has_binding?
320
+ static_scope.parent.has_binding?
321
+ end
322
+ end
323
+
324
+ class Rescue < Node
325
+ child :body
326
+ child :clauses
327
+ def initialize(parent, position, &block)
328
+ super(parent, position, &block)
329
+ @body, @clauses = children
330
+ end
331
+
332
+ def infer(typer)
333
+ unless resolved?
334
+ types = [typer.infer(body)] + clauses.map {|c| typer.infer(c)}
335
+ if types.any? {|t| t.nil?}
336
+ typer.defer(self)
337
+ else
338
+ # TODO check types for compatibility (maybe only if an expression)
339
+ resolved!
340
+ @inferred_type = types[0]
341
+ end
342
+ end
343
+ @inferred_type
344
+ end
345
+ end
346
+
347
+ class Ensure < Node
348
+ child :body
349
+ child :clause
350
+ attr_accessor :state # Used by the some compilers.
351
+
352
+ def initialize(parent, position, &block)
353
+ super(parent, position, &block)
354
+ end
355
+
356
+ def infer(typer)
357
+ resolve_if(typer) do
358
+ typer.infer(clause)
359
+ typer.infer(body)
360
+ end
361
+ end
362
+ end
363
+ end
364
+ end
@@ -0,0 +1,470 @@
1
+ require 'fileutils'
2
+
3
+ module Duby::AST
4
+
5
+ class Unquote < Node
6
+ child :value
7
+
8
+ def infer(typer)
9
+ raise "Unquote used outside of macro"
10
+ end
11
+
12
+ def _dump(depth)
13
+ vals = Unquote.__extracted
14
+ index = vals.size
15
+ # Make sure the scope is saved
16
+ if Scoped === value
17
+ value.scope
18
+ scoped_value = value
19
+ else
20
+ scoped_value = ScopedBody.new(value.parent, value.position) {[value]}
21
+ scoped_value.static_scope = scoped_value.scope.static_scope
22
+ end
23
+ vals << self.value
24
+ Marshal.dump([position, index])
25
+ end
26
+
27
+
28
+ def self._load(str)
29
+ if str =~ /^\d+$/
30
+ # This just returns the exact node passed in.
31
+ index = str.to_i
32
+ Unquote.__injected[index].dup
33
+ else
34
+ position, index = Marshal.load(str)
35
+ holder = UnquotedValue.new(nil, position)
36
+ holder << Unquote.__injected[index].dup
37
+ holder
38
+ end
39
+ end
40
+
41
+ def self.__extracted
42
+ Thread.current[:'Duby::AST::Unqote.extracted']
43
+ end
44
+
45
+ def self.__extracted=(value)
46
+ Thread.current[:'Duby::AST::Unqote.extracted'] = value
47
+ end
48
+
49
+ def self.__injected
50
+ Thread.current[:'Duby::AST::Unqote.injected']
51
+ end
52
+
53
+ def self.__injected=(value)
54
+ Thread.current[:'Duby::AST::Unqote.injected'] = value
55
+ end
56
+
57
+ def self.extract_values
58
+ values = self.__extracted = []
59
+ begin
60
+ yield
61
+ return values
62
+ ensure
63
+ self.__extracted = nil
64
+ end
65
+ end
66
+
67
+ def self.inject_values(values)
68
+ self.__injected = values
69
+ begin
70
+ yield
71
+ ensure
72
+ self.__injected = nil
73
+ end
74
+ end
75
+ end
76
+
77
+ class UnquotedValue < Node
78
+ java_import 'java.lang.Character'
79
+ child :value
80
+
81
+ def name
82
+ case value
83
+ when Duby::AST::String
84
+ value.literal
85
+ when ::String
86
+ value
87
+ when Named
88
+ value.name
89
+ else
90
+ raise "Bad unquote value #{value}"
91
+ end
92
+ end
93
+
94
+ def node
95
+ case value
96
+ when Node
97
+ value
98
+ when ::String
99
+ c = value[0]
100
+ if c == ?@
101
+ return Field.new(nil, position, value[1, value.length])
102
+ elsif Character.isUpperCase(c)
103
+ return Constant.new(nil, position, value)
104
+ else
105
+ return Local.new(nil, position, value)
106
+ end
107
+ else
108
+ raise "Bad unquote value"
109
+ end
110
+ end
111
+
112
+ def f_arg
113
+ case value
114
+ when Arguments, Argument
115
+ value
116
+ when Named
117
+ RequiredArgument.new(nil, position, value.name)
118
+ when ::String
119
+ RequiredArgument.new(nil, position, value)
120
+ else
121
+ raise "Bad unquote value"
122
+ end
123
+ end
124
+ end
125
+
126
+ class UnquoteAssign < Node
127
+ child :name
128
+ child :value
129
+
130
+ def infer(typer)
131
+ raise "UnquoteAssign used outside of macro"
132
+ end
133
+
134
+ def _dump(depth)
135
+ vals = Unquote.__extracted
136
+ index = vals.size
137
+ vals << self.name
138
+ Marshal.dump([position, index, self.value])
139
+ end
140
+
141
+
142
+ def self._load(str)
143
+ position, index, value = Marshal.load(str)
144
+ holder = UnquotedValueAssign.new(nil, position)
145
+ holder << Unquote.__injected[index].dup
146
+ holder << value
147
+ holder
148
+ end
149
+ end
150
+
151
+ class UnquotedValueAssign < UnquotedValue
152
+ child :name_node
153
+ child :value
154
+
155
+ def name
156
+ raise "Bad unquote value #{value}"
157
+ end
158
+
159
+ def node
160
+ klass = LocalAssignment
161
+ if Field === name_node
162
+ name = name_node.name
163
+ klass = FieldAssignment
164
+ # TODO support AttrAssign
165
+ elsif Named === name_node
166
+ name = name_node.name
167
+ elsif String === name_node
168
+ name = name_node.literal
169
+ elsif ::String === name_node
170
+ name = name
171
+ else
172
+ raise "Bad unquote value"
173
+ end
174
+ if name[0] == ?@
175
+ name = name[1, name.length]
176
+ klass = FieldAssignment
177
+ end
178
+ n = klass.new(nil, position, name)
179
+ n << value
180
+ n.validate_children
181
+ return n
182
+ end
183
+
184
+ def f_arg
185
+ raise "Bad unquote value"
186
+ end
187
+ end
188
+
189
+ class MacroDefinition < Node
190
+ include Named
191
+ include Scoped
192
+
193
+ child :arguments
194
+ child :body
195
+
196
+ attr_accessor :proxy
197
+
198
+ def self.new(*args, &block)
199
+ real_node = super
200
+ real_node.proxy = NodeProxy.new(real_node)
201
+ end
202
+
203
+ def initialize(parent, line_number, name, &block)
204
+ super(parent, line_number, &block)
205
+ @name = name
206
+ end
207
+
208
+ def infer(typer)
209
+ resolve_if(typer) do
210
+ self_type = scope.static_scope.self_type
211
+ extension_name = "%s$%s" % [self_type.name,
212
+ typer.transformer.tmp("Extension%s")]
213
+ klass = build_and_load_extension(self_type,
214
+ extension_name,
215
+ typer.transformer.state)
216
+
217
+ # restore the self type since we're sharing a type factory
218
+ typer.known_types['self'] = self_type
219
+
220
+ arg_types = argument_types
221
+ macro = self_type.add_compiled_macro(klass, name, arg_types)
222
+ if arguments[-1].kind_of?(BlockArgument) && arguments[-1].optional?
223
+ arg_types.pop
224
+ self_type.add_method(name, arg_types, macro)
225
+ end
226
+ proxy.__inline__(Noop.new(parent, position))
227
+ proxy.infer(typer)
228
+ end
229
+ end
230
+
231
+ def argument_types
232
+ arguments.map do |arg|
233
+ if arg.kind_of?(BlockArgument)
234
+ TypeReference::BlockType
235
+ else
236
+ # TODO support typed args. Also there should be a way
237
+ # to accept any AST node.
238
+ Duby::JVM::Types::Object
239
+ end
240
+ end
241
+ end
242
+
243
+ def signature
244
+ args = argument_types
245
+ if args.size > 0 && args[-1].block?
246
+ args[-1] = BiteScript::ASM::Type.getObjectType('duby.lang.compiler.Block')
247
+ end
248
+ [nil] + args
249
+ end
250
+
251
+ def build_and_load_extension(parent, name, state)
252
+ transformer = Duby::Transform::Transformer.new(state)
253
+ transformer.filename = name.gsub(".", "/")
254
+ orig_factory = Duby::AST.type_factory
255
+ new_factory = orig_factory.dup
256
+ Duby::AST.type_factory = new_factory
257
+ ast = build_ast(name, parent, transformer)
258
+ puts ast.inspect if state.verbose
259
+ classes = compile_ast(name, ast, transformer)
260
+ loader = DubyClassLoader.new(
261
+ JRuby.runtime.jruby_class_loader, classes)
262
+ klass = loader.loadClass(name, true)
263
+ annotate(parent, name)
264
+ Duby::AST.type_factory = orig_factory
265
+ klass
266
+ end
267
+
268
+ def annotate(type, class_name)
269
+ node = type.unmeta.node
270
+ if node
271
+ extension = node.annotation('duby.anno.Extensions')
272
+ extension ||= begin
273
+ node.annotations << Annotation.new(
274
+ nil, nil, BiteScript::ASM::Type.getObjectType('duby/anno/Extensions'))
275
+ node.annotations[-1].runtime = false
276
+ node.annotations[-1]
277
+ end
278
+ extension['macros'] ||= []
279
+ macro = Annotation.new(nil, nil,
280
+ BiteScript::ASM::Type.getObjectType('duby/anno/Macro'))
281
+ macro['name'] = name
282
+ macro['signature'] = BiteScript::Signature.signature(*signature)
283
+ macro['class'] = class_name
284
+ extension['macros'] << macro
285
+ # TODO deal with optional blocks.
286
+ else
287
+ puts "Warning: No ClassDefinition for #{type.name}. Macros can't be loaded from disk."
288
+ end
289
+ end
290
+
291
+ def compile_ast(name, ast, transformer)
292
+ typer = Duby::Typer::JVM.new(transformer)
293
+ typer.infer(ast)
294
+ typer.resolve(true)
295
+ compiler = Duby::Compiler::JVM.new
296
+ ast.compile(compiler, false)
297
+ class_map = {}
298
+ compiler.generate do |outfile, builder|
299
+ outfile = "#{transformer.destination}#{outfile}"
300
+ FileUtils.mkdir_p(File.dirname(outfile))
301
+ File.open(outfile, 'wb') do |f|
302
+ bytes = builder.generate
303
+ name = builder.class_name.gsub(/\//, '.')
304
+ class_map[name] = bytes
305
+ f.write(bytes)
306
+ end
307
+ end
308
+ class_map
309
+ end
310
+
311
+ def build_ast(name, parent, transformer)
312
+ # TODO should use a new type factory too.
313
+
314
+ ast = Duby::AST.parse_ruby("begin;end")
315
+ ast = transformer.transform(ast, nil)
316
+
317
+ # Start building the extension class
318
+ extension = transformer.define_class(position, name)
319
+ #extension.superclass = Duby::AST.type(nil, 'duby.lang.compiler.Macro')
320
+ extension.implements(Duby::AST.type(nil, 'duby.lang.compiler.Macro'))
321
+
322
+ extension.static_scope.import('duby.lang.compiler.Node', 'Node')
323
+
324
+ # The constructor just saves the state
325
+ extension.define_constructor(
326
+ position,
327
+ ['mirah', Duby::AST.type(nil, 'duby.lang.compiler.Compiler')],
328
+ ['call', Duby::AST.type(nil, 'duby.lang.compiler.Call')]) do |c|
329
+ transformer.eval("@mirah = mirah;@call = call", '-', c, 'mirah', 'call')
330
+ end
331
+
332
+ node_type = Duby::AST.type(nil, 'duby.lang.compiler.Node')
333
+
334
+ # expand() parses the arguments out of call and then passes them off to
335
+ # _expand
336
+ expand = extension.define_method(
337
+ position, 'expand', node_type)
338
+ args = []
339
+ arguments.each_with_index do |arg, i|
340
+ # TODO optional args
341
+ args << if arg.kind_of?(BlockArgument)
342
+ "@call.block"
343
+ else
344
+ "Node(args.get(#{i}))"
345
+ end
346
+ end
347
+ expand.body = transformer.eval(<<-end)
348
+ args = @call.arguments
349
+ _expand(#{args.join(', ')})
350
+ end
351
+ actual_args = arguments.map do |arg|
352
+ type = if arg.kind_of?(BlockArgument)
353
+ Duby::AST.type(nil, 'duby.lang.compiler.Block')
354
+ else
355
+ node_type
356
+ end
357
+ [arg.name, type, arg.position]
358
+ end
359
+ m = extension.define_method(position, '_expand', node_type, *actual_args)
360
+ m.body = self.body
361
+ ast.body = extension
362
+ ast
363
+ end
364
+ end
365
+
366
+ defmacro('defmacro') do |duby, fcall, parent|
367
+ macro = fcall.parameters[0]
368
+ block_arg = nil
369
+ args = macro.parameters if macro.respond_to?(:parameters)
370
+ body = if macro.respond_to?(:block) && macro.block
371
+ macro.block
372
+ else
373
+ fcall.block
374
+ end
375
+ body = body.body if body
376
+
377
+ MacroDefinition.new(parent, fcall.position, macro.name) do |mdef|
378
+ # TODO optional args?
379
+ args = if args
380
+ args.map do |arg|
381
+ case arg
382
+ when LocalAssignment
383
+ OptionalArgument.new(mdef, arg.position, arg.name) do |optarg|
384
+ # TODO check that they actually passed nil as the value
385
+ Null.new(parent, arg.value_node.position)
386
+ end
387
+ when FunctionalCall
388
+ RequiredArgument.new(mdef, arg.position, arg.name)
389
+ when BlockPass
390
+ farg = BlockArgument.new(mdef, arg.position, arg.value.name)
391
+ farg.optional = true if LocalAssignment === arg.value
392
+ farg
393
+ else
394
+ raise "Unsupported argument #{arg}"
395
+ end
396
+ end
397
+ else
398
+ []
399
+ end
400
+ body.parent = mdef if body
401
+ [args, body]
402
+ end
403
+ end
404
+
405
+ defmacro('macro') do |transformer, fcall, parent|
406
+ # Alternate macro syntax.
407
+ # macro def foo(...);...;end
408
+ # This one supports special names like []=,
409
+ # but you can't use optional blocks.
410
+ method = fcall.parameters[0]
411
+ macro = MacroDefinition.new(parent, fcall.position, method.name)
412
+ macro.arguments = method.arguments.args || []
413
+ macro.body = method.body
414
+ macro
415
+ end
416
+
417
+ defmacro('abstract') do |transformer, fcall, parent|
418
+ class_or_method = fcall.parameters[0]
419
+ class_or_method.abstract = true
420
+ class_or_method
421
+ end
422
+
423
+ defmacro('puts') do |transformer, fcall, parent|
424
+ Call.new(parent, fcall.position, "println") do |x|
425
+ args = fcall.parameters
426
+ args.each do |arg|
427
+ arg.parent = x
428
+ end
429
+ [
430
+ Call.new(x, fcall.position, "out") do |y|
431
+ [
432
+ Constant.new(y, fcall.position, "System"),
433
+ []
434
+ ]
435
+ end,
436
+ args,
437
+ nil
438
+ ]
439
+ end
440
+ end
441
+
442
+ defmacro('print') do |transformer, fcall, parent|
443
+ Call.new(parent, fcall.position, "print") do |x|
444
+ args = fcall.parameters
445
+ args.each do |arg|
446
+ arg.parent = x
447
+ end
448
+ [
449
+ Call.new(x, fcall.position, "out") do |y|
450
+ [
451
+ Constant.new(y, fcall.position, "System"),
452
+ []
453
+ ]
454
+ end,
455
+ args,
456
+ nil
457
+ ]
458
+ end
459
+ end
460
+
461
+ class InlineCode
462
+ def initialize(&block)
463
+ @block = block
464
+ end
465
+
466
+ def inline(transformer, call)
467
+ @block.call(transformer, call)
468
+ end
469
+ end
470
+ end