ruby_scribe 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/ruby_scribe/emitter.rb +111 -129
- data/lib/ruby_scribe/emitter_config.rb +28 -0
- data/lib/ruby_scribe/emitter_helpers.rb +1 -1
- data/lib/ruby_scribe/ext/sexp.rb +1 -7
- data/lib/ruby_scribe/sexp_helpers.rb +285 -0
- data/lib/ruby_scribe/version.rb +1 -1
- data/lib/ruby_scribe.rb +2 -0
- data/spec/ruby_scribe/sexp_helpers_spec.rb +256 -0
- metadata +7 -17
- data/lib/ruby_scribe/transformer.rb +0 -18
- data/lib/ruby_scribe/transformer_helpers.rb +0 -7
- data/lib/ruby_scribe/transformers/block_method_to_procifier.rb +0 -43
- data/lib/ruby_scribe/transformers/custom.rb +0 -27
- data/lib/ruby_scribe/transformers/eachifier.rb +0 -39
- data/lib/ruby_scribe/transformers/symbol_names.rb +0 -68
- data/lib/ruby_scribe/transformers/tapifier.rb +0 -73
- data/spec/ruby_scribe/transformer_spec.rb +0 -27
- data/spec/ruby_scribe/transformers/block_method_to_procifier_spec.rb +0 -20
- data/spec/ruby_scribe/transformers/custom_spec.rb +0 -25
- data/spec/ruby_scribe/transformers/eachifier_spec.rb +0 -27
- data/spec/ruby_scribe/transformers/symbol_names_spec.rb +0 -44
- data/spec/ruby_scribe/transformers/tapifier_spec.rb +0 -57
@@ -0,0 +1,285 @@
|
|
1
|
+
module RubyScribe
|
2
|
+
|
3
|
+
# Various helpers for matching and constructing S-expressions without having to deal with
|
4
|
+
# array soup
|
5
|
+
module SexpHelpers
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def module!(name, body = nil)
|
10
|
+
s(:module, generate_module_or_class_name(name), ensure_scope_wrapped(body))
|
11
|
+
end
|
12
|
+
|
13
|
+
def class!(name, extends = nil, body = nil)
|
14
|
+
s(:class, generate_module_or_class_name(name), generate_class_extend_from(extends), ensure_scope_wrapped(body))
|
15
|
+
end
|
16
|
+
|
17
|
+
def method!(name, arguments = nil, body = nil)
|
18
|
+
s(:defn, name.to_sym, method_args!(arguments), method_body!(body))
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_on!(on, name, arguments = nil, body = nil)
|
22
|
+
s(:defs, on, name.to_sym, method_args!(arguments), method_body!(body))
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_args!(arguments = nil)
|
26
|
+
arguments ||= []
|
27
|
+
options = arguments.extract_options!
|
28
|
+
|
29
|
+
s(*([:args] + arguments.map(&:to_sym))).tap do |args|
|
30
|
+
args.push s(*([:block] + options.map {|k, v| s(:lasgn, k.to_sym, v) })) if options && !options.empty?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_body!(body = nil)
|
35
|
+
if body.is_a?(Sexp) && body.kind == :scope
|
36
|
+
body
|
37
|
+
elsif body.is_a?(Sexp) && body.kind == :block
|
38
|
+
ensure_scope_wrapped(body)
|
39
|
+
elsif body.is_a?(Sexp)
|
40
|
+
method_body!(s(:block, body))
|
41
|
+
elsif body.is_a?(Array)
|
42
|
+
method_body!(s(*([:block] + body)))
|
43
|
+
else
|
44
|
+
method_body!(s(:block, s(:nil)))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def call!(name, arguments = nil, body = nil)
|
49
|
+
call_on!(nil, name.to_sym, arguments, body)
|
50
|
+
end
|
51
|
+
|
52
|
+
def call_on!(receiver, name, arguments = nil, body = nil)
|
53
|
+
s(:call, receiver, name.to_sym, call_args!(arguments))
|
54
|
+
end
|
55
|
+
|
56
|
+
def call_args!(arguments = nil)
|
57
|
+
arguments ||= []
|
58
|
+
|
59
|
+
if arguments.is_a?(Sexp) && arguments.kind == :arglist
|
60
|
+
arguments
|
61
|
+
elsif arguments.is_a?(Sexp)
|
62
|
+
s(:arglist, arguments)
|
63
|
+
elsif arguments.is_a?(Array)
|
64
|
+
s(*([:arglist] + arguments))
|
65
|
+
else
|
66
|
+
s(:arglist)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
def generate_module_or_class_name(name)
|
74
|
+
if name.nil?
|
75
|
+
nil
|
76
|
+
elsif name.is_a?(Sexp) or name.is_a?(Symbol)
|
77
|
+
name
|
78
|
+
elsif name.to_s !~ /::/
|
79
|
+
name.to_sym
|
80
|
+
else
|
81
|
+
RubyParser.new.parse(name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def generate_class_extend_from(name)
|
86
|
+
if name.nil?
|
87
|
+
nil
|
88
|
+
elsif name.is_a?(Sexp) or name.is_a?(Symbol)
|
89
|
+
name
|
90
|
+
else
|
91
|
+
RubyParser.new.parse(name)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def ensure_scope_wrapped(sexp)
|
96
|
+
if sexp.is_a?(Sexp) && sexp.kind == :scope
|
97
|
+
sexp
|
98
|
+
elsif sexp.nil?
|
99
|
+
s(:scope)
|
100
|
+
else
|
101
|
+
s(:scope, sexp)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
module InstanceMethods
|
107
|
+
def kind
|
108
|
+
sexp_type.to_sym
|
109
|
+
end
|
110
|
+
|
111
|
+
def body
|
112
|
+
sexp_body
|
113
|
+
end
|
114
|
+
|
115
|
+
def name
|
116
|
+
case kind
|
117
|
+
when :call
|
118
|
+
body[1]
|
119
|
+
when :lasgn, :iasgn, :class, :module
|
120
|
+
body[0]
|
121
|
+
when :iter
|
122
|
+
body[0].name
|
123
|
+
else
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def receiver
|
129
|
+
case kind
|
130
|
+
when :call
|
131
|
+
body[0]
|
132
|
+
else
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def arguments
|
138
|
+
case kind
|
139
|
+
when :call, :defs
|
140
|
+
body[2]
|
141
|
+
when :defn, :iter
|
142
|
+
body[1]
|
143
|
+
else
|
144
|
+
nil
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def to_args
|
149
|
+
emit_as_args_array(arguments)
|
150
|
+
end
|
151
|
+
|
152
|
+
def block
|
153
|
+
case kind
|
154
|
+
when :defn
|
155
|
+
strip_scope_wrapper(body[2])
|
156
|
+
when :defs
|
157
|
+
strip_scope_wrapper(body[3])
|
158
|
+
when :class
|
159
|
+
strip_scope_wrapper(body[2])
|
160
|
+
when :module
|
161
|
+
strip_scope_wrapper(body[1])
|
162
|
+
else
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def call!(name, arguments = nil, body = nil)
|
168
|
+
Sexp.call_on!(self, name, arguments, body)
|
169
|
+
end
|
170
|
+
|
171
|
+
def module?(name = nil)
|
172
|
+
kind == :module &&
|
173
|
+
(name.nil? || match_expression(body[0], name))
|
174
|
+
end
|
175
|
+
|
176
|
+
def class?(name = nil, options = {})
|
177
|
+
kind == :class &&
|
178
|
+
(name.nil? || match_expression(body[0], name))
|
179
|
+
end
|
180
|
+
|
181
|
+
def method?(name = nil)
|
182
|
+
(kind == :defn && (name.nil? || match_expression(body[0], name))) ||
|
183
|
+
(kind == :defs && (name.nil? || match_expression(body[1], name)))
|
184
|
+
end
|
185
|
+
|
186
|
+
def call?(name = nil, options = {})
|
187
|
+
call_without_block?(name, options) || call_with_block?(name, options)
|
188
|
+
end
|
189
|
+
|
190
|
+
def call_without_block?(name = nil, options = {})
|
191
|
+
kind == :call &&
|
192
|
+
(name.nil? || match_expression(body[1], name)) &&
|
193
|
+
(options[:arguments].nil? || match_arguments_expression(self, options[:arguments])) &&
|
194
|
+
(!options[:block])
|
195
|
+
end
|
196
|
+
|
197
|
+
def call_with_block?(name = nil, options = {})
|
198
|
+
kind == :iter && body[0] && body[0].kind == :call &&
|
199
|
+
(name.nil? || match_expression(body[0].name, name)) &&
|
200
|
+
(options[:arguments].nil? || match_arguments_expression(body[0], options[:arguments])) &&
|
201
|
+
(options[:block].nil? || match_arguments_expression(self, options[:block]))
|
202
|
+
end
|
203
|
+
|
204
|
+
def rescue?
|
205
|
+
kind == :rescue
|
206
|
+
end
|
207
|
+
|
208
|
+
def conditional?(options = {})
|
209
|
+
kind == :if &&
|
210
|
+
(options[:type].nil? || match_conditional_type(self, options[:type]))
|
211
|
+
end
|
212
|
+
|
213
|
+
def case?
|
214
|
+
kind == :case
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
protected
|
219
|
+
|
220
|
+
def emit_as_args_array(e)
|
221
|
+
return e unless e.is_a?(Sexp)
|
222
|
+
|
223
|
+
case e.kind
|
224
|
+
when :arglist, :args
|
225
|
+
e.body.map {|c| emit_as_args_array(c) }.flatten
|
226
|
+
when :lasgn
|
227
|
+
[e.body[0]]
|
228
|
+
when :masgn
|
229
|
+
e.body[0].body.map {|c| emit_as_args_array(c) }.flatten
|
230
|
+
when :lit
|
231
|
+
[e.body[0]]
|
232
|
+
else
|
233
|
+
[e]
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def match_expression(match_against, expression)
|
238
|
+
case expression
|
239
|
+
when String
|
240
|
+
match_against.to_s == expression
|
241
|
+
when Regexp
|
242
|
+
match_against.to_s =~ expression
|
243
|
+
when Array
|
244
|
+
expression.map(&:to_s).include?(match_against.to_s)
|
245
|
+
else
|
246
|
+
false
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def match_arguments_expression(match_against, expression)
|
251
|
+
case expression
|
252
|
+
when Fixnum
|
253
|
+
expression == match_against.to_args.size
|
254
|
+
when Range
|
255
|
+
expression.include?(match_against.to_args.size)
|
256
|
+
when Array
|
257
|
+
expression == match_against.to_args
|
258
|
+
when TrueClass
|
259
|
+
match_against.to_args.size > 0
|
260
|
+
when FalseClass
|
261
|
+
match_against.to_args.size == 0
|
262
|
+
else
|
263
|
+
false
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def match_conditional_type(match_against, expression)
|
268
|
+
case expression
|
269
|
+
when :if
|
270
|
+
!match_against.body[1].nil? && match_against.body[2].nil?
|
271
|
+
when :unless
|
272
|
+
match_against.body[1].nil? && !match_against.body[2].nil?
|
273
|
+
when :if_else
|
274
|
+
!match_against.body[1].nil? && !match_against.body[2].nil?
|
275
|
+
else
|
276
|
+
false
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def strip_scope_wrapper(e)
|
281
|
+
e.kind == :scope ? strip_scope_wrapper(e.body[0]) : e
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
data/lib/ruby_scribe/version.rb
CHANGED
data/lib/ruby_scribe.rb
CHANGED
@@ -2,7 +2,9 @@ require "rubygems"
|
|
2
2
|
require "active_support"
|
3
3
|
require "ruby_parser"
|
4
4
|
|
5
|
+
require "ruby_scribe/sexp_helpers"
|
5
6
|
require "ruby_scribe/emitter_helpers"
|
7
|
+
require "ruby_scribe/emitter_config"
|
6
8
|
require "ruby_scribe/emitter"
|
7
9
|
require "ruby_scribe/transformer_helpers"
|
8
10
|
require "ruby_scribe/transformer"
|
@@ -0,0 +1,256 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe RubyScribe::SexpHelpers do
|
4
|
+
context "generating" do
|
5
|
+
context "module" do
|
6
|
+
describe "with string name" do
|
7
|
+
subject { Sexp.module!("MyModule") }
|
8
|
+
it { should == s(:module, :MyModule, s(:scope)) }
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "with string colonized name" do
|
12
|
+
subject { Sexp.module!("Namespace::MyModule") }
|
13
|
+
it { should == s(:module, s(:colon2, s(:const, :Namespace), :MyModule), s(:scope)) }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "with string double-colonized name" do
|
17
|
+
subject { Sexp.module!("Double::Namespace::MyModule") }
|
18
|
+
it { should == s(:module, s(:colon2, s(:colon2, s(:const, :Double), :Namespace), :MyModule), s(:scope)) }
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "with sexp name" do
|
22
|
+
subject { Sexp.module!(s(:colon2, s(:const, :Namespace), :MyModule)) }
|
23
|
+
it { should == s(:module, s(:colon2, s(:const, :Namespace), :MyModule), s(:scope)) }
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "with single-statement body passed" do
|
27
|
+
subject { Sexp.module!("MyModule", s(:lit, 1)) }
|
28
|
+
it { should == s(:module, :MyModule, s(:scope, s(:lit, 1))) }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "with single-statement scoped body passed" do
|
32
|
+
subject { Sexp.module!("MyModule", s(:scope, s(:lit, 1))) }
|
33
|
+
it { should == s(:module, :MyModule, s(:scope, s(:lit, 1))) }
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "with block body passed" do
|
37
|
+
subject { Sexp.module!("MyModule", s(:block, s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist)))) }
|
38
|
+
it { should == s(:module, :MyModule, s(:scope, s(:block, s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist))))) }
|
39
|
+
specify { subject.block.should == s(:block, s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist))) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "class" do
|
44
|
+
describe "with string name" do
|
45
|
+
subject { Sexp.class!("MyClass") }
|
46
|
+
it { should == s(:class, :MyClass, nil, s(:scope)) }
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "with string colonized name" do
|
50
|
+
subject { Sexp.class!("Namespace::MyClass") }
|
51
|
+
it { should == s(:class, s(:colon2, s(:const, :Namespace), :MyClass), nil, s(:scope)) }
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "with string double-colonized name" do
|
55
|
+
subject { Sexp.class!("Double::Namespace::MyClass") }
|
56
|
+
it { should == s(:class, s(:colon2, s(:colon2, s(:const, :Double), :Namespace), :MyClass), nil, s(:scope)) }
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "with sexp name" do
|
60
|
+
subject { Sexp.class!(s(:colon2, s(:const, :Namespace), :MyClass)) }
|
61
|
+
it { should == s(:class, s(:colon2, s(:const, :Namespace), :MyClass), nil, s(:scope)) }
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "with single-statement body passed" do
|
65
|
+
subject { Sexp.class!("MyClass", nil, s(:lit, 1)) }
|
66
|
+
it { should == s(:class, :MyClass, nil, s(:scope, s(:lit, 1))) }
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "with single-statement scoped body passed" do
|
70
|
+
subject { Sexp.class!("MyClass", nil, s(:scope, s(:lit, 1))) }
|
71
|
+
it { should == s(:class, :MyClass, nil, s(:scope, s(:lit, 1))) }
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "with block body passed" do
|
75
|
+
subject { Sexp.class!("MyClass", nil, s(:block, s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist)))) }
|
76
|
+
it { should == s(:class, :MyClass, nil, s(:scope, s(:block, s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist))))) }
|
77
|
+
specify { subject.block.should == s(:block, s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist))) }
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "extending a string name" do
|
81
|
+
subject { Sexp.class!("MyClass", "Base") }
|
82
|
+
it { should == s(:class, :MyClass, s(:const, :Base), s(:scope)) }
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "extending a colonized string name" do
|
86
|
+
subject { Sexp.class!("MyClass", "Base::Two") }
|
87
|
+
it { should == s(:class, :MyClass, s(:colon2, s(:const, :Base), :Two), s(:scope)) }
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "extending an sexp name" do
|
91
|
+
subject { Sexp.class!("MyClass", s(:const, :Base)) }
|
92
|
+
it { should == s(:class, :MyClass, s(:const, :Base), s(:scope)) }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "method" do
|
97
|
+
describe "blank method" do
|
98
|
+
subject { Sexp.method!("my_method") }
|
99
|
+
it { should == s(:defn, :my_method, s(:args), s(:scope, s(:block, s(:nil)))) }
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "method with arguments" do
|
103
|
+
subject { Sexp.method!("my_method", [:arg1, :arg2]) }
|
104
|
+
it { should == s(:defn, :my_method, s(:args, :arg1, :arg2), s(:scope, s(:block, s(:nil)))) }
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "method with argument default" do
|
108
|
+
subject { Sexp.method!("my_method", [:arg1, :arg2, {:arg2 => s(:lit, 1)}]) }
|
109
|
+
it { should == s(:defn, :my_method, s(:args, :arg1, :arg2, s(:block, s(:lasgn, :arg2, s(:lit, 1)))), s(:scope, s(:block, s(:nil)))) }
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "method with single-statement body" do
|
113
|
+
subject { Sexp.method!("my_method", [], s(:lit, 1)) }
|
114
|
+
it { should == s(:defn, :my_method, s(:args), s(:scope, s(:block, s(:lit, 1)))) }
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "method with multiple-statement body as sexp" do
|
118
|
+
subject { Sexp.method!("my_method", [], s(:block, s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist)))) }
|
119
|
+
it { should == s(:defn, :my_method, s(:args), s(:scope, s(:block, s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist))))) }
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "method with multiple-statement body including scope" do
|
123
|
+
subject { Sexp.method!("my_method", [], s(:scope, s(:block, s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist))))) }
|
124
|
+
it { should == s(:defn, :my_method, s(:args), s(:scope, s(:block, s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist))))) }
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "method with multiple-statement body as array" do
|
128
|
+
subject { Sexp.method!("my_method", [], [s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist))]) }
|
129
|
+
it { should == s(:defn, :my_method, s(:args), s(:scope, s(:block, s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist))))) }
|
130
|
+
specify { subject.block.should == s(:block, s(:call, nil, :one, s(:arglist)), s(:call, nil, :two, s(:arglist))) }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context "method call" do
|
135
|
+
describe "without arguments" do
|
136
|
+
subject { Sexp.call!("my_method") }
|
137
|
+
it { should == s(:call, nil, :my_method, s(:arglist)) }
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "with arguments as sexp" do
|
141
|
+
subject { Sexp.call!("my_method", s(:arglist, s(:lit, 1), s(:lit, 2))) }
|
142
|
+
it { should == s(:call, nil, :my_method, s(:arglist, s(:lit, 1), s(:lit, 2))) }
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "with arguments as array" do
|
146
|
+
subject { Sexp.call!("my_method", [s(:lit, 1), s(:lit, 2)]) }
|
147
|
+
it { should == s(:call, nil, :my_method, s(:arglist, s(:lit, 1), s(:lit, 2))) }
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "with explicit self" do
|
151
|
+
subject { s(:self).call!("my_method") }
|
152
|
+
it { should == s(:call, s(:self), :my_method, s(:arglist)) }
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "with receiver" do
|
156
|
+
subject { s(:lit, 1).call!("my_method") }
|
157
|
+
it { should == s(:call, s(:lit, 1), :my_method, s(:arglist)) }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "an s-expression for" do
|
163
|
+
context "module" do
|
164
|
+
subject { s(:module, :MyModule, s(:scope, s(:lit, 1))) }
|
165
|
+
it { should be_module }
|
166
|
+
it { should be_module("MyModule") }
|
167
|
+
it { should be_module(/dul/) }
|
168
|
+
it { should be_module([:MyModule, :YourModule]) }
|
169
|
+
end
|
170
|
+
|
171
|
+
context "class" do
|
172
|
+
subject { s(:class, :MyClass, nil, s(:scope, s(:lit, 1))) }
|
173
|
+
it { should be_class }
|
174
|
+
it { should be_class("MyClass") }
|
175
|
+
it { should be_class(/las/) }
|
176
|
+
it { should be_class([:MyClass, :YourClass]) }
|
177
|
+
end
|
178
|
+
|
179
|
+
context "instance method" do
|
180
|
+
subject { s(:defn, :my_method, s(:args, :arg), s(:scope, s(:block, s(:lit, 1)))) }
|
181
|
+
it { should be_method }
|
182
|
+
it { should be_method("my_method") }
|
183
|
+
it { should be_method(/met/) }
|
184
|
+
it { should be_method([:my_method, :your_method]) }
|
185
|
+
specify { subject.to_args.should == [:arg] }
|
186
|
+
|
187
|
+
describe "with two arguments" do
|
188
|
+
subject { s(:defn, :my_method, s(:args, :one, :two), s(:scope, s(:block, s(:lit, 1)))) }
|
189
|
+
specify { subject.to_args.should == [:one, :two] }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context "class method" do
|
194
|
+
subject { s(:defs, s(:self), :my_method, s(:args, :arg), s(:scope, s(:block, s(:lit, 1)))) }
|
195
|
+
it { should be_method }
|
196
|
+
it { should be_method("my_method") }
|
197
|
+
it { should be_method(/met/) }
|
198
|
+
it { should be_method([:my_method, :your_method]) }
|
199
|
+
end
|
200
|
+
|
201
|
+
context "method call" do
|
202
|
+
describe "with one argument" do
|
203
|
+
subject { s(:call, nil, :invoke, s(:arglist, s(:lit, 1))) }
|
204
|
+
it { should be_call }
|
205
|
+
it { should be_call("invoke") }
|
206
|
+
it { should be_call(/vok/) }
|
207
|
+
it { should be_call([:invoke, :another]) }
|
208
|
+
it { should be_call("invoke", :arguments => true) }
|
209
|
+
it { should be_call("invoke", :arguments => 1) }
|
210
|
+
it { should_not be_call("invoke", :arguments => 2) }
|
211
|
+
it { should_not be_call("invoke", :block => true) }
|
212
|
+
end
|
213
|
+
|
214
|
+
describe "with block" do
|
215
|
+
subject { s(:iter, s(:call, nil, :each, s(:arglist)), s(:lasgn, :i), s(:lit, 1)) }
|
216
|
+
it { should be_call }
|
217
|
+
it { should be_call("each") }
|
218
|
+
it { should be_call("each", :block => true) }
|
219
|
+
it { should be_call("each", :block => 1) }
|
220
|
+
it { should_not be_call("each", :block => 2) }
|
221
|
+
specify { subject.to_args.should == [:i] }
|
222
|
+
end
|
223
|
+
|
224
|
+
describe "with block (2 arguments) and 1 method argument" do
|
225
|
+
subject { s(:iter, s(:call, nil, :inject, s(:arglist, s(:lit, 1))), s(:masgn, s(:array, s(:lasgn, :b), s(:lasgn, :i))), s(:lit, 1)) }
|
226
|
+
it { should be_call(nil, :block => true) }
|
227
|
+
it { should be_call(nil, :arguments => true) }
|
228
|
+
it { should be_call(nil, :arguments => 1, :block => 2) }
|
229
|
+
it { should_not be_call(nil, :arguments => 2, :block => 2) }
|
230
|
+
specify { subject.to_args.should == [:b, :i] }
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
context "conditional" do
|
235
|
+
describe "if-else" do
|
236
|
+
subject { s(:if, s(:true), s(:lit, 1), s(:lit, 2)) }
|
237
|
+
it { should be_conditional(:type => :if_else) }
|
238
|
+
end
|
239
|
+
|
240
|
+
describe "if" do
|
241
|
+
subject { s(:if, s(:true), s(:lit, 1), nil) }
|
242
|
+
it { should be_conditional(:type => :if) }
|
243
|
+
end
|
244
|
+
|
245
|
+
describe "unless" do
|
246
|
+
subject { s(:if, s(:true), nil, s(:lit, 2)) }
|
247
|
+
it { should be_conditional(:type => :unless) }
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context "case statement" do
|
252
|
+
subject { s(:case, nil, s(:when, s(:array, s(:true)), s(:lit, 1)), s(:lit, 2)) }
|
253
|
+
it { should be_case }
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_scribe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.4
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ben Hughes
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-11-01 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -74,16 +74,11 @@ extra_rdoc_files: []
|
|
74
74
|
|
75
75
|
files:
|
76
76
|
- lib/ruby_scribe/emitter.rb
|
77
|
+
- lib/ruby_scribe/emitter_config.rb
|
77
78
|
- lib/ruby_scribe/emitter_helpers.rb
|
78
79
|
- lib/ruby_scribe/ext/sexp.rb
|
79
80
|
- lib/ruby_scribe/runner.rb
|
80
|
-
- lib/ruby_scribe/
|
81
|
-
- lib/ruby_scribe/transformer_helpers.rb
|
82
|
-
- lib/ruby_scribe/transformers/block_method_to_procifier.rb
|
83
|
-
- lib/ruby_scribe/transformers/custom.rb
|
84
|
-
- lib/ruby_scribe/transformers/eachifier.rb
|
85
|
-
- lib/ruby_scribe/transformers/symbol_names.rb
|
86
|
-
- lib/ruby_scribe/transformers/tapifier.rb
|
81
|
+
- lib/ruby_scribe/sexp_helpers.rb
|
87
82
|
- lib/ruby_scribe/version.rb
|
88
83
|
- lib/ruby_scribe.rb
|
89
84
|
- spec/examples/identity.rb
|
@@ -93,12 +88,7 @@ files:
|
|
93
88
|
- spec/matchers/should_transform_to.rb
|
94
89
|
- spec/ruby_scribe/emitter_examples_spec.rb
|
95
90
|
- spec/ruby_scribe/emitter_spec.rb
|
96
|
-
- spec/ruby_scribe/
|
97
|
-
- spec/ruby_scribe/transformers/block_method_to_procifier_spec.rb
|
98
|
-
- spec/ruby_scribe/transformers/custom_spec.rb
|
99
|
-
- spec/ruby_scribe/transformers/eachifier_spec.rb
|
100
|
-
- spec/ruby_scribe/transformers/symbol_names_spec.rb
|
101
|
-
- spec/ruby_scribe/transformers/tapifier_spec.rb
|
91
|
+
- spec/ruby_scribe/sexp_helpers_spec.rb
|
102
92
|
- spec/spec_helper.rb
|
103
93
|
- LICENSE
|
104
94
|
- Rakefile
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module RubyScribe
|
2
|
-
|
3
|
-
# Takes a raw S-expression and transforms it (replaces the node with the return value of the process method).
|
4
|
-
# This is meant to be subclassed, and in the process method you should either return a transformed version of the node,
|
5
|
-
# or just call super which will leave the node in place and iterate through the children.
|
6
|
-
#
|
7
|
-
class Transformer
|
8
|
-
include TransformerHelpers
|
9
|
-
|
10
|
-
def transform(sexp)
|
11
|
-
if sexp.is_a?(Sexp)
|
12
|
-
Sexp.new(*([sexp.kind] + sexp.body.map {|c| transform(c) }))
|
13
|
-
else
|
14
|
-
sexp
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
module RubyScribe
|
2
|
-
module Transformers
|
3
|
-
|
4
|
-
# = Block Method To Proc Transform
|
5
|
-
# Looks for cases where we're calling a single method on a single-parameter block argument.
|
6
|
-
# These instances can use the Ruby 1.9 and ActiveSupport "Symbol#to_proc" trick.
|
7
|
-
#
|
8
|
-
# Example:
|
9
|
-
#
|
10
|
-
# collection.map {|d| d.name }
|
11
|
-
#
|
12
|
-
# Transforms To:
|
13
|
-
#
|
14
|
-
# collect.map(&:name)
|
15
|
-
#
|
16
|
-
class BlockMethodToProcifier < Transformer
|
17
|
-
def transform(e)
|
18
|
-
if matches_block_to_use_tap?(e)
|
19
|
-
super transform_block_to_use_tap(e)
|
20
|
-
else
|
21
|
-
super
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def matches_block_to_use_tap?(e)
|
26
|
-
e.is_a?(Sexp) && e.kind == :iter && # Calls block
|
27
|
-
e.body[2] && e.body[2].kind == :call && # Body of block is a simple method call
|
28
|
-
e.body[2].body[0].kind == :lvar && # Simple method call is on a local variable
|
29
|
-
e.body[1].kind == :lasgn && # Block parameter is a single assign
|
30
|
-
e.body[2].body[0].body[0] == e.body[1].body[0] # Local variable is identical to the first block argument
|
31
|
-
end
|
32
|
-
|
33
|
-
def transform_block_to_use_tap(e)
|
34
|
-
s(:call,
|
35
|
-
e.body[0].body[0],
|
36
|
-
e.body[0].body[1],
|
37
|
-
s(:arglist, s(:block_pass, s(:lit, e.body[2].body[1])))
|
38
|
-
)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|