ruby2ruby 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,7 @@
1
+ == 1.1.0 / 2006-10-11
2
+
3
+ * 2 major enhancements
4
+ * Released separately from ZenHacks.
5
+ * Major overhaul/audit from the new ParseTree test infrastructure. Very complete now.
6
+
7
+
data/Manifest.txt ADDED
@@ -0,0 +1,6 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/ruby2ruby.rb
6
+ test/test_ruby2ruby.rb
data/README.txt ADDED
@@ -0,0 +1,50 @@
1
+ ruby2ruby
2
+ http://seattlerb.rubyforge.org/
3
+ http://rubyforge.org/projects/seattlerb
4
+
5
+ == DESCRIPTION:
6
+
7
+ ruby2ruby provides a means of generating pure ruby code easily from
8
+ ParseTree's Sexps. This makes making dynamic language processors much
9
+ easier in ruby than ever before.
10
+
11
+ == FEATURES/PROBLEMS:
12
+
13
+ * Clean, simple SexpProcessor generates ruby code from ParseTree's output.
14
+
15
+ == SYNOPSYS:
16
+
17
+ RubyToRuby.translate(MyClass, :mymethod) # => "def mymethod..."
18
+
19
+ == REQUIREMENTS:
20
+
21
+ + ParseTree
22
+
23
+ == INSTALL:
24
+
25
+ + sudo gem install ruby2ruby
26
+
27
+ == LICENSE:
28
+
29
+ (The MIT License)
30
+
31
+ Copyright (c) 2006 Ryan Davis
32
+
33
+ Permission is hereby granted, free of charge, to any person obtaining
34
+ a copy of this software and associated documentation files (the
35
+ 'Software'), to deal in the Software without restriction, including
36
+ without limitation the rights to use, copy, modify, merge, publish,
37
+ distribute, sublicense, and/or sell copies of the Software, and to
38
+ permit persons to whom the Software is furnished to do so, subject to
39
+ the following conditions:
40
+
41
+ The above copyright notice and this permission notice shall be
42
+ included in all copies or substantial portions of the Software.
43
+
44
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
45
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
46
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
47
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
48
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
49
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
50
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/ruby2ruby.rb'
6
+
7
+ Hoe.new('ruby2ruby', RubyToRuby::VERSION) do |p|
8
+ p.rubyforge_name = 'seattlerb'
9
+ p.summary = 'ruby2ruby provides a means of generating pure ruby code easily from ParseTree\'s Sexps.'
10
+ p.description = p.paragraphs_of('README.txt', 2).join
11
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1].map {|u| u.strip }
12
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
13
+ p.extra_deps << "ParseTree"
14
+ end
15
+
16
+ # vim: syntax=Ruby
data/lib/ruby2ruby.rb ADDED
@@ -0,0 +1,954 @@
1
+ # begin require 'rubygems'; rescue LoadError; end
2
+ require 'parse_tree'
3
+ require 'sexp_processor'
4
+
5
+ class NilClass
6
+ def method_missing(msg, *args, &block)
7
+ nil
8
+ end
9
+ end
10
+
11
+ class RubyToRuby < SexpProcessor
12
+ VERSION = '1.1.0'
13
+
14
+ def self.translate(klass_or_str, method=nil)
15
+ self.new.process(ParseTree.translate(klass_or_str, method))
16
+ end
17
+
18
+ def initialize
19
+ super
20
+ @indent = " "
21
+ self.auto_shift_type = true
22
+ self.strict = true
23
+ self.expected = String
24
+ end
25
+
26
+ ############################################################
27
+ # Processors (rewriters at bottom)
28
+
29
+ def process_alias(exp)
30
+ "alias_method #{process(exp.shift)}, #{process(exp.shift)}"
31
+ end
32
+
33
+ def process_and(exp)
34
+ "(#{process exp.shift} and #{process exp.shift})"
35
+ end
36
+
37
+ def process_args(exp)
38
+ args = []
39
+
40
+ until exp.empty? do
41
+ arg = exp.shift
42
+ case arg
43
+ when Symbol then
44
+ args << arg
45
+ when Array then
46
+ case arg.first
47
+ when :block then
48
+ asgns = {}
49
+ arg[1..-1].each do |lasgn|
50
+ asgns[lasgn[1]] = process(lasgn)
51
+ end
52
+
53
+ args.each_with_index do |name, index|
54
+ args[index] = asgns[name] if asgns.has_key? name
55
+ end
56
+ when :block_arg then
57
+ args << "&#{arg.last}"
58
+ when :array then
59
+ names = arg
60
+ vals = exp.shift
61
+ names.shift
62
+ vals.shift
63
+ v_size = vals.size
64
+
65
+ args << process(names.shift) until names.size == v_size
66
+ names.zip(vals) do |name, val|
67
+ args << "#{process name} = #{process val}"
68
+ end
69
+ else
70
+ raise "unknown arg type #{arg.first.inspect}"
71
+ end
72
+ else
73
+ raise "unknown arg type #{arg.inspect}"
74
+ end
75
+ end
76
+
77
+ return "(#{args.join ', '})"
78
+ end
79
+
80
+ def process_arglist(exp) # custom made node
81
+ code = []
82
+ until exp.empty? do
83
+ code << process(exp.shift)
84
+ end
85
+ code.join ', '
86
+ end
87
+
88
+ def process_argscat(exp)
89
+ args = []
90
+ ary = exp.shift
91
+ ary.shift # :array
92
+ until ary.empty? do
93
+ args << process(ary.shift)
94
+ end
95
+ args << "*#{process(exp.shift)}"
96
+ args.join ', '
97
+ end
98
+
99
+ def process_argspush(exp)
100
+ args = []
101
+
102
+ until exp.empty? do
103
+ args << process(exp.shift)
104
+ end
105
+
106
+ "#{args.join ', '}"
107
+ end
108
+
109
+ def process_array(exp)
110
+ "[#{process_arglist(exp)}]"
111
+ end
112
+
113
+ def process_attrasgn(exp)
114
+ process_call(exp)
115
+ end
116
+
117
+ def process_back_ref(exp)
118
+ "$#{exp.shift}"
119
+ end
120
+
121
+ def process_begin(exp)
122
+ is_rescue = exp.first.first == :rescue rescue false
123
+ code = []
124
+ code << "begin"
125
+ until exp.empty?
126
+ src = process(exp.shift)
127
+ src = indent(src) unless src =~ /\nrescue/ # ensures no level 0 rescues
128
+ code << src
129
+ end
130
+ code << "end" unless is_rescue
131
+ return code.join("\n")
132
+ end
133
+
134
+ def process_block(exp)
135
+ result = []
136
+
137
+ until exp.empty? do
138
+ found = exp.first.first == :block_arg rescue false
139
+ if found then
140
+ raise "wtf"
141
+ result[-1] = result[-1][0..-2] + ", #{process(exp.shift)})"
142
+ else
143
+ code = exp.shift
144
+ if code.nil? or code.first == :nil then
145
+ result << "# do nothing"
146
+ else
147
+ result << process(code)
148
+ end
149
+ end
150
+ end
151
+
152
+ return result.join("\n") + "\n"
153
+ end
154
+
155
+ def process_block_arg(exp)
156
+ "&#{exp.shift}"
157
+ end
158
+
159
+ def process_block_pass(exp)
160
+ bname = [:lvar, "&" + process(exp.shift)]
161
+ call = exp.shift
162
+ has_args = Array === call.last and call.last.first == :array
163
+ call << [:array] unless has_args
164
+ call.last << bname
165
+
166
+ process(call)
167
+ end
168
+
169
+ def process_break(exp)
170
+ val = process(exp.shift)
171
+ "break" + (val ? " #{val}" : "")
172
+ end
173
+
174
+ def process_call(exp)
175
+ receiver = process exp.shift
176
+ name = exp.shift
177
+ args_exp = exp.shift
178
+ if args_exp && args_exp.first == :array
179
+ args = "#{process(args_exp)[1..-2]}"
180
+ else
181
+ args = process args_exp
182
+ end
183
+
184
+ case name
185
+ when :<=>, :==, :<, :>, :<=, :>=, :-, :+, :*, :/, :%, :<<, :>> then #
186
+ "(#{receiver} #{name} #{args})"
187
+ when :[] then
188
+ "#{receiver}[#{args}]"
189
+ else
190
+ unless receiver.nil? then
191
+ "#{receiver}.#{name}#{args ? "(#{args})" : args}"
192
+ else
193
+ "#{name}#{args ? "(#{args})" : args}"
194
+ end
195
+ end
196
+ end
197
+
198
+ def process_case(exp)
199
+ result = []
200
+ result << "case #{process exp.shift}"
201
+ until exp.empty?
202
+ pt = exp.shift
203
+ if pt and pt.first == :when
204
+ result << "#{process(pt)}"
205
+ else
206
+ code = indent(process(pt))
207
+ code = indent("# do nothing") if code =~ /^\s*$/
208
+ result << "else\n#{code}"
209
+ end
210
+ end
211
+ result << "end"
212
+ result.join("\n")
213
+ end
214
+
215
+ def process_cdecl(exp)
216
+ "#{exp.shift} = #{process(exp.shift)}"
217
+ end
218
+
219
+ def process_class(exp)
220
+ s = "class #{exp.shift}"
221
+ superk = process(exp.shift)
222
+
223
+ s << " < #{superk}" if superk
224
+ s << "\n"
225
+
226
+ body = ""
227
+ body << "#{process exp.shift}\n\n" until exp.empty?
228
+ s + indent(body) + "end"
229
+ end
230
+
231
+ def process_colon2(exp)
232
+ "#{process(exp.shift)}::#{exp.shift}"
233
+ end
234
+
235
+ def process_colon3(exp)
236
+ "::#{exp.shift}"
237
+ end
238
+
239
+ def process_const(exp)
240
+ exp.shift.to_s
241
+ end
242
+
243
+ def process_cvar(exp)
244
+ "#{exp.shift}"
245
+ end
246
+
247
+ def process_cvasgn(exp)
248
+ "#{exp.shift} = #{process(exp.shift)}"
249
+ end
250
+
251
+ def process_cvdecl(exp)
252
+ "#{exp.shift} = #{process(exp.shift)}"
253
+ end
254
+
255
+ def process_dasgn_curr(exp)
256
+ s = exp.shift.to_s
257
+ s += "=" + process(exp.shift) unless exp.empty?
258
+ s
259
+ end
260
+
261
+ def process_dasgn(exp)
262
+ "#{exp.shift.to_s} = #{process(exp.shift)}"
263
+ end
264
+
265
+ def process_defined(exp)
266
+ "defined? #{process(exp.shift)}"
267
+ end
268
+
269
+ def process_defs(exp)
270
+ process_defn(exp)
271
+ end
272
+
273
+ def process_defn(exp)
274
+ t = exp[1].first
275
+ t2 = exp[2].first rescue nil
276
+ if t == :args and [:ivar, :attrset].include? t2 then
277
+ name = exp.shift
278
+ case t2
279
+ when :ivar then
280
+ exp.clear
281
+ return "attr_reader #{name.inspect}"
282
+ when :attrset then
283
+ exp.clear
284
+ return "attr_writer :#{name.to_s[0..-2]}"
285
+ else
286
+ raise "Unknown defn type: #{exp.inspect}"
287
+ end
288
+ end
289
+
290
+ case t
291
+ when :cfunc then
292
+ s = "# method '#{exp.shift}' defined in a C function"
293
+ exp.shift
294
+ return s
295
+ when :scope, :args then
296
+ name = exp.shift
297
+ args = process(exp.shift)
298
+ args = "" if args == "()"
299
+ body = indent(process(exp.shift))
300
+ return "def #{name}#{args}\n#{body}\nend".gsub(/\n\s*\n+/, "\n")
301
+ when :fcall then
302
+ # skip the fcall (to define_method and such) and grab the body
303
+ name = exp.shift
304
+ exp.shift # :fcall to define_method
305
+ body = process(exp.shift)
306
+ p name, body
307
+ raise "no"
308
+ else
309
+ raise "Unknown defn type: #{t} for #{exp.inspect}"
310
+ end
311
+ end
312
+
313
+ def process_defx(exp) # custom node type - TODO why in r2r?
314
+ name = exp.shift
315
+ args = process(exp.shift)
316
+ body = indent(process(exp.shift))
317
+ return "defx #{name}#{args}\n#{body}end".gsub(/\n\s*\n+/, "\n")
318
+ end
319
+
320
+ def process_dot2(exp)
321
+ "(#{process exp.shift}..#{process exp.shift})"
322
+ end
323
+
324
+ def process_dot3(exp)
325
+ "(#{process exp.shift}...#{process exp.shift})"
326
+ end
327
+
328
+ def process_dregx(exp)
329
+ "/#{process_dstr(exp)[1..-2]}/"
330
+ end
331
+
332
+ def process_dregx_once(exp)
333
+ process_dregx(exp) + "o"
334
+ end
335
+
336
+ def process_dstr(exp)
337
+ s = exp.shift.dump[0..-2]
338
+ until exp.empty?
339
+ pt = exp.shift
340
+ if pt.first == :str
341
+ s << process(pt)[1..-2]
342
+ else
343
+ s << '#{' + process(pt) + '}'
344
+ end
345
+ end
346
+ s + '"'
347
+ end
348
+
349
+ def process_dsym(exp)
350
+ s = ":" + exp.shift.dump[0..-2]
351
+ until exp.empty?
352
+ pt = exp.shift
353
+ if pt.first == :str
354
+ s << process(pt)[1..-2]
355
+ else
356
+ s << '#{' + process(pt) + '}'
357
+ end
358
+ end
359
+ s + '"'
360
+ end
361
+
362
+ def process_dvar(exp)
363
+ exp.shift.to_s
364
+ end
365
+
366
+ def process_dxstr(exp)
367
+ "`#{process_dstr(exp)[1..-2]}`"
368
+ end
369
+
370
+ def process_ensure(exp)
371
+ body = process exp.shift
372
+ ens = process exp.shift
373
+ return "#{body}\nensure\n#{indent ens}"
374
+ end
375
+
376
+ def process_false(exp)
377
+ "false"
378
+ end
379
+
380
+ def process_fcall(exp)
381
+ exp_orig = exp.deep_clone
382
+ name = exp.shift.to_s
383
+ args = exp.shift
384
+ code = []
385
+ unless args.nil? then
386
+ args[0] = :arglist if args.first == :array
387
+ code << process(args)
388
+ end
389
+ return "#{name}(#{code.join(', ')})"
390
+ end
391
+
392
+ def process_flip2(exp)
393
+ "#{process(exp.shift)}..#{process(exp.shift)}"
394
+ end
395
+
396
+ def process_flip3(exp)
397
+ "#{process(exp.shift)}...#{process(exp.shift)}"
398
+ end
399
+
400
+ def process_for(exp)
401
+ recv = process exp.shift
402
+ iter = process exp.shift
403
+ body = process exp.shift
404
+ return "for #{iter} in #{recv}\n#{indent body}\nend\n"
405
+ end
406
+
407
+ def process_gasgn(exp)
408
+ process_iasgn(exp)
409
+ end
410
+
411
+ def process_gvar(exp)
412
+ return exp.shift.to_s
413
+ end
414
+
415
+ def process_hash(exp)
416
+ result = []
417
+ until exp.empty?
418
+ result << "#{process(exp.shift)} => #{process(exp.shift)}"
419
+ end
420
+ return "{ #{result.join(', ')} }"
421
+ end
422
+
423
+ def process_iasgn(exp)
424
+ "#{exp.shift} = #{process exp.shift}"
425
+ end
426
+
427
+ def cond_indent_process(pt)
428
+ (pt and pt.first == :block) ? process(pt) : indent(process(pt))
429
+ end
430
+
431
+ def process_if(exp)
432
+ cond = process exp.shift
433
+ t = process exp.shift
434
+
435
+ type = t ? "if" : "unless"
436
+
437
+ s = ["#{type} #{cond} then"]
438
+ s << indent(t) if t
439
+
440
+ until exp.empty?
441
+ code = exp.shift
442
+ case code.first
443
+ when nil
444
+ # do nothing
445
+ when :if then
446
+ s << "els#{process(code).sub(/\nend\Z/, '')}"
447
+ else
448
+ s << "else\n#{cond_indent_process(code)}"
449
+ end
450
+
451
+ if t.nil? then
452
+ s[-1] = s[-1][5..-1] # remove else\n
453
+ t = true
454
+ end
455
+ end
456
+ s << "end"
457
+
458
+ s.join("\n")
459
+ end
460
+
461
+ def process_iter(exp)
462
+ iter = process exp.shift
463
+ args = process exp.shift
464
+ body = process exp.shift
465
+
466
+ b, e = if iter == "END" then
467
+ [ "{", "}" ]
468
+ else
469
+ [ "do", "end" ]
470
+ end
471
+
472
+ iter.sub!(/\(\)$/, '')
473
+
474
+ result = []
475
+ result << "#{iter} #{b}"
476
+ result << " |#{args}|" if args
477
+ if body then
478
+ result << "\n"
479
+ result << indent(body).chomp
480
+ result << "\n"
481
+ else
482
+ result << ' '
483
+ end
484
+ result << e
485
+ result.join
486
+ end
487
+
488
+ def process_ivar(exp)
489
+ exp.shift.to_s
490
+ end
491
+
492
+ def process_lasgn(exp)
493
+ s = "#{exp.shift}"
494
+ s += " = #{process exp.shift}" unless exp.empty?
495
+ s
496
+ end
497
+
498
+ def process_lit(exp)
499
+ obj = exp.shift
500
+ if obj.is_a? Range # to get around how parsed ranges turn into lits and lose parens
501
+ "(" + obj.inspect + ")"
502
+ else
503
+ obj.inspect
504
+ end
505
+ end
506
+
507
+ def process_lvar(exp)
508
+ exp.shift.to_s
509
+ end
510
+
511
+ def process_masgn(exp)
512
+ lhs = exp.shift
513
+ rhs = exp.shift
514
+
515
+ assert_type lhs, :array
516
+ lhs.shift
517
+ lhs = lhs.map { |l| process(l) }
518
+
519
+ unless rhs.nil? then
520
+ # HACK - but seems to work (see to_ary test) assert_type rhs, :array
521
+ rhs.shift
522
+ rhs = rhs.map { |r| process(r) }
523
+ return "#{lhs.join(", ")} = #{rhs.join(", ")}"
524
+ else
525
+ return lhs.join(", ")
526
+ end
527
+
528
+ end
529
+
530
+ def process_match(exp)
531
+ "#{process(exp.shift)}"
532
+ end
533
+
534
+ def process_match2(exp)
535
+ lhs = process(exp.shift)
536
+ rhs = process(exp.shift)
537
+ "#{lhs} =~ #{rhs}"
538
+ end
539
+
540
+ def process_match3(exp)
541
+ rhs = process(exp.shift)
542
+ lhs = process(exp.shift)
543
+ "#{lhs} =~ #{rhs}"
544
+ end
545
+
546
+ def process_module(exp)
547
+ s = "module #{exp.shift}\n"
548
+ body = ""
549
+ body << "#{process exp.shift}\n\n" until exp.empty?
550
+ s + indent(body) + "end"
551
+ end
552
+
553
+ def process_next(exp)
554
+ "next"
555
+ end
556
+
557
+ def process_nil(exp)
558
+ "nil"
559
+ end
560
+
561
+ def process_not(exp)
562
+ "(not #{process exp.shift})"
563
+ end
564
+
565
+ def process_nth_ref(exp)
566
+ "$#{exp.shift}"
567
+ end
568
+
569
+ def process_op_asgn1(exp)
570
+ # [[:lvar, :b], [:array, [:lit, 1]], :"||", [:lit, 10]]
571
+ lhs = process(exp.shift)
572
+ index = process(exp.shift)
573
+ msg = exp.shift
574
+ rhs = process(exp.shift)
575
+
576
+ "#{lhs}#{index} #{msg}= #{rhs}"
577
+ end
578
+
579
+ def process_op_asgn2(exp)
580
+ # [[:lvar, :c], :var=, :"||", [:lit, 20]]
581
+ lhs = process(exp.shift)
582
+ index = exp.shift.to_s[0..-2]
583
+ msg = exp.shift
584
+
585
+ rhs = process(exp.shift)
586
+
587
+ "#{lhs}.#{index} #{msg}= #{rhs}"
588
+ end
589
+
590
+ def process_op_asgn_or(exp)
591
+ # a ||= 1
592
+ # [[:lvar, :a], [:lasgn, :a, [:lit, 1]]]
593
+ exp.shift
594
+ process(exp.shift).sub(/=/, '||=')
595
+ end
596
+
597
+ def process_op_asgn_and(exp)
598
+ # a &&= 1
599
+ # [[:lvar, :a], [:lasgn, :a, [:lit, 1]]]
600
+ exp.shift
601
+ process(exp.shift).sub(/=/, '&&=')
602
+ end
603
+
604
+ def process_or(exp)
605
+ "(#{process exp.shift} or #{process exp.shift})"
606
+ end
607
+
608
+ def process_postexe(exp)
609
+ "END"
610
+ end
611
+
612
+ def process_redo(exp)
613
+ "redo"
614
+ end
615
+
616
+ def process_resbody(exp) # TODO: rewrite this fucker
617
+ code = []
618
+
619
+ until exp.nil? or exp.empty?
620
+ list = exp.shift
621
+ body = exp.shift
622
+
623
+ var = if list.last.first == :lasgn then
624
+ list.pop[1]
625
+ else
626
+ nil
627
+ end
628
+
629
+ list[0] = :arglist
630
+
631
+ if list then
632
+ code << "rescue #{process(list)}"
633
+ else
634
+ code << "rescue"
635
+ end
636
+ code.last << " => #{var}" if var
637
+
638
+ if body then
639
+ code << indent(process(body))
640
+ else
641
+ code << indent("# do nothing")
642
+ end
643
+
644
+ exp = exp.shift
645
+ exp.shift if exp
646
+ end
647
+
648
+ code.join("\n")
649
+ end
650
+
651
+ def process_rescue(exp)
652
+ # TODO: proper formatting depends on knowing the context
653
+ #
654
+ # a = b rescue c => [lasgn a [rescue b c]]
655
+ # begin; a = b; rescue c => [begin [rescue [lasgn a b] c]]
656
+ body = process exp.shift
657
+ resbody = process exp.shift
658
+ els = process exp.shift
659
+
660
+ code = []
661
+ code << indent(body)
662
+ code << resbody
663
+ if els then
664
+ code << "else"
665
+ code << indent(els)
666
+ else
667
+ code << "end\n"
668
+ end
669
+ code.join("\n")
670
+ end
671
+
672
+ def process_retry(exp)
673
+ "retry"
674
+ end
675
+
676
+ def process_return(exp)
677
+ return "return #{process exp.shift}"
678
+ end
679
+
680
+ def process_sclass(exp)
681
+ "class << #{process(exp.shift)}\n#{indent(process(exp.shift))}\nend"
682
+ end
683
+
684
+ def process_scope(exp)
685
+ return process(exp.shift)
686
+ end
687
+
688
+ def process_self(exp)
689
+ "self"
690
+ end
691
+
692
+ def process_splat(exp)
693
+ "*#{process(exp.shift)}"
694
+ end
695
+
696
+ def process_str(exp)
697
+ return exp.shift.dump
698
+ end
699
+
700
+ def process_super(exp)
701
+ args = exp.shift
702
+ args[0] = :arglist
703
+ "super(#{process(args)})"
704
+ end
705
+
706
+ def process_svalue(exp)
707
+ process(exp.shift)
708
+ end
709
+
710
+ def process_to_ary(exp)
711
+ process(exp.shift)
712
+ end
713
+
714
+ def process_true(exp)
715
+ "true"
716
+ end
717
+
718
+ def process_undef(exp)
719
+ "undef #{process(exp.shift)}"
720
+ end
721
+
722
+ def process_until(exp)
723
+ cond_loop(exp, 'until')
724
+ end
725
+
726
+ def process_valias(exp)
727
+ "alias #{exp.shift} #{exp.shift}"
728
+ end
729
+
730
+ def process_vcall(exp)
731
+ return exp.shift.to_s
732
+ end
733
+
734
+ def process_when(exp)
735
+ cond = process(exp.shift).to_s[1..-2]
736
+ code = indent(process(exp.shift))
737
+ code = indent "# do nothing" if code =~ /\A\s*\Z/
738
+ "when #{cond} then\n#{code.chomp}"
739
+ end
740
+
741
+ def process_while(exp)
742
+ cond_loop(exp, 'while')
743
+ end
744
+
745
+ def process_xstr(exp)
746
+ "`#{process_str(exp)[1..-2]}`"
747
+ end
748
+
749
+ def process_yield(exp)
750
+ args = exp.shift
751
+ if args then
752
+ args[0] = :arglist if args.first == :array
753
+ args = process(args)
754
+ end
755
+
756
+ "yield" + (args ? "(#{args})" : "")
757
+ end
758
+
759
+ def process_zarray(exp)
760
+ "[]"
761
+ end
762
+
763
+ def process_zsuper(exp)
764
+ "super"
765
+ end
766
+
767
+ def cond_loop(exp, name)
768
+ cond = process(exp.shift)
769
+ body = indent(process(exp.shift)).chomp
770
+ head_controlled = exp.empty? ? false : exp.shift
771
+
772
+ code = []
773
+ if head_controlled then
774
+ code << "#{name} #{cond} do"
775
+ code << body
776
+ code << "end"
777
+ else
778
+ code << "begin"
779
+ code << body
780
+ code << "end #{name} #{cond}"
781
+ end
782
+ code.join("\n")
783
+ end
784
+
785
+ ############################################################
786
+ # Rewriters
787
+
788
+ ##
789
+ # defn: [:defn, :name, [:args...], [:scope, [:block, ...]]]
790
+
791
+ def rewrite_defs(exp)
792
+ target = exp.delete_at 1
793
+ exp[1] = :"#{target}.#{exp[1]}"
794
+ rewrite_defn(exp)
795
+ end
796
+
797
+ def rewrite_defn(exp)
798
+ # REFACTOR this needs help now
799
+ exp.shift # :defn
800
+ name = exp.shift
801
+ args = s(:args)
802
+ body = Sexp.from_array exp.shift
803
+
804
+ case body.first
805
+ when :args then # already normalized
806
+ args = body
807
+ body = exp.shift
808
+ assert_type args, :args
809
+ assert_type body, :scope
810
+ assert_type body[1], :block
811
+ when :scope, :fbody then
812
+ body = body[1] if body.first == :fbody
813
+ args = body.last.delete_at 1
814
+ assert_type args, :args
815
+ assert_type body, :scope
816
+ assert_type body[1], :block
817
+
818
+ if body[1][1].first == :block_arg then
819
+ block_arg = body[1].delete_at 1
820
+ args << block_arg
821
+ end
822
+ when :bmethod then
823
+ body.shift # :bmethod
824
+ if body.first.first == :dasgn_curr then
825
+ # WARN: there are some implications here of having an empty
826
+ # :args below namely, "proc { || " does not allow extra args
827
+ # passed in.
828
+ dasgn = body.shift
829
+ assert_type dasgn, :dasgn_curr
830
+ dasgn.shift # type
831
+ args.push(*dasgn)
832
+ body.find_and_replace_all(:dvar, :lvar)
833
+ end
834
+ if body.first.first == :block then
835
+ body = s(:scope, body.shift)
836
+ else
837
+ body = s(:scope, s(:block, body.shift)) # single statement
838
+ end
839
+ when :dmethod
840
+ # BEFORE: [:defn, :dmethod_added, [:dmethod, :bmethod_maker, ...]]
841
+ # AFTER: [:defn, :dmethod_added, ...]
842
+ iter = body[2][1][2] # UGH! FIX
843
+ iter.delete_at 1 # fcall define_method
844
+ args = iter[1].find_and_replace_all(:dasgn_curr, :args)
845
+ iter.delete_at 1 # args
846
+ iter[0] = :block
847
+ body = s(:scope, iter.find_and_replace_all(:dvar, :lvar))
848
+ when :ivar, :attrset then
849
+ # do nothing
850
+ else
851
+ raise "Unknown :defn format: #{name.inspect} #{args.inspect} #{body.inspect}"
852
+ end
853
+
854
+ return s(:defn, name, args, body)
855
+ end
856
+
857
+ def rewrite_resbody(exp)
858
+ result = []
859
+
860
+ code = result
861
+ while exp.first == :resbody do
862
+ code << exp.shift
863
+ list = exp.shift
864
+ body = exp.shift
865
+ exp = exp.shift # either another resbody or nil
866
+
867
+ # code may be nil, :lasgn, or :block
868
+ case body.first
869
+ when nil then
870
+ # do nothing
871
+ when :lasgn then
872
+ # TODO: check that it is assigning $!
873
+ list << body
874
+ body = nil
875
+ when :block then
876
+ # TODO: check that it is assigning $!
877
+ list << body.delete_at(1) if body[1].first == :lasgn
878
+ else
879
+ raise "unknown: #{code.inspect}"
880
+ end
881
+
882
+ code << list << body
883
+ if exp then
884
+ code = []
885
+ result << code
886
+ end
887
+ end
888
+
889
+ result
890
+ end
891
+
892
+ ############################################################
893
+ # Utility Methods:
894
+
895
+ def indent(s)
896
+ s.to_s.map{|line| @indent + line}.join
897
+ end
898
+ end
899
+
900
+ class Method
901
+ def with_class_and_method_name
902
+ if self.inspect =~ /<Method: (.*)\#(.*)>/ then
903
+ klass = eval $1
904
+ method = $2.intern
905
+ raise "Couldn't determine class from #{self.inspect}" if klass.nil?
906
+ return yield(klass, method)
907
+ else
908
+ raise "Can't parse signature: #{self.inspect}"
909
+ end
910
+ end
911
+
912
+ def to_sexp
913
+ with_class_and_method_name do |klass, method|
914
+ ParseTree.new(false).parse_tree_for_method(klass, method)
915
+ end
916
+ end
917
+ end
918
+
919
+ class ProcStoreTmp
920
+ @@n = 0
921
+ def self.name
922
+ @@n += 1
923
+ return :"myproc#{@@n}"
924
+ end
925
+ end
926
+
927
+ class UnboundMethod
928
+ def to_ruby
929
+ name = ProcStoreTmp.name
930
+ ProcStoreTmp.send(:define_method, name, self)
931
+ m = ProcStoreTmp.new.method(name)
932
+ result = m.to_ruby.sub(/def #{name}\(([^\)]*)\)/,
933
+ 'proc { |\1|').sub(/end\Z/, '}')
934
+ return result
935
+ end
936
+ end
937
+
938
+ class Proc
939
+ def to_method
940
+ name = ProcStoreTmp.name
941
+ ProcStoreTmp.send(:define_method, name, self)
942
+ ProcStoreTmp.new.method(name)
943
+ end
944
+
945
+ def to_sexp
946
+ body = self.to_method.to_sexp[2][1..-1]
947
+ [:proc, *body]
948
+ end
949
+
950
+ def to_ruby
951
+ self.to_method.to_ruby.sub(/def #{name}\(([^\)]*)\)/,
952
+ 'proc { |\1|').sub(/end\Z/, '}')
953
+ end
954
+ end
@@ -0,0 +1,134 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require 'test/unit'
4
+ begin require 'rubygems'; rescue LoadError; end
5
+ require 'ruby2ruby'
6
+ require 'pt_testcase'
7
+
8
+ class TestRubyToRuby < Test::Unit::TestCase
9
+ def setup
10
+ @processor = RubyToRuby.new
11
+ end
12
+
13
+ def test_rewrite_resbody
14
+ inn = [:resbody,
15
+ [:array, [:const, :SyntaxError]],
16
+ [:block, [:lasgn, :e1, [:gvar, :$!]], [:lit, 2]],
17
+ [:resbody,
18
+ [:array, [:const, :Exception]],
19
+ [:block, [:lasgn, :e2, [:gvar, :$!]], [:lit, 3]]]]
20
+
21
+ out = [:resbody,
22
+ [:array, [:const, :SyntaxError], [:lasgn, :e1, [:gvar, :$!]]],
23
+ [:block, [:lit, 2]],
24
+ [:resbody,
25
+ [:array, [:const, :Exception], [:lasgn, :e2, [:gvar, :$!]]],
26
+ [:block, [:lit, 3]]]]
27
+
28
+ assert_equal out, @processor.rewrite_resbody(inn)
29
+ end
30
+
31
+ def test_rewrite_resbody_lasgn
32
+ inn = [:resbody,
33
+ [:array, [:const, :SyntaxError]],
34
+ [:lasgn, :e1, [:gvar, :$!]],
35
+ [:resbody,
36
+ [:array, [:const, :Exception]],
37
+ [:block, [:lasgn, :e2, [:gvar, :$!]], [:lit, 3]]]]
38
+
39
+ out = [:resbody,
40
+ [:array, [:const, :SyntaxError], [:lasgn, :e1, [:gvar, :$!]]],
41
+ nil,
42
+ [:resbody,
43
+ [:array, [:const, :Exception], [:lasgn, :e2, [:gvar, :$!]]],
44
+ [:block, [:lit, 3]]]]
45
+
46
+ assert_equal out, @processor.rewrite_resbody(inn)
47
+ end
48
+
49
+
50
+ ParseTreeTestCase.testcases.each do |node, data|
51
+ define_method :"test_#{node}" do
52
+ pt = data['ParseTree']
53
+ rb = data['Ruby2Ruby'] || data['Ruby']
54
+
55
+ result = @processor.process(pt)
56
+
57
+ assert_not_nil pt, "ParseTree for #{node} undefined"
58
+ assert_not_nil rb, "Ruby for #{node} undefined"
59
+ assert_equal rb, result
60
+ end
61
+ end
62
+
63
+
64
+ end
65
+
66
+ class R2RTest < Test::Unit::TestCase
67
+
68
+ def xtest_self_translation
69
+ r2r2r = RubyToRuby.translate(RubyToRuby).sub("RubyToRuby","RubyToRubyToRuby")
70
+
71
+ Object.class_eval r2r2r
72
+
73
+ r2r2r2 = RubyToRubyToRuby.translate(RubyToRuby).sub("RubyToRuby","RubyToRubyToRuby")
74
+ r2r2r2r = RubyToRubyToRuby.translate(RubyToRubyToRuby)
75
+ assert_equal(r2r2r, r2r2r2, "first generation must equal second generation")
76
+ assert_equal(r2r2r, r2r2r2r, "first generation must equal third generation")
77
+ end
78
+
79
+ def hairy_method(z,x=10,y=20*z.abs,&blok)
80
+ n = 1 + 2 * 40.0 / (z - 2)
81
+ retried = false
82
+ begin
83
+ raise ArgumentError, n if retried
84
+ n -= yield x,y,z,[z,x,y].map(&blok)
85
+ n = n / 1.1 until n < 500 # TODO: translated isn't respecting post iter
86
+ n = "hop hop #{"%.4f" % n}"
87
+ raise n
88
+ rescue RuntimeError => e
89
+ raise if n != e.message
90
+ n = lambda do |i|
91
+ lambda do |j|
92
+ "#{i} #{z+2*2} #{j.message.reverse}"
93
+ end
94
+ end[n].call(e)
95
+ unless retried
96
+ retried = true
97
+ retry
98
+ end
99
+ rescue ArgumentError => e
100
+ e.message
101
+ rescue
102
+ end
103
+ ensure
104
+ x << "ensure a-working"
105
+ end
106
+
107
+ def foobar a, &block; block.call(a) end
108
+ def k; foobar [1,2,3].each { |x| x*2 } do |x| x*2 end end
109
+
110
+ def test_block_precedence_escape
111
+ eval RubyToRuby.translate(self.class, :k).sub(" k"," l")
112
+ assert_equal(k, l)
113
+ end
114
+
115
+ def HACK_test_hairy_method
116
+ src = RubyToRuby.translate(self.class, :hairy_method).sub(" h", " f")
117
+
118
+ eval src
119
+
120
+ blk = lambda{|x,y,z,arr|
121
+ unless y
122
+ x.to_i*2
123
+ else
124
+ x.to_i*y*z*arr.inject(1){|s,i| s+i}
125
+ end
126
+ }
127
+ x1 = ""
128
+ x2 = ""
129
+ res = [hairy_method(-5,x1,&blk), fairy_method(-5,x2,&blk)]
130
+ assert_equal(res.first, res.last)
131
+ assert_equal(x1, x2)
132
+ assert_equal("ensure a-working", x1)
133
+ end
134
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: ruby2ruby
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.1.0
7
+ date: 2006-10-11 00:00:00 -07:00
8
+ summary: ruby2ruby provides a means of generating pure ruby code easily from ParseTree's Sexps.
9
+ require_paths:
10
+ - lib
11
+ - test
12
+ email: ryand-ruby@zenspider.com
13
+ homepage: http://seattlerb.rubyforge.org/
14
+ rubyforge_project: seattlerb
15
+ description: ruby2ruby provides a means of generating pure ruby code easily from ParseTree's Sexps. This makes making dynamic language processors much easier in ruby than ever before.
16
+ autorequire:
17
+ default_executable:
18
+ bindir: bin
19
+ has_rdoc: true
20
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
21
+ requirements:
22
+ - - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.0
25
+ version:
26
+ platform: ruby
27
+ signing_key:
28
+ cert_chain:
29
+ post_install_message:
30
+ authors:
31
+ - Ryan Davis
32
+ files:
33
+ - History.txt
34
+ - Manifest.txt
35
+ - README.txt
36
+ - Rakefile
37
+ - lib/ruby2ruby.rb
38
+ - test/test_ruby2ruby.rb
39
+ test_files: []
40
+
41
+ rdoc_options: []
42
+
43
+ extra_rdoc_files: []
44
+
45
+ executables: []
46
+
47
+ extensions: []
48
+
49
+ requirements: []
50
+
51
+ dependencies:
52
+ - !ruby/object:Gem::Dependency
53
+ name: hoe
54
+ version_requirement:
55
+ version_requirements: !ruby/object:Gem::Version::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 1.1.1
60
+ version:
61
+ - !ruby/object:Gem::Dependency
62
+ name: ParseTree
63
+ version_requirement:
64
+ version_requirements: !ruby/object:Gem::Version::Requirement
65
+ requirements:
66
+ - - ">"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.0.0
69
+ version: