virtual_keywords 0.0.0 → 0.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/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: []
|