ruby2ruby 1.1.0

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