virtual_keywords 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/sexps/sexps_case_when.txt +34 -0
- data/lib/sexps/sexps_while.txt +65 -0
- data/lib/spec/and_rewriter_spec.rb +59 -0
- data/lib/spec/if_rewriter_spec.rb +74 -0
- data/lib/spec/keyword_rewriter_spec.rb +11 -197
- data/lib/spec/or_rewriter_spec.rb +35 -0
- data/lib/spec/spec_helper.rb +186 -3
- data/lib/spec/virtualizer_spec.rb +24 -1
- data/lib/spec/while_rewriter_spec.rb +25 -0
- data/lib/virtual_keywords/keyword_rewriter.rb +46 -3
- data/lib/virtual_keywords/rewritten_keywords.rb +22 -8
- data/lib/virtual_keywords/version.rb +1 -1
- data/lib/virtual_keywords/virtualizer.rb +50 -36
- metadata +9 -3
@@ -0,0 +1,34 @@
|
|
1
|
+
# Before translation
|
2
|
+
# Ok, so each s(:when) has 2 elements: the condition and the consequence
|
3
|
+
# They're not wrapped in an array or anything, so this is a lot more
|
4
|
+
# complicated...
|
5
|
+
s(:defn, :describe_value,
|
6
|
+
s(:scope,
|
7
|
+
s(:block, s(:args),
|
8
|
+
s(:case, s(:ivar, :@value),
|
9
|
+
s(:when,
|
10
|
+
s(:array, s(:lit, 1)),
|
11
|
+
s(:lit, :one)
|
12
|
+
),
|
13
|
+
s(:when,
|
14
|
+
s(:array, s(:lit, 3..5)),
|
15
|
+
s(:lit, :three_to_five)
|
16
|
+
),
|
17
|
+
s(:when,
|
18
|
+
s(:array, s(:lit, 7), s(:lit, 9)),
|
19
|
+
s(:lit, :seven_or_nine)
|
20
|
+
),
|
21
|
+
s(:when,
|
22
|
+
s(:array,
|
23
|
+
s(:call,
|
24
|
+
s(:call, s(:vcall, :value), :*, s(:array, s(:lit, 10))),
|
25
|
+
:<, s(:array, s(:lit, 90))
|
26
|
+
)
|
27
|
+
),
|
28
|
+
s(:lit, :passes_multiplication_test)
|
29
|
+
),
|
30
|
+
s(:lit, :something_else)
|
31
|
+
)
|
32
|
+
)
|
33
|
+
)
|
34
|
+
)
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# Structure of a while
|
2
|
+
# So the first element is the condition, and the second is the body, wrapped
|
3
|
+
# in a :block (TODO and then a "true", what is it for?)
|
4
|
+
s(:defn, :while_count_to_value,
|
5
|
+
s(:scope,
|
6
|
+
s(:block, s(:args),
|
7
|
+
s(:while,
|
8
|
+
# The condition
|
9
|
+
s(:call, s(:ivar, :@i), :<,
|
10
|
+
s(:array, s(:vcall, :value))
|
11
|
+
),
|
12
|
+
# End condition
|
13
|
+
# The body
|
14
|
+
s(:block,
|
15
|
+
s(:call, s(:ivar, :@counts), :<<,
|
16
|
+
s(:array, s(:ivar, :@i))
|
17
|
+
),
|
18
|
+
s(:iasgn, :@i,
|
19
|
+
s(:call, s(:ivar, :@i), :+,
|
20
|
+
s(:array, s(:lit, 1))
|
21
|
+
)
|
22
|
+
)
|
23
|
+
),
|
24
|
+
# End body
|
25
|
+
true
|
26
|
+
)
|
27
|
+
)
|
28
|
+
)
|
29
|
+
)
|
30
|
+
|
31
|
+
# After
|
32
|
+
s(:defn, :while_result,
|
33
|
+
s(:scope,
|
34
|
+
s(:block, s(:args),
|
35
|
+
s(:fcall, :my_while,
|
36
|
+
s(:array,
|
37
|
+
s(:iter,
|
38
|
+
s(:fcall, :lambda), nil,
|
39
|
+
# The condition
|
40
|
+
s(:call, s(:ivar, :@i), :<,
|
41
|
+
s(:array, s(:vcall, :value))
|
42
|
+
)
|
43
|
+
# End condition
|
44
|
+
),
|
45
|
+
s(:iter,
|
46
|
+
s(:fcall, :lambda), nil,
|
47
|
+
# The body
|
48
|
+
s(:block,
|
49
|
+
s(:call, s(:ivar, :@counts), :<<,
|
50
|
+
s(:array, s(:ivar, :@i))
|
51
|
+
),
|
52
|
+
s(:iasgn, :@i,
|
53
|
+
s(:call, s(:ivar, :@i), :+,
|
54
|
+
s(:array, s(:lit, 1))
|
55
|
+
)
|
56
|
+
)
|
57
|
+
)
|
58
|
+
# End body
|
59
|
+
)
|
60
|
+
)
|
61
|
+
)
|
62
|
+
)
|
63
|
+
)
|
64
|
+
)
|
65
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'AndRewriter' do
|
4
|
+
include TrackIfs, TrackAnds, DoRewrite
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@and_user = AndUser.new false
|
8
|
+
@methods = sexpify_instance_methods AndUser
|
9
|
+
@if_rewriter = VirtualKeywords::IfRewriter.new
|
10
|
+
@and_rewriter = VirtualKeywords::AndRewriter.new
|
11
|
+
|
12
|
+
@my_and_calls = 0
|
13
|
+
@my_if_calls = 0
|
14
|
+
|
15
|
+
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
16
|
+
@and_user, :and, my_and)
|
17
|
+
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
18
|
+
@and_user, :if, my_if)
|
19
|
+
end
|
20
|
+
|
21
|
+
def rewriters
|
22
|
+
[@if_rewriter, @and_rewriter]
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'rewrites "and" statements' do
|
26
|
+
do_rewrite(:method_with_and, @and_user)
|
27
|
+
@my_and_calls.should eql 1
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'handles ifs with "and"s in the predicate' do
|
31
|
+
do_rewrite(:if_with_and, @and_user)
|
32
|
+
@my_and_calls.should eql 1
|
33
|
+
@my_if_calls.should eql 1
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'AndRewriter on &&' do
|
38
|
+
include TrackAnds, DoRewrite
|
39
|
+
|
40
|
+
before :each do
|
41
|
+
@operator_user = OperatorUser.new false
|
42
|
+
@methods = sexpify_instance_methods OperatorUser
|
43
|
+
@and_rewriter = VirtualKeywords::AndRewriter.new
|
44
|
+
|
45
|
+
@my_and_calls = 0
|
46
|
+
|
47
|
+
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
48
|
+
@operator_user, :and, my_and)
|
49
|
+
end
|
50
|
+
|
51
|
+
def rewriters
|
52
|
+
[@and_rewriter]
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'rewrites &&' do
|
56
|
+
do_rewrite(:symbolic_and, @operator_user)
|
57
|
+
@my_and_calls.should eql 1
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'IfRewriter' do
|
4
|
+
include TrackIfs, DoRewrite
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@greeter = Greeter.new true
|
8
|
+
@methods = sexpify_instance_methods Greeter
|
9
|
+
@if_rewriter = VirtualKeywords::IfRewriter.new
|
10
|
+
|
11
|
+
@my_if_calls = 0
|
12
|
+
|
13
|
+
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
14
|
+
@greeter, :if, my_if)
|
15
|
+
end
|
16
|
+
|
17
|
+
def rewriters
|
18
|
+
[@if_rewriter]
|
19
|
+
end
|
20
|
+
|
21
|
+
def greeter_rewrite_should_work(method_name,
|
22
|
+
required_calls = 1, verbose = false)
|
23
|
+
do_rewrite(method_name, @greeter, verbose = verbose)
|
24
|
+
@my_if_calls.should eql required_calls
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'rewrites greet with if and else' do
|
28
|
+
greeter_rewrite_should_work :greet_if_else
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'rewrites greet with if without else' do
|
32
|
+
# We don't need to do anything special for if without else
|
33
|
+
# They use the same sexp as if with else, with an empty block for the
|
34
|
+
# else clause
|
35
|
+
greeter_rewrite_should_work :greet_if_without_else
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'rewrites greet with postfix if' do
|
39
|
+
# Again, we don't need to do anything special - they turn into the same sexp
|
40
|
+
greeter_rewrite_should_work :greet_postfix_if
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'rewrites greet with if then else on one line' do
|
44
|
+
greeter_rewrite_should_work :greet_if_then_else
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'rewrites greet with if then but no else on one line' do
|
48
|
+
greeter_rewrite_should_work :greet_if_then_no_else
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'rewrites greet with unless' do
|
52
|
+
greeter_rewrite_should_work :greet_unless
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'rewrites greet with unless and else' do
|
56
|
+
greeter_rewrite_should_work :greet_unless_else
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'rewrites greet with postfix unless' do
|
60
|
+
greeter_rewrite_should_work :greet_postfix_unless
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'combines ifs without interference' do
|
64
|
+
greeter_rewrite_should_work(:greet_all, required_calls = 5)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'handles nested ifs' do
|
68
|
+
greeter_rewrite_should_work(:greet_nested, required_calls = 2)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'rewrites ifs with compound clauses' do
|
72
|
+
greeter_rewrite_should_work :greet_block
|
73
|
+
end
|
74
|
+
end
|
@@ -2,100 +2,8 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'KeywordRewriter' do
|
4
4
|
before :each do
|
5
|
-
|
6
|
-
|
7
|
-
def method_to_sexp(klass, method)
|
8
|
-
@sexp_processor.process(ParseTree.translate(klass, method))
|
9
|
-
end
|
10
|
-
|
11
|
-
@greeter = Greeter.new true
|
12
|
-
@and_user = AndUser.new false
|
13
|
-
@or_user = OrUser.new true
|
14
|
-
@operator_user = OperatorUser.new false
|
15
|
-
|
16
|
-
@greet_if_else_sexp = method_to_sexp(Greeter, :greet_if_else)
|
17
|
-
@greet_if_without_else_sexp = method_to_sexp(Greeter,
|
18
|
-
:greet_if_without_else)
|
19
|
-
@greet_postfix_if_sexp = method_to_sexp(Greeter, :greet_postfix_if)
|
20
|
-
@greet_if_then_else_sexp = method_to_sexp(Greeter, :greet_if_then_else)
|
21
|
-
@greet_if_then_no_else_sexp = method_to_sexp(Greeter,
|
22
|
-
:greet_if_then_no_else)
|
23
|
-
@greet_unless_sexp = method_to_sexp(Greeter, :greet_unless)
|
24
|
-
@greet_unless_else_sexp = method_to_sexp(Greeter, :greet_unless_else)
|
25
|
-
@greet_postfix_unless_sexp = method_to_sexp(Greeter, :greet_postfix_unless)
|
26
|
-
@greet_all_sexp = method_to_sexp(Greeter, :greet_all)
|
27
|
-
@greet_nested_sexp = method_to_sexp(Greeter, :greet_nested)
|
28
|
-
|
29
|
-
|
30
|
-
@method_with_and_sexp = method_to_sexp(AndUser, :method_with_and)
|
31
|
-
@if_with_and_sexp = method_to_sexp(AndUser, :if_with_and)
|
32
|
-
|
33
|
-
@method_with_or_sexp = method_to_sexp(OrUser, :method_with_or)
|
34
|
-
@if_with_or_sexp = method_to_sexp(OrUser, :if_with_or)
|
35
|
-
|
36
|
-
@symbolic_and_sexp = method_to_sexp(OperatorUser, :symbolic_and)
|
37
|
-
|
38
|
-
@greet_changed_sexp = method_to_sexp(Greeter, :greet_changed)
|
39
|
-
@method_with_and_result_sexp = method_to_sexp(AndUser,
|
40
|
-
:method_with_and_result)
|
41
|
-
@symbolic_and_result_sexp = method_to_sexp(OperatorUser,
|
42
|
-
:symbolic_and_result)
|
43
|
-
|
44
|
-
|
45
|
-
@if_rewriter = VirtualKeywords::IfRewriter.new
|
46
|
-
@and_rewriter = VirtualKeywords::AndRewriter.new
|
47
|
-
@or_rewriter = VirtualKeywords::OrRewriter.new
|
48
|
-
|
49
|
-
@my_if_calls = 0
|
50
|
-
def increment_my_if_calls
|
51
|
-
@my_if_calls += 1
|
52
|
-
end
|
53
|
-
|
54
|
-
@my_and_calls = 0
|
55
|
-
def increment_my_and_calls
|
56
|
-
@my_and_calls += 1
|
57
|
-
end
|
58
|
-
@my_or_calls = 0
|
59
|
-
def increment_my_or_calls
|
60
|
-
@my_or_calls += 1
|
61
|
-
end
|
62
|
-
$my_symbolic_and_calls = 0
|
63
|
-
|
64
|
-
spec = self
|
65
|
-
my_if = lambda { |condition, then_do, else_do|
|
66
|
-
increment_my_if_calls()
|
67
|
-
if condition.call
|
68
|
-
then_do.call
|
69
|
-
else
|
70
|
-
else_do.call
|
71
|
-
end
|
72
|
-
}
|
73
|
-
my_and = lambda { |first, second|
|
74
|
-
increment_my_and_calls()
|
75
|
-
first.call and second.call
|
76
|
-
}
|
77
|
-
my_or = lambda { |first, second|
|
78
|
-
increment_my_or_calls()
|
79
|
-
first.call or second.call
|
80
|
-
}
|
81
|
-
|
82
|
-
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
83
|
-
@greeter, :if, my_if)
|
84
|
-
|
85
|
-
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
86
|
-
@and_user, :and, my_and)
|
87
|
-
|
88
|
-
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
89
|
-
@and_user, :if, my_if)
|
90
|
-
|
91
|
-
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
92
|
-
@or_user, :or, my_or)
|
93
|
-
|
94
|
-
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
95
|
-
@or_user, :if, my_if)
|
96
|
-
|
97
|
-
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
98
|
-
@operator_user, :and, my_and)
|
5
|
+
#@while_count_sexp = method_to_sexp(WhileUser, :while_count_to_value)
|
6
|
+
#@while_result_sexp = method_to_sexp(WhileUser, :while_result)
|
99
7
|
end
|
100
8
|
|
101
9
|
# These two "specs" produce sexps that I used to figure out how
|
@@ -140,108 +48,14 @@ describe 'KeywordRewriter' do
|
|
140
48
|
#p @symbolic_and_result_sexp
|
141
49
|
#puts ''
|
142
50
|
#end
|
143
|
-
#
|
144
|
-
def do_rewrite(sexp, method_name, object, verbose = false)
|
145
|
-
result1 = @if_rewriter.process sexp
|
146
|
-
result2 = @and_rewriter.process result1
|
147
|
-
result = @or_rewriter.process result2
|
148
|
-
stringifier = VirtualKeywords::SexpStringifier.new
|
149
|
-
|
150
|
-
# Visually inspecting this result, it appears to be right
|
151
|
-
code_result = stringifier.stringify result
|
152
|
-
if verbose
|
153
|
-
puts code_result
|
154
|
-
end
|
155
|
-
|
156
|
-
# my_* are dummy methods that do not change behavior, so both the
|
157
|
-
# old and new code should produce the same result,
|
158
|
-
# except that @my_*_calls is incremented
|
159
|
-
old_result = object.send method_name
|
160
|
-
VirtualKeywords::ClassReflection.install_method_on_instance(object, code_result)
|
161
|
-
new_result = object.send method_name
|
162
|
-
|
163
|
-
new_result.should eql old_result
|
164
|
-
end
|
165
|
-
|
166
|
-
def greeter_rewrite_should_work(sexp, method_name,
|
167
|
-
required_calls = 1, verbose = false)
|
168
|
-
do_rewrite(sexp, method_name, @greeter, verbose = verbose)
|
169
|
-
@my_if_calls.should eql required_calls
|
170
|
-
end
|
171
|
-
|
172
|
-
it 'rewrites greet with if and else' do
|
173
|
-
greeter_rewrite_should_work(@greet_if_else_sexp, :greet_if_else)
|
174
|
-
end
|
175
|
-
|
176
|
-
it 'rewrites greet with if without else' do
|
177
|
-
# We don't need to do anything special for if without else
|
178
|
-
# They use the same sexp as if with else, with an empty block for the
|
179
|
-
# else clause
|
180
|
-
greeter_rewrite_should_work(@greet_if_without_else_sexp,
|
181
|
-
:greet_if_without_else)
|
182
|
-
end
|
183
51
|
|
184
|
-
it '
|
185
|
-
#
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
end
|
193
|
-
|
194
|
-
it 'rewrites greet with if then but no else on one line' do
|
195
|
-
greeter_rewrite_should_work(@greet_if_then_no_else_sexp,
|
196
|
-
:greet_if_then_no_else)
|
197
|
-
end
|
198
|
-
|
199
|
-
it 'rewrites greet with unless' do
|
200
|
-
greeter_rewrite_should_work(@greet_unless_sexp, :greet_unless)
|
201
|
-
end
|
202
|
-
|
203
|
-
it 'rewrites greet with unless and else' do
|
204
|
-
greeter_rewrite_should_work(@greet_unless_else_sexp, :greet_unless_else)
|
205
|
-
end
|
206
|
-
|
207
|
-
it 'rewrites greet with postfix unless' do
|
208
|
-
greeter_rewrite_should_work(@greet_postfix_unless_sexp,
|
209
|
-
:greet_postfix_unless)
|
210
|
-
end
|
211
|
-
|
212
|
-
it 'combines ifs without interference' do
|
213
|
-
greeter_rewrite_should_work(@greet_all_sexp, :greet_all, required_calls = 5)
|
214
|
-
end
|
215
|
-
|
216
|
-
it 'handles nested ifs' do
|
217
|
-
greeter_rewrite_should_work(@greet_nested_sexp, :greet_nested,
|
218
|
-
required_calls = 2)
|
219
|
-
end
|
220
|
-
|
221
|
-
it 'rewrites "and" statements' do
|
222
|
-
do_rewrite(@method_with_and_sexp, :method_with_and, @and_user)
|
223
|
-
@my_and_calls.should eql 1
|
224
|
-
end
|
225
|
-
|
226
|
-
it 'handles ifs with "and"s in the predicate' do
|
227
|
-
do_rewrite(@if_with_and_sexp, :if_with_and, @and_user)
|
228
|
-
@my_and_calls.should eql 1
|
229
|
-
@my_if_calls.should eql 1
|
230
|
-
end
|
231
|
-
|
232
|
-
it 'rewrites "or" statements' do
|
233
|
-
do_rewrite(@method_with_or_sexp, :method_with_or, @or_user)
|
234
|
-
@my_or_calls.should eql 1
|
235
|
-
end
|
236
|
-
|
237
|
-
it 'handles ifs with "or"s in the predicate' do
|
238
|
-
do_rewrite(@if_with_or_sexp, :if_with_or, @or_user)
|
239
|
-
@my_or_calls.should eql 1
|
240
|
-
@my_if_calls.should eql 1
|
241
|
-
end
|
242
|
-
|
243
|
-
it 'rewrites &&' do
|
244
|
-
do_rewrite(@symbolic_and_sexp, :symbolic_and, @operator_user)
|
245
|
-
@my_and_calls.should eql 1
|
246
|
-
end
|
52
|
+
#it 'turns a case-when into a sexp' do
|
53
|
+
#p @describe_value_sexp
|
54
|
+
#end
|
55
|
+
|
56
|
+
#it 'turns a while into a sexp' do
|
57
|
+
#p @while_count_sexp
|
58
|
+
#puts ''
|
59
|
+
#p @while_result_sexp
|
60
|
+
#end
|
247
61
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'OrRewriter' do
|
4
|
+
include TrackIfs, TrackOrs, DoRewrite
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@or_user = OrUser.new true
|
8
|
+
@methods = sexpify_instance_methods OrUser
|
9
|
+
@if_rewriter = VirtualKeywords::IfRewriter.new
|
10
|
+
@or_rewriter = VirtualKeywords::OrRewriter.new
|
11
|
+
|
12
|
+
@my_if_calls = 0
|
13
|
+
@my_or_calls = 0
|
14
|
+
|
15
|
+
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
16
|
+
@or_user, :or, my_or)
|
17
|
+
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
18
|
+
@or_user, :if, my_if)
|
19
|
+
end
|
20
|
+
|
21
|
+
def rewriters
|
22
|
+
[@if_rewriter, @or_rewriter]
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'rewrites "or" statements' do
|
26
|
+
do_rewrite(:method_with_or, @or_user)
|
27
|
+
@my_or_calls.should eql 1
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'handles ifs with "or"s in the predicate' do
|
31
|
+
do_rewrite(:if_with_or, @or_user)
|
32
|
+
@my_or_calls.should eql 1
|
33
|
+
@my_if_calls.should eql 1
|
34
|
+
end
|
35
|
+
end
|
data/lib/spec/spec_helper.rb
CHANGED
@@ -149,6 +149,20 @@ class Greeter < ApplicationController
|
|
149
149
|
end
|
150
150
|
end
|
151
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
|
+
|
152
166
|
def count_to_ten
|
153
167
|
[1..10].each do |index|
|
154
168
|
puts index
|
@@ -212,7 +226,176 @@ class OperatorUser < ActiveRecord::Base
|
|
212
226
|
end
|
213
227
|
end
|
214
228
|
|
215
|
-
|
216
|
-
|
217
|
-
|
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
|
+
# Helper classes and functions for rewriter specs
|
278
|
+
|
279
|
+
# Given a class and a method name, return a sexpified method.
|
280
|
+
def method_to_sexp(klass, method)
|
281
|
+
SexpProcessor.new.process(ParseTree.translate(klass, method))
|
282
|
+
end
|
283
|
+
|
284
|
+
# Sexpify all non-inherited instance methods of a class and return them in
|
285
|
+
# a hash mapping names to sexps.
|
286
|
+
def sexpify_instance_methods klass
|
287
|
+
sexps = {}
|
288
|
+
klass.instance_methods(false).each do |method_name|
|
289
|
+
sexps[method_name.to_sym] = method_to_sexp(klass, method_name.to_sym)
|
290
|
+
end
|
291
|
+
|
292
|
+
sexps
|
293
|
+
end
|
294
|
+
|
295
|
+
module TrackIfs
|
296
|
+
@my_if_calls = 0 # Don't forget to reset this before each spec!
|
297
|
+
|
298
|
+
def increment_my_if_calls
|
299
|
+
@my_if_calls += 1
|
300
|
+
end
|
301
|
+
|
302
|
+
def my_if
|
303
|
+
# 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
|
306
|
+
if condition.call
|
307
|
+
then_do.call
|
308
|
+
else
|
309
|
+
else_do.call
|
310
|
+
end
|
311
|
+
}
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
module TrackAnds
|
316
|
+
@my_and_calls = 0
|
317
|
+
|
318
|
+
def increment_my_and_calls
|
319
|
+
@my_and_calls += 1
|
320
|
+
end
|
321
|
+
|
322
|
+
def my_and
|
323
|
+
# Dummy if that increments @my_if_calls, then runs as normal
|
324
|
+
@my_and ||= lambda { |first, second|
|
325
|
+
increment_my_and_calls
|
326
|
+
first.call and second.call
|
327
|
+
}
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
module TrackOrs
|
332
|
+
@my_or_calls = 0
|
333
|
+
|
334
|
+
def increment_my_or_calls
|
335
|
+
@my_or_calls += 1
|
336
|
+
end
|
337
|
+
|
338
|
+
def my_or
|
339
|
+
@my_or ||= lambda { |first, second|
|
340
|
+
increment_my_or_calls
|
341
|
+
first.call or second.call
|
342
|
+
}
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
module TrackWhiles
|
347
|
+
@my_while_calls = 0
|
348
|
+
|
349
|
+
def increment_my_while_calls
|
350
|
+
@my_while_calls += 1
|
351
|
+
end
|
352
|
+
|
353
|
+
def my_while
|
354
|
+
@my_while ||= lambda { |condition, body|
|
355
|
+
increment_my_while_calls
|
356
|
+
while condition.call
|
357
|
+
body.call
|
358
|
+
end
|
359
|
+
}
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
class Abstract < StandardError
|
364
|
+
end
|
365
|
+
|
366
|
+
module DoRewrite
|
367
|
+
# Override this and return a list of rewriters, in order, so do_rewrite
|
368
|
+
# can call them
|
369
|
+
def rewriters
|
370
|
+
raise Abstract
|
371
|
+
end
|
372
|
+
|
373
|
+
def do_rewrite(method_name, object, verbose = false)
|
374
|
+
sexp = @methods[method_name]
|
375
|
+
result = sexp
|
376
|
+
rewriters.each do |rewriter|
|
377
|
+
result = rewriter.process result
|
378
|
+
end
|
379
|
+
stringifier = VirtualKeywords::SexpStringifier.new
|
380
|
+
|
381
|
+
# Visually inspecting this result, it appears to be right
|
382
|
+
code_result = stringifier.stringify result
|
383
|
+
if verbose
|
384
|
+
puts code_result
|
385
|
+
end
|
386
|
+
|
387
|
+
# my_* are dummy methods that do not change behavior, so both the
|
388
|
+
# old and new code should produce the same result,
|
389
|
+
# except that @my_*_calls is incremented
|
390
|
+
old_result = object.send method_name
|
391
|
+
VirtualKeywords::ClassReflection.install_method_on_instance(object, code_result)
|
392
|
+
new_result = object.send method_name
|
393
|
+
|
394
|
+
new_result.should eql old_result
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
RSpec.configure do |configuration|
|
399
|
+
configuration.color_enabled = true
|
400
|
+
configuration.formatter = 'documentation'
|
218
401
|
end
|
@@ -113,12 +113,25 @@ describe 'Virtualizer' do
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
|
+
class Counter
|
117
|
+
def run
|
118
|
+
a = []
|
119
|
+
i = 0
|
120
|
+
while i <= 10
|
121
|
+
a << i
|
122
|
+
end
|
123
|
+
|
124
|
+
a
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
116
128
|
@my_class = MyClass.new
|
117
129
|
@another_class = AnotherClass.new
|
118
130
|
@yet_another_class = YetAnotherClass.new
|
119
131
|
@operator_user = OperatorUser.new false
|
132
|
+
@counter = Counter.new
|
120
133
|
@virtualizer = VirtualKeywords::Virtualizer.new(
|
121
|
-
:for_instances => [@greeter, @my_class]
|
134
|
+
:for_instances => [@greeter, @my_class, @counter]
|
122
135
|
)
|
123
136
|
@class_virtualizer = VirtualKeywords::Virtualizer.new(
|
124
137
|
:for_classes => [AnotherClass]
|
@@ -178,4 +191,14 @@ describe 'Virtualizer' do
|
|
178
191
|
@another_class.quux.should eql :right
|
179
192
|
@yet_another_class.quux.should eql :if_modified
|
180
193
|
end
|
194
|
+
|
195
|
+
it 'virtualizes "while" on instances' do
|
196
|
+
@virtualizer.virtual_while do |condition, body|
|
197
|
+
# call the body once, regardless of condition
|
198
|
+
body.call
|
199
|
+
end
|
200
|
+
|
201
|
+
result = @counter.run
|
202
|
+
result.should eql [0]
|
203
|
+
end
|
181
204
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'WhileRewriter' do
|
4
|
+
include TrackWhiles, DoRewrite
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@while_user = WhileUser.new 10
|
8
|
+
@methods = sexpify_instance_methods WhileUser
|
9
|
+
@while_rewriter = VirtualKeywords::WhileRewriter.new
|
10
|
+
|
11
|
+
@my_while_calls = 0
|
12
|
+
|
13
|
+
VirtualKeywords::REWRITTEN_KEYWORDS.register_lambda_for_object(
|
14
|
+
@while_user, :while, my_while)
|
15
|
+
end
|
16
|
+
|
17
|
+
def rewriters
|
18
|
+
[@while_rewriter]
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'rewrites "while" expressions' do
|
22
|
+
do_rewrite(:while_count_to_value, @while_user)
|
23
|
+
@my_while_calls.should eql 1
|
24
|
+
end
|
25
|
+
end
|
@@ -1,4 +1,7 @@
|
|
1
1
|
module VirtualKeywords
|
2
|
+
# SexpProcessor subclass that rewrites "if" expressions.
|
3
|
+
# Calls VirtualKeywords::REWRITTEN_KEYWORDS.call_if and lets it pick a
|
4
|
+
# virtual implementation.
|
2
5
|
class IfRewriter < SexpProcessor
|
3
6
|
# Initialize an IfRewriter (self.strict is false)
|
4
7
|
def initialize
|
@@ -25,6 +28,9 @@ module VirtualKeywords
|
|
25
28
|
then_do = expression[2]
|
26
29
|
else_do = expression[3]
|
27
30
|
|
31
|
+
# This ugly sexp turns into the following Ruby code:
|
32
|
+
# VirtualKeywords::REWRITTEN_KEYWORDS.call_if(
|
33
|
+
# self, lambda { condition }, lambda { then_do }, lambda { else_do })
|
28
34
|
s(:call,
|
29
35
|
s(:colon2,
|
30
36
|
s(:const, :VirtualKeywords),
|
@@ -45,11 +51,11 @@ module VirtualKeywords
|
|
45
51
|
#
|
46
52
|
# Arguments:
|
47
53
|
# method_name: (Symbol) the name of the REWRITTEN_KEYWORDS method that
|
48
|
-
#
|
54
|
+
# should be called in the sexp.
|
49
55
|
# first: (Sexp) the first argument to the method, which should be
|
50
|
-
#
|
56
|
+
# wrapped in a lambda then passed to REWRITTEN_KEYWORDS.
|
51
57
|
# second: (Sexp) the second argument to the method, which should be
|
52
|
-
#
|
58
|
+
# wrapped in a lambda then passed to REWRITTEN_KEYWORDS.
|
53
59
|
def self.call_operator_replacement(function_name, first, second)
|
54
60
|
s(:call,
|
55
61
|
s(:colon2,
|
@@ -64,6 +70,7 @@ module VirtualKeywords
|
|
64
70
|
)
|
65
71
|
end
|
66
72
|
|
73
|
+
# SexpProcessor subclass that rewrites "and" expressions.
|
67
74
|
class AndRewriter < SexpProcessor
|
68
75
|
def initialize
|
69
76
|
super
|
@@ -85,6 +92,7 @@ module VirtualKeywords
|
|
85
92
|
end
|
86
93
|
end
|
87
94
|
|
95
|
+
# SexpProcessor subclass that rewrites "or" expressions.
|
88
96
|
class OrRewriter < SexpProcessor
|
89
97
|
def initialize
|
90
98
|
super
|
@@ -105,4 +113,39 @@ module VirtualKeywords
|
|
105
113
|
VirtualKeywords.call_operator_replacement(:call_or, first, second)
|
106
114
|
end
|
107
115
|
end
|
116
|
+
|
117
|
+
class UnexpectedSexp < StandardError
|
118
|
+
end
|
119
|
+
|
120
|
+
class WhileRewriter < SexpProcessor
|
121
|
+
def initialize
|
122
|
+
super
|
123
|
+
self.strict = false
|
124
|
+
end
|
125
|
+
|
126
|
+
def rewrite_while(expression)
|
127
|
+
condition = expression[1]
|
128
|
+
body = expression[2]
|
129
|
+
|
130
|
+
# This was a true in the example I checked (in sexps_while.txt)
|
131
|
+
# but I'm not sure what it's for.
|
132
|
+
third = expression[3]
|
133
|
+
if third != true # Should be true, not just a truthy object
|
134
|
+
raise UnexpectedSexp, 'Expected true as the 3rd element in a while, ' +
|
135
|
+
"but got #{third}, this is probably a bug."
|
136
|
+
end
|
137
|
+
|
138
|
+
s(:call,
|
139
|
+
s(:colon2,
|
140
|
+
s(:const, :VirtualKeywords),
|
141
|
+
:REWRITTEN_KEYWORDS
|
142
|
+
), :call_while,
|
143
|
+
s(:array,
|
144
|
+
s(:self),
|
145
|
+
s(:iter, s(:fcall, :lambda), nil, condition),
|
146
|
+
s(:iter, s(:fcall, :lambda), nil, body)
|
147
|
+
)
|
148
|
+
)
|
149
|
+
end
|
150
|
+
end
|
108
151
|
end
|
@@ -17,10 +17,9 @@ module VirtualKeywords
|
|
17
17
|
# Arguments:
|
18
18
|
# A Hash with the following key:
|
19
19
|
# predicates_to_blocks: (Hash[Proc, Proc]) a hash mapping predicates that
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
# (optional, an empty Hash is the default).
|
20
|
+
# take ObjectAndKeywords and return true for matches to the lambdas
|
21
|
+
# that should be called in place of the keyword in the object's
|
22
|
+
# methods (optional, an empty Hash is the default).
|
24
23
|
def initialize(input)
|
25
24
|
@predicates_to_blocks = input[:predicates_to_blocks] || {}
|
26
25
|
end
|
@@ -72,7 +71,7 @@ module VirtualKeywords
|
|
72
71
|
def lambda_or_raise(caller_object, keyword)
|
73
72
|
object_and_keyword = ObjectAndKeyword.new(caller_object, keyword)
|
74
73
|
matching = @predicates_to_blocks.keys.find { |predicate|
|
75
|
-
predicate.call
|
74
|
+
predicate.call object_and_keyword
|
76
75
|
}
|
77
76
|
|
78
77
|
if matching.nil?
|
@@ -90,11 +89,11 @@ module VirtualKeywords
|
|
90
89
|
# Arguments:
|
91
90
|
# caller_object: (Object) the object whose method this is being called in.
|
92
91
|
# condition: (Proc) The condition of the if statement, wrapped in a
|
93
|
-
#
|
92
|
+
# lambda.
|
94
93
|
# then_do: (Proc) the lambda to execute if the condition is true (but
|
95
|
-
#
|
94
|
+
# the user-supplied block may do something else)
|
96
95
|
# else_do: (Proc) the lambda to execute if the condition is false (but
|
97
|
-
#
|
96
|
+
# the user-supplied block may do something else)
|
98
97
|
#
|
99
98
|
# Raises:
|
100
99
|
# RewriteLambdaNotProvided if no "if" lambda is available.
|
@@ -130,6 +129,21 @@ module VirtualKeywords
|
|
130
129
|
or_lambda = lambda_or_raise(caller_object, :or)
|
131
130
|
or_lambda.call(first, second)
|
132
131
|
end
|
132
|
+
|
133
|
+
# Call a "while" virtual block in place of an "while" expression.
|
134
|
+
#
|
135
|
+
# Arguments:
|
136
|
+
# caller_object: (Object) the object whose method this is being called in.
|
137
|
+
# condition: (Proc) The condition of the while expression.
|
138
|
+
# body: (Proc) The body of the while expression (which is normally
|
139
|
+
# executed repeatedly)
|
140
|
+
#
|
141
|
+
# Raises:
|
142
|
+
# RewriteLambdaNotProvided if no "while" lambda is available.
|
143
|
+
def call_while(caller_object, condition, body)
|
144
|
+
while_lambda = lambda_or_raise(caller_object, :while)
|
145
|
+
while_lambda.call(condition, body)
|
146
|
+
end
|
133
147
|
end
|
134
148
|
|
135
149
|
|
@@ -58,9 +58,9 @@ module VirtualKeywords
|
|
58
58
|
# Arguments:
|
59
59
|
# klass: (Class) the class which should be modified.
|
60
60
|
# method_code: (String) the code for the method to install, of the format:
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
61
|
+
# def method_name(args)
|
62
|
+
# ...
|
63
|
+
# end
|
64
64
|
def self.install_method_on_class(klass, method_code)
|
65
65
|
klass.class_eval method_code
|
66
66
|
end
|
@@ -76,9 +76,9 @@ module VirtualKeywords
|
|
76
76
|
# Arguments:
|
77
77
|
# object: (Object) the object instance that should be modified.
|
78
78
|
# method_code: (String) the code for the method to install, of the format:
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
79
|
+
# def method_name(args)
|
80
|
+
# ...
|
81
|
+
# end
|
82
82
|
def self.install_method_on_instance(object, method_code)
|
83
83
|
object.instance_eval method_code
|
84
84
|
end
|
@@ -102,33 +102,36 @@ module VirtualKeywords
|
|
102
102
|
# Arguments:
|
103
103
|
# A Hash with the following arguments (all optional):
|
104
104
|
# for_classes: (Array[Class]) an array of classes. All methods of objects
|
105
|
-
#
|
106
|
-
#
|
105
|
+
# created from the given classes will be virtualized (optional, the
|
106
|
+
# default is an empty Array).
|
107
107
|
# for_instances: (Array[Object]) an array of object. All of these objects'
|
108
|
-
#
|
109
|
-
#
|
108
|
+
# methods will be virtualized
|
109
|
+
# (optional, the default is an empty Array).
|
110
110
|
# for subclasses_of: (Array[Class]) an array of classes. All methods of
|
111
|
-
#
|
112
|
-
#
|
113
|
-
# virtualized.
|
111
|
+
# objects created from the given classes' subclasses (but NOT those
|
112
|
+
# from the given classes) will be virtualized.
|
114
113
|
# if_rewriter: (IfRewriter) the SexpProcessor descendant that
|
115
|
-
#
|
116
|
-
#
|
114
|
+
# rewrites "if"s in methods (optional, the default is
|
115
|
+
# IfRewriter.new).
|
117
116
|
# and_rewriter: (AndRewriter) the SexpProcessor descendant that
|
118
|
-
#
|
119
|
-
#
|
117
|
+
# rewrites "and"s in methods (optional, the default is
|
118
|
+
# AndRewriter.new).
|
120
119
|
# or_rewriter: (OrRewriter) the SexpProcessor descendant that
|
121
|
-
#
|
122
|
-
#
|
120
|
+
# rewrites "or"s in methods (optional, the default is
|
121
|
+
# OrRewriter.new).
|
122
|
+
# while_rewriter: (WhileRewriter) the SexpProcessor descendant that
|
123
|
+
# rewrites "while"s in methods (optional, the default is
|
124
|
+
# WhileRewriter.new).
|
123
125
|
# sexp_processor: (SexpProcessor) the sexp_processor that can turn
|
124
|
-
#
|
125
|
-
#
|
126
|
+
# ParseTree results into sexps (optional, the default is
|
127
|
+
# SexpProcessor.new).
|
126
128
|
# sexp_stringifier: (SexpStringifier) an object that can turn sexps
|
127
|
-
#
|
128
|
-
#
|
129
|
+
# back into Ruby code (optional, the default is
|
130
|
+
# SexpStringifier.new).
|
129
131
|
# rewritten_keywords: (RewrittenKeywords) a repository for keyword
|
130
|
-
#
|
131
|
-
#
|
132
|
+
# replacement lambdas (optional, the default is REWRITTEN_KEYWORDS).
|
133
|
+
# class_reflection: (Class) an object that provides methods to modify the
|
134
|
+
# methods of classes (optional, the default is ClassReflection).
|
132
135
|
def initialize(input_hash)
|
133
136
|
@for_classes = input_hash[:for_classes] || []
|
134
137
|
@for_instances = input_hash[:for_instances] || []
|
@@ -136,19 +139,21 @@ module VirtualKeywords
|
|
136
139
|
@if_rewriter = input_hash[:if_rewriter] || IfRewriter.new
|
137
140
|
@and_rewriter = input_hash[:and_rewriter] || AndRewriter.new
|
138
141
|
@or_rewriter = input_hash[:or_rewriter] || OrRewriter.new
|
142
|
+
@while_rewriter = input_hash[:while_rewriter] || WhileRewriter.new
|
139
143
|
@sexp_processor = input_hash[:sexp_processor] || SexpProcessor.new
|
140
144
|
@sexp_stringifier = input_hash[:sexp_stringifier] || SexpStringifier.new
|
141
145
|
@rewritten_keywords =
|
142
146
|
input_hash[:rewritten_keywords] || REWRITTEN_KEYWORDS
|
147
|
+
@class_reflection = input_hash[:class_reflection] || ClassReflection
|
143
148
|
end
|
144
149
|
|
145
150
|
# Helper method to rewrite code.
|
146
151
|
#
|
147
152
|
# Arguments:
|
148
153
|
# translated: (Array) the output of ParseTree.translate on the original
|
149
|
-
#
|
154
|
+
# code
|
150
155
|
# rewriter: (SexpProcessor) the object that will rewrite the sexp, to
|
151
|
-
#
|
156
|
+
# virtualize the keywords.
|
152
157
|
def rewritten_code(translated, rewriter)
|
153
158
|
sexp = @sexp_processor.process(
|
154
159
|
VirtualKeywords.deep_copy_array(translated))
|
@@ -166,10 +171,10 @@ module VirtualKeywords
|
|
166
171
|
def rewrite_methods_of_instance(instance, keyword, rewriter, block)
|
167
172
|
@rewritten_keywords.register_lambda_for_object(instance, keyword, block)
|
168
173
|
|
169
|
-
methods =
|
174
|
+
methods = @class_reflection.instance_methods_of instance.class
|
170
175
|
methods.each do |name, translated|
|
171
176
|
new_code = rewritten_code(translated, rewriter)
|
172
|
-
|
177
|
+
@class_reflection.install_method_on_instance(instance, new_code)
|
173
178
|
end
|
174
179
|
end
|
175
180
|
|
@@ -181,12 +186,12 @@ module VirtualKeywords
|
|
181
186
|
# rewriter: (SexpProcessor) the object that will do the rewriting.
|
182
187
|
# block: (Proc) the lambda that will replace the keyword.
|
183
188
|
def rewrite_methods_of_class(klass, keyword, rewriter, block)
|
184
|
-
@rewritten_keywords.register_lambda_for_class(klass, keyword, block)
|
189
|
+
@rewritten_keywords.register_lambda_for_class(klass, keyword, block)
|
185
190
|
|
186
|
-
methods =
|
191
|
+
methods = @class_reflection.instance_methods_of klass
|
187
192
|
methods.each do |name, translated|
|
188
193
|
new_code = rewritten_code(translated, rewriter)
|
189
|
-
|
194
|
+
@class_reflection.install_method_on_class(klass, new_code)
|
190
195
|
end
|
191
196
|
end
|
192
197
|
|
@@ -205,7 +210,7 @@ module VirtualKeywords
|
|
205
210
|
rewrite_methods_of_class(klass, keyword, rewriter, block)
|
206
211
|
end
|
207
212
|
|
208
|
-
subclasses =
|
213
|
+
subclasses = @class_reflection.subclasses_of_classes @for_subclasses_of
|
209
214
|
subclasses.each do |subclass|
|
210
215
|
rewrite_methods_of_class(subclass, keyword, rewriter, block)
|
211
216
|
end
|
@@ -215,7 +220,7 @@ module VirtualKeywords
|
|
215
220
|
#
|
216
221
|
# Arguments:
|
217
222
|
# &block: The block that will replace "if"s in the objects being
|
218
|
-
#
|
223
|
+
# virtualized
|
219
224
|
def virtual_if(&block)
|
220
225
|
virtualize_keyword(:if, @if_rewriter, block)
|
221
226
|
end
|
@@ -224,7 +229,7 @@ module VirtualKeywords
|
|
224
229
|
#
|
225
230
|
# Arguments:
|
226
231
|
# &block: The block that will replace "and"s in the objects being
|
227
|
-
#
|
232
|
+
# virtualized
|
228
233
|
def virtual_and(&block)
|
229
234
|
virtualize_keyword(:and, @and_rewriter, block)
|
230
235
|
end
|
@@ -233,9 +238,18 @@ module VirtualKeywords
|
|
233
238
|
#
|
234
239
|
# Arguments:
|
235
240
|
# &block: The block that will replace "or"s in the objects being
|
236
|
-
#
|
241
|
+
# virtualized
|
237
242
|
def virtual_or(&block)
|
238
243
|
virtualize_keyword(:or, @or_rewriter, block)
|
239
244
|
end
|
245
|
+
|
246
|
+
# Rewrite "while" expressions.
|
247
|
+
#
|
248
|
+
# Arguments:
|
249
|
+
# &block: The block that will replace "while"s in the objects being
|
250
|
+
# virtualized
|
251
|
+
def virtual_while(&block)
|
252
|
+
virtualize_keyword(:while, @while_rewriter, block)
|
253
|
+
end
|
240
254
|
end
|
241
255
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: virtual_keywords
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.0
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Rahul Rajagopalan
|
@@ -88,17 +88,23 @@ files:
|
|
88
88
|
- lib/virtual_keywords/keyword_rewriter.rb
|
89
89
|
- lib/virtual_keywords/version.rb
|
90
90
|
- lib/virtual_keywords/virtualizer.rb
|
91
|
+
- lib/sexps/sexps_while.txt
|
91
92
|
- lib/sexps/count_to_ten_sexp.txt
|
92
93
|
- lib/sexps/sexps_and.txt
|
93
94
|
- lib/sexps/sexps_symbolic_and.txt
|
95
|
+
- lib/sexps/sexps_case_when.txt
|
94
96
|
- lib/sexps/sexps_rewritten_keywords.txt
|
95
97
|
- lib/sexps/sexps_greet.txt
|
96
98
|
- lib/virtual_keywords.rb
|
97
99
|
- lib/spec/virtualizer_spec.rb
|
100
|
+
- lib/spec/while_rewriter_spec.rb
|
101
|
+
- lib/spec/and_rewriter_spec.rb
|
102
|
+
- lib/spec/if_rewriter_spec.rb
|
98
103
|
- lib/spec/rewritten_keywords_spec.rb
|
99
104
|
- lib/spec/keyword_rewriter_spec.rb
|
100
105
|
- lib/spec/class_mirrorer_spec.rb
|
101
106
|
- lib/spec/spec_helper.rb
|
107
|
+
- lib/spec/or_rewriter_spec.rb
|
102
108
|
- lib/Rakefile
|
103
109
|
homepage: http://github.com/rahulraj/virtual_keywords
|
104
110
|
licenses: []
|