sorcerer 0.0.7 → 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/README.textile +55 -0
- data/Rakefile +18 -19
- data/lib/sorcerer.rb +2 -2
- data/lib/sorcerer/resource.rb +201 -86
- data/lib/sorcerer/version.rb +2 -2
- data/test/sorcerer/resource_test.rb +127 -85
- metadata +9 -17
data/README.textile
CHANGED
@@ -44,3 +44,58 @@ Ripper may be used to produce the s-expressions used by Sorcerer. The following
|
|
44
44
|
puts Sorcerer.source(sexp)
|
45
45
|
</pre>
|
46
46
|
|
47
|
+
h2. Options
|
48
|
+
|
49
|
+
h3. Multi-Line Output
|
50
|
+
|
51
|
+
If you want multi-line output of source, add the multiline option to
|
52
|
+
the source command.
|
53
|
+
|
54
|
+
For example, given:
|
55
|
+
|
56
|
+
<pre style="background: LightGrey">
|
57
|
+
sexp = Ripper::SexpBuilder.new("def foo; end").parse
|
58
|
+
</pre>
|
59
|
+
|
60
|
+
Then the following
|
61
|
+
|
62
|
+
<pre style="background: LightGrey">
|
63
|
+
puts Sorcerer.source(sexp)
|
64
|
+
</pre>
|
65
|
+
|
66
|
+
generates single line output (the default):
|
67
|
+
|
68
|
+
<pre style="background: LightBlue">
|
69
|
+
def foo; end
|
70
|
+
</pre>
|
71
|
+
|
72
|
+
And the following
|
73
|
+
|
74
|
+
<pre style="background: LightGrey">
|
75
|
+
puts Sorcerer.source(sexp, multiline: true)
|
76
|
+
</pre>
|
77
|
+
|
78
|
+
generates multi-line output
|
79
|
+
|
80
|
+
<pre style="background: LightBlue">
|
81
|
+
def foo
|
82
|
+
end
|
83
|
+
</pre>
|
84
|
+
|
85
|
+
h3. Debugging Output
|
86
|
+
|
87
|
+
If you wish to see the S-Expressions processed by Sorcerer and the
|
88
|
+
output emitted, then use the debug option:
|
89
|
+
|
90
|
+
<pre style="background: LightGrey">
|
91
|
+
puts Sorcerer.source(sexp, debug: true)
|
92
|
+
</pre>
|
93
|
+
|
94
|
+
h2. History
|
95
|
+
|
96
|
+
* 0.0.7 - Basic single line version
|
97
|
+
* 0.1.0 - Added support for multi-line output. Improved rendering of a
|
98
|
+
number of constructs
|
99
|
+
|
100
|
+
|
101
|
+
|
data/Rakefile
CHANGED
@@ -2,13 +2,13 @@
|
|
2
2
|
|
3
3
|
require 'rake/clean'
|
4
4
|
require 'rake/testtask'
|
5
|
-
require '
|
5
|
+
#require 'rdoc/task'
|
6
6
|
|
7
7
|
require './lib/sorcerer/version'
|
8
8
|
|
9
9
|
begin
|
10
10
|
require 'rubygems'
|
11
|
-
require '
|
11
|
+
require 'rubygems/package_task'
|
12
12
|
rescue Exception
|
13
13
|
nil
|
14
14
|
end
|
@@ -25,7 +25,7 @@ PKG_FILES = FileList[
|
|
25
25
|
'lib/**/*.rb',
|
26
26
|
'test/**/*.rb',
|
27
27
|
]
|
28
|
-
|
28
|
+
|
29
29
|
BASE_RDOC_OPTIONS = [
|
30
30
|
'--line-numbers', '--inline-source',
|
31
31
|
'--main' , 'README.textile',
|
@@ -34,16 +34,17 @@ BASE_RDOC_OPTIONS = [
|
|
34
34
|
|
35
35
|
task :default => :test
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
37
|
+
RDOC_FILES = FileList['lib/**/*.rb', 'doc/**/*.rdoc'].exclude(/\bcontrib\b/)
|
38
|
+
|
39
|
+
if defined?(Rake::RDocTesk)
|
40
|
+
rd = Rake::RDocTask.new("rdoc") do |rdoc|
|
41
|
+
rdoc.rdoc_dir = 'html'
|
42
|
+
rdoc.template = 'doc/jamis.rb'
|
43
|
+
rdoc.title = "Sorcerer -- Its Like Magic"
|
44
|
+
rdoc.options = BASE_RDOC_OPTIONS.dup
|
45
|
+
rdoc.options << '-SHN' << '-f' << 'darkfish' if defined?(DARKFISH_ENABLED) && DARKFISH_ENABLED
|
46
|
+
rdoc.rdoc_files = RDOC_FILES
|
47
|
+
end
|
47
48
|
end
|
48
49
|
|
49
50
|
if ! defined?(Gem)
|
@@ -53,13 +54,11 @@ else
|
|
53
54
|
s.name = 'sorcerer'
|
54
55
|
s.version = PKG_VERSION
|
55
56
|
s.summary = "Generate Source from Ripper ASTs"
|
56
|
-
s.description =
|
57
|
-
|
58
|
-
EOF
|
59
|
-
s.files = PKG_FILES.to_a
|
57
|
+
s.description = "Generate the original Ruby source from a Ripper-style abstract syntax tree."
|
58
|
+
s.files = PKG_FILES
|
60
59
|
s.require_path = 'lib' # Use these for libraries.
|
61
60
|
s.has_rdoc = true
|
62
|
-
s.extra_rdoc_files =
|
61
|
+
s.extra_rdoc_files = RDOC_FILES.reject { |fn| fn =~ /\.rb$/ }.to_a
|
63
62
|
s.rdoc_options = BASE_RDOC_OPTIONS
|
64
63
|
s.author = "Jim Weirich"
|
65
64
|
s.email = "jim.weirich@gmail.com"
|
@@ -67,7 +66,7 @@ else
|
|
67
66
|
s.homepage = "http://github.com/jimweirich/sorcerer"
|
68
67
|
end
|
69
68
|
|
70
|
-
package_task =
|
69
|
+
package_task = Gem::PackageTask.new(SPEC) do |pkg|
|
71
70
|
pkg.need_zip = true
|
72
71
|
pkg.need_tar = true
|
73
72
|
end
|
data/lib/sorcerer.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Sorcerer
|
2
2
|
# Generate the source code for teh given Ripper S-Expression.
|
3
|
-
def self.source(sexp,
|
4
|
-
Sorcerer::Resource.new(sexp,
|
3
|
+
def self.source(sexp, options={})
|
4
|
+
Sorcerer::Resource.new(sexp, options).source
|
5
5
|
end
|
6
6
|
|
7
7
|
# Generate a list of interesting subexpressions for sexp.
|
data/lib/sorcerer/resource.rb
CHANGED
@@ -2,62 +2,104 @@
|
|
2
2
|
|
3
3
|
require 'ripper'
|
4
4
|
|
5
|
-
module Sorcerer
|
5
|
+
module Sorcerer
|
6
6
|
class Resource
|
7
|
-
class
|
7
|
+
class SorcererError < StandardError
|
8
8
|
end
|
9
|
-
|
10
|
-
|
9
|
+
|
10
|
+
class NoHandlerError < SorcererError
|
11
|
+
end
|
12
|
+
|
13
|
+
class NotSexpError < SorcererError
|
14
|
+
end
|
15
|
+
|
16
|
+
class UnexpectedSexpError < SorcererError
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(sexp, options={})
|
11
20
|
@sexp = sexp
|
12
21
|
@source = ''
|
13
|
-
@debug = debug
|
22
|
+
@debug = options[:debug]
|
23
|
+
@multiline = options[:multiline]
|
14
24
|
@word_level = 0
|
25
|
+
@stack = []
|
15
26
|
end
|
16
|
-
|
27
|
+
|
17
28
|
def source
|
29
|
+
@stack.clear
|
18
30
|
resource(@sexp)
|
19
31
|
@source
|
20
32
|
end
|
21
|
-
|
33
|
+
|
34
|
+
def sexp?(obj)
|
35
|
+
obj && obj.respond_to?(:each) && obj.first.is_a?(Symbol)
|
36
|
+
end
|
37
|
+
|
38
|
+
def nested_sexp?(obj)
|
39
|
+
obj && obj.respond_to?(:first) && sexp?(obj.first)
|
40
|
+
end
|
41
|
+
|
22
42
|
def resource(sexp)
|
23
|
-
|
43
|
+
sexp = sexp.first if nested_sexp?(sexp)
|
44
|
+
fail NotSexpError, "Not an S-EXPER: #{sexp.inspect}" unless sexp?(sexp)
|
24
45
|
handler = HANDLERS[sexp.first]
|
25
46
|
raise NoHandlerError.new(sexp.first) unless handler
|
26
47
|
if @debug
|
27
48
|
puts "----------------------------------------------------------"
|
28
49
|
pp sexp
|
29
50
|
end
|
51
|
+
@stack.push(sexp.first)
|
30
52
|
handler.call(self, sexp)
|
53
|
+
@stack.pop
|
31
54
|
end
|
32
|
-
|
33
|
-
def
|
34
|
-
|
55
|
+
|
56
|
+
def emit_block(sexp, do_word, end_word)
|
57
|
+
emit(" ")
|
58
|
+
emit(do_word)
|
59
|
+
resource(sexp[1]) if sexp[1] # Arguments
|
35
60
|
if ! void?(sexp[2])
|
61
|
+
soft_newline
|
62
|
+
resource(sexp[2]) # Statements
|
63
|
+
end
|
64
|
+
if !void?(sexp[2])
|
65
|
+
soft_newline
|
66
|
+
else
|
36
67
|
emit(" ")
|
37
|
-
resource(sexp[2]) # Statements
|
38
68
|
end
|
39
|
-
emit(
|
69
|
+
emit(end_word)
|
70
|
+
end
|
71
|
+
|
72
|
+
def params_have_parens?(sexp)
|
73
|
+
sexp.first == :arg_paren || sexp.first == :paren
|
40
74
|
end
|
41
|
-
|
75
|
+
|
76
|
+
def params_are_empty?(sexp)
|
77
|
+
params = sexp
|
78
|
+
params = sexp[1] if sexp.first == :paren || sexp.first == :arg_paren
|
79
|
+
params[1].nil? || params[1].empty?
|
80
|
+
end
|
81
|
+
|
42
82
|
def opt_parens(sexp)
|
43
|
-
|
83
|
+
if !params_have_parens?(sexp) && ! params_are_empty?(sexp)
|
84
|
+
emit(" ")
|
85
|
+
end
|
44
86
|
resource(sexp)
|
45
87
|
end
|
46
|
-
|
88
|
+
|
47
89
|
def emit(string)
|
48
|
-
puts "EMITTING '#{string}'" if @debug
|
90
|
+
puts "EMITTING '#{string}' (#{last_handler})" if @debug
|
49
91
|
@source << string.to_s
|
50
92
|
end
|
51
|
-
|
93
|
+
|
52
94
|
def nyi(sexp)
|
53
95
|
raise "Handler for #{sexp.first} not implemented (#{sexp.inspect})"
|
54
96
|
end
|
55
|
-
|
97
|
+
|
56
98
|
def emit_separator(sep, first)
|
57
99
|
emit(sep) unless first
|
58
100
|
false
|
59
101
|
end
|
60
|
-
|
102
|
+
|
61
103
|
def params(normal_args, default_args, rest_args, unknown, block_arg)
|
62
104
|
first = true
|
63
105
|
if normal_args
|
@@ -83,7 +125,7 @@ module Sorcerer
|
|
83
125
|
resource(block_arg)
|
84
126
|
end
|
85
127
|
end
|
86
|
-
|
128
|
+
|
87
129
|
def words(marker, sexp)
|
88
130
|
emit("%#{marker}{") if @word_level == 0
|
89
131
|
@word_level += 1
|
@@ -95,18 +137,62 @@ module Sorcerer
|
|
95
137
|
@word_level -= 1
|
96
138
|
emit("}") if @word_level == 0
|
97
139
|
end
|
98
|
-
|
140
|
+
|
99
141
|
VOID_STATEMENT = [:stmts_add, [:stmts_new], [:void_stmt]]
|
142
|
+
VOID_STATEMENT2 = [:stmts_add, [:stmts_new]]
|
100
143
|
VOID_BODY = [:body_stmt, VOID_STATEMENT, nil, nil, nil]
|
101
144
|
VOID_BODY2 = [:bodystmt, VOID_STATEMENT, nil, nil, nil]
|
102
|
-
|
145
|
+
|
103
146
|
def void?(sexp)
|
104
147
|
sexp.nil? ||
|
105
148
|
sexp == VOID_STATEMENT ||
|
149
|
+
sexp == VOID_STATEMENT2 ||
|
106
150
|
sexp == VOID_BODY ||
|
107
151
|
sexp == VOID_BODY2
|
108
152
|
end
|
109
|
-
|
153
|
+
|
154
|
+
def multiline?
|
155
|
+
@multiline
|
156
|
+
end
|
157
|
+
|
158
|
+
def last_handler
|
159
|
+
@stack.last
|
160
|
+
end
|
161
|
+
|
162
|
+
def newline
|
163
|
+
if multiline?
|
164
|
+
emit("\n")
|
165
|
+
else
|
166
|
+
emit("; ")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def soft_newline
|
171
|
+
if multiline?
|
172
|
+
emit("\n")
|
173
|
+
else
|
174
|
+
emit(" ")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
BALANCED_DELIMS = {
|
179
|
+
'}' => '{',
|
180
|
+
')' => '(',
|
181
|
+
'>' => '<',
|
182
|
+
']' => '[',
|
183
|
+
}
|
184
|
+
|
185
|
+
def self.determine_regexp_delimiters(sexp)
|
186
|
+
sym, end_delim, other = sexp
|
187
|
+
fail UnexpectedSexpError, "Expected :@regexp_end, got #{sym.inspect}" unless sym == :@regexp_end
|
188
|
+
end_delim_char = end_delim[0]
|
189
|
+
first_delim = BALANCED_DELIMS[end_delim_char] || end_delim_char
|
190
|
+
if first_delim != '/'
|
191
|
+
first_delim = "%r#{first_delim}"
|
192
|
+
end
|
193
|
+
[first_delim, end_delim]
|
194
|
+
end
|
195
|
+
|
110
196
|
NYI = lambda { |src, sexp| src.nyi(sexp) }
|
111
197
|
DBG = lambda { |src, sexp| pp(sexp) }
|
112
198
|
NOOP = lambda { |src, sexp| }
|
@@ -114,25 +200,31 @@ module Sorcerer
|
|
114
200
|
PASS1 = lambda { |src, sexp| src.resource(sexp[1]) }
|
115
201
|
PASS2 = lambda { |src, sexp| src.resource(sexp[2]) }
|
116
202
|
EMIT1 = lambda { |src, sexp| src.emit(sexp[1]) }
|
117
|
-
|
203
|
+
|
118
204
|
HANDLERS = {
|
119
205
|
# parser keywords
|
120
|
-
|
206
|
+
|
121
207
|
:BEGIN => lambda { |src, sexp|
|
122
208
|
src.emit("BEGIN {")
|
123
|
-
|
124
|
-
src.emit
|
209
|
+
if src.void?(sexp[1])
|
210
|
+
src.emit " }"
|
211
|
+
else
|
212
|
+
src.soft_newline
|
125
213
|
src.resource(sexp[1])
|
214
|
+
src.soft_newline
|
215
|
+
src.emit("}")
|
126
216
|
end
|
127
|
-
src.emit(" }")
|
128
217
|
},
|
129
218
|
:END => lambda { |src, sexp|
|
130
219
|
src.emit("END {")
|
131
|
-
|
132
|
-
src.emit(" ")
|
220
|
+
if src.void?(sexp[1])
|
221
|
+
src.emit(" }")
|
222
|
+
else
|
223
|
+
src.soft_newline
|
133
224
|
src.resource(sexp[1])
|
225
|
+
src.soft_newline
|
226
|
+
src.emit("}")
|
134
227
|
end
|
135
|
-
src.emit(" }")
|
136
228
|
},
|
137
229
|
:alias => lambda { |src, sexp|
|
138
230
|
src.emit("alias ")
|
@@ -190,7 +282,7 @@ module Sorcerer
|
|
190
282
|
:args_prepend => NYI,
|
191
283
|
:array => lambda { |src, sexp|
|
192
284
|
src.emit("[")
|
193
|
-
src.resource(sexp[1])
|
285
|
+
src.resource(sexp[1]) if sexp[1]
|
194
286
|
src.emit("]")
|
195
287
|
},
|
196
288
|
:assign => lambda { |src, sexp|
|
@@ -214,7 +306,7 @@ module Sorcerer
|
|
214
306
|
},
|
215
307
|
:bare_assoc_hash => lambda { |src, sexp|
|
216
308
|
first = true
|
217
|
-
sexp[1].each do |sx|
|
309
|
+
sexp[1].each do |sx|
|
218
310
|
src.emit(", ") unless first
|
219
311
|
first = false
|
220
312
|
src.resource(sx)
|
@@ -222,7 +314,12 @@ module Sorcerer
|
|
222
314
|
},
|
223
315
|
:begin => lambda { |src, sexp|
|
224
316
|
src.emit("begin")
|
225
|
-
src.
|
317
|
+
if src.void?(sexp[1])
|
318
|
+
src.emit(" ")
|
319
|
+
else
|
320
|
+
src.soft_newline
|
321
|
+
src.resource(sexp[1])
|
322
|
+
end
|
226
323
|
src.emit("end")
|
227
324
|
},
|
228
325
|
:binary => lambda { |src, sexp|
|
@@ -243,14 +340,12 @@ module Sorcerer
|
|
243
340
|
},
|
244
341
|
:body_stmt => lambda { |src, sexp|
|
245
342
|
src.resource(sexp[1]) # Main Body
|
246
|
-
src.
|
247
|
-
src.resource(sexp[2])
|
248
|
-
src.resource(sexp[4])
|
343
|
+
src.newline unless src.void?(sexp[1])
|
344
|
+
src.resource(sexp[2]) if sexp[2] # Rescue
|
345
|
+
src.resource(sexp[4]) if sexp[4] # Ensure
|
249
346
|
},
|
250
347
|
:brace_block => lambda { |src, sexp|
|
251
|
-
src.
|
252
|
-
src.handle_block(sexp)
|
253
|
-
src.emit("}")
|
348
|
+
src.emit_block(sexp, "{", "}")
|
254
349
|
},
|
255
350
|
:break => lambda { |src, sexp|
|
256
351
|
src.emit("break")
|
@@ -265,9 +360,10 @@ module Sorcerer
|
|
265
360
|
:case => lambda { |src, sexp|
|
266
361
|
src.emit("case ")
|
267
362
|
src.resource(sexp[1])
|
268
|
-
src.
|
363
|
+
src.soft_newline
|
269
364
|
src.resource(sexp[2])
|
270
|
-
src.
|
365
|
+
src.newline
|
366
|
+
src.emit("end")
|
271
367
|
},
|
272
368
|
:class => lambda { |src, sexp|
|
273
369
|
src.emit("class ")
|
@@ -276,7 +372,7 @@ module Sorcerer
|
|
276
372
|
src.emit " < "
|
277
373
|
src.resource(sexp[2])
|
278
374
|
end
|
279
|
-
src.
|
375
|
+
src.newline
|
280
376
|
src.resource(sexp[3]) unless src.void?(sexp[3])
|
281
377
|
src.emit("end")
|
282
378
|
},
|
@@ -302,6 +398,7 @@ module Sorcerer
|
|
302
398
|
src.emit("def ")
|
303
399
|
src.resource(sexp[1])
|
304
400
|
src.opt_parens(sexp[2])
|
401
|
+
src.newline
|
305
402
|
src.resource(sexp[3])
|
306
403
|
src.emit("end")
|
307
404
|
},
|
@@ -312,9 +409,7 @@ module Sorcerer
|
|
312
409
|
},
|
313
410
|
:defs => NYI,
|
314
411
|
:do_block => lambda { |src, sexp|
|
315
|
-
src.
|
316
|
-
src.handle_block(sexp)
|
317
|
-
src.emit("end")
|
412
|
+
src.emit_block(sexp, "do", "end")
|
318
413
|
},
|
319
414
|
:dot2 => lambda { |src, sexp|
|
320
415
|
src.resource(sexp[1])
|
@@ -332,21 +427,31 @@ module Sorcerer
|
|
332
427
|
src.emit('"')
|
333
428
|
},
|
334
429
|
:else => lambda { |src, sexp|
|
335
|
-
src.
|
430
|
+
src.soft_newline
|
431
|
+
src.emit("else")
|
432
|
+
src.soft_newline
|
336
433
|
src.resource(sexp[1])
|
337
434
|
},
|
338
435
|
:elsif => lambda { |src, sexp|
|
339
|
-
src.
|
436
|
+
src.soft_newline
|
437
|
+
src.emit("elsif ")
|
340
438
|
src.resource(sexp[1])
|
341
|
-
src.
|
439
|
+
if src.multiline?
|
440
|
+
src.soft_newline
|
441
|
+
else
|
442
|
+
src.emit(" then ")
|
443
|
+
end
|
342
444
|
src.resource(sexp[2])
|
343
|
-
src.resource(sexp[3])
|
445
|
+
src.resource(sexp[3]) if sexp[3]
|
344
446
|
},
|
345
447
|
:ensure => lambda { |src, sexp|
|
346
|
-
src.emit("ensure
|
347
|
-
if sexp[1]
|
448
|
+
src.emit("ensure")
|
449
|
+
if src.void?(sexp[1])
|
450
|
+
src.soft_newline
|
451
|
+
else
|
452
|
+
src.soft_newline
|
348
453
|
src.resource(sexp[1])
|
349
|
-
src.
|
454
|
+
src.newline unless src.void?(sexp[1])
|
350
455
|
end
|
351
456
|
},
|
352
457
|
:excessed_comma => NYI,
|
@@ -370,16 +475,21 @@ module Sorcerer
|
|
370
475
|
},
|
371
476
|
:hash => lambda { |src, sexp|
|
372
477
|
src.emit("{")
|
373
|
-
src.resource(sexp[1])
|
478
|
+
src.resource(sexp[1]) if sexp[1]
|
374
479
|
src.emit("}")
|
375
480
|
},
|
376
481
|
:if => lambda { |src, sexp|
|
377
482
|
src.emit("if ")
|
378
483
|
src.resource(sexp[1])
|
379
|
-
src.
|
484
|
+
if src.multiline?
|
485
|
+
src.newline
|
486
|
+
else
|
487
|
+
src.emit(" then ")
|
488
|
+
end
|
380
489
|
src.resource(sexp[2])
|
381
|
-
src.resource(sexp[3])
|
382
|
-
src.
|
490
|
+
src.resource(sexp[3]) if sexp[3]
|
491
|
+
src.soft_newline
|
492
|
+
src.emit("end")
|
383
493
|
},
|
384
494
|
:if_mod => lambda { |src, sexp|
|
385
495
|
src.resource(sexp[2])
|
@@ -438,8 +548,8 @@ module Sorcerer
|
|
438
548
|
:module => lambda { |src, sexp|
|
439
549
|
src.emit("module ")
|
440
550
|
src.resource(sexp[1])
|
551
|
+
src.newline
|
441
552
|
if src.void?(sexp[2])
|
442
|
-
src.emit("; ")
|
443
553
|
else
|
444
554
|
src.resource(sexp[2])
|
445
555
|
end
|
@@ -486,10 +596,12 @@ module Sorcerer
|
|
486
596
|
:redo => lambda { |src, sexp|
|
487
597
|
src.emit("redo")
|
488
598
|
},
|
599
|
+
:regexp_add => PASS2,
|
489
600
|
:regexp_literal => lambda { |src, sexp|
|
490
|
-
|
601
|
+
delims = determine_regexp_delimiters(sexp[2])
|
602
|
+
src.emit(delims[0])
|
491
603
|
src.resource(sexp[1])
|
492
|
-
src.emit(
|
604
|
+
src.emit(delims[1])
|
493
605
|
},
|
494
606
|
:rescue => lambda { |src, sexp|
|
495
607
|
src.emit("rescue")
|
@@ -501,17 +613,12 @@ module Sorcerer
|
|
501
613
|
src.resource(sexp[1].first)
|
502
614
|
end
|
503
615
|
src.emit(" => ")
|
504
|
-
src.resource(sexp[2])
|
616
|
+
src.resource(sexp[2])
|
505
617
|
end
|
506
|
-
src.
|
507
|
-
if sexp[3]
|
508
|
-
|
509
|
-
|
510
|
-
else
|
511
|
-
src.emit(" ")
|
512
|
-
src.resource(sexp[3])
|
513
|
-
src.emit("; ")
|
514
|
-
end
|
618
|
+
src.newline
|
619
|
+
if sexp[3] && ! src.void?(sexp[3])
|
620
|
+
src.resource(sexp[3])
|
621
|
+
src.newline
|
515
622
|
end
|
516
623
|
},
|
517
624
|
:rescue_mod => lambda { |src, sexp|
|
@@ -528,18 +635,18 @@ module Sorcerer
|
|
528
635
|
},
|
529
636
|
:return => lambda { |src, sexp|
|
530
637
|
src.emit("return")
|
531
|
-
src.opt_parens(sexp[1])
|
638
|
+
src.opt_parens(sexp[1])
|
532
639
|
},
|
533
640
|
:return0 => lambda { |src, sexp|
|
534
641
|
src.emit("return")
|
535
642
|
},
|
536
643
|
:sclass => NYI,
|
537
644
|
:stmts_add => lambda { |src, sexp|
|
538
|
-
if sexp[1] != [:stmts_new]
|
645
|
+
if sexp[1] != [:stmts_new] && ! src.void?(sexp[1])
|
539
646
|
src.resource(sexp[1])
|
540
|
-
src.
|
647
|
+
src.newline
|
541
648
|
end
|
542
|
-
src.resource(sexp[2])
|
649
|
+
src.resource(sexp[2]) if sexp[2]
|
543
650
|
},
|
544
651
|
:stmts_new => NOOP,
|
545
652
|
:string_add => lambda { |src, sexp|
|
@@ -585,10 +692,15 @@ module Sorcerer
|
|
585
692
|
:unless => lambda { |src, sexp|
|
586
693
|
src.emit("unless ")
|
587
694
|
src.resource(sexp[1])
|
588
|
-
src.
|
695
|
+
if src.multiline?
|
696
|
+
src.newline
|
697
|
+
else
|
698
|
+
src.emit(" then ")
|
699
|
+
end
|
589
700
|
src.resource(sexp[2])
|
590
|
-
src.resource(sexp[3])
|
591
|
-
src.
|
701
|
+
src.resource(sexp[3]) if sexp[3]
|
702
|
+
src.soft_newline
|
703
|
+
src.emit("end")
|
592
704
|
},
|
593
705
|
:unless_mod => lambda { |src, sexp|
|
594
706
|
src.resource(sexp[2])
|
@@ -614,19 +726,22 @@ module Sorcerer
|
|
614
726
|
:when => lambda { |src, sexp|
|
615
727
|
src.emit("when ")
|
616
728
|
src.resource(sexp[1])
|
617
|
-
src.
|
729
|
+
src.newline
|
618
730
|
src.resource(sexp[2])
|
619
731
|
if sexp[3] && sexp[3].first == :when
|
620
732
|
src.emit(" ")
|
621
733
|
end
|
622
|
-
src.resource(sexp[3])
|
734
|
+
src.resource(sexp[3]) if sexp[3]
|
623
735
|
},
|
624
736
|
:while => lambda { |src, sexp|
|
625
737
|
src.emit("while ")
|
626
738
|
src.resource(sexp[1])
|
627
|
-
src.
|
628
|
-
src.
|
629
|
-
|
739
|
+
src.newline
|
740
|
+
unless src.void?(sexp[2])
|
741
|
+
src.resource(sexp[2])
|
742
|
+
src.newline
|
743
|
+
end
|
744
|
+
src.emit("end")
|
630
745
|
},
|
631
746
|
:while_mod => lambda { |src, sexp|
|
632
747
|
src.resource(sexp[2])
|
@@ -659,9 +774,9 @@ module Sorcerer
|
|
659
774
|
:zsuper => lambda { |src, sexp|
|
660
775
|
src.emit("super")
|
661
776
|
},
|
662
|
-
|
777
|
+
|
663
778
|
# Scanner keywords
|
664
|
-
|
779
|
+
|
665
780
|
:@CHAR => NYI,
|
666
781
|
:@__end__ => NYI,
|
667
782
|
:@backref => NYI,
|
@@ -711,5 +826,5 @@ module Sorcerer
|
|
711
826
|
}
|
712
827
|
HANDLERS[:bodystmt] = HANDLERS[:body_stmt]
|
713
828
|
end
|
714
|
-
|
829
|
+
|
715
830
|
end
|
data/lib/sorcerer/version.rb
CHANGED
@@ -5,13 +5,13 @@ require 'ripper'
|
|
5
5
|
require 'sorcerer'
|
6
6
|
|
7
7
|
class SourcerTest < Test::Unit::TestCase
|
8
|
-
def source(string,
|
9
|
-
if debug
|
8
|
+
def source(string, options={})
|
9
|
+
if options[:debug]
|
10
10
|
puts
|
11
|
-
puts "
|
11
|
+
puts "***************************** options: #{options.inspect}"
|
12
12
|
end
|
13
13
|
sexp = Ripper::SexpBuilder.new(string).parse
|
14
|
-
Sorcerer.source(sexp,
|
14
|
+
Sorcerer.source(sexp, options)
|
15
15
|
end
|
16
16
|
|
17
17
|
def test_can_source_variables
|
@@ -48,7 +48,7 @@ class SourcerTest < Test::Unit::TestCase
|
|
48
48
|
assert_resource "obj.meth()"
|
49
49
|
end
|
50
50
|
|
51
|
-
def test_can_source_method_call_with_args
|
51
|
+
def test_can_source_method_call_with_args
|
52
52
|
assert_resource "obj.meth(a)"
|
53
53
|
assert_resource "obj.meth(a, b)"
|
54
54
|
assert_resource "obj.meth(a, b, c)"
|
@@ -85,7 +85,7 @@ class SourcerTest < Test::Unit::TestCase
|
|
85
85
|
assert_resource "meth &code"
|
86
86
|
assert_resource "meth a, &code"
|
87
87
|
assert_resource "meth a, *args, &code"
|
88
|
-
assert_resource "meth a, *args do |x| x.y end"
|
88
|
+
assert_resource "meth a, *args do |x| x.y end",
|
89
89
|
end
|
90
90
|
|
91
91
|
def test_can_source_method_with_bare_assoc
|
@@ -96,14 +96,14 @@ class SourcerTest < Test::Unit::TestCase
|
|
96
96
|
end
|
97
97
|
|
98
98
|
def test_can_source_method_with_do_block
|
99
|
-
|
100
|
-
|
101
|
-
|
99
|
+
assert_resource_ml "meth do end"
|
100
|
+
assert_resource_ml "meth do |a| end"
|
101
|
+
assert_resource_ml "meth(x, y, *rest, &code) do |a, b=1, c=x, *args, &block|~one; two; three~end"
|
102
102
|
end
|
103
103
|
|
104
104
|
def test_can_source_method_with_block
|
105
105
|
assert_resource "meth { }"
|
106
|
-
assert_resource "meth { |a| }"
|
106
|
+
assert_resource "meth { |a| }"
|
107
107
|
assert_resource "meth { |a, b| }"
|
108
108
|
assert_resource "meth { |*args| }"
|
109
109
|
assert_resource "meth { |a, *args| }"
|
@@ -138,7 +138,7 @@ class SourcerTest < Test::Unit::TestCase
|
|
138
138
|
assert_resource "p.(a)"
|
139
139
|
end
|
140
140
|
end
|
141
|
-
|
141
|
+
|
142
142
|
def test_can_source_numbers
|
143
143
|
assert_resource "1"
|
144
144
|
assert_resource "3.14"
|
@@ -178,12 +178,12 @@ class SourcerTest < Test::Unit::TestCase
|
|
178
178
|
assert_resource '%W{a b c}'
|
179
179
|
assert_resource '%W{Now is the time for all good men}'
|
180
180
|
end
|
181
|
-
|
181
|
+
|
182
182
|
def test_can_source_many_words
|
183
183
|
assert_resource '[%w{a b}, %w{c d}]'
|
184
184
|
assert_resource '[%W{a b}, %W{c d}]'
|
185
185
|
end
|
186
|
-
|
186
|
+
|
187
187
|
def test_can_source_symbols
|
188
188
|
assert_resource ":sym"
|
189
189
|
end
|
@@ -203,6 +203,19 @@ class SourcerTest < Test::Unit::TestCase
|
|
203
203
|
assert_resource '/#{name}/'
|
204
204
|
end
|
205
205
|
|
206
|
+
def test_can_source_regular_expressions_with_alternate_delimiters
|
207
|
+
assert_resource "%r{a}"
|
208
|
+
assert_resource "%r<a>"
|
209
|
+
assert_resource "%r[a]"
|
210
|
+
assert_resource "%r(a)"
|
211
|
+
assert_resource "%r|a|"
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_can_source_regular_expressions_with_flags
|
215
|
+
assert_resource "%r{a}im"
|
216
|
+
assert_resource "/a/i"
|
217
|
+
end
|
218
|
+
|
206
219
|
def test_can_source_range
|
207
220
|
assert_resource "1..10"
|
208
221
|
assert_resource "1...10"
|
@@ -252,7 +265,7 @@ class SourcerTest < Test::Unit::TestCase
|
|
252
265
|
def test_can_source_trinary_expressions
|
253
266
|
assert_resource "a ? b : c"
|
254
267
|
end
|
255
|
-
|
268
|
+
|
256
269
|
def test_can_source_complex_expressions
|
257
270
|
assert_resource "a + 1 * 3"
|
258
271
|
assert_resource "a + b"
|
@@ -308,41 +321,41 @@ class SourcerTest < Test::Unit::TestCase
|
|
308
321
|
end
|
309
322
|
|
310
323
|
def test_can_source_statement_sequences
|
311
|
-
|
312
|
-
|
313
|
-
|
324
|
+
assert_resource_ml "a"
|
325
|
+
assert_resource_ml "a; b"
|
326
|
+
assert_resource_ml "a; b; c"
|
314
327
|
end
|
315
328
|
|
316
329
|
def test_can_source_begin_end
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
330
|
+
assert_resource_ml "begin end"
|
331
|
+
assert_resource_ml "begin~a; end"
|
332
|
+
assert_resource_ml "begin~a(); end"
|
333
|
+
assert_resource_ml "begin~a; b; c; end"
|
321
334
|
end
|
322
335
|
|
323
336
|
def test_can_source_begin_rescue_end
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
337
|
+
assert_resource_ml "begin~rescue; end"
|
338
|
+
assert_resource_ml "begin~rescue E => ex; b; end"
|
339
|
+
assert_resource_ml "begin~a; rescue E => ex; b; end"
|
340
|
+
assert_resource_ml "begin~a; rescue E, F => ex; b; end"
|
341
|
+
assert_resource_ml "begin~a; rescue E, F => ex; b; c; end"
|
342
|
+
assert_resource_ml "begin~rescue E, F => ex; b; c; end"
|
330
343
|
end
|
331
344
|
|
332
345
|
def test_can_source_begin_ensure_end
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
346
|
+
assert_resource_ml "begin~ensure~end"
|
347
|
+
assert_resource_ml "begin~ensure~b; end"
|
348
|
+
assert_resource_ml "begin~a; ensure~b; end"
|
349
|
+
assert_resource_ml "begin~a; ensure~b; end"
|
337
350
|
end
|
338
351
|
|
339
352
|
def test_can_source_begin_rescue_ensure_end
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
353
|
+
assert_resource_ml "begin~rescue; end"
|
354
|
+
assert_resource_ml "begin~rescue E => ex; b; ensure~c; end"
|
355
|
+
assert_resource_ml "begin~a; rescue E => ex; b; ensure~c; end"
|
356
|
+
assert_resource_ml "begin~a; rescue E, F => ex; b; ensure~c; end"
|
357
|
+
assert_resource_ml "begin~a; rescue E, F => ex; b; c; ensure~d; end"
|
358
|
+
assert_resource_ml "begin~rescue E, F => ex; b; c; ensure~d; end"
|
346
359
|
end
|
347
360
|
|
348
361
|
def test_can_source_rescue_modifier
|
@@ -351,30 +364,37 @@ class SourcerTest < Test::Unit::TestCase
|
|
351
364
|
|
352
365
|
def test_can_source_if
|
353
366
|
assert_resource "if a then b end"
|
367
|
+
assert_resource "if a\nb\nend", multiline: true
|
354
368
|
end
|
355
369
|
|
356
370
|
def test_can_source_if_else
|
357
371
|
assert_resource "if a then b else c end"
|
358
|
-
|
359
|
-
|
360
|
-
def test_can_source_if_elsif_else
|
361
|
-
assert_resource "if a then b elsif c then d else e end"
|
372
|
+
assert_resource "if a\nb\nelse\nc\nend", multiline: true
|
362
373
|
end
|
363
374
|
|
364
375
|
def test_can_source_if_elsif
|
365
376
|
assert_resource "if a then b elsif c then d end"
|
377
|
+
assert_resource "if a\nb\nelsif c\nd\nend", multiline: true
|
378
|
+
end
|
379
|
+
|
380
|
+
def test_can_source_if_elsif_else
|
381
|
+
assert_resource "if a then b elsif c then d else e end"
|
382
|
+
assert_resource "if a\nb\nelsif c\nd\nelse\ne\nend", multiline: true
|
366
383
|
end
|
367
384
|
|
368
385
|
def test_can_source_unless
|
369
386
|
assert_resource "unless a then b end"
|
387
|
+
assert_resource "unless a\nb\nend", multiline: true
|
370
388
|
end
|
371
389
|
|
372
390
|
def test_can_source_unless_else
|
373
391
|
assert_resource "unless a then b else c end"
|
392
|
+
assert_resource "unless a\nb\nelse\nc\nend", multiline: true
|
374
393
|
end
|
375
394
|
|
376
395
|
def test_can_source_while
|
377
|
-
|
396
|
+
assert_resource_ml "while c; end"
|
397
|
+
assert_resource_ml "while c; body; end"
|
378
398
|
end
|
379
399
|
|
380
400
|
def test_can_source_until
|
@@ -387,18 +407,19 @@ class SourcerTest < Test::Unit::TestCase
|
|
387
407
|
end
|
388
408
|
|
389
409
|
def test_can_source_break
|
390
|
-
|
391
|
-
|
410
|
+
assert_resource_ml "while c; a; break if b; c; end"
|
411
|
+
assert_resource_ml "while c; a; break value if b; c; end"
|
392
412
|
end
|
393
413
|
|
394
414
|
def test_can_source_next
|
395
|
-
|
415
|
+
assert_resource_ml "while c; a; next if b; c; end"
|
416
|
+
assert_resource_ml "while c; a; next if b; c; end"
|
396
417
|
end
|
397
418
|
|
398
419
|
def test_can_source_case
|
399
|
-
|
400
|
-
|
401
|
-
|
420
|
+
assert_resource_ml "case a~when b; c; end"
|
421
|
+
assert_resource_ml "case a~when b; c when d; e; end"
|
422
|
+
assert_resource_ml "case a~when b; c when d; e~else~f; end"
|
402
423
|
end
|
403
424
|
|
404
425
|
def test_can_source_if_modifier
|
@@ -429,16 +450,16 @@ class SourcerTest < Test::Unit::TestCase
|
|
429
450
|
def test_can_source_retry
|
430
451
|
assert_resource "retry"
|
431
452
|
end
|
432
|
-
|
453
|
+
|
433
454
|
def test_can_source_redo
|
434
455
|
assert_resource "redo"
|
435
456
|
end
|
436
|
-
|
457
|
+
|
437
458
|
def test_can_source_return
|
438
459
|
assert_resource "return"
|
439
460
|
assert_resource "return value"
|
440
461
|
end
|
441
|
-
|
462
|
+
|
442
463
|
def test_can_source_super
|
443
464
|
assert_resource "super"
|
444
465
|
assert_resource "super a"
|
@@ -457,72 +478,93 @@ class SourcerTest < Test::Unit::TestCase
|
|
457
478
|
assert_resource "yield a"
|
458
479
|
assert_resource "yield(a)"
|
459
480
|
end
|
460
|
-
|
481
|
+
|
461
482
|
def test_can_source_self
|
462
483
|
assert_resource "self"
|
463
484
|
end
|
464
485
|
|
465
486
|
def test_can_source_def
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
487
|
+
assert_resource_ml "def f; end"
|
488
|
+
assert_resource_ml "def f; x; end"
|
489
|
+
assert_resource_ml "def f a; end"
|
490
|
+
assert_resource_ml "def f(); end"
|
491
|
+
assert_resource_ml "def f(a); end"
|
492
|
+
assert_resource_ml "def f(a, b); end"
|
493
|
+
assert_resource_ml "def f(a, *args); end"
|
494
|
+
assert_resource_ml "def f(a, *args, &block); end"
|
495
|
+
assert_resource_ml "def f(a); x; end"
|
496
|
+
assert_resource_ml "def f(a); x; y; end"
|
474
497
|
end
|
475
498
|
|
476
499
|
def test_can_source_class_without_parent
|
477
|
-
|
478
|
-
|
479
|
-
|
500
|
+
assert_resource_ml "class X; end"
|
501
|
+
assert_resource_ml "class X; x; end"
|
502
|
+
assert_resource_ml "class X; def f(); end; end"
|
480
503
|
end
|
481
504
|
|
482
505
|
def test_can_source_class_with_parent
|
483
|
-
|
484
|
-
|
506
|
+
assert_resource_ml "class X < Y; end"
|
507
|
+
assert_resource_ml "class X < Y; x; end"
|
485
508
|
end
|
486
509
|
|
487
510
|
def test_can_source_class_with_self_parent
|
488
|
-
|
511
|
+
assert_resource_ml "class X < self; end"
|
489
512
|
end
|
490
513
|
|
491
514
|
def test_can_source_private_etc_in_class
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
515
|
+
assert_resource_ml "class X; public; def f(); end; end"
|
516
|
+
assert_resource_ml "class X; protected; def f(); end; end"
|
517
|
+
assert_resource_ml "class X; private; def f(); end; end"
|
518
|
+
assert_resource_ml "class X; def f(); end; public :f; end"
|
519
|
+
assert_resource_ml "class X; def f(); end; protected :f; end"
|
520
|
+
assert_resource_ml "class X; def f(); end; private :f; end"
|
498
521
|
end
|
499
522
|
|
500
523
|
def test_can_source_module
|
501
|
-
|
502
|
-
|
503
|
-
|
524
|
+
assert_resource_ml "module X; end"
|
525
|
+
assert_resource_ml "module X; x; end"
|
526
|
+
assert_resource_ml "module X; def f(); end; end"
|
504
527
|
end
|
505
528
|
|
506
529
|
def test_can_source_BEGIN
|
507
|
-
|
508
|
-
|
509
|
-
|
530
|
+
assert_resource_ml "BEGIN { }"
|
531
|
+
assert_resource_ml "BEGIN {~x~}"
|
532
|
+
assert_resource_ml "BEGIN {~x; y~}"
|
510
533
|
end
|
511
534
|
|
512
535
|
def test_can_source_END
|
513
|
-
|
514
|
-
|
515
|
-
|
536
|
+
assert_resource_ml "END { }"
|
537
|
+
assert_resource_ml "END {~x~}"
|
538
|
+
assert_resource_ml "END {~x; y~}"
|
516
539
|
end
|
517
540
|
|
518
541
|
def test_can_source_then
|
519
|
-
|
542
|
+
assert_resource_ml "Then {~a == b~}"
|
543
|
+
assert_resource_ml "Then {~a == b; x~}"
|
544
|
+
end
|
545
|
+
|
546
|
+
def test_can_use_ripper_sexp_output
|
547
|
+
sexp = Ripper.sexp("a = 1")
|
548
|
+
assert_equal "a = 1", Sorcerer.source(sexp)
|
549
|
+
end
|
550
|
+
|
551
|
+
def test_can_handle_missing_statements
|
552
|
+
sexp = [:bodystmt, [:stmts_add, [:stmts_new]], nil, nil, nil]
|
553
|
+
assert_equal "", Sorcerer.source(sexp)
|
520
554
|
end
|
521
555
|
|
522
556
|
private
|
523
557
|
|
524
|
-
def assert_resource(string,
|
525
|
-
assert_equal string, source(string,
|
558
|
+
def assert_resource(string, options={})
|
559
|
+
assert_equal string, source(string, options)
|
526
560
|
end
|
527
|
-
|
561
|
+
|
562
|
+
def assert_resource_ml(string, options={})
|
563
|
+
expected = string.gsub(/~/, " ")
|
564
|
+
assert_equal expected, source(expected, options)
|
565
|
+
|
566
|
+
expected_ml = string.gsub(/~/, "\n").gsub(/; /, "\n")
|
567
|
+
assert_equal expected_ml, source(expected_ml, {multiline: true}.merge(options))
|
568
|
+
end
|
569
|
+
|
528
570
|
end
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sorcerer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 0
|
8
|
-
- 7
|
9
|
-
version: 0.0.7
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- Jim Weirich
|
@@ -14,18 +10,17 @@ autorequire:
|
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
12
|
|
17
|
-
date:
|
18
|
-
default_executable:
|
13
|
+
date: 2012-07-01 00:00:00 Z
|
19
14
|
dependencies: []
|
20
15
|
|
21
|
-
description:
|
16
|
+
description: Generate the original Ruby source from a Ripper-style abstract syntax tree.
|
22
17
|
email: jim.weirich@gmail.com
|
23
18
|
executables: []
|
24
19
|
|
25
20
|
extensions: []
|
26
21
|
|
27
|
-
extra_rdoc_files:
|
28
|
-
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
29
24
|
files:
|
30
25
|
- README.textile
|
31
26
|
- Rakefile
|
@@ -39,7 +34,6 @@ files:
|
|
39
34
|
- lib/sorcerer.rb
|
40
35
|
- test/sorcerer/resource_test.rb
|
41
36
|
- test/sorcerer/subexpression_test.rb
|
42
|
-
has_rdoc: true
|
43
37
|
homepage: http://github.com/jimweirich/sorcerer
|
44
38
|
licenses: []
|
45
39
|
|
@@ -54,23 +48,21 @@ rdoc_options:
|
|
54
48
|
require_paths:
|
55
49
|
- lib
|
56
50
|
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
57
52
|
requirements:
|
58
53
|
- - ">="
|
59
54
|
- !ruby/object:Gem::Version
|
60
|
-
segments:
|
61
|
-
- 0
|
62
55
|
version: "0"
|
63
56
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
64
58
|
requirements:
|
65
59
|
- - ">="
|
66
60
|
- !ruby/object:Gem::Version
|
67
|
-
segments:
|
68
|
-
- 0
|
69
61
|
version: "0"
|
70
62
|
requirements: []
|
71
63
|
|
72
64
|
rubyforge_project: sorcerer
|
73
|
-
rubygems_version: 1.
|
65
|
+
rubygems_version: 1.8.15
|
74
66
|
signing_key:
|
75
67
|
specification_version: 3
|
76
68
|
summary: Generate Source from Ripper ASTs
|