virtual_keywords 0.1.0 → 0.3.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.
Files changed (57) hide show
  1. data/lib/parsetree/History.txt +477 -0
  2. data/lib/parsetree/Manifest.txt +19 -0
  3. data/lib/parsetree/README.txt +97 -0
  4. data/lib/parsetree/Rakefile +37 -0
  5. data/lib/parsetree/bin/parse_tree_abc +89 -0
  6. data/lib/parsetree/bin/parse_tree_audit +28 -0
  7. data/lib/parsetree/bin/parse_tree_deps +62 -0
  8. data/lib/parsetree/bin/parse_tree_show +63 -0
  9. data/lib/parsetree/demo/printer.rb +20 -0
  10. data/lib/parsetree/lib/gauntlet_parsetree.rb +121 -0
  11. data/lib/parsetree/lib/parse_tree.rb +1202 -0
  12. data/lib/parsetree/lib/parse_tree_extensions.rb +59 -0
  13. data/lib/parsetree/lib/unified_ruby.rb +421 -0
  14. data/lib/parsetree/test/something.rb +53 -0
  15. data/lib/parsetree/test/test_parse_tree.rb +2567 -0
  16. data/lib/parsetree/test/test_parse_tree_extensions.rb +107 -0
  17. data/lib/parsetree/test/test_unified_ruby.rb +57 -0
  18. data/lib/parsetree/validate.sh +31 -0
  19. data/lib/sexps/{count_to_ten_sexp.txt → count_to_ten_sexp.rb} +0 -0
  20. data/lib/sexps/{sexps_and.txt → sexps_and.rb} +0 -0
  21. data/lib/sexps/{sexps_case_when.txt → sexps_case_when.rb} +0 -0
  22. data/lib/sexps/{sexps_greet.txt → sexps_greet.rb} +0 -0
  23. data/lib/sexps/sexps_not.rb +24 -0
  24. data/lib/sexps/{sexps_rewritten_keywords.txt → sexps_rewritten_keywords.rb} +0 -0
  25. data/lib/sexps/{sexps_symbolic_and.txt → sexps_symbolic_and.rb} +0 -0
  26. data/lib/sexps/sexps_until.rb +60 -0
  27. data/lib/sexps/{sexps_while.txt → sexps_while.rb} +0 -0
  28. data/lib/spec/class_reflection_spec.rb +64 -0
  29. data/lib/spec/keyword_rewriter_spec.rb +7 -54
  30. data/lib/spec/not_rewriter_spec.rb +25 -0
  31. data/lib/spec/parser_strategy_spec.rb +23 -0
  32. data/lib/spec/sexp_stringifier_spec.rb +19 -0
  33. data/lib/spec/spec_helper.rb +61 -307
  34. data/lib/spec/until_rewriter_spec.rb +26 -0
  35. data/lib/spec/virtualizees/and_user.rb +17 -0
  36. data/lib/spec/virtualizees/case_when_user.rb +20 -0
  37. data/lib/spec/virtualizees/fizzbuzzer.rb +15 -0
  38. data/lib/spec/virtualizees/greeter.rb +127 -0
  39. data/lib/spec/virtualizees/not_user.rb +9 -0
  40. data/lib/spec/virtualizees/operator_user.rb +15 -0
  41. data/lib/spec/virtualizees/or_user.rb +17 -0
  42. data/lib/spec/virtualizees/parents.rb +8 -0
  43. data/lib/spec/virtualizees/until_user.rb +16 -0
  44. data/lib/spec/virtualizees/while_user.rb +16 -0
  45. data/lib/spec/virtualizer_spec.rb +46 -83
  46. data/lib/virtual_keywords/class_reflection.rb +88 -0
  47. data/lib/virtual_keywords/deep_copy_array.rb +12 -0
  48. data/lib/virtual_keywords/keyword_rewriter.rb +54 -0
  49. data/lib/virtual_keywords/parser_strategy.rb +40 -0
  50. data/lib/virtual_keywords/rewritten_keywords.rb +15 -0
  51. data/lib/virtual_keywords/sexp_stringifier.rb +1 -5
  52. data/lib/virtual_keywords/version.rb +1 -1
  53. data/lib/virtual_keywords/virtualizer.rb +30 -115
  54. data/lib/virtual_keywords.rb +31 -5
  55. metadata +117 -90
  56. data/lib/spec/class_mirrorer_spec.rb +0 -18
  57. data/lib/virtual_keywords/class_mirrorer.rb +0 -39
