syntax_tree 6.1.1 → 6.3.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.
- checksums.yaml +4 -4
- data/.github/workflows/auto-merge.yml +1 -1
- data/.github/workflows/gh-pages.yml +6 -6
- data/.github/workflows/main.yml +2 -1
- data/.rubocop.yml +10 -1
- data/CHANGELOG.md +28 -1
- data/Gemfile +2 -0
- data/Gemfile.lock +30 -20
- data/README.md +3 -3
- data/lib/syntax_tree/cli.rb +69 -14
- data/lib/syntax_tree/database.rb +331 -0
- data/lib/syntax_tree/formatter.rb +1 -1
- data/lib/syntax_tree/language_server.rb +8 -2
- data/lib/syntax_tree/node.rb +70 -67
- data/lib/syntax_tree/parser.rb +8 -3
- data/lib/syntax_tree/pattern.rb +1 -0
- data/lib/syntax_tree/plugin/{disable_ternary.rb → disable_auto_ternary.rb} +1 -1
- data/lib/syntax_tree/reflection.rb +1 -1
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/with_scope.rb +1 -4
- data/lib/syntax_tree/yarv/assembler.rb +2 -3
- data/lib/syntax_tree/yarv/calldata.rb +21 -15
- data/lib/syntax_tree/yarv/decompiler.rb +6 -6
- data/lib/syntax_tree/yarv/instruction_sequence.rb +56 -10
- data/lib/syntax_tree/yarv/instructions.rb +165 -64
- data/lib/syntax_tree/yarv/legacy.rb +104 -0
- data/lib/syntax_tree.rb +1 -0
- data/tasks/whitequark.rake +7 -2
- metadata +5 -9
- data/.ruby-version +0 -1
@@ -0,0 +1,331 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
# Provides the ability to index source files into a database, then query for
|
5
|
+
# the nodes.
|
6
|
+
module Database
|
7
|
+
class IndexingVisitor < SyntaxTree::FieldVisitor
|
8
|
+
attr_reader :database, :filepath, :node_id
|
9
|
+
|
10
|
+
def initialize(database, filepath)
|
11
|
+
@database = database
|
12
|
+
@filepath = filepath
|
13
|
+
@node_id = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def comments(node)
|
19
|
+
end
|
20
|
+
|
21
|
+
def field(name, value)
|
22
|
+
return unless value.is_a?(SyntaxTree::Node)
|
23
|
+
|
24
|
+
binds = [node_id, visit(value), name]
|
25
|
+
database.execute(<<~SQL, binds)
|
26
|
+
INSERT INTO edges (from_id, to_id, name)
|
27
|
+
VALUES (?, ?, ?)
|
28
|
+
SQL
|
29
|
+
end
|
30
|
+
|
31
|
+
def list(name, values)
|
32
|
+
values.each_with_index do |value, index|
|
33
|
+
binds = [node_id, visit(value), name, index]
|
34
|
+
database.execute(<<~SQL, binds)
|
35
|
+
INSERT INTO edges (from_id, to_id, name, list_index)
|
36
|
+
VALUES (?, ?, ?, ?)
|
37
|
+
SQL
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def node(node, _name)
|
42
|
+
previous = node_id
|
43
|
+
binds = [
|
44
|
+
node.class.name.delete_prefix("SyntaxTree::"),
|
45
|
+
filepath,
|
46
|
+
node.location.start_line,
|
47
|
+
node.location.start_column
|
48
|
+
]
|
49
|
+
|
50
|
+
database.execute(<<~SQL, binds)
|
51
|
+
INSERT INTO nodes (type, path, line, column)
|
52
|
+
VALUES (?, ?, ?, ?)
|
53
|
+
SQL
|
54
|
+
|
55
|
+
begin
|
56
|
+
@node_id = database.last_insert_row_id
|
57
|
+
yield
|
58
|
+
@node_id
|
59
|
+
ensure
|
60
|
+
@node_id = previous
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def text(name, value)
|
65
|
+
end
|
66
|
+
|
67
|
+
def pairs(name, values)
|
68
|
+
values.each_with_index do |(key, value), index|
|
69
|
+
binds = [node_id, visit(key), "#{name}[0]", index]
|
70
|
+
database.execute(<<~SQL, binds)
|
71
|
+
INSERT INTO edges (from_id, to_id, name, list_index)
|
72
|
+
VALUES (?, ?, ?, ?)
|
73
|
+
SQL
|
74
|
+
|
75
|
+
binds = [node_id, visit(value), "#{name}[1]", index]
|
76
|
+
database.execute(<<~SQL, binds)
|
77
|
+
INSERT INTO edges (from_id, to_id, name, list_index)
|
78
|
+
VALUES (?, ?, ?, ?)
|
79
|
+
SQL
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Query for a specific type of node.
|
85
|
+
class TypeQuery
|
86
|
+
attr_reader :type
|
87
|
+
|
88
|
+
def initialize(type)
|
89
|
+
@type = type
|
90
|
+
end
|
91
|
+
|
92
|
+
def each(database, &block)
|
93
|
+
sql = "SELECT * FROM nodes WHERE type = ?"
|
94
|
+
database.execute(sql, type).each(&block)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Query for the attributes of a node, optionally also filtering by type.
|
99
|
+
class AttrQuery
|
100
|
+
attr_reader :type, :attrs
|
101
|
+
|
102
|
+
def initialize(type, attrs)
|
103
|
+
@type = type
|
104
|
+
@attrs = attrs
|
105
|
+
end
|
106
|
+
|
107
|
+
def each(database, &block)
|
108
|
+
joins = []
|
109
|
+
binds = []
|
110
|
+
|
111
|
+
attrs.each do |name, query|
|
112
|
+
ids = query.each(database).map { |row| row[0] }
|
113
|
+
joins << <<~SQL
|
114
|
+
JOIN edges AS #{name}
|
115
|
+
ON #{name}.from_id = nodes.id
|
116
|
+
AND #{name}.name = ?
|
117
|
+
AND #{name}.to_id IN (#{(["?"] * ids.size).join(", ")})
|
118
|
+
SQL
|
119
|
+
|
120
|
+
binds.push(name).concat(ids)
|
121
|
+
end
|
122
|
+
|
123
|
+
sql = +"SELECT nodes.* FROM nodes, edges #{joins.join(" ")}"
|
124
|
+
|
125
|
+
if type
|
126
|
+
sql << " WHERE nodes.type = ?"
|
127
|
+
binds << type
|
128
|
+
end
|
129
|
+
|
130
|
+
sql << " GROUP BY nodes.id"
|
131
|
+
database.execute(sql, binds).each(&block)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Query for the results of either query.
|
136
|
+
class OrQuery
|
137
|
+
attr_reader :left, :right
|
138
|
+
|
139
|
+
def initialize(left, right)
|
140
|
+
@left = left
|
141
|
+
@right = right
|
142
|
+
end
|
143
|
+
|
144
|
+
def each(database, &block)
|
145
|
+
left.each(database, &block)
|
146
|
+
right.each(database, &block)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# A lazy query result.
|
151
|
+
class QueryResult
|
152
|
+
attr_reader :database, :query
|
153
|
+
|
154
|
+
def initialize(database, query)
|
155
|
+
@database = database
|
156
|
+
@query = query
|
157
|
+
end
|
158
|
+
|
159
|
+
def each(&block)
|
160
|
+
return enum_for(__method__) unless block_given?
|
161
|
+
query.each(database, &block)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# A pattern matching expression that will be compiled into a query.
|
166
|
+
class Pattern
|
167
|
+
class CompilationError < StandardError
|
168
|
+
end
|
169
|
+
|
170
|
+
attr_reader :query
|
171
|
+
|
172
|
+
def initialize(query)
|
173
|
+
@query = query
|
174
|
+
end
|
175
|
+
|
176
|
+
def compile
|
177
|
+
program =
|
178
|
+
begin
|
179
|
+
SyntaxTree.parse("case nil\nin #{query}\nend")
|
180
|
+
rescue Parser::ParseError
|
181
|
+
raise CompilationError, query
|
182
|
+
end
|
183
|
+
|
184
|
+
compile_node(program.statements.body.first.consequent.pattern)
|
185
|
+
end
|
186
|
+
|
187
|
+
private
|
188
|
+
|
189
|
+
def compile_error(node)
|
190
|
+
raise CompilationError, PP.pp(node, +"").chomp
|
191
|
+
end
|
192
|
+
|
193
|
+
# Shortcut for combining two queries into one that returns the results of
|
194
|
+
# if either query matches.
|
195
|
+
def combine_or(left, right)
|
196
|
+
OrQuery.new(left, right)
|
197
|
+
end
|
198
|
+
|
199
|
+
# in foo | bar
|
200
|
+
def compile_binary(node)
|
201
|
+
compile_error(node) if node.operator != :|
|
202
|
+
|
203
|
+
combine_or(compile_node(node.left), compile_node(node.right))
|
204
|
+
end
|
205
|
+
|
206
|
+
# in Ident
|
207
|
+
def compile_const(node)
|
208
|
+
value = node.value
|
209
|
+
|
210
|
+
if SyntaxTree.const_defined?(value, false)
|
211
|
+
clazz = SyntaxTree.const_get(value)
|
212
|
+
TypeQuery.new(clazz.name.delete_prefix("SyntaxTree::"))
|
213
|
+
else
|
214
|
+
compile_error(node)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# in SyntaxTree::Ident
|
219
|
+
def compile_const_path_ref(node)
|
220
|
+
parent = node.parent
|
221
|
+
if !parent.is_a?(SyntaxTree::VarRef) ||
|
222
|
+
!parent.value.is_a?(SyntaxTree::Const)
|
223
|
+
compile_error(node)
|
224
|
+
end
|
225
|
+
|
226
|
+
if parent.value.value == "SyntaxTree"
|
227
|
+
compile_node(node.constant)
|
228
|
+
else
|
229
|
+
compile_error(node)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# in Ident[value: String]
|
234
|
+
def compile_hshptn(node)
|
235
|
+
compile_error(node) unless node.keyword_rest.nil?
|
236
|
+
|
237
|
+
attrs = {}
|
238
|
+
node.keywords.each do |keyword, value|
|
239
|
+
compile_error(node) unless keyword.is_a?(SyntaxTree::Label)
|
240
|
+
attrs[keyword.value.chomp(":")] = compile_node(value)
|
241
|
+
end
|
242
|
+
|
243
|
+
type = node.constant ? compile_node(node.constant).type : nil
|
244
|
+
AttrQuery.new(type, attrs)
|
245
|
+
end
|
246
|
+
|
247
|
+
# in Foo
|
248
|
+
def compile_var_ref(node)
|
249
|
+
value = node.value
|
250
|
+
|
251
|
+
if value.is_a?(SyntaxTree::Const)
|
252
|
+
compile_node(value)
|
253
|
+
else
|
254
|
+
compile_error(node)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def compile_node(node)
|
259
|
+
case node
|
260
|
+
when SyntaxTree::Binary
|
261
|
+
compile_binary(node)
|
262
|
+
when SyntaxTree::Const
|
263
|
+
compile_const(node)
|
264
|
+
when SyntaxTree::ConstPathRef
|
265
|
+
compile_const_path_ref(node)
|
266
|
+
when SyntaxTree::HshPtn
|
267
|
+
compile_hshptn(node)
|
268
|
+
when SyntaxTree::VarRef
|
269
|
+
compile_var_ref(node)
|
270
|
+
else
|
271
|
+
compile_error(node)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
class Connection
|
277
|
+
attr_reader :raw_connection
|
278
|
+
|
279
|
+
def initialize(raw_connection)
|
280
|
+
@raw_connection = raw_connection
|
281
|
+
end
|
282
|
+
|
283
|
+
def execute(query, binds = [])
|
284
|
+
raw_connection.execute(query, binds)
|
285
|
+
end
|
286
|
+
|
287
|
+
def index_file(filepath)
|
288
|
+
program = SyntaxTree.parse(SyntaxTree.read(filepath))
|
289
|
+
program.accept(IndexingVisitor.new(self, filepath))
|
290
|
+
end
|
291
|
+
|
292
|
+
def last_insert_row_id
|
293
|
+
raw_connection.last_insert_row_id
|
294
|
+
end
|
295
|
+
|
296
|
+
def prepare
|
297
|
+
raw_connection.execute(<<~SQL)
|
298
|
+
CREATE TABLE nodes (
|
299
|
+
id integer primary key,
|
300
|
+
type varchar(20),
|
301
|
+
path varchar(200),
|
302
|
+
line integer,
|
303
|
+
column integer
|
304
|
+
);
|
305
|
+
SQL
|
306
|
+
|
307
|
+
raw_connection.execute(<<~SQL)
|
308
|
+
CREATE INDEX nodes_type ON nodes (type);
|
309
|
+
SQL
|
310
|
+
|
311
|
+
raw_connection.execute(<<~SQL)
|
312
|
+
CREATE TABLE edges (
|
313
|
+
id integer primary key,
|
314
|
+
from_id integer,
|
315
|
+
to_id integer,
|
316
|
+
name varchar(20),
|
317
|
+
list_index integer
|
318
|
+
);
|
319
|
+
SQL
|
320
|
+
|
321
|
+
raw_connection.execute(<<~SQL)
|
322
|
+
CREATE INDEX edges_name ON edges (name);
|
323
|
+
SQL
|
324
|
+
end
|
325
|
+
|
326
|
+
def search(query)
|
327
|
+
QueryResult.new(self, Pattern.new(query).compile)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
@@ -60,7 +60,7 @@ module SyntaxTree
|
|
60
60
|
# constant. That constant is responsible for determining the default
|
61
61
|
# disable ternary value. If it's defined, then we default to true.
|
62
62
|
# Otherwise we default to false.
|
63
|
-
defined?(
|
63
|
+
defined?(DISABLE_AUTO_TERNARY)
|
64
64
|
else
|
65
65
|
disable_auto_ternary
|
66
66
|
end
|
@@ -217,11 +217,13 @@ module SyntaxTree
|
|
217
217
|
def initialize(
|
218
218
|
input: $stdin,
|
219
219
|
output: $stdout,
|
220
|
-
print_width: DEFAULT_PRINT_WIDTH
|
220
|
+
print_width: DEFAULT_PRINT_WIDTH,
|
221
|
+
ignore_files: []
|
221
222
|
)
|
222
223
|
@input = input.binmode
|
223
224
|
@output = output.binmode
|
224
225
|
@print_width = print_width
|
226
|
+
@ignore_files = ignore_files
|
225
227
|
end
|
226
228
|
|
227
229
|
# rubocop:disable Layout/LineLength
|
@@ -255,8 +257,12 @@ module SyntaxTree
|
|
255
257
|
store.delete(request.dig(:params, :textDocument, :uri))
|
256
258
|
when Request[method: "textDocument/formatting", id: :any, params: { textDocument: { uri: :any } }]
|
257
259
|
uri = request.dig(:params, :textDocument, :uri)
|
260
|
+
filepath = uri.split("///").last
|
261
|
+
ignore = @ignore_files.any? do |glob|
|
262
|
+
File.fnmatch(glob, filepath)
|
263
|
+
end
|
258
264
|
contents = store[uri]
|
259
|
-
write(id: request[:id], result: contents ? format(contents, uri.split(".").last) : nil)
|
265
|
+
write(id: request[:id], result: contents && !ignore ? format(contents, uri.split(".").last) : nil)
|
260
266
|
when Request[method: "textDocument/inlayHint", id: :any, params: { textDocument: { uri: :any } }]
|
261
267
|
uri = request.dig(:params, :textDocument, :uri)
|
262
268
|
contents = store[uri]
|
data/lib/syntax_tree/node.rb
CHANGED
@@ -288,7 +288,7 @@ module SyntaxTree
|
|
288
288
|
q.text(value)
|
289
289
|
else
|
290
290
|
q.text(q.quote)
|
291
|
-
q.text(value[1] ==
|
291
|
+
q.text(value[1] == q.quote ? "\\#{q.quote}" : value[1])
|
292
292
|
q.text(q.quote)
|
293
293
|
end
|
294
294
|
end
|
@@ -1299,7 +1299,7 @@ module SyntaxTree
|
|
1299
1299
|
end
|
1300
1300
|
end
|
1301
1301
|
|
1302
|
-
# [nil | VarRef] the optional constant wrapper
|
1302
|
+
# [nil | VarRef | ConstPathRef] the optional constant wrapper
|
1303
1303
|
attr_reader :constant
|
1304
1304
|
|
1305
1305
|
# [Array[ Node ]] the regular positional arguments that this array
|
@@ -1783,45 +1783,60 @@ module SyntaxTree
|
|
1783
1783
|
end
|
1784
1784
|
end
|
1785
1785
|
|
1786
|
-
|
1787
|
-
container
|
1788
|
-
|
1789
|
-
|
1790
|
-
|
1791
|
-
|
1792
|
-
|
1793
|
-
|
1794
|
-
|
1795
|
-
|
1796
|
-
|
1797
|
-
|
1798
|
-
|
1799
|
-
# Otherwise, we need to check the type of the key. If it's a label or
|
1800
|
-
# dynamic symbol, we can use labels. If it's a symbol literal then it
|
1801
|
-
# needs to match a certain pattern to be used as a label. If it's
|
1802
|
-
# anything else, then we need to use hash rockets.
|
1803
|
-
case assoc.key
|
1804
|
-
when Label, DynaSymbol
|
1805
|
-
# Here labels can be used.
|
1806
|
-
when SymbolLiteral
|
1807
|
-
# When attempting to convert a hash rocket into a hash label,
|
1808
|
-
# you need to take care because only certain patterns are
|
1809
|
-
# allowed. Ruby source says that they have to match keyword
|
1810
|
-
# arguments to methods, but don't specify what that is. After
|
1811
|
-
# some experimentation, it looks like it's:
|
1812
|
-
value = assoc.key.value.value
|
1813
|
-
|
1814
|
-
if !value.match?(/^[_A-Za-z]/) || value.end_with?("=")
|
1815
|
-
return Rockets.new
|
1816
|
-
end
|
1786
|
+
class << self
|
1787
|
+
def for(container)
|
1788
|
+
(assocs = container.assocs).each_with_index do |assoc, index|
|
1789
|
+
if assoc.is_a?(AssocSplat)
|
1790
|
+
# Splat nodes do not impact the formatting choice.
|
1791
|
+
elsif assoc.value.nil?
|
1792
|
+
# If the value is nil, then it has been omitted. In this case we
|
1793
|
+
# have to match the existing formatting because standardizing would
|
1794
|
+
# potentially break the code. For example:
|
1795
|
+
#
|
1796
|
+
# { first:, "second" => "value" }
|
1797
|
+
#
|
1798
|
+
return Identity.new
|
1817
1799
|
else
|
1818
|
-
#
|
1819
|
-
|
1800
|
+
# Otherwise, we need to check the type of the key. If it's a label
|
1801
|
+
# or dynamic symbol, we can use labels. If it's a symbol literal
|
1802
|
+
# then it needs to match a certain pattern to be used as a label. If
|
1803
|
+
# it's anything else, then we need to use hash rockets.
|
1804
|
+
case assoc.key
|
1805
|
+
when Label, DynaSymbol
|
1806
|
+
# Here labels can be used.
|
1807
|
+
when SymbolLiteral
|
1808
|
+
# When attempting to convert a hash rocket into a hash label,
|
1809
|
+
# you need to take care because only certain patterns are
|
1810
|
+
# allowed. Ruby source says that they have to match keyword
|
1811
|
+
# arguments to methods, but don't specify what that is. After
|
1812
|
+
# some experimentation, it looks like it's:
|
1813
|
+
value = assoc.key.value.value
|
1814
|
+
|
1815
|
+
if !value.match?(/^[_A-Za-z]/) || value.end_with?("=")
|
1816
|
+
if omitted_value?(assocs[(index + 1)..])
|
1817
|
+
return Identity.new
|
1818
|
+
else
|
1819
|
+
return Rockets.new
|
1820
|
+
end
|
1821
|
+
end
|
1822
|
+
else
|
1823
|
+
if omitted_value?(assocs[(index + 1)..])
|
1824
|
+
return Identity.new
|
1825
|
+
else
|
1826
|
+
return Rockets.new
|
1827
|
+
end
|
1828
|
+
end
|
1820
1829
|
end
|
1821
1830
|
end
|
1831
|
+
|
1832
|
+
Labels.new
|
1822
1833
|
end
|
1823
1834
|
|
1824
|
-
|
1835
|
+
private
|
1836
|
+
|
1837
|
+
def omitted_value?(assocs)
|
1838
|
+
assocs.any? { |assoc| !assoc.is_a?(AssocSplat) && assoc.value.nil? }
|
1839
|
+
end
|
1825
1840
|
end
|
1826
1841
|
end
|
1827
1842
|
|
@@ -2849,7 +2864,10 @@ module SyntaxTree
|
|
2849
2864
|
# to print the operator trailing in order to keep it working.
|
2850
2865
|
last_child = children.last
|
2851
2866
|
if last_child.is_a?(CallNode) && last_child.message != :call &&
|
2852
|
-
|
2867
|
+
(
|
2868
|
+
(last_child.message.comments.any? && last_child.operator) ||
|
2869
|
+
(last_child.operator && last_child.operator.comments.any?)
|
2870
|
+
)
|
2853
2871
|
q.format(CallOperatorFormatter.new(last_child.operator))
|
2854
2872
|
skip_operator = true
|
2855
2873
|
else
|
@@ -5413,7 +5431,7 @@ module SyntaxTree
|
|
5413
5431
|
# end
|
5414
5432
|
#
|
5415
5433
|
class FndPtn < Node
|
5416
|
-
# [nil |
|
5434
|
+
# [nil | VarRef | ConstPathRef] the optional constant wrapper
|
5417
5435
|
attr_reader :constant
|
5418
5436
|
|
5419
5437
|
# [VarField] the splat on the left-hand side
|
@@ -6035,7 +6053,7 @@ module SyntaxTree
|
|
6035
6053
|
end
|
6036
6054
|
end
|
6037
6055
|
|
6038
|
-
# [nil |
|
6056
|
+
# [nil | VarRef | ConstPathRef] the optional constant wrapper
|
6039
6057
|
attr_reader :constant
|
6040
6058
|
|
6041
6059
|
# [Array[ [DynaSymbol | Label, nil | Node] ]] the set of tuples
|
@@ -7207,36 +7225,17 @@ module SyntaxTree
|
|
7207
7225
|
q.text(" ")
|
7208
7226
|
q
|
7209
7227
|
.if_break do
|
7210
|
-
|
7211
|
-
q.parents.any? do |node|
|
7212
|
-
node.is_a?(Command) || node.is_a?(CommandCall)
|
7213
|
-
end
|
7214
|
-
|
7215
|
-
if force_parens
|
7216
|
-
q.text("{")
|
7228
|
+
q.text("do")
|
7217
7229
|
|
7218
|
-
|
7219
|
-
|
7220
|
-
q.breakable_space
|
7221
|
-
q.format(statements)
|
7222
|
-
end
|
7230
|
+
unless statements.empty?
|
7231
|
+
q.indent do
|
7223
7232
|
q.breakable_space
|
7233
|
+
q.format(statements)
|
7224
7234
|
end
|
7225
|
-
|
7226
|
-
q.text("}")
|
7227
|
-
else
|
7228
|
-
q.text("do")
|
7229
|
-
|
7230
|
-
unless statements.empty?
|
7231
|
-
q.indent do
|
7232
|
-
q.breakable_space
|
7233
|
-
q.format(statements)
|
7234
|
-
end
|
7235
|
-
end
|
7236
|
-
|
7237
|
-
q.breakable_space
|
7238
|
-
q.text("end")
|
7239
7235
|
end
|
7236
|
+
|
7237
|
+
q.breakable_space
|
7238
|
+
q.text("end")
|
7240
7239
|
end
|
7241
7240
|
.if_flat do
|
7242
7241
|
q.text("{")
|
@@ -8293,8 +8292,8 @@ module SyntaxTree
|
|
8293
8292
|
# parameter
|
8294
8293
|
attr_reader :rest
|
8295
8294
|
|
8296
|
-
# [Array[ Ident ]] any positional parameters that exist after a
|
8297
|
-
# parameter
|
8295
|
+
# [Array[ Ident | MLHSParen ]] any positional parameters that exist after a
|
8296
|
+
# rest parameter
|
8298
8297
|
attr_reader :posts
|
8299
8298
|
|
8300
8299
|
# [Array[ [ Label, nil | Node ] ]] any keyword parameters and their
|
@@ -11660,6 +11659,10 @@ module SyntaxTree
|
|
11660
11659
|
elsif value.is_a?(Array) && (index = value.index(self))
|
11661
11660
|
parent.public_send(key)[index] = replace
|
11662
11661
|
break
|
11662
|
+
elsif value.is_a?(Array) &&
|
11663
|
+
(index = value.index { |(_k, v)| v == self })
|
11664
|
+
parent.public_send(key)[index][1] = replace
|
11665
|
+
break
|
11663
11666
|
end
|
11664
11667
|
end
|
11665
11668
|
end
|
data/lib/syntax_tree/parser.rb
CHANGED
@@ -670,7 +670,11 @@ module SyntaxTree
|
|
670
670
|
|
671
671
|
visit_methods do
|
672
672
|
def visit_var_ref(node)
|
673
|
-
node.
|
673
|
+
if node.start_char > pins.first.start_char
|
674
|
+
node.pin(stack[-2], pins.shift)
|
675
|
+
else
|
676
|
+
super
|
677
|
+
end
|
674
678
|
end
|
675
679
|
end
|
676
680
|
|
@@ -1732,13 +1736,13 @@ module SyntaxTree
|
|
1732
1736
|
# :call-seq:
|
1733
1737
|
# on_field: (
|
1734
1738
|
# untyped parent,
|
1735
|
-
# (:"::" | Op | Period) operator
|
1739
|
+
# (:"::" | Op | Period | 73) operator
|
1736
1740
|
# (Const | Ident) name
|
1737
1741
|
# ) -> Field
|
1738
1742
|
def on_field(parent, operator, name)
|
1739
1743
|
Field.new(
|
1740
1744
|
parent: parent,
|
1741
|
-
operator: operator,
|
1745
|
+
operator: operator == 73 ? :"::" : operator,
|
1742
1746
|
name: name,
|
1743
1747
|
location: parent.location.to(name.location)
|
1744
1748
|
)
|
@@ -2867,6 +2871,7 @@ module SyntaxTree
|
|
2867
2871
|
alias on_assign_error on_parse_error
|
2868
2872
|
alias on_class_name_error on_parse_error
|
2869
2873
|
alias on_param_error on_parse_error
|
2874
|
+
alias compile_error on_parse_error
|
2870
2875
|
|
2871
2876
|
# :call-seq:
|
2872
2877
|
# on_period: (String value) -> Period
|
data/lib/syntax_tree/pattern.rb
CHANGED
data/lib/syntax_tree/version.rb
CHANGED
@@ -152,10 +152,7 @@ module SyntaxTree
|
|
152
152
|
# arguments.
|
153
153
|
def visit_params(node)
|
154
154
|
add_argument_definitions(node.requireds)
|
155
|
-
|
156
|
-
node.posts.each do |param|
|
157
|
-
current_scope.add_local_definition(param, :argument)
|
158
|
-
end
|
155
|
+
add_argument_definitions(node.posts)
|
159
156
|
|
160
157
|
node.keywords.each do |param|
|
161
158
|
current_scope.add_local_definition(param.first, :argument)
|
@@ -31,7 +31,6 @@ module SyntaxTree
|
|
31
31
|
"FCALL" => CallData::CALL_FCALL,
|
32
32
|
"VCALL" => CallData::CALL_VCALL,
|
33
33
|
"ARGS_SIMPLE" => CallData::CALL_ARGS_SIMPLE,
|
34
|
-
"BLOCKISEQ" => CallData::CALL_BLOCKISEQ,
|
35
34
|
"KWARG" => CallData::CALL_KWARG,
|
36
35
|
"KW_SPLAT" => CallData::CALL_KW_SPLAT,
|
37
36
|
"TAILCALL" => CallData::CALL_TAILCALL,
|
@@ -409,7 +408,7 @@ module SyntaxTree
|
|
409
408
|
def find_local(iseq, operands)
|
410
409
|
name_string, level_string = operands.split(/,\s*/)
|
411
410
|
name = name_string.to_sym
|
412
|
-
level = level_string
|
411
|
+
level = level_string.to_i
|
413
412
|
|
414
413
|
iseq.local_table.plain(name)
|
415
414
|
iseq.local_table.find(name, level)
|
@@ -456,7 +455,7 @@ module SyntaxTree
|
|
456
455
|
CallData::CALL_ARGS_SIMPLE
|
457
456
|
end
|
458
457
|
|
459
|
-
YARV.calldata(message.to_sym, argc_value
|
458
|
+
YARV.calldata(message.to_sym, argc_value.to_i, flags)
|
460
459
|
end
|
461
460
|
end
|
462
461
|
end
|