ruby_scribe 0.0.4 → 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/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
|