@@ -1,284 +1,29 @@
1
- require 'sexp_processor'
2
- require 'parse_tree'
3
- require 'ruby2ruby'
4
-
5
- require 'virtual_keywords/sexp_stringifier'
6
- require 'virtual_keywords/class_mirrorer'
7
- require 'virtual_keywords/virtualizer'
8
- require 'virtual_keywords/keyword_rewriter'
9
- require 'virtual_keywords/rewritten_keywords'
1
+ if RUBY_VERSION.start_with? '1.8'
2
+ require 'virtual_keywords'
3
+ else
4
+ require_relative '../virtual_keywords'
5
+ end
6
+
7
+ # Classes containing code that will be rewritten
8
+ # They act as test data for this gem.
9
+ require 'virtualizees/parents'
10
+ require 'virtualizees/fizzbuzzer'
11
+ require 'virtualizees/greeter'
12
+ require 'virtualizees/and_user'
13
+ require 'virtualizees/or_user'
14
+ require 'virtualizees/not_user'
15
+ require 'virtualizees/operator_user'
16
+ require 'virtualizees/while_user'
17
+ require 'virtualizees/until_user'
18
+ require 'virtualizees/case_when_user'
10
19
 
11
20
  require 'rspec'
12
21
 
13
- # Classes the specs should use.
14
- module ActiveRecord
15
- class Base
16
- end
17
- end
18
-
19
- class ApplicationController
20
- end
21
-
22
- class Fizzbuzzer < ActiveRecord::Base
23
- def fizzbuzz(n)
24
- (1..n).map { |i|
25
- if i % 3 == 0 and i % 5 == 0
26
- "fizzbuzz"
27
- elsif i % 3 == 0
28
- "fizz"
29
- elsif i % 5 == 0
30
- "buzz"
31
- else
32
- i.to_s
33
- end
34
- }
35
- end
36
- end
37
-
38
- class Greeter < ApplicationController
39
- def initialize(hello)
40
- @hello = hello
41
- end
42
-
43
- # The following two methods are before/after examples. The rewriter
44
- # should turn greet's body into greet_changed's. Running ParseTree over
45
- # them, I got two sexps (in sexps_greet.txt), which I read to
46
- # reverse-engineer the format. There may be edge cases (Ruby's grammar
47
- # is much more complex than Lisp's!)
48
- #
49
- # spec/if_processor_spec runs a test over greet
50
-
51
- # An example conditional: if and else
52
- def greet_if_else
53
- if @hello
54
- 'Hello World! (if else)'
55
- else
56
- # Compound expressions should be preserved
57
- # (i.e. not evaluated too early or in the wrong context)
58
- 'Good' + 'bye (if else)'
59
- end
60
- end
61
-
62
- # If without else
63
- def greet_if_without_else
64
- if @hello
65
- 'Hello World! (if without else)'
66
- end
67
- end
68
-
69
- # Postfix if
70
- def greet_postfix_if
71
- 'Hello World! (postfix if)' if @hello
72
- end
73
-
74
- # If, then, else
75
- def greet_if_then_else
76
- if @hello then 'Hello World! (if then else)' else 'Goodbye (if then else)' end
77
- end
78
-
79
- # If, then, no else
80
- def greet_if_then_no_else
81
- if @hello then 'Hello World! (if then)' end
82
- end
83
-
84
- # Unless
85
- def greet_unless
86
- unless @hello
87
- 'Goodbye (unless)'
88
- end
89
- end
90
-
91
- # Unless, then else
92
- def greet_unless_else
93
- unless @hello
94
- 'Goodbye (unless else)'
95
- else
96
- 'Hello World! (unless else)'
97
- end
98
- end
99
-
100
- # Postfix unless
101
- def greet_postfix_unless
102
- 'Goodbye (postfix unless)' unless @hello
103
- end
104
-
105
- # What the conditional in greet should look like after processing
106
- def greet_changed
107
- VirtualKeywords::REWRITTEN_KEYWORDS.call_if(
108
- self, lambda { @hello }, lambda { 'Hello World! (if else)' },
109
- lambda { 'Good' + 'bye (if else)' })
110
- end
111
-
112
- # All together now
113
- def greet_all
114
- result = ''
115
- # 1
116
- if @hello
117
- result = 'Hello'
118
- else
119
- result = 'Goodbye'
120
- end
121
- # 2
122
- if 2 + 2 == 4
123
- result += '\nMath is right'
124
- end
125
- # 3
126
- result += '\nThis is supposed to look like English' if false
127
- # 4
128
- unless 2 + 9 == 10
129
- result += '\nMath should work in unless too'
130
- end
131
- # 5
132
- result += '\nWorld!' unless true
133
-
134
- result
135
- end
136
-
137
- def greet_nested
138
- if true
139
- # This if should be processed, even though it never happens!
140
- if 2 + 2 == 4
141
- 'Math is right'
142
- else
143
- 'Weird'
144
- end
145
- else
146
- # This if should be expanded, but NOT evaluated!
147
- puts 'hi there' if true
148
- 'The false case'
149
- end
150
- end
151
-
152
- def greet_block
153
- # Multiple statements in the if/else clauses
154
- if @hello
155
- value = 5
156
- value += 5
157
-
158
- value
159
- else
160
- thing = 9
161
-
162
- thing
163
- end
164
- end
165
-
166
- def count_to_ten
167
- [1..10].each do |index|
168
- puts index
169
- end
170
- end
171
- end
172
-
173
- class AndUser < ActiveRecord::Base
174
- def initialize(value)
175
- @value = value
176
- end
177
-
178
- def method_with_and
179
- @value and true
180
- end
181
-
182
- def if_with_and
183
- if @value and true
184
- 'Both @value and true were true (the latter is no surprise)'
185
- else
186
- '@value must have been false, I doubt true was false!'
187
- end
188
- end
189
-
190
- def method_with_and_result
191
- my_and(lambda { @value }, lambda { true })
192
- end
193
- end
194
-
195
- class OrUser < ApplicationController
196
- def initialize(value)
197
- @value = value
198
- end
199
-
200
- def method_with_or
201
- @value or false
202
- end
203
-
204
- def if_with_or
205
- if @value or true
206
- 'Both @value or true were true (the latter is no surprise)'
207
- else
208
- "This can't happen!"
209
- end
210
- end
211
- end
212
-
213
- # Ruby also lets you use the && and || operators
214
- # I think they have different precedence rules
215
- class OperatorUser < ActiveRecord::Base
216
- def initialize(value)
217
- @value = value
218
- end
219
-
220
- def symbolic_and
221
- @value && false
222
- end
223
-
224
- def symbolic_and_result
225
- my_conditional_and(lambda { @value }, lambda { false })
226
- end
227
- end
228
-
229
- class WhileUser < ApplicationController
230
- def initialize(value)
231
- @value = value
232
- @i = 0
233
- @counts = []
234
- end
235
-
236
- def while_count_to_value
237
- while @i < @value
238
- @counts << @i
239
- @i += 1
240
- end
241
-
242
- @counts
243
- end
244
-
245
- def while_result
246
- my_while(
247
- lambda { @i < value },
248
- lambda do
249
- @counts << @i
250
- @i += 1
251
- end
252
- )
253
- end
254
- end
255
-
256
- class CaseWhenUser < ApplicationController
257
- def initialize(value)
258
- @value = value
259
- end
260
-
261
- def describe_value
262
- case @value
263
- when 1
264
- :one
265
- when 3..5
266
- :three_to_five
267
- when 7, 9
268
- :seven_or_nine
269
- when value * 10 < 90
270
- :passes_multiplication_test
271
- else
272
- :something_else
273
- end
274
- end
275
- end
276
-
277
22
  # Helper classes and functions for rewriter specs
