rephrase 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35b9da1b746b755618ad23cc57fa70559f35064277b61965278544333d678f1d
4
- data.tar.gz: 0467c2c8bc54226c445ed39cedb4a16ca85a945c326bd3bae26abf049f5622b2
3
+ metadata.gz: 50dd6a940c19895850f10694544b163b9362ab853242972760a885f555750a54
4
+ data.tar.gz: 0d41895ceb222d573384ae073bc183d0eaf5ee3d3429acc5f59f894af4af628f
5
5
  SHA512:
6
- metadata.gz: '0912d93aac2a5fdbdf6c69490947530b6147f2fb55eff205c3839fdc1ad48dd6adddf35d2db868cef4da109b82fdae5b74a489f93d73d6dfed3a65ee8cd8d381'
7
- data.tar.gz: aa45eefab2311a46c513af8f161345065cb08e2efdd56f7cbfc20e75c56bccf9b1907a90ba4a97e43976fc00efac096387b272895e68a49d212694e1ed7b9f92
6
+ metadata.gz: '0513931ee88c085b5026f71e9e5f427664a745ba93fa94e0d20422d7b690dec9bce8142fc8129ebd59379c0f3ee7f425d898a671011762c2c373c8266ea47a83'
7
+ data.tar.gz: 42757fb4d2ca4cad5feff6b19a42350662875864e8c262bced0dd6dcb73c1ed12d20630884ee09e9351a3411ca438360b76909e2a927467561c25e911415f50d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 0.2 2011-11-16
2
+
3
+ - Rename `#to_ruby` to `#to_source`
4
+ - Add documentation
5
+
1
6
  ## 0.1 2021-11-16
2
7
 
3
8
  - First working version
data/README.md CHANGED
@@ -3,3 +3,43 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/rephrase.svg)](http://rubygems.org/gems/rephrase)
4
4
  [![Modulation Test](https://github.com/digital-fabric/rephrase/workflows/Tests/badge.svg)](https://github.com/digital-fabric/rephrase/actions?query=workflow%3ATests)
5
5
  [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/digital-fabric/rephrase/blob/master/LICENSE)
6
+
7
+ ## Summary
8
+
9
+ Rephrase converts Ruby procs or methods to source code, allowing you to
10
+ reformat, reinterpret or otherwise manipulate the generated source code.
11
+ Possible uses include:
12
+
13
+ - Generating code from DSL blocks.
14
+ - Inlining loops.
15
+ - Macro exansion.
16
+ - World domination (???)
17
+
18
+ ## How does it do it?
19
+
20
+ Rephrase uses the `RubyVM::AbstractSyntaxTree` API to get the AST of a proc or
21
+ method. This allows you to manipulate code at runtime, and to be able to access
22
+ its binding.
23
+
24
+ ## How to use it
25
+
26
+ Use `Rephrase.to_source` to get the unmodified source code of a proc or method,
27
+ e.g.:
28
+
29
+ ```ruby
30
+ require 'rephrase'
31
+
32
+ example = proc { 2 + 2 }
33
+ Rephrase.to_source(example) #=> "proc do\n2 + 2\nend"
34
+ ```
35
+
36
+ ## How to rephrase code
37
+
38
+ Further documentation is forthcoming...
39
+
40
+ ## Limitations
41
+
42
+ - Works only on MRI.
43
+ - Ruby 2.7 or newer.
44
+ - Generated source code will not be formatted identically to the source
45
+ (indentation, line breaks etc.)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Rephrase
4
- VERSION = '0.1'
4
+ VERSION = '0.2'
5
5
  end
data/lib/rephrase.rb CHANGED
@@ -4,35 +4,76 @@
4
4
  # for use in Pry, see https://dev.to/okinawarb/using-rubyvm-abstractsyntaxtree-of-in-pry-4cm3
5
5
 
6
6
  class Rephrase
7
+ # Class for faking a node
7
8
  class FakeNode
9
+ # Constructs a FakeNode with type `:LIST_EMBEDDED`
10
+ # @param children [Array] child nodes
8
11
  def self.list(children)
9
12
  new(:LIST_EMBEDDED, children)
10
13
  end
11
14
 
15
+ # Constructs a FakeNode with type `:ITER_SCOPE`
16
+ # @param children [Array] child nodes
12
17
  def self.iter_scope(children)
13
18
  new(:ITER_SCOPE, children)
14
19
  end
15
20
 
16
21
  attr_reader :type, :children
17
22
 
23
+ # Initializes a FakeNode
24
+ # @param type [Symbol] node type
25
+ # @param children [Array] child nodes
18
26
  def initialize(type, children)
19
27
  @type = type
20
28
  @children = children
21
29
  end
22
30
  end
31
+
32
+ # Converts a :block or method to its source code
33
+ # @param code [Proc, BoundMethod, UnboundMethod] proc or method
34
+ # @return [String] converted source code
35
+ def self.to_source(code)
36
+ ast = RubyVM::AbstractSyntaxTree.of(code)
37
+ new.convert({}, ast)
38
+ end
39
+
40
+ # Pretty-prints an AST
41
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
42
+ def self.pp_ast(node, level = 0)
43
+ case node
44
+ when RubyVM::AbstractSyntaxTree::Node
45
+ puts "#{' ' * level}#{node.type.inspect} (#{node.first_lineno})"
46
+ node.children.each { |c| pp_ast(c, level + 1) }
47
+ when Array
48
+ puts "#{' ' * level}["
49
+ node.each { |c| pp_ast(c, level + 1) }
50
+ puts "#{' ' * level}]"
51
+ else
52
+ puts "#{' ' * level}#{node.inspect}"
53
+ return
54
+ end
55
+ end
23
56
 
57
+ # Converts an AST node to a string containing its source code
58
+ # @param ctx [Hash] context object
59
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
60
+ # @return [String] source code
24
61
  def convert(ctx, node)
25
62
  ctx[:buffer] ||= +''
26
63
  ctx[:indent] ||= 0
27
64
  method_name = :"on_#{node.type.downcase}"
28
- if respond_to?(method_name)
29
- send(method_name, ctx, node)
30
- ctx[:buffer]
31
- else
65
+
66
+ if !respond_to?(method_name)
32
67
  raise "Could not convert #{node.type} node to ruby"
33
68
  end
69
+
70
+ send(method_name, ctx, node)
71
+ ctx[:buffer]
34
72
  end
35
73
 
74
+ # Converts a :SCOPE AST node
75
+ # @param ctx [Hash] context object
76
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
36
77
  def on_scope(ctx, node)
37
78
  body = node.children.last
38
79
  # if body.type != :BLOCK
@@ -41,6 +82,9 @@ class Rephrase
41
82
  emit(ctx, "proc do", [body], "end")
42
83
  end
43
84
 
85
+ # Converts a :BLOCK AST node
86
+ # @param ctx [Hash] context object
87
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
44
88
  def on_block(ctx, node)
45
89
  last_idx = node.children.size - 1
46
90
  node.children.each_with_index do |c, idx|
@@ -49,12 +93,18 @@ class Rephrase
49
93
  end
50
94
  end
51
95
 
96
+ # Converts a :ITER AST node
97
+ # @param ctx [Hash] context object
98
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
52
99
  def on_iter(ctx, node)
53
100
  call, scope = node.children
54
101
  emit(ctx, call)
55
102
  emit(ctx, FakeNode.iter_scope(scope.children))
56
103
  end
57
104
 
105
+ # Converts a :ITER_SCOPE AST (fake) node
106
+ # @param ctx [Hash] context object
107
+ # @param node [Rephrase::FakeNode] AST node
58
108
  def on_iter_scope(ctx, node)
59
109
  args, arg_spec, body = node.children
60
110
  emit(ctx, " do")
@@ -64,15 +114,24 @@ class Rephrase
64
114
  emit(ctx, "\nend")
65
115
  end
66
116
 
117
+ # Converts a :CONST AST node
118
+ # @param ctx [Hash] context object
119
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
67
120
  def on_const(ctx, node)
68
121
  emit(ctx, node.children.first.to_s)
69
122
  end
70
123
 
124
+ # Converts a :COLON2 AST node
125
+ # @param ctx [Hash] context object
126
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
71
127
  def on_colon2(ctx, node)
72
128
  left, right = node.children
73
129
  emit(ctx, left, "::", right.to_s)
74
130
  end
75
131
 
132
+ # Converts a :CALL AST node
133
+ # @param ctx [Hash] context object
134
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
76
135
  def on_call(ctx, node)
77
136
  receiver, method, args = node.children
78
137
  args = args && FakeNode.list(args.children)
@@ -88,16 +147,25 @@ class Rephrase
88
147
  end
89
148
  end
90
149
 
150
+ # Converts a :FCALL AST node
151
+ # @param ctx [Hash] context object
152
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
91
153
  def on_fcall(ctx, node)
92
154
  method, args = node.children
93
155
  args = args && FakeNode.list(args.children)
94
156
  emit(ctx, "#{method}(", args, ")")
95
157
  end
96
158
 
159
+ # Converts a :VCALL AST node
160
+ # @param ctx [Hash] context object
161
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
97
162
  def on_vcall(ctx, node)
98
163
  emit(ctx, node.children.first.to_s, "()")
99
164
  end
100
165
 
166
+ # Converts a :OPCALL AST node
167
+ # @param ctx [Hash] context object
168
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
101
169
  def on_opcall(ctx, node)
102
170
  left, op, right = node.children
103
171
  if op == :!
@@ -107,16 +175,25 @@ class Rephrase
107
175
  end
108
176
  end
109
177
 
178
+ # Converts a :DASGN_CURR AST node
179
+ # @param ctx [Hash] context object
180
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
110
181
  def on_dasgn_curr(ctx, node)
111
182
  left, right = node.children
112
183
  emit(ctx, left.to_s, " = ", right)
113
184
  end
114
185
 
186
+ # Converts a :IASGN AST node
187
+ # @param ctx [Hash] context object
188
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
115
189
  def on_iasgn(ctx, node)
116
190
  left, right = node.children
117
191
  emit(ctx, left.to_s, " = ", right)
118
192
  end
119
193
 
194
+ # Converts a :IF AST node
195
+ # @param ctx [Hash] context object
196
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
120
197
  def on_if(ctx, node)
121
198
  cond, branch1, branch2 = node.children
122
199
  if branch2
@@ -126,6 +203,9 @@ class Rephrase
126
203
  end
127
204
  end
128
205
 
206
+ # Converts a :UNLESS AST node
207
+ # @param ctx [Hash] context object
208
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
129
209
  def on_unless(ctx, node)
130
210
  cond, branch1, branch2 = node.children
131
211
  if branch2
@@ -135,39 +215,66 @@ class Rephrase
135
215
  end
136
216
  end
137
217
 
218
+ # Converts a :WHILE AST node
219
+ # @param ctx [Hash] context object
220
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
138
221
  def on_while(ctx, node)
139
222
  cond, body = node.children
140
223
  emit(ctx, "while ", cond, "\n", body, "\nend")
141
224
  end
142
225
 
226
+ # Converts a :LIT AST node
227
+ # @param ctx [Hash] context object
228
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
143
229
  def on_lit(ctx, node)
144
230
  emit(ctx, node.children.first.inspect)
145
231
  end
146
232
 
233
+ # Converts a :NIL AST node
234
+ # @param ctx [Hash] context object
235
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
147
236
  def on_nil(ctx, node)
148
237
  emit(ctx, "nil")
149
238
  end
150
239
 
240
+ # Converts a :TRUE AST node
241
+ # @param ctx [Hash] context object
242
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
151
243
  def on_true(ctx, node)
152
244
  emit(ctx, "true")
153
245
  end
154
246
 
247
+ # Converts a :FALSE AST node
248
+ # @param ctx [Hash] context object
249
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
155
250
  def on_false(ctx, node)
156
251
  emit(ctx, "false")
157
252
  end
158
253
 
254
+ # Converts a :DVAR AST node
255
+ # @param ctx [Hash] context object
256
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
159
257
  def on_dvar(ctx, node)
160
258
  emit(ctx, node.children.first.to_s)
161
259
  end
162
260
 
261
+ # Converts a :IVAR AST node
262
+ # @param ctx [Hash] context object
263
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
163
264
  def on_ivar(ctx, node)
164
265
  emit(ctx, node.children.first.to_s)
165
266
  end
166
267
 
268
+ # Converts a :STR AST node
269
+ # @param ctx [Hash] context object
270
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
167
271
  def on_str(ctx, node)
168
272
  emit(ctx, node.children.first.inspect)
169
273
  end
170
274
 
275
+ # Converts a :DSTR AST node
276
+ # @param ctx [Hash] context object
277
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
171
278
  def on_dstr(ctx, node)
172
279
  prefix, evstr1, rest = node.children
173
280
  emit(ctx, "\"", prefix.inspect[1..-2], evstr1)
@@ -186,20 +293,32 @@ class Rephrase
186
293
  emit(ctx, "\"")
187
294
  end
188
295
 
296
+ # Converts a :EVSTR AST node
297
+ # @param ctx [Hash] context object
298
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
189
299
  def on_evstr(ctx, node)
190
300
  emit(ctx, "\#{", node.children.first, "}")
191
301
  end
192
302
 
303
+ # Converts a :AND AST node
304
+ # @param ctx [Hash] context object
305
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
193
306
  def on_and(ctx, node)
194
307
  left, right = node.children
195
308
  emit(ctx, left, " && ", right)
196
309
  end
197
310
 
311
+ # Converts a :OR AST node
312
+ # @param ctx [Hash] context object
313
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
198
314
  def on_or(ctx, node)
199
315
  left, right = node.children
200
316
  emit(ctx, left, " || ", right)
201
317
  end
202
318
 
319
+ # Converts a :LIST AST node
320
+ # @param ctx [Hash] context object
321
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
203
322
  def on_list(ctx, node)
204
323
  items = node.children[0..-2]
205
324
  last_idx = items.size - 1
@@ -211,6 +330,9 @@ class Rephrase
211
330
  emit(ctx, "]")
212
331
  end
213
332
 
333
+ # Converts a :LIST_EMBEDDED AST (fake) node
334
+ # @param ctx [Hash] context object
335
+ # @param node [Rephrase::FakeNode] AST node
214
336
  def on_list_embedded(ctx, node)
215
337
  items = node.children.compact
216
338
  last_idx = items.size - 1
@@ -220,6 +342,9 @@ class Rephrase
220
342
  end
221
343
  end
222
344
 
345
+ # Converts a :HASH AST node
346
+ # @param ctx [Hash] context object
347
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
223
348
  def on_hash(ctx, node)
224
349
  list = node.children.first
225
350
  idx = 0
@@ -235,6 +360,9 @@ class Rephrase
235
360
  emit(ctx, "}")
236
361
  end
237
362
 
363
+ # Converts a :ATTRASGN AST node
364
+ # @param ctx [Hash] context object
365
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
238
366
  def on_attrasgn(ctx, node)
239
367
  left, op, args = node.children
240
368
  emit(ctx, left)
@@ -249,12 +377,18 @@ class Rephrase
249
377
  end
250
378
  end
251
379
 
380
+ # Converts a :RESCUE AST node
381
+ # @param ctx [Hash] context object
382
+ # @param node [RubyVM::AbstractSyntaxTree::Node] AST node
252
383
  def on_rescue(ctx, node)
253
384
  code, rescue_body = node.children
254
385
  emit(ctx, code)
255
386
  emit(ctx, " rescue ", rescue_body.children[1])
256
387
  end
257
388
 
389
+ # Emits code into the context buffer
390
+ # @param ctx [Hash] context object
391
+ # @param entries [Array<String, Symbol>] code entries
258
392
  def emit(ctx, *entries)
259
393
  entries.each do |e|
260
394
  case e
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rephrase
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner