sirop 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +110 -1
- data/lib/sirop/finder.rb +29 -0
- data/lib/sirop/prism_ext.rb +10 -0
- data/lib/sirop/sourcifier.rb +290 -0
- data/lib/sirop/version.rb +1 -1
- data/lib/sirop.rb +42 -8
- metadata +7 -6
- data/lib/sirop/block_finder.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1641ee60187094fe9dcdeebefbfc5283c0d2ea535aa161b17f7bf6f720d63854
|
4
|
+
data.tar.gz: c9abaef1a8b7116760f92e143bcb2e58fbc4424fe3d4afe110bc10bf552852cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3900ece58c932df6181676abb597fa6deedfbd8801e28f4993cc36cce7584a188e88b73e6ef2b0fea33b5dbc8a8d284949159973cb108e149dd0d9b3ea5dd762
|
7
|
+
data.tar.gz: 7f848b0e842792a33e3fc7c9b7d6cf72e924040307e2db5ccb0cff16af1e947e7053ee81e54ab688c131532b66432dc1283651ceeef5820ded58c3b8447d193a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# 2024-02-27 0.2
|
2
|
+
|
3
|
+
- Update README
|
4
|
+
- Remove support for Ruby < 3.2
|
5
|
+
- Implement general purpose Finder for finding nodes
|
6
|
+
- Implement DSL compiler (for tests)
|
7
|
+
- Implement Sirop.to_source
|
8
|
+
|
1
9
|
# 2024-02-20 0.1
|
2
10
|
|
3
11
|
- Find node for a given `Proc` object
|
data/README.md
CHANGED
@@ -1,4 +1,113 @@
|
|
1
1
|
# Sirop
|
2
2
|
|
3
|
-
Sirop is a Ruby code
|
3
|
+
Sirop is a Ruby gem for manipulating Ruby source code. Sirop is very young, so
|
4
|
+
the following information might be incomplete, out of date, or simply wrong!
|
4
5
|
|
6
|
+
## Use Cases
|
7
|
+
|
8
|
+
Some of the use cases addressed by Sirop are:
|
9
|
+
|
10
|
+
- Compile DSLs into optimized Ruby code. This is especially interesting for HTML
|
11
|
+
templating DSLs in libraries like Phlex, Papercraft etc.
|
12
|
+
[Example](https://github.com/digital-fabric/sirop/blob/main/test/dsl_compiler.rb)
|
13
|
+
- Get the source of a given block or method.
|
14
|
+
- Rewrite parts of Ruby code, for implementing Ruby macros (and why not?).
|
15
|
+
|
16
|
+
## Limitations
|
17
|
+
|
18
|
+
- Sirop supports Ruby 3.2 or newer.
|
19
|
+
- Sirop can be used only on blocks and methods defined in a file, so cannot
|
20
|
+
really be used on dynamically `eval`'d Ruby code, or in an IRB/Pry session.
|
21
|
+
|
22
|
+
## Getting the AST/source of a Ruby proc or method
|
23
|
+
|
24
|
+
To get the AST of a proc or a method, use `Sirop.to_ast`:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
# for a proc
|
28
|
+
mul = ->(x, y) { x * y }
|
29
|
+
Sirop.to_ast(mul) #=> ...
|
30
|
+
|
31
|
+
# for a method
|
32
|
+
def foo; :bar; end
|
33
|
+
Sirop.to_ast(method(:foo)) #=> ...
|
34
|
+
```
|
35
|
+
|
36
|
+
To get the source of a proc or a method, use `Sirop.to_source`:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
mul = ->(x, y) { x * y }
|
40
|
+
Sirop.to_source(mul) #=> "->(x, y) { x * y }"
|
41
|
+
|
42
|
+
def foo; :bar; end
|
43
|
+
Sirop.to_source(method(:foo)) #=> "def foo; :bar; end"
|
44
|
+
```
|
45
|
+
|
46
|
+
## Rewriting Ruby code
|
47
|
+
|
48
|
+
You can consult the [DSL compiler
|
49
|
+
example](https://github.com/digital-fabric/sirop/blob/main/test/dsl_compiler.rb). This example intercepts method calls by defining a `visit_call_node` method:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
# Annotated with some explanations
|
53
|
+
def visit_call_node(node)
|
54
|
+
# don't rewrite if the call has a receiver
|
55
|
+
return super if node.receiver
|
56
|
+
|
57
|
+
# set HTML location start
|
58
|
+
@html_location_start ||= node.location
|
59
|
+
# get method arguments...
|
60
|
+
inner_text, attrs = tag_args(node)
|
61
|
+
# and block
|
62
|
+
block = node.block
|
63
|
+
|
64
|
+
# emit HTML tag according to given arguments
|
65
|
+
if inner_text
|
66
|
+
emit_tag_open(node, attrs)
|
67
|
+
emit_tag_inner_text(inner_text)
|
68
|
+
emit_tag_close(node)
|
69
|
+
elsif block
|
70
|
+
emit_tag_open(node, attrs)
|
71
|
+
visit(block.body)
|
72
|
+
emit_tag_close(node)
|
73
|
+
else
|
74
|
+
emit_tag_open_close(node, attrs)
|
75
|
+
end
|
76
|
+
# set HTML location end
|
77
|
+
@html_location_end = node.location
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
## Future directions
|
82
|
+
|
83
|
+
- Implement a macro expander with support for `quote`/`unquote`:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
trace_macro = Sirop.macro do |ast|
|
87
|
+
source = Sirop.to_source(ast)
|
88
|
+
quote do
|
89
|
+
result = unquote(ast)
|
90
|
+
puts "The result of #{source} is: #{result}"
|
91
|
+
result
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def add(x, y)
|
96
|
+
trace(x + y)
|
97
|
+
end
|
98
|
+
|
99
|
+
Sirop.expand_macros(method(:add), trace: trace_macro)
|
100
|
+
```
|
101
|
+
|
102
|
+
- Implement a DSL compiler with hooks for easier usage in DSL libraries.
|
103
|
+
|
104
|
+
## Contributing
|
105
|
+
|
106
|
+
We gladly welcome contributions from anyone! Some areas that need work currently
|
107
|
+
are:
|
108
|
+
|
109
|
+
- Documentation
|
110
|
+
- More test cases for Ruby syntax in the Sirop tests. Look here:
|
111
|
+
https://github.com/digital-fabric/sirop/tree/main/test/fixtures
|
112
|
+
|
113
|
+
Please feel free to contribute PR's and issues
|
data/lib/sirop/finder.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'prism'
|
4
|
+
|
5
|
+
module Sirop
|
6
|
+
class Finder < Prism::BasicVisitor
|
7
|
+
def self.find(*, &)
|
8
|
+
finder = self.new
|
9
|
+
finder.find(*, &)
|
10
|
+
end
|
11
|
+
|
12
|
+
def find(root, key, &)
|
13
|
+
instance_exec(&)
|
14
|
+
@key = key
|
15
|
+
catch(key) do
|
16
|
+
visit(root)
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def found!(node)
|
22
|
+
throw(@key, node)
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing(sym, node, *args)
|
26
|
+
visit_child_nodes(node)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,290 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'prism'
|
4
|
+
|
5
|
+
module Sirop
|
6
|
+
#
|
7
|
+
class Sourcifier < Prism::BasicVisitor
|
8
|
+
attr_reader :buffer
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@buffer = +''
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_source(node)
|
15
|
+
@buffer.clear
|
16
|
+
visit(node)
|
17
|
+
@buffer
|
18
|
+
end
|
19
|
+
|
20
|
+
def loc_start(loc)
|
21
|
+
[loc.start_line, loc.start_column]
|
22
|
+
end
|
23
|
+
|
24
|
+
def loc_end(loc)
|
25
|
+
[loc.end_line, loc.end_column]
|
26
|
+
end
|
27
|
+
|
28
|
+
def emit(str)
|
29
|
+
@buffer << str
|
30
|
+
end
|
31
|
+
|
32
|
+
def adjust_whitespace(loc)
|
33
|
+
if @last_loc_start
|
34
|
+
if @last_loc_end.first != loc.start_line
|
35
|
+
@buffer << "\n" * (loc.start_line - @last_loc_end.first)
|
36
|
+
@buffer << ' ' * loc.start_column
|
37
|
+
else
|
38
|
+
ofs = loc.start_column - @last_loc_end.last
|
39
|
+
if ofs > 0
|
40
|
+
@buffer << ' ' * ofs
|
41
|
+
end
|
42
|
+
end
|
43
|
+
else
|
44
|
+
# empty buffer
|
45
|
+
@buffer << ' ' * loc.start_column
|
46
|
+
end
|
47
|
+
@last_loc = loc
|
48
|
+
@last_loc_start = loc_start(loc)
|
49
|
+
@last_loc_end = loc_end(loc)
|
50
|
+
end
|
51
|
+
|
52
|
+
def emit_code(loc, semicolon: false)
|
53
|
+
return if !loc
|
54
|
+
|
55
|
+
emit_semicolon(loc) if semicolon
|
56
|
+
return visit(loc) if loc.is_a?(Prism::Node)
|
57
|
+
|
58
|
+
adjust_whitespace(loc)
|
59
|
+
emit(loc.slice)
|
60
|
+
end
|
61
|
+
|
62
|
+
def emit_verbatim(node)
|
63
|
+
emit_code(node.location)
|
64
|
+
end
|
65
|
+
|
66
|
+
def emit_str(str)
|
67
|
+
emit(str)
|
68
|
+
@last_loc_end[1] += str.size
|
69
|
+
end
|
70
|
+
|
71
|
+
def emit_comma
|
72
|
+
emit_str(',')
|
73
|
+
end
|
74
|
+
|
75
|
+
def emit_semicolon(loc)
|
76
|
+
loc = loc.location if loc.is_a?(Prism::Node)
|
77
|
+
emit_str(';') if loc.start_line == @last_loc.end_line
|
78
|
+
end
|
79
|
+
|
80
|
+
def method_missing(sym, node, *args)
|
81
|
+
puts '!' * 40
|
82
|
+
p node
|
83
|
+
raise NotImplementedError, "Don't know how to handle #{sym}"
|
84
|
+
visit_child_nodes(node)
|
85
|
+
end
|
86
|
+
|
87
|
+
VISIT_PLANS = {
|
88
|
+
and: [:left, :operator_loc, :right],
|
89
|
+
assoc: :visit_child_nodes,
|
90
|
+
assoc_splat: [:operator_loc, :value],
|
91
|
+
block: [:opening_loc, :parameters, :body, :closing_loc],
|
92
|
+
block_argument: [:operator_loc, :expression],
|
93
|
+
block_parameter: [:operator_loc, :name_loc],
|
94
|
+
block_parameters: [:opening_loc, :parameters, :closing_loc],
|
95
|
+
break: [:keyword_loc, :arguments],
|
96
|
+
constant_path: [:parent, :delimiter_loc, :child],
|
97
|
+
constant_read: :emit_verbatim,
|
98
|
+
else: [:else_keyword_loc, :statements],
|
99
|
+
embedded_statements: [:opening_loc, :statements, :closing_loc],
|
100
|
+
false: :emit_verbatim,
|
101
|
+
integer: :emit_verbatim,
|
102
|
+
keyword_rest_parameter: [:operator_loc, :name_loc],
|
103
|
+
lambda: [:operator_loc, :parameters, :opening_loc, :body,
|
104
|
+
:closing_loc],
|
105
|
+
local_variable_read: :emit_verbatim,
|
106
|
+
local_variable_write: [:name_loc, :operator_loc, :value],
|
107
|
+
next: [:keyword_loc, :arguments],
|
108
|
+
nil: :emit_verbatim,
|
109
|
+
optional_parameter: [:name_loc, :operator_loc, :value],
|
110
|
+
or: [:left, :operator_loc, :right],
|
111
|
+
parentheses: [:opening_loc, :body, :closing_loc],
|
112
|
+
required_parameter: :emit_verbatim,
|
113
|
+
rest_parameter: [:operator_loc, :name_loc],
|
114
|
+
splat: [:operator_loc, :expression],
|
115
|
+
statements: :visit_child_nodes,
|
116
|
+
string: :emit_verbatim,
|
117
|
+
symbol: :emit_verbatim,
|
118
|
+
true: :emit_verbatim,
|
119
|
+
yield: [:keyword_loc, :lparen_loc, :arguments, :rparen_loc],
|
120
|
+
}
|
121
|
+
|
122
|
+
VISIT_PLANS.each do |key, plan|
|
123
|
+
sym = :"visit_#{key}_node"
|
124
|
+
define_method(sym) { |n| visit_plan(plan, n) }
|
125
|
+
end
|
126
|
+
|
127
|
+
def visit_plan(plan, node)
|
128
|
+
return send(plan, node) if plan.is_a?(Symbol)
|
129
|
+
|
130
|
+
insert_semicolon = false
|
131
|
+
plan.each_with_index do |sym, idx|
|
132
|
+
if sym == :semicolon
|
133
|
+
insert_semicolon = true
|
134
|
+
next
|
135
|
+
end
|
136
|
+
|
137
|
+
obj = node.send(sym)
|
138
|
+
emit_code(obj, semicolon: insert_semicolon)
|
139
|
+
insert_semicolon = false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def visit_comma_separated_nodes(list, comma = false)
|
144
|
+
if list
|
145
|
+
list.each_with_index do |child, idx|
|
146
|
+
emit_comma if comma
|
147
|
+
emit_code(child)
|
148
|
+
comma = true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
comma
|
152
|
+
end
|
153
|
+
|
154
|
+
def visit_parameters_node(node)
|
155
|
+
comma = visit_comma_separated_nodes(node.requireds)
|
156
|
+
comma = visit_comma_separated_nodes(node.optionals, comma)
|
157
|
+
comma = visit_comma_separated_nodes(node.posts, comma)
|
158
|
+
if node.rest
|
159
|
+
emit_comma if comma
|
160
|
+
comma = true
|
161
|
+
emit_code(node.rest)
|
162
|
+
end
|
163
|
+
if node.keyword_rest
|
164
|
+
emit_comma if comma
|
165
|
+
comma = true
|
166
|
+
emit_code(node.keyword_rest)
|
167
|
+
end
|
168
|
+
if node.block
|
169
|
+
emit_comma if comma
|
170
|
+
comma = true
|
171
|
+
emit_code(node.block)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def visit_arguments_node(node)
|
176
|
+
visit_comma_separated_nodes(node.arguments)
|
177
|
+
end
|
178
|
+
|
179
|
+
def visit_keyword_hash_node(node)
|
180
|
+
visit_comma_separated_nodes(node.elements)
|
181
|
+
end
|
182
|
+
|
183
|
+
def visit_if_node(node)
|
184
|
+
if !node.if_keyword_loc
|
185
|
+
return visit_if_node_ternary(node)
|
186
|
+
elsif !node.end_keyword_loc
|
187
|
+
return visit_if_node_guard(node)
|
188
|
+
end
|
189
|
+
|
190
|
+
emit_code(node.if_keyword_loc)
|
191
|
+
emit_code(node.predicate)
|
192
|
+
emit_code(node.then_keyword_loc)
|
193
|
+
emit_code(node.statements)
|
194
|
+
emit_code(node.consequent) if node.consequent
|
195
|
+
emit_code(node.end_keyword_loc) if node.if_keyword_loc.slice == 'if'
|
196
|
+
end
|
197
|
+
|
198
|
+
def visit_if_node_ternary(node)
|
199
|
+
emit_code(node.predicate)
|
200
|
+
emit_code(node.then_keyword_loc)
|
201
|
+
emit_code(node.statements)
|
202
|
+
emit_code(node.consequent)
|
203
|
+
end
|
204
|
+
|
205
|
+
def visit_if_node_guard(node)
|
206
|
+
emit_code(node.statements)
|
207
|
+
emit_code(node.if_keyword_loc)
|
208
|
+
emit_code(node.predicate)
|
209
|
+
end
|
210
|
+
|
211
|
+
def visit_case_node(node)
|
212
|
+
emit_code(node.case_keyword_loc)
|
213
|
+
emit_code(node.predicate)
|
214
|
+
node.conditions.each { |c| emit_code(c) }
|
215
|
+
emit_code(node.consequent)
|
216
|
+
emit_code(node.end_keyword_loc)
|
217
|
+
end
|
218
|
+
|
219
|
+
def visit_when_node(node)
|
220
|
+
emit_code(node.keyword_loc)
|
221
|
+
visit_comma_separated_nodes(node.conditions)
|
222
|
+
emit_code(node.statements)
|
223
|
+
end
|
224
|
+
|
225
|
+
def visit_interpolated_symbol_node(node)
|
226
|
+
emit_code(node.opening_loc)
|
227
|
+
node.parts.each { |p| emit_code(p) }
|
228
|
+
emit_code(node.closing_loc)
|
229
|
+
end
|
230
|
+
alias_method :visit_interpolated_string_node, :visit_interpolated_symbol_node
|
231
|
+
|
232
|
+
def visit_def_node(node)
|
233
|
+
emit_code(node.def_keyword_loc)
|
234
|
+
emit_code(node.name_loc)
|
235
|
+
last_loc = node.name_loc
|
236
|
+
|
237
|
+
if node.parameters
|
238
|
+
emit_str('(')
|
239
|
+
emit_code(node.parameters)
|
240
|
+
emit_str(')')
|
241
|
+
last_loc = node.parameters.location
|
242
|
+
end
|
243
|
+
|
244
|
+
emit_code(node.body, semicolon: true)
|
245
|
+
emit_code(node.end_keyword_loc, semicolon: true)
|
246
|
+
end
|
247
|
+
|
248
|
+
def visit_call_node(node)
|
249
|
+
if node.receiver && !node.call_operator_loc && !node.arguments
|
250
|
+
return visit_call_node_unary_op(node)
|
251
|
+
end
|
252
|
+
|
253
|
+
block = node.block
|
254
|
+
|
255
|
+
emit_code(node.receiver)
|
256
|
+
emit_code(node.call_operator_loc)
|
257
|
+
emit_code(node.message_loc)
|
258
|
+
emit_code(node.opening_loc)
|
259
|
+
emit_code(node.arguments)
|
260
|
+
|
261
|
+
if block.is_a?(Prism::BlockArgumentNode)
|
262
|
+
emit_comma if node.arguments&.arguments.size > 0
|
263
|
+
emit_code(block)
|
264
|
+
block = nil
|
265
|
+
end
|
266
|
+
emit_code(node.closing_loc)
|
267
|
+
emit_code(block)
|
268
|
+
end
|
269
|
+
|
270
|
+
def visit_call_node_unary_op(node)
|
271
|
+
emit_code(node.message_loc)
|
272
|
+
emit_code(node.receiver)
|
273
|
+
end
|
274
|
+
|
275
|
+
def visit_while_node(node)
|
276
|
+
return visit_while_node_guard(node) if !node.closing_loc
|
277
|
+
|
278
|
+
emit_code(node.keyword_loc)
|
279
|
+
emit_code(node.predicate)
|
280
|
+
emit_code(node.statements, semicolon: true)
|
281
|
+
emit_code(node.closing_loc, semicolon: true)
|
282
|
+
end
|
283
|
+
|
284
|
+
def visit_while_node_guard(node)
|
285
|
+
emit_code(node.statements)
|
286
|
+
emit_code(node.keyword_loc)
|
287
|
+
emit_code(node.predicate)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
data/lib/sirop/version.rb
CHANGED
data/lib/sirop.rb
CHANGED
@@ -1,27 +1,61 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'prism'
|
4
|
-
require 'sirop/
|
4
|
+
require 'sirop/prism_ext'
|
5
|
+
require 'sirop/finder'
|
6
|
+
require 'sirop/sourcifier'
|
5
7
|
|
6
8
|
module Sirop
|
7
9
|
class << self
|
8
|
-
def
|
10
|
+
def to_ast(obj)
|
9
11
|
case obj
|
10
12
|
when Proc
|
11
|
-
|
13
|
+
proc_ast(obj)
|
14
|
+
when UnboundMethod, Method
|
15
|
+
method_ast(obj)
|
12
16
|
else
|
13
17
|
raise ArgumentError, "Invalid object type"
|
14
18
|
end
|
15
19
|
end
|
16
20
|
|
17
|
-
def
|
21
|
+
def to_source(obj)
|
22
|
+
obj = to_ast(obj) if !obj.is_a?(Prism::Node)
|
23
|
+
Sourcifier.new.to_source(obj)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def proc_ast(proc)
|
18
29
|
fn, lineno = proc.source_location
|
19
30
|
pr = Prism.parse(IO.read(fn), filepath: fn)
|
20
31
|
program = pr.value
|
21
|
-
|
22
|
-
|
23
|
-
|
32
|
+
|
33
|
+
Finder.find(program, proc) do
|
34
|
+
on(:lambda) do |node|
|
35
|
+
found!(node) if node.location.start_line == lineno
|
36
|
+
super(node)
|
37
|
+
end
|
38
|
+
on(:call) do |node|
|
39
|
+
case node.name
|
40
|
+
when :proc, :lambda
|
41
|
+
found!(node) if node.block && node.block.location.start_line == lineno
|
42
|
+
end
|
43
|
+
super(node)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def method_ast(method)
|
49
|
+
fn, lineno = method.source_location
|
50
|
+
pr = Prism.parse(IO.read(fn), filepath: fn)
|
51
|
+
program = pr.value
|
52
|
+
|
53
|
+
Finder.find(program, method) do
|
54
|
+
on(:def) do |node|
|
55
|
+
found!(node) if node.name == method.name && node.location.start_line == lineno
|
56
|
+
super(node)
|
57
|
+
end
|
58
|
+
end
|
24
59
|
end
|
25
|
-
|
26
60
|
end
|
27
61
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sirop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.2'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
11
|
+
date: 2024-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prism
|
@@ -48,15 +48,16 @@ files:
|
|
48
48
|
- CHANGELOG.md
|
49
49
|
- README.md
|
50
50
|
- lib/sirop.rb
|
51
|
-
- lib/sirop/
|
51
|
+
- lib/sirop/finder.rb
|
52
|
+
- lib/sirop/prism_ext.rb
|
53
|
+
- lib/sirop/sourcifier.rb
|
52
54
|
- lib/sirop/version.rb
|
53
55
|
homepage: http://github.com/digital-fabric/sirop
|
54
56
|
licenses:
|
55
57
|
- MIT
|
56
58
|
metadata:
|
57
|
-
source_code_uri: https://github.com/digital-fabric/sirop
|
58
|
-
documentation_uri: https://www.rubydoc.info/gems/sirop
|
59
59
|
homepage_uri: https://github.com/digital-fabric/sirop
|
60
|
+
documentation_uri: https://www.rubydoc.info/gems/sirop
|
60
61
|
changelog_uri: https://github.com/digital-fabric/sirop/blob/main/CHANGELOG.md
|
61
62
|
post_install_message:
|
62
63
|
rdoc_options:
|
@@ -70,7 +71,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
70
71
|
requirements:
|
71
72
|
- - ">="
|
72
73
|
- !ruby/object:Gem::Version
|
73
|
-
version: '3.
|
74
|
+
version: '3.2'
|
74
75
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
76
|
requirements:
|
76
77
|
- - ">="
|
data/lib/sirop/block_finder.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Sirop
|
4
|
-
class BlockFinder < Prism::BasicVisitor
|
5
|
-
attr_accessor :block_node
|
6
|
-
|
7
|
-
def initialize(proc, lineno)
|
8
|
-
@proc = proc
|
9
|
-
@lineno = lineno
|
10
|
-
end
|
11
|
-
|
12
|
-
def find(program)
|
13
|
-
# p program
|
14
|
-
# puts
|
15
|
-
catch(@proc) {
|
16
|
-
visit(program)
|
17
|
-
nil
|
18
|
-
}
|
19
|
-
end
|
20
|
-
|
21
|
-
def visit_lambda_node(node)
|
22
|
-
if node.location.start_line == @lineno
|
23
|
-
throw @proc, node
|
24
|
-
else
|
25
|
-
visit_child_nodes(node)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def visit_call_node(node)
|
30
|
-
case node.name
|
31
|
-
when :proc, :lambda
|
32
|
-
if node.block && node.block.location.start_line == @lineno
|
33
|
-
throw @proc, node
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def method_missing(sym, node, *args)
|
39
|
-
visit_child_nodes(node)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|