278
23
 
279
24
  # Given a class and a method name, return a sexpified method.
280
25
  def method_to_sexp(klass, method)
281
- SexpProcessor.new.process(ParseTree.translate(klass, method))
26
+ VirtualKeywords::ParserStrategy.new.translate_instance_method(klass, method)
282
27
  end
283
28
 
284
29
  # Sexpify all non-inherited instance methods of a class and return them in
@@ -295,68 +40,76 @@ end
295
40
  module TrackIfs
296
41
  @my_if_calls = 0 # Don't forget to reset this before each spec!
297
42
 
298
- def increment_my_if_calls
299
- @my_if_calls += 1
300
- end
301
-
302
43
  def my_if
303
44
  # Dummy if that increments @my_if_calls, then runs as normal
304
- @my_if ||= lambda { |condition, then_do, else_do|
305
- increment_my_if_calls
45
+ @my_if ||= lambda do |condition, then_do, else_do|
46
+ @my_if_calls += 1
306
47
  if condition.call
307
48
  then_do.call
308
49
  else
309
50
  else_do.call
310
51
  end
311
- }
52
+ end
312
53
  end
313
54
  end
314
55
 
315
56
  module TrackAnds
316
57
  @my_and_calls = 0
317
58
 
318
- def increment_my_and_calls
319
- @my_and_calls += 1
320
- end
321
-
322
59
  def my_and
323
60
  # Dummy if that increments @my_if_calls, then runs as normal
324
- @my_and ||= lambda { |first, second|
325
- increment_my_and_calls
61
+ @my_and ||= lambda do |first, second|
62
+ @my_and_calls += 1
326
63
  first.call and second.call
327
- }
64
+ end
328
65
  end
329
66
  end
330
67
 
331
68
  module TrackOrs
332
69
  @my_or_calls = 0
333
70
 
334
- def increment_my_or_calls
335
- @my_or_calls += 1
336
- end
337
-
338
71
  def my_or
339
- @my_or ||= lambda { |first, second|
340
- increment_my_or_calls
72
+ @my_or ||= lambda do |first, second|
73
+ @my_or_calls += 1
341
74
  first.call or second.call
342
- }
75
+ end
343
76
  end
344
77
  end
345
78
 
346
- module TrackWhiles
347
- @my_while_calls = 0
79
+ module TrackNots
80
+ @my_not_calls = 0
348
81
 
349
- def increment_my_while_calls
350
- @my_while_calls += 1
82
+ def my_not
83
+ @my_not ||= lambda do |value|
84
+ @my_not_calls += 1
85
+ not value.call
86
+ end
351
87
  end
88
+ end
89
+
90
+ module TrackWhiles
91
+ @my_while_calls = 0
352
92
 
353
93
  def my_while
354
- @my_while ||= lambda { |condition, body|
355
- increment_my_while_calls
94
+ @my_while ||= lambda do |condition, body|
95
+ @my_while_calls += 1
356
96
  while condition.call
357
97
  body.call
358
98
  end
359
- }
99
+ end
100
+ end
101
+ end
102
+
103
+ module TrackUntils
104
+ @my_until_calls = 0
105
+
106
+ def my_until
107
+ @my_until ||= lambda do |condition, body|
108
+ @my_until_calls += 1
109
+ until condition.call
110
+ body.call
111
+ end
112
+ end
360
113
  end
361
114
  end
362
115
 
@@ -372,10 +125,10 @@ module DoRewrite
372
125
 
373
126
  def do_rewrite(method_name, object, verbose = false)
374
127
  sexp = @methods[method_name]
375
- result = sexp
376
- rewriters.each do |rewriter|
377
- result = rewriter.process result
378
- end
128
+ # Run all rewriters on the sexp
129
+ result = rewriters.reduce(sexp) { |rewritee, rewriter|
130
+ rewriter.process rewritee
131
+ }
379
132
  stringifier = VirtualKeywords::SexpStringifier.new
380
133
 
381
134
  # Visually inspecting this result, it appears to be right
@@ -388,7 +141,8 @@ module DoRewrite
388
141
  # old and new code should produce the same result,
389
142
  # except that @my_*_calls is incremented
390
143
  old_result = object.send method_name
391
- VirtualKeywords::ClassReflection.install_method_on_instance(object, code_result)
144
+ VirtualKeywords::ClassReflection.new.install_method_on_instance(
145
+ object, code_result)
392
146
  new_result = object.send method_name
393
147
 
394
148
  new_result.should eql old_result
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'UntilRewriter' do
4
+ include TrackUntils, DoRewrite
5
+
6
+ before :each do
7
+ @until_user = UntilUser.new 10
8
+ @methods = sexpify_instance_methods UntilUser
9
+ @until_rewriter = VirtualKeywords::UntilRewriter.new
10
+
11
+ @my_until_calls = 0
12
+
13
+ VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
14
+ @until_user, :until, my_until)
15
+ end
16
+
17
+ def rewriters
18
+ [@until_rewriter]
19
+ end
20
+
21
+ it 'rewrites "until" expressions' do
22
+ do_rewrite(:until_count_to_value, @until_user)
23
+ @my_until_calls.should eql 1
24
+ end
25
+
26
+ end
@@ -0,0 +1,17 @@
1
+ class AndUser < ActiveRecord::Base
2
+ def initialize(value)
3
+ @value = value
4
+ end
5
+
6
+ def method_with_and
7
+ @value and true
8
+ end
9
+
10
+ def if_with_and
11
+ if @value and true
12
+ 'Both @value and true were true (the latter is no surprise)'
13
+ else
14
+ '@value must have been false, I doubt true was false!'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ class CaseWhenUser < ApplicationController
2
+ def initialize(value)
3
+ @value = value
4
+ end
5
+
6
+ def describe_value
7
+ case @value
8
+ when 1
9
+ :one
10
+ when 3..5
11
+ :three_to_five
12
+ when 7, 9
13
+ :seven_or_nine
14
+ when value * 10 < 90
15
+ :passes_multiplication_test
16
+ else
17
+ :something_else
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ class Fizzbuzzer < ActiveRecord::Base
2
+ def fizzbuzz(n)
3
+ (1..n).map { |i|
4
+ if i % 3 == 0 and i % 5 == 0
5
+ "fizzbuzz"
6
+ elsif i % 3 == 0
7
+ "fizz"
8
+ elsif i % 5 == 0
9
+ "buzz"
10
+ else
11
+ i.to_s
12
+ end
13
+ }
14
+ end
15
+ end
@@ -0,0 +1,127 @@
1
+ class Greeter < ApplicationController
2
+ def initialize(hello)
3
+ @hello = hello
4
+ end
5
+
6
+ # The following two methods are before/after examples. The rewriter
7
+ # should turn greet's body into greet_changed's. Running ParseTree over
8
+ # them, I got two sexps (in sexps_greet.txt), which I read to
9
+ # reverse-engineer the format. There may be edge cases (Ruby's grammar
10
+ # is much more complex than Lisp's!)
11
+ #
12
+ # spec/if_processor_spec runs a test over greet
13
+
14
+ # An example conditional: if and else
15
+ def greet_if_else
16
+ if @hello
17
+ 'Hello World! (if else)'
18
+ else
19
+ # Compound expressions should be preserved
20
+ # (i.e. not evaluated too early or in the wrong context)
21
+ 'Good' + 'bye (if else)'
22
+ end
23
+ end
24
+
25
+ # If without else
26
+ def greet_if_without_else
27
+ if @hello
28
+ 'Hello World! (if without else)'
29
+ end
30
+ end
31
+
32
+ # Postfix if
33
+ def greet_postfix_if
34
+ 'Hello World! (postfix if)' if @hello
35
+ end
36
+
37
+ # If, then, else
38
+ def greet_if_then_else
39
+ if @hello then 'Hello World! (if then else)' else 'Goodbye (if then else)' end
40
+ end
41
+
42
+ # If, then, no else
43
+ def greet_if_then_no_else
44
+ if @hello then 'Hello World! (if then)' end
45
+ end
46
+
47
+ # Unless
48
+ def greet_unless
49
+ unless @hello
50
+ 'Goodbye (unless)'
51
+ end
52
+ end
53
+
54
+ # Unless, then else
55
+ def greet_unless_else
56
+ unless @hello
57
+ 'Goodbye (unless else)'
58
+ else
59
+ 'Hello World! (unless else)'
60
+ end
61
+ end
62
+
63
+ # Postfix unless
64
+ def greet_postfix_unless
65
+ 'Goodbye (postfix unless)' unless @hello
66
+ end
67
+
68
+ # All together now
69
+ def greet_all
70
+ result = ''
71
+ # 1
72
+ if @hello
73
+ result = 'Hello'
74
+ else
75
+ result = 'Goodbye'
76
+ end
77
+ # 2
78
+ if 2 + 2 == 4
79
+ result += '\nMath is right'
80
+ end
81
+ # 3
82
+ result += '\nThis is supposed to look like English' if false
83
+ # 4
84
+ unless 2 + 9 == 10
85
+ result += '\nMath should work in unless too'
86
+ end
87
+ # 5
88
+ result += '\nWorld!' unless true
89
+
90
+ result
91
+ end
92
+
93
+ def greet_nested
94
+ if true
95
+ # This if should be processed, even though it never happens!
96
+ if 2 + 2 == 4
97
+ 'Math is right'
98
+ else
99
+ 'Weird'
100
+ end
101
+ else
102
+ # This if should be expanded, but NOT evaluated!
103
+ puts 'hi there' if true
104
+ 'The false case'
105
+ end
106
+ end
107
+
108
+ def greet_block
109
+ # Multiple statements in the if/else clauses
110
+ if @hello
111
+ value = 5
112
+ value += 5
113
+
114
+ value
115
+ else
116
+ thing = 9
117
+
118
+ thing
119
+ end
120
+ end
121
+
122
+ def count_to_ten
123
+ [1..10].each do |index|
124
+ puts index
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,9 @@
1
+ class NotUser
2
+ def initialize value
3
+ @value = value
4
+ end
5
+
6
+ def negate
7
+ not @value
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ # Ruby also lets you use the && and || operators
2
+ # I think they have different precedence rules
3
+ class OperatorUser < ActiveRecord::Base
4
+ def initialize(value)
5
+ @value = value
6
+ end
7
+
8
+ def symbolic_and
9
+ @value && false
10
+ end
11
+
12
+ def symbolic_and_result
13
+ my_conditional_and(lambda { @value }, lambda { false })
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ class OrUser < ApplicationController
2
+ def initialize(value)
3
+ @value = value
4
+ end
5
+
6
+ def method_with_or
7
+ @value or false
8
+ end
9
+
10
+ def if_with_or
11
+ if @value or true
12
+ 'Both @value or true were true (the latter is no surprise)'
13
+ else
14
+ "This can't happen!"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ # Classes the specs should use.
2
+ module ActiveRecord
3
+ class Base
4
+ end
5
+ end
6
+
7
+ class ApplicationController
8
+ end
@@ -0,0 +1,16 @@
1
+ class UntilUser
2
+ def initialize(value)
3
+ @value = value
4
+ @i = 0
5
+ @counts = []
6
+ end
7
+
8
+ def until_count_to_value
9
+ until @i > @value
10
+ @counts << @i
11
+ @i += 1
12
+ end
13
+
14
+ @counts
15
+ end
16
+ end