syntax_tree 6.0.1 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|