syntax_tree 6.0.1 → 6.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -1
- data/.ruby-version +1 -0
- data/CHANGELOG.md +27 -1
- data/Gemfile.lock +7 -7
- data/README.md +29 -0
- data/lib/syntax_tree/cli.rb +91 -0
- data/lib/syntax_tree/field_visitor.rb +2 -0
- data/lib/syntax_tree/index.rb +233 -23
- data/lib/syntax_tree/node.rb +65 -32
- data/lib/syntax_tree/parser.rb +14 -1
- data/lib/syntax_tree/reflection.rb +22 -6
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/with_scope.rb +71 -1
- data/lib/syntax_tree/yarv/compiler.rb +1 -2
- data/lib/syntax_tree/yarv/instruction_sequence.rb +14 -3
- data/lib/syntax_tree/yarv/instructions.rb +58 -0
- data/lib/syntax_tree.rb +1 -0
- data/tasks/sorbet.rake +96 -0
- metadata +3 -2
data/lib/syntax_tree/node.rb
CHANGED
@@ -792,9 +792,10 @@ module SyntaxTree
|
|
792
792
|
private
|
793
793
|
|
794
794
|
def trailing_comma?
|
795
|
+
arguments = self.arguments
|
795
796
|
return false unless arguments.is_a?(Args)
|
796
|
-
parts = arguments.parts
|
797
797
|
|
798
|
+
parts = arguments.parts
|
798
799
|
if parts.last.is_a?(ArgBlock)
|
799
800
|
# If the last argument is a block, then we can't put a trailing comma
|
800
801
|
# after it without resulting in a syntax error.
|
@@ -1188,8 +1189,11 @@ module SyntaxTree
|
|
1188
1189
|
end
|
1189
1190
|
|
1190
1191
|
def format(q)
|
1191
|
-
|
1192
|
-
|
1192
|
+
lbracket = self.lbracket
|
1193
|
+
contents = self.contents
|
1194
|
+
|
1195
|
+
if lbracket.is_a?(LBracket) && lbracket.comments.empty? && contents &&
|
1196
|
+
contents.comments.empty? && contents.parts.length > 1
|
1193
1197
|
if qwords?
|
1194
1198
|
QWordsFormatter.new(contents).format(q)
|
1195
1199
|
return
|
@@ -2091,6 +2095,7 @@ module SyntaxTree
|
|
2091
2095
|
end
|
2092
2096
|
|
2093
2097
|
def format(q)
|
2098
|
+
left = self.left
|
2094
2099
|
power = operator == :**
|
2095
2100
|
|
2096
2101
|
q.group do
|
@@ -2307,6 +2312,8 @@ module SyntaxTree
|
|
2307
2312
|
end
|
2308
2313
|
|
2309
2314
|
def bind(parser, start_char, start_column, end_char, end_column)
|
2315
|
+
rescue_clause = self.rescue_clause
|
2316
|
+
|
2310
2317
|
@location =
|
2311
2318
|
Location.new(
|
2312
2319
|
start_line: location.start_line,
|
@@ -2330,6 +2337,7 @@ module SyntaxTree
|
|
2330
2337
|
# Next we're going to determine the rescue clause if there is one
|
2331
2338
|
if rescue_clause
|
2332
2339
|
consequent = else_clause || ensure_clause
|
2340
|
+
|
2333
2341
|
rescue_clause.bind_end(
|
2334
2342
|
consequent ? consequent.location.start_char : end_char,
|
2335
2343
|
consequent ? consequent.location.start_column : end_column
|
@@ -2735,7 +2743,7 @@ module SyntaxTree
|
|
2735
2743
|
children << receiver
|
2736
2744
|
end
|
2737
2745
|
when MethodAddBlock
|
2738
|
-
if receiver.call.is_a?(CallNode) && !
|
2746
|
+
if (call = receiver.call).is_a?(CallNode) && !call.receiver.nil?
|
2739
2747
|
children << receiver
|
2740
2748
|
else
|
2741
2749
|
break
|
@@ -2744,8 +2752,8 @@ module SyntaxTree
|
|
2744
2752
|
break
|
2745
2753
|
end
|
2746
2754
|
when MethodAddBlock
|
2747
|
-
if child.call.is_a?(CallNode) && !
|
2748
|
-
children <<
|
2755
|
+
if (call = child.call).is_a?(CallNode) && !call.receiver.nil?
|
2756
|
+
children << call
|
2749
2757
|
else
|
2750
2758
|
break
|
2751
2759
|
end
|
@@ -2767,8 +2775,8 @@ module SyntaxTree
|
|
2767
2775
|
# of just Statements nodes.
|
2768
2776
|
parent = parents[3] if parent.is_a?(BlockNode) && parent.keywords?
|
2769
2777
|
|
2770
|
-
if parent.is_a?(MethodAddBlock) &&
|
2771
|
-
parent.call.message.value == "sig"
|
2778
|
+
if parent.is_a?(MethodAddBlock) &&
|
2779
|
+
(call = parent.call).is_a?(CallNode) && call.message.value == "sig"
|
2772
2780
|
threshold = 2
|
2773
2781
|
end
|
2774
2782
|
end
|
@@ -2813,10 +2821,10 @@ module SyntaxTree
|
|
2813
2821
|
|
2814
2822
|
while (child = children.pop)
|
2815
2823
|
if child.is_a?(CallNode)
|
2816
|
-
if child.receiver.is_a?(CallNode) &&
|
2817
|
-
(
|
2818
|
-
(
|
2819
|
-
(
|
2824
|
+
if (receiver = child.receiver).is_a?(CallNode) &&
|
2825
|
+
(receiver.message != :call) &&
|
2826
|
+
(receiver.message.value == "where") &&
|
2827
|
+
(message.value == "not")
|
2820
2828
|
# This is very specialized behavior wherein we group
|
2821
2829
|
# .where.not calls together because it looks better. For more
|
2822
2830
|
# information, see
|
@@ -2872,7 +2880,8 @@ module SyntaxTree
|
|
2872
2880
|
when CallNode
|
2873
2881
|
!node.receiver.nil?
|
2874
2882
|
when MethodAddBlock
|
2875
|
-
|
2883
|
+
call = node.call
|
2884
|
+
call.is_a?(CallNode) && !call.receiver.nil?
|
2876
2885
|
else
|
2877
2886
|
false
|
2878
2887
|
end
|
@@ -3629,6 +3638,10 @@ module SyntaxTree
|
|
3629
3638
|
end
|
3630
3639
|
|
3631
3640
|
def format(q)
|
3641
|
+
message = self.message
|
3642
|
+
arguments = self.arguments
|
3643
|
+
block = self.block
|
3644
|
+
|
3632
3645
|
q.group do
|
3633
3646
|
doc =
|
3634
3647
|
q.nest(0) do
|
@@ -3637,7 +3650,7 @@ module SyntaxTree
|
|
3637
3650
|
# If there are leading comments on the message then we know we have
|
3638
3651
|
# a newline in the source that is forcing these things apart. In
|
3639
3652
|
# this case we will have to use a trailing operator.
|
3640
|
-
if message.comments.any?(&:leading?)
|
3653
|
+
if message != :call && message.comments.any?(&:leading?)
|
3641
3654
|
q.format(CallOperatorFormatter.new(operator), stackable: false)
|
3642
3655
|
q.indent do
|
3643
3656
|
q.breakable_empty
|
@@ -4153,6 +4166,9 @@ module SyntaxTree
|
|
4153
4166
|
end
|
4154
4167
|
|
4155
4168
|
def format(q)
|
4169
|
+
params = self.params
|
4170
|
+
bodystmt = self.bodystmt
|
4171
|
+
|
4156
4172
|
q.group do
|
4157
4173
|
q.group do
|
4158
4174
|
q.text("def")
|
@@ -4209,6 +4225,8 @@ module SyntaxTree
|
|
4209
4225
|
end
|
4210
4226
|
|
4211
4227
|
def arity
|
4228
|
+
params = self.params
|
4229
|
+
|
4212
4230
|
case params
|
4213
4231
|
when Params
|
4214
4232
|
params.arity
|
@@ -5293,6 +5311,7 @@ module SyntaxTree
|
|
5293
5311
|
end
|
5294
5312
|
|
5295
5313
|
def child_nodes
|
5314
|
+
operator = self.operator
|
5296
5315
|
[parent, (operator if operator != :"::"), name]
|
5297
5316
|
end
|
5298
5317
|
|
@@ -5674,7 +5693,7 @@ module SyntaxTree
|
|
5674
5693
|
end
|
5675
5694
|
|
5676
5695
|
def child_nodes
|
5677
|
-
[lbrace]
|
5696
|
+
[lbrace].concat(assocs)
|
5678
5697
|
end
|
5679
5698
|
|
5680
5699
|
def copy(lbrace: nil, assocs: nil, location: nil)
|
@@ -5766,7 +5785,7 @@ module SyntaxTree
|
|
5766
5785
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
5767
5786
|
attr_reader :comments
|
5768
5787
|
|
5769
|
-
def initialize(beginning:, ending: nil, dedent: 0, parts: []
|
5788
|
+
def initialize(beginning:, location:, ending: nil, dedent: 0, parts: [])
|
5770
5789
|
@beginning = beginning
|
5771
5790
|
@ending = ending
|
5772
5791
|
@dedent = dedent
|
@@ -6134,6 +6153,8 @@ module SyntaxTree
|
|
6134
6153
|
private
|
6135
6154
|
|
6136
6155
|
def format_contents(q, parts, nested)
|
6156
|
+
keyword_rest = self.keyword_rest
|
6157
|
+
|
6137
6158
|
q.group { q.seplist(parts) { |part| q.format(part, stackable: false) } }
|
6138
6159
|
|
6139
6160
|
# If there isn't a constant, and there's a blank keyword_rest, then we
|
@@ -6763,10 +6784,13 @@ module SyntaxTree
|
|
6763
6784
|
|
6764
6785
|
def format(q)
|
6765
6786
|
keyword = "in "
|
6787
|
+
pattern = self.pattern
|
6788
|
+
consequent = self.consequent
|
6766
6789
|
|
6767
6790
|
q.group do
|
6768
6791
|
q.text(keyword)
|
6769
6792
|
q.nest(keyword.length) { q.format(pattern) }
|
6793
|
+
q.text(" then") if pattern.is_a?(RangeNode) && pattern.right.nil?
|
6770
6794
|
|
6771
6795
|
unless statements.empty?
|
6772
6796
|
q.indent do
|
@@ -7164,6 +7188,8 @@ module SyntaxTree
|
|
7164
7188
|
end
|
7165
7189
|
|
7166
7190
|
def format(q)
|
7191
|
+
params = self.params
|
7192
|
+
|
7167
7193
|
q.text("->")
|
7168
7194
|
q.group do
|
7169
7195
|
if params.is_a?(Paren)
|
@@ -7642,7 +7668,7 @@ module SyntaxTree
|
|
7642
7668
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
7643
7669
|
attr_reader :comments
|
7644
7670
|
|
7645
|
-
def initialize(parts:, comma: false
|
7671
|
+
def initialize(parts:, location:, comma: false)
|
7646
7672
|
@parts = parts
|
7647
7673
|
@comma = comma
|
7648
7674
|
@location = location
|
@@ -7703,7 +7729,7 @@ module SyntaxTree
|
|
7703
7729
|
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
7704
7730
|
attr_reader :comments
|
7705
7731
|
|
7706
|
-
def initialize(contents:, comma: false
|
7732
|
+
def initialize(contents:, location:, comma: false)
|
7707
7733
|
@contents = contents
|
7708
7734
|
@comma = comma
|
7709
7735
|
@location = location
|
@@ -8286,14 +8312,14 @@ module SyntaxTree
|
|
8286
8312
|
attr_reader :comments
|
8287
8313
|
|
8288
8314
|
def initialize(
|
8315
|
+
location:,
|
8289
8316
|
requireds: [],
|
8290
8317
|
optionals: [],
|
8291
8318
|
rest: nil,
|
8292
8319
|
posts: [],
|
8293
8320
|
keywords: [],
|
8294
8321
|
keyword_rest: nil,
|
8295
|
-
block: nil
|
8296
|
-
location:
|
8322
|
+
block: nil
|
8297
8323
|
)
|
8298
8324
|
@requireds = requireds
|
8299
8325
|
@optionals = optionals
|
@@ -8320,6 +8346,8 @@ module SyntaxTree
|
|
8320
8346
|
end
|
8321
8347
|
|
8322
8348
|
def child_nodes
|
8349
|
+
keyword_rest = self.keyword_rest
|
8350
|
+
|
8323
8351
|
[
|
8324
8352
|
*requireds,
|
8325
8353
|
*optionals.flatten(1),
|
@@ -8374,16 +8402,19 @@ module SyntaxTree
|
|
8374
8402
|
end
|
8375
8403
|
|
8376
8404
|
def format(q)
|
8405
|
+
rest = self.rest
|
8406
|
+
keyword_rest = self.keyword_rest
|
8407
|
+
|
8377
8408
|
parts = [
|
8378
8409
|
*requireds,
|
8379
8410
|
*optionals.map { |(name, value)| OptionalFormatter.new(name, value) }
|
8380
8411
|
]
|
8381
8412
|
|
8382
8413
|
parts << rest if rest && !rest.is_a?(ExcessedComma)
|
8383
|
-
parts
|
8384
|
-
|
8385
|
-
|
8386
|
-
|
8414
|
+
parts.concat(posts)
|
8415
|
+
parts.concat(
|
8416
|
+
keywords.map { |(name, value)| KeywordFormatter.new(name, value) }
|
8417
|
+
)
|
8387
8418
|
|
8388
8419
|
parts << KeywordRestFormatter.new(keyword_rest) if keyword_rest
|
8389
8420
|
parts << block if block
|
@@ -8510,6 +8541,8 @@ module SyntaxTree
|
|
8510
8541
|
end
|
8511
8542
|
|
8512
8543
|
def format(q)
|
8544
|
+
contents = self.contents
|
8545
|
+
|
8513
8546
|
q.group do
|
8514
8547
|
q.format(lparen)
|
8515
8548
|
|
@@ -9424,11 +9457,11 @@ module SyntaxTree
|
|
9424
9457
|
end_column: end_column
|
9425
9458
|
)
|
9426
9459
|
|
9427
|
-
if consequent
|
9428
|
-
|
9460
|
+
if (next_node = consequent)
|
9461
|
+
next_node.bind_end(end_char, end_column)
|
9429
9462
|
statements.bind_end(
|
9430
|
-
|
9431
|
-
|
9463
|
+
next_node.location.start_char,
|
9464
|
+
next_node.location.start_column
|
9432
9465
|
)
|
9433
9466
|
else
|
9434
9467
|
statements.bind_end(end_char, end_column)
|
@@ -9871,8 +9904,8 @@ module SyntaxTree
|
|
9871
9904
|
end_column: end_column
|
9872
9905
|
)
|
9873
9906
|
|
9874
|
-
if body[0].is_a?(VoidStmt)
|
9875
|
-
location =
|
9907
|
+
if (void_stmt = body[0]).is_a?(VoidStmt)
|
9908
|
+
location = void_stmt.location
|
9876
9909
|
location =
|
9877
9910
|
Location.new(
|
9878
9911
|
start_line: location.start_line,
|
@@ -10351,7 +10384,7 @@ module SyntaxTree
|
|
10351
10384
|
opening_quote, closing_quote =
|
10352
10385
|
if !Quotes.locked?(self, q.quote)
|
10353
10386
|
[q.quote, q.quote]
|
10354
|
-
elsif quote
|
10387
|
+
elsif quote&.start_with?("%")
|
10355
10388
|
[quote, Quotes.matching(quote[/%[qQ]?(.)/, 1])]
|
10356
10389
|
else
|
10357
10390
|
[quote, quote]
|
@@ -11520,7 +11553,7 @@ module SyntaxTree
|
|
11520
11553
|
end
|
11521
11554
|
|
11522
11555
|
def child_nodes
|
11523
|
-
[value]
|
11556
|
+
value == :nil ? [] : [value]
|
11524
11557
|
end
|
11525
11558
|
|
11526
11559
|
def copy(value: nil, location: nil)
|
data/lib/syntax_tree/parser.rb
CHANGED
@@ -2132,13 +2132,20 @@ module SyntaxTree
|
|
2132
2132
|
ending = consequent || consume_keyword(:end)
|
2133
2133
|
|
2134
2134
|
statements_start = pattern
|
2135
|
-
if (token =
|
2135
|
+
if (token = find_keyword_between(:then, pattern, statements))
|
2136
2136
|
tokens.delete(token)
|
2137
2137
|
statements_start = token
|
2138
2138
|
end
|
2139
2139
|
|
2140
2140
|
start_char =
|
2141
2141
|
find_next_statement_start((token || statements_start).location.end_char)
|
2142
|
+
|
2143
|
+
# Ripper ignores parentheses on patterns, so we need to do the same in
|
2144
|
+
# order to attach comments correctly to the pattern.
|
2145
|
+
if source[start_char] == ")"
|
2146
|
+
start_char = find_next_statement_start(start_char + 1)
|
2147
|
+
end
|
2148
|
+
|
2142
2149
|
statements.bind(
|
2143
2150
|
self,
|
2144
2151
|
start_char,
|
@@ -2391,8 +2398,14 @@ module SyntaxTree
|
|
2391
2398
|
}
|
2392
2399
|
}
|
2393
2400
|
|
2401
|
+
parent_line = lineno - 1
|
2402
|
+
parent_column =
|
2403
|
+
consume_token(Semicolon).location.start_column - tokens[index][0][1]
|
2404
|
+
|
2394
2405
|
tokens[(index + 1)..].each_with_object([]) do |token, locals|
|
2395
2406
|
(lineno, column), type, value, = token
|
2407
|
+
column += parent_column if lineno == 1
|
2408
|
+
lineno += parent_line
|
2396
2409
|
|
2397
2410
|
# Make the state transition for the parser. If there isn't a transition
|
2398
2411
|
# from the current state to a new state for this type, then we're in a
|
@@ -138,12 +138,13 @@ module SyntaxTree
|
|
138
138
|
# as a placeholder for collecting all of the various places that nodes are
|
139
139
|
# used.
|
140
140
|
class Node
|
141
|
-
attr_reader :name, :comment, :attributes
|
141
|
+
attr_reader :name, :comment, :attributes, :visitor_method
|
142
142
|
|
143
|
-
def initialize(name, comment, attributes)
|
143
|
+
def initialize(name, comment, attributes, visitor_method)
|
144
144
|
@name = name
|
145
145
|
@comment = comment
|
146
146
|
@attributes = attributes
|
147
|
+
@visitor_method = visitor_method
|
147
148
|
end
|
148
149
|
end
|
149
150
|
|
@@ -176,16 +177,18 @@ module SyntaxTree
|
|
176
177
|
program =
|
177
178
|
SyntaxTree.parse(SyntaxTree.read(File.expand_path("node.rb", __dir__)))
|
178
179
|
|
179
|
-
|
180
|
+
program_statements = program.statements
|
181
|
+
main_statements = program_statements.body.last.bodystmt.statements.body
|
180
182
|
main_statements.each_with_index do |main_statement, main_statement_index|
|
181
183
|
# Ensure we are only looking at class declarations.
|
182
184
|
next unless main_statement.is_a?(SyntaxTree::ClassDeclaration)
|
183
185
|
|
184
186
|
# Ensure we're looking at class declarations with superclasses.
|
185
|
-
|
187
|
+
superclass = main_statement.superclass
|
188
|
+
next unless superclass.is_a?(SyntaxTree::VarRef)
|
186
189
|
|
187
190
|
# Ensure we're looking at class declarations that inherit from Node.
|
188
|
-
next unless
|
191
|
+
next unless superclass.value.value == "Node"
|
189
192
|
|
190
193
|
# All child nodes inherit the location attr_reader from Node, so we'll add
|
191
194
|
# that to the list of attributes first.
|
@@ -194,6 +197,10 @@ module SyntaxTree
|
|
194
197
|
Attribute.new(:location, "[Location] the location of this node")
|
195
198
|
}
|
196
199
|
|
200
|
+
# This is the name of the method tha gets called on the given visitor when
|
201
|
+
# the accept method is called on this node.
|
202
|
+
visitor_method = nil
|
203
|
+
|
197
204
|
statements = main_statement.bodystmt.statements.body
|
198
205
|
statements.each_with_index do |statement, statement_index|
|
199
206
|
case statement
|
@@ -223,16 +230,25 @@ module SyntaxTree
|
|
223
230
|
end
|
224
231
|
|
225
232
|
attributes[attribute.name] = attribute
|
233
|
+
when SyntaxTree::DefNode
|
234
|
+
if statement.name.value == "accept"
|
235
|
+
call_node = statement.bodystmt.statements.body.first
|
236
|
+
visitor_method = call_node.message.value.to_sym
|
237
|
+
end
|
226
238
|
end
|
227
239
|
end
|
228
240
|
|
241
|
+
# If we never found a visitor method, then we have an error.
|
242
|
+
raise if visitor_method.nil?
|
243
|
+
|
229
244
|
# Finally, set it up in the hash of nodes so that we can use it later.
|
230
245
|
comments = parse_comments(main_statements, main_statement_index)
|
231
246
|
node =
|
232
247
|
Node.new(
|
233
248
|
main_statement.constant.constant.value.to_sym,
|
234
249
|
"#{comments.join("\n")}\n",
|
235
|
-
attributes
|
250
|
+
attributes,
|
251
|
+
visitor_method
|
236
252
|
)
|
237
253
|
|
238
254
|
@nodes[node.name] = node
|
data/lib/syntax_tree/version.rb
CHANGED
@@ -189,6 +189,15 @@ module SyntaxTree
|
|
189
189
|
super
|
190
190
|
end
|
191
191
|
|
192
|
+
def visit_block_var(node)
|
193
|
+
node.locals.each do |local|
|
194
|
+
current_scope.add_local_definition(local, :variable)
|
195
|
+
end
|
196
|
+
|
197
|
+
super
|
198
|
+
end
|
199
|
+
alias visit_lambda_var visit_block_var
|
200
|
+
|
192
201
|
# Visit for keeping track of local variable definitions
|
193
202
|
def visit_var_field(node)
|
194
203
|
value = node.value
|
@@ -217,11 +226,72 @@ module SyntaxTree
|
|
217
226
|
super
|
218
227
|
end
|
219
228
|
|
229
|
+
# When using regex named capture groups, vcalls might actually be a variable
|
230
|
+
def visit_vcall(node)
|
231
|
+
value = node.value
|
232
|
+
definition = current_scope.find_local(value.value)
|
233
|
+
current_scope.add_local_usage(value, definition.type) if definition
|
234
|
+
|
235
|
+
super
|
236
|
+
end
|
237
|
+
|
238
|
+
# Visit for capturing local variables defined in regex named capture groups
|
239
|
+
def visit_binary(node)
|
240
|
+
if node.operator == :=~
|
241
|
+
left = node.left
|
242
|
+
|
243
|
+
if left.is_a?(RegexpLiteral) && left.parts.length == 1 &&
|
244
|
+
left.parts.first.is_a?(TStringContent)
|
245
|
+
content = left.parts.first
|
246
|
+
|
247
|
+
value = content.value
|
248
|
+
location = content.location
|
249
|
+
start_line = location.start_line
|
250
|
+
|
251
|
+
Regexp
|
252
|
+
.new(value, Regexp::FIXEDENCODING)
|
253
|
+
.names
|
254
|
+
.each do |name|
|
255
|
+
offset = value.index(/\(\?<#{Regexp.escape(name)}>/)
|
256
|
+
line = start_line + value[0...offset].count("\n")
|
257
|
+
|
258
|
+
# We need to add 3 to account for these three characters
|
259
|
+
# prefixing a named capture (?<
|
260
|
+
column = location.start_column + offset + 3
|
261
|
+
if value[0...offset].include?("\n")
|
262
|
+
column =
|
263
|
+
value[0...offset].length - value[0...offset].rindex("\n") +
|
264
|
+
3 - 1
|
265
|
+
end
|
266
|
+
|
267
|
+
ident_location =
|
268
|
+
Location.new(
|
269
|
+
start_line: line,
|
270
|
+
start_char: location.start_char + offset,
|
271
|
+
start_column: column,
|
272
|
+
end_line: line,
|
273
|
+
end_char: location.start_char + offset + name.length,
|
274
|
+
end_column: column + name.length
|
275
|
+
)
|
276
|
+
|
277
|
+
identifier = Ident.new(value: name, location: ident_location)
|
278
|
+
current_scope.add_local_definition(identifier, :variable)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
super
|
284
|
+
end
|
285
|
+
|
220
286
|
private
|
221
287
|
|
222
288
|
def add_argument_definitions(list)
|
223
289
|
list.each do |param|
|
224
|
-
|
290
|
+
case param
|
291
|
+
when ArgStar
|
292
|
+
value = param.value
|
293
|
+
current_scope.add_local_definition(value, :argument) if value
|
294
|
+
when MLHSParen
|
225
295
|
add_argument_definitions(param.contents.parts)
|
226
296
|
else
|
227
297
|
current_scope.add_local_definition(param, :argument)
|
@@ -875,8 +875,7 @@ module SyntaxTree
|
|
875
875
|
when Ident
|
876
876
|
iseq.putobject("local-variable")
|
877
877
|
when IVar
|
878
|
-
iseq.
|
879
|
-
iseq.defined(Defined::TYPE_IVAR, name, "instance-variable")
|
878
|
+
iseq.definedivar(name, iseq.inline_storage, "instance-variable")
|
880
879
|
when Kw
|
881
880
|
case name
|
882
881
|
when :false
|
@@ -50,7 +50,7 @@ module SyntaxTree
|
|
50
50
|
@tail_node = nil
|
51
51
|
end
|
52
52
|
|
53
|
-
def each
|
53
|
+
def each(&_blk)
|
54
54
|
return to_enum(__method__) unless block_given?
|
55
55
|
each_node { |node| yield node.value }
|
56
56
|
end
|
@@ -673,12 +673,21 @@ module SyntaxTree
|
|
673
673
|
push(ConcatStrings.new(number))
|
674
674
|
end
|
675
675
|
|
676
|
+
def defineclass(name, class_iseq, flags)
|
677
|
+
push(DefineClass.new(name, class_iseq, flags))
|
678
|
+
end
|
679
|
+
|
676
680
|
def defined(type, name, message)
|
677
681
|
push(Defined.new(type, name, message))
|
678
682
|
end
|
679
683
|
|
680
|
-
def
|
681
|
-
|
684
|
+
def definedivar(name, cache, message)
|
685
|
+
if RUBY_VERSION < "3.3"
|
686
|
+
push(PutNil.new)
|
687
|
+
push(Defined.new(Defined::TYPE_IVAR, name, message))
|
688
|
+
else
|
689
|
+
push(DefinedIVar.new(name, cache, message))
|
690
|
+
end
|
682
691
|
end
|
683
692
|
|
684
693
|
def definemethod(name, method_iseq)
|
@@ -1058,6 +1067,8 @@ module SyntaxTree
|
|
1058
1067
|
iseq.defineclass(opnds[0], from(opnds[1], options, iseq), opnds[2])
|
1059
1068
|
when :defined
|
1060
1069
|
iseq.defined(opnds[0], opnds[1], opnds[2])
|
1070
|
+
when :definedivar
|
1071
|
+
iseq.definedivar(opnds[0], opnds[1], opnds[2])
|
1061
1072
|
when :definemethod
|
1062
1073
|
iseq.definemethod(opnds[0], from(opnds[1], options, iseq))
|
1063
1074
|
when :definesmethod
|
@@ -994,6 +994,64 @@ module SyntaxTree
|
|
994
994
|
end
|
995
995
|
end
|
996
996
|
|
997
|
+
# ### Summary
|
998
|
+
#
|
999
|
+
# `definedivar` checks if an instance variable is defined. It is a
|
1000
|
+
# specialization of the `defined` instruction. It accepts three arguments:
|
1001
|
+
# the name of the instance variable, an inline cache, and the string that
|
1002
|
+
# should be pushed onto the stack in the event that the instance variable
|
1003
|
+
# is defined.
|
1004
|
+
#
|
1005
|
+
# ### Usage
|
1006
|
+
#
|
1007
|
+
# ~~~ruby
|
1008
|
+
# defined?(@value)
|
1009
|
+
# ~~~
|
1010
|
+
#
|
1011
|
+
class DefinedIVar < Instruction
|
1012
|
+
attr_reader :name, :cache, :message
|
1013
|
+
|
1014
|
+
def initialize(name, cache, message)
|
1015
|
+
@name = name
|
1016
|
+
@cache = cache
|
1017
|
+
@message = message
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
def disasm(fmt)
|
1021
|
+
fmt.instruction(
|
1022
|
+
"definedivar",
|
1023
|
+
[fmt.object(name), fmt.inline_storage(cache), fmt.object(message)]
|
1024
|
+
)
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
def to_a(_iseq)
|
1028
|
+
[:definedivar, name, cache, message]
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
def deconstruct_keys(_keys)
|
1032
|
+
{ name: name, cache: cache, message: message }
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
def ==(other)
|
1036
|
+
other.is_a?(DefinedIVar) && other.name == name &&
|
1037
|
+
other.cache == cache && other.message == message
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
def length
|
1041
|
+
4
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
def pushes
|
1045
|
+
1
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
def call(vm)
|
1049
|
+
result = (message if vm.frame._self.instance_variable_defined?(name))
|
1050
|
+
|
1051
|
+
vm.push(result)
|
1052
|
+
end
|
1053
|
+
end
|
1054
|
+
|
997
1055
|
# ### Summary
|
998
1056
|
#
|
999
1057
|
# `definemethod` defines a method on the class of the current value of
|