rephrase 0.1 → 0.2

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.
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