syntax_tree 5.3.0 → 6.0.1
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/.rubocop.yml +12 -1
- data/CHANGELOG.md +78 -1
- data/Gemfile.lock +7 -7
- data/README.md +33 -9
- data/Rakefile +12 -8
- data/bin/console +1 -0
- data/bin/whitequark +79 -0
- data/doc/changing_structure.md +16 -0
- data/lib/syntax_tree/basic_visitor.rb +44 -5
- data/lib/syntax_tree/cli.rb +2 -2
- data/lib/syntax_tree/dsl.rb +23 -11
- data/lib/syntax_tree/{visitor/field_visitor.rb → field_visitor.rb} +54 -55
- data/lib/syntax_tree/formatter.rb +1 -1
- data/lib/syntax_tree/index.rb +158 -59
- data/lib/syntax_tree/json_visitor.rb +55 -0
- data/lib/syntax_tree/language_server.rb +157 -2
- data/lib/syntax_tree/match_visitor.rb +120 -0
- data/lib/syntax_tree/mermaid.rb +177 -0
- data/lib/syntax_tree/mermaid_visitor.rb +69 -0
- data/lib/syntax_tree/{visitor/mutation_visitor.rb → mutation_visitor.rb} +27 -27
- data/lib/syntax_tree/node.rb +245 -123
- data/lib/syntax_tree/parser.rb +332 -119
- data/lib/syntax_tree/pretty_print_visitor.rb +83 -0
- data/lib/syntax_tree/reflection.rb +241 -0
- data/lib/syntax_tree/translation/parser.rb +3107 -0
- data/lib/syntax_tree/translation/rubocop_ast.rb +21 -0
- data/lib/syntax_tree/translation.rb +28 -0
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/with_scope.rb +244 -0
- data/lib/syntax_tree/yarv/basic_block.rb +53 -0
- data/lib/syntax_tree/yarv/calldata.rb +91 -0
- data/lib/syntax_tree/yarv/compiler.rb +110 -100
- data/lib/syntax_tree/yarv/control_flow_graph.rb +257 -0
- data/lib/syntax_tree/yarv/data_flow_graph.rb +338 -0
- data/lib/syntax_tree/yarv/decompiler.rb +1 -1
- data/lib/syntax_tree/yarv/disassembler.rb +104 -80
- data/lib/syntax_tree/yarv/instruction_sequence.rb +43 -18
- data/lib/syntax_tree/yarv/instructions.rb +203 -649
- data/lib/syntax_tree/yarv/legacy.rb +12 -24
- data/lib/syntax_tree/yarv/sea_of_nodes.rb +534 -0
- data/lib/syntax_tree/yarv.rb +18 -0
- data/lib/syntax_tree.rb +88 -56
- data/tasks/sorbet.rake +277 -0
- data/tasks/whitequark.rake +87 -0
- metadata +23 -11
- data/.gitmodules +0 -9
- data/lib/syntax_tree/language_server/inlay_hints.rb +0 -159
- data/lib/syntax_tree/visitor/environment.rb +0 -84
- data/lib/syntax_tree/visitor/json_visitor.rb +0 -55
- data/lib/syntax_tree/visitor/match_visitor.rb +0 -122
- data/lib/syntax_tree/visitor/pretty_print_visitor.rb +0 -85
- data/lib/syntax_tree/visitor/with_environment.rb +0 -140
data/lib/syntax_tree/parser.rb
CHANGED
|
@@ -256,11 +256,37 @@ module SyntaxTree
|
|
|
256
256
|
tokens[index] if index
|
|
257
257
|
end
|
|
258
258
|
|
|
259
|
+
def find_token_between(type, left, right)
|
|
260
|
+
bounds = left.location.end_char...right.location.start_char
|
|
261
|
+
index =
|
|
262
|
+
tokens.rindex do |token|
|
|
263
|
+
char = token.location.start_char
|
|
264
|
+
break if char < bounds.begin
|
|
265
|
+
|
|
266
|
+
token.is_a?(type) && bounds.cover?(char)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
tokens[index] if index
|
|
270
|
+
end
|
|
271
|
+
|
|
259
272
|
def find_keyword(name)
|
|
260
273
|
index = tokens.rindex { |token| token.is_a?(Kw) && (token.name == name) }
|
|
261
274
|
tokens[index] if index
|
|
262
275
|
end
|
|
263
276
|
|
|
277
|
+
def find_keyword_between(name, left, right)
|
|
278
|
+
bounds = left.end_char...right.start_char
|
|
279
|
+
index =
|
|
280
|
+
tokens.rindex do |token|
|
|
281
|
+
char = token.location.start_char
|
|
282
|
+
break if char < bounds.begin
|
|
283
|
+
|
|
284
|
+
token.is_a?(Kw) && (token.name == name) && bounds.cover?(char)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
tokens[index] if index
|
|
288
|
+
end
|
|
289
|
+
|
|
264
290
|
def find_operator(name)
|
|
265
291
|
index = tokens.rindex { |token| token.is_a?(Op) && (token.name == name) }
|
|
266
292
|
tokens[index] if index
|
|
@@ -348,6 +374,7 @@ module SyntaxTree
|
|
|
348
374
|
|
|
349
375
|
start_char = find_next_statement_start(lbrace.location.end_char)
|
|
350
376
|
statements.bind(
|
|
377
|
+
self,
|
|
351
378
|
start_char,
|
|
352
379
|
start_char - line_counts[lbrace.location.start_line - 1].start,
|
|
353
380
|
rbrace.location.start_char,
|
|
@@ -386,6 +413,7 @@ module SyntaxTree
|
|
|
386
413
|
|
|
387
414
|
start_char = find_next_statement_start(lbrace.location.end_char)
|
|
388
415
|
statements.bind(
|
|
416
|
+
self,
|
|
389
417
|
start_char,
|
|
390
418
|
start_char - line_counts[lbrace.location.start_line - 1].start,
|
|
391
419
|
rbrace.location.start_char,
|
|
@@ -640,13 +668,14 @@ module SyntaxTree
|
|
|
640
668
|
stack.pop
|
|
641
669
|
end
|
|
642
670
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
671
|
+
visit_methods do
|
|
672
|
+
def visit_var_ref(node)
|
|
673
|
+
node.pin(stack[-2], pins.shift)
|
|
674
|
+
end
|
|
646
675
|
end
|
|
647
676
|
|
|
648
677
|
def self.visit(node, tokens)
|
|
649
|
-
start_char = node.
|
|
678
|
+
start_char = node.start_char
|
|
650
679
|
allocated = []
|
|
651
680
|
|
|
652
681
|
tokens.reverse_each do |token|
|
|
@@ -670,18 +699,22 @@ module SyntaxTree
|
|
|
670
699
|
# (nil | Array[untyped]) posts
|
|
671
700
|
# ) -> AryPtn
|
|
672
701
|
def on_aryptn(constant, requireds, rest, posts)
|
|
673
|
-
|
|
702
|
+
lbracket = find_token(LBracket)
|
|
703
|
+
lbracket ||= find_token(LParen) if constant
|
|
674
704
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
705
|
+
rbracket = find_token(RBracket)
|
|
706
|
+
rbracket ||= find_token(RParen) if constant
|
|
707
|
+
|
|
708
|
+
parts = [constant, lbracket, *requireds, rest, *posts, rbracket].compact
|
|
709
|
+
|
|
710
|
+
# The location is going to be determined by the first part to the last
|
|
711
|
+
# part. This includes potential brackets.
|
|
712
|
+
location = parts[0].location.to(parts[-1].location)
|
|
713
|
+
|
|
714
|
+
# Now that we have the location calculated, we can remove the brackets
|
|
715
|
+
# from the list of tokens.
|
|
716
|
+
tokens.delete(lbracket) if lbracket
|
|
717
|
+
tokens.delete(rbracket) if rbracket
|
|
685
718
|
|
|
686
719
|
# If there is a plain *, then we're going to fix up the location of it
|
|
687
720
|
# here because it currently doesn't have anything to use for its precise
|
|
@@ -820,6 +853,7 @@ module SyntaxTree
|
|
|
820
853
|
end
|
|
821
854
|
|
|
822
855
|
bodystmt.bind(
|
|
856
|
+
self,
|
|
823
857
|
find_next_statement_start(keyword.location.end_char),
|
|
824
858
|
keyword.location.end_column,
|
|
825
859
|
end_location.end_char,
|
|
@@ -871,13 +905,34 @@ module SyntaxTree
|
|
|
871
905
|
# on_block_var: (Params params, (nil | Array[Ident]) locals) -> BlockVar
|
|
872
906
|
def on_block_var(params, locals)
|
|
873
907
|
index =
|
|
874
|
-
tokens.rindex
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
908
|
+
tokens.rindex { |node| node.is_a?(Op) && %w[| ||].include?(node.value) }
|
|
909
|
+
|
|
910
|
+
ending = tokens.delete_at(index)
|
|
911
|
+
beginning = ending.value == "||" ? ending : consume_operator(:|)
|
|
912
|
+
|
|
913
|
+
# If there are no parameters, then we didn't have anything to base the
|
|
914
|
+
# location information of off. Now that we have an opening of the
|
|
915
|
+
# block, we can correct this.
|
|
916
|
+
if params.empty?
|
|
917
|
+
start_line = params.location.start_line
|
|
918
|
+
start_char =
|
|
919
|
+
(
|
|
920
|
+
if beginning.value == "||"
|
|
921
|
+
beginning.location.start_char
|
|
922
|
+
else
|
|
923
|
+
find_next_statement_start(beginning.location.end_char)
|
|
924
|
+
end
|
|
925
|
+
)
|
|
878
926
|
|
|
879
|
-
|
|
880
|
-
|
|
927
|
+
location =
|
|
928
|
+
Location.fixed(
|
|
929
|
+
line: start_line,
|
|
930
|
+
char: start_char,
|
|
931
|
+
column: start_char - line_counts[start_line - 1].start
|
|
932
|
+
)
|
|
933
|
+
|
|
934
|
+
params = params.copy(location: location)
|
|
935
|
+
end
|
|
881
936
|
|
|
882
937
|
BlockVar.new(
|
|
883
938
|
params: params,
|
|
@@ -905,6 +960,14 @@ module SyntaxTree
|
|
|
905
960
|
# (nil | Ensure) ensure_clause
|
|
906
961
|
# ) -> BodyStmt
|
|
907
962
|
def on_bodystmt(statements, rescue_clause, else_clause, ensure_clause)
|
|
963
|
+
# In certain versions of Ruby, the `statements` argument can be any node
|
|
964
|
+
# in the case that we're inside of an endless method definition. In this
|
|
965
|
+
# case we'll wrap it in a Statements node to be consistent.
|
|
966
|
+
unless statements.is_a?(Statements)
|
|
967
|
+
statements =
|
|
968
|
+
Statements.new(body: [statements], location: statements.location)
|
|
969
|
+
end
|
|
970
|
+
|
|
908
971
|
parts = [statements, rescue_clause, else_clause, ensure_clause].compact
|
|
909
972
|
|
|
910
973
|
BodyStmt.new(
|
|
@@ -929,6 +992,7 @@ module SyntaxTree
|
|
|
929
992
|
|
|
930
993
|
start_char = find_next_statement_start(location.end_char)
|
|
931
994
|
statements.bind(
|
|
995
|
+
self,
|
|
932
996
|
start_char,
|
|
933
997
|
start_char - line_counts[location.start_line - 1].start,
|
|
934
998
|
rbrace.location.start_char,
|
|
@@ -1036,6 +1100,7 @@ module SyntaxTree
|
|
|
1036
1100
|
start_char = find_next_statement_start(location.end_char)
|
|
1037
1101
|
|
|
1038
1102
|
bodystmt.bind(
|
|
1103
|
+
self,
|
|
1039
1104
|
start_char,
|
|
1040
1105
|
start_char - line_counts[location.start_line - 1].start,
|
|
1041
1106
|
ending.location.start_char,
|
|
@@ -1154,13 +1219,23 @@ module SyntaxTree
|
|
|
1154
1219
|
end
|
|
1155
1220
|
|
|
1156
1221
|
# :call-seq:
|
|
1157
|
-
# on_const_path_field: (untyped parent, Const constant) ->
|
|
1222
|
+
# on_const_path_field: (untyped parent, Const constant) ->
|
|
1223
|
+
# ConstPathField | Field
|
|
1158
1224
|
def on_const_path_field(parent, constant)
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1225
|
+
if constant.is_a?(Const)
|
|
1226
|
+
ConstPathField.new(
|
|
1227
|
+
parent: parent,
|
|
1228
|
+
constant: constant,
|
|
1229
|
+
location: parent.location.to(constant.location)
|
|
1230
|
+
)
|
|
1231
|
+
else
|
|
1232
|
+
Field.new(
|
|
1233
|
+
parent: parent,
|
|
1234
|
+
operator: consume_operator(:"::"),
|
|
1235
|
+
name: constant,
|
|
1236
|
+
location: parent.location.to(constant.location)
|
|
1237
|
+
)
|
|
1238
|
+
end
|
|
1164
1239
|
end
|
|
1165
1240
|
|
|
1166
1241
|
# :call-seq:
|
|
@@ -1235,6 +1310,7 @@ module SyntaxTree
|
|
|
1235
1310
|
start_char = find_next_statement_start(params.location.end_char)
|
|
1236
1311
|
|
|
1237
1312
|
bodystmt.bind(
|
|
1313
|
+
self,
|
|
1238
1314
|
start_char,
|
|
1239
1315
|
start_char - line_counts[params.location.start_line - 1].start,
|
|
1240
1316
|
ending.location.start_char,
|
|
@@ -1323,6 +1399,7 @@ module SyntaxTree
|
|
|
1323
1399
|
start_char = find_next_statement_start(params.location.end_char)
|
|
1324
1400
|
|
|
1325
1401
|
bodystmt.bind(
|
|
1402
|
+
self,
|
|
1326
1403
|
start_char,
|
|
1327
1404
|
start_char - line_counts[params.location.start_line - 1].start,
|
|
1328
1405
|
ending.location.start_char,
|
|
@@ -1362,6 +1439,7 @@ module SyntaxTree
|
|
|
1362
1439
|
start_char = find_next_statement_start(location.end_char)
|
|
1363
1440
|
|
|
1364
1441
|
bodystmt.bind(
|
|
1442
|
+
self,
|
|
1365
1443
|
start_char,
|
|
1366
1444
|
start_char - line_counts[location.start_line - 1].start,
|
|
1367
1445
|
ending.location.start_char,
|
|
@@ -1457,6 +1535,7 @@ module SyntaxTree
|
|
|
1457
1535
|
|
|
1458
1536
|
start_char = find_next_statement_start(keyword.location.end_char)
|
|
1459
1537
|
statements.bind(
|
|
1538
|
+
self,
|
|
1460
1539
|
start_char,
|
|
1461
1540
|
start_char - line_counts[keyword.location.start_line - 1].start,
|
|
1462
1541
|
ending.location.start_char,
|
|
@@ -1480,8 +1559,16 @@ module SyntaxTree
|
|
|
1480
1559
|
beginning = consume_keyword(:elsif)
|
|
1481
1560
|
ending = consequent || consume_keyword(:end)
|
|
1482
1561
|
|
|
1483
|
-
|
|
1562
|
+
delimiter =
|
|
1563
|
+
find_keyword_between(:then, predicate, statements) ||
|
|
1564
|
+
find_token_between(Semicolon, predicate, statements)
|
|
1565
|
+
|
|
1566
|
+
tokens.delete(delimiter) if delimiter
|
|
1567
|
+
start_char =
|
|
1568
|
+
find_next_statement_start((delimiter || predicate).location.end_char)
|
|
1569
|
+
|
|
1484
1570
|
statements.bind(
|
|
1571
|
+
self,
|
|
1485
1572
|
start_char,
|
|
1486
1573
|
start_char - line_counts[predicate.location.start_line - 1].start,
|
|
1487
1574
|
ending.location.start_char,
|
|
@@ -1605,6 +1692,7 @@ module SyntaxTree
|
|
|
1605
1692
|
ending = find_keyword(:end)
|
|
1606
1693
|
start_char = find_next_statement_start(keyword.location.end_char)
|
|
1607
1694
|
statements.bind(
|
|
1695
|
+
self,
|
|
1608
1696
|
start_char,
|
|
1609
1697
|
start_char - line_counts[keyword.location.start_line - 1].start,
|
|
1610
1698
|
ending.location.start_char,
|
|
@@ -1679,6 +1767,22 @@ module SyntaxTree
|
|
|
1679
1767
|
# VarField right
|
|
1680
1768
|
# ) -> FndPtn
|
|
1681
1769
|
def on_fndptn(constant, left, values, right)
|
|
1770
|
+
# The left and right of a find pattern are always going to be splats, so
|
|
1771
|
+
# we're going to consume the * operators and use their location
|
|
1772
|
+
# information to extend the location of the splats.
|
|
1773
|
+
right, left =
|
|
1774
|
+
[right, left].map do |node|
|
|
1775
|
+
operator = consume_operator(:*)
|
|
1776
|
+
location =
|
|
1777
|
+
if node.value
|
|
1778
|
+
operator.location.to(node.location)
|
|
1779
|
+
else
|
|
1780
|
+
operator.location
|
|
1781
|
+
end
|
|
1782
|
+
|
|
1783
|
+
node.copy(location: location)
|
|
1784
|
+
end
|
|
1785
|
+
|
|
1682
1786
|
# The opening of this find pattern is either going to be a left bracket, a
|
|
1683
1787
|
# right left parenthesis, or the left splat. We're going to use this to
|
|
1684
1788
|
# determine how to find the closing of the pattern, as well as determining
|
|
@@ -1719,21 +1823,20 @@ module SyntaxTree
|
|
|
1719
1823
|
in_keyword = consume_keyword(:in)
|
|
1720
1824
|
ending = consume_keyword(:end)
|
|
1721
1825
|
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
keyword.location.end_char < ending.location.start_char
|
|
1728
|
-
tokens.delete(keyword)
|
|
1729
|
-
end
|
|
1826
|
+
delimiter =
|
|
1827
|
+
find_keyword_between(:do, collection, ending) ||
|
|
1828
|
+
find_token_between(Semicolon, collection, ending)
|
|
1829
|
+
|
|
1830
|
+
tokens.delete(delimiter) if delimiter
|
|
1730
1831
|
|
|
1731
1832
|
start_char =
|
|
1732
|
-
find_next_statement_start((
|
|
1833
|
+
find_next_statement_start((delimiter || collection).location.end_char)
|
|
1834
|
+
|
|
1733
1835
|
statements.bind(
|
|
1836
|
+
self,
|
|
1734
1837
|
start_char,
|
|
1735
1838
|
start_char -
|
|
1736
|
-
line_counts[(
|
|
1839
|
+
line_counts[(delimiter || collection).location.end_line - 1].start,
|
|
1737
1840
|
ending.location.start_char,
|
|
1738
1841
|
ending.location.start_column
|
|
1739
1842
|
)
|
|
@@ -1787,7 +1890,7 @@ module SyntaxTree
|
|
|
1787
1890
|
line: lineno,
|
|
1788
1891
|
char: char_pos,
|
|
1789
1892
|
column: current_column,
|
|
1790
|
-
size: value.size
|
|
1893
|
+
size: value.size
|
|
1791
1894
|
)
|
|
1792
1895
|
|
|
1793
1896
|
# Here we're going to artificially create an extra node type so that if
|
|
@@ -1822,7 +1925,7 @@ module SyntaxTree
|
|
|
1822
1925
|
line: lineno,
|
|
1823
1926
|
char: char_pos,
|
|
1824
1927
|
column: current_column,
|
|
1825
|
-
size: value.size
|
|
1928
|
+
size: value.size
|
|
1826
1929
|
)
|
|
1827
1930
|
|
|
1828
1931
|
heredoc_end = HeredocEnd.new(value: value.chomp, location: location)
|
|
@@ -1837,9 +1940,9 @@ module SyntaxTree
|
|
|
1837
1940
|
start_line: heredoc.location.start_line,
|
|
1838
1941
|
start_char: heredoc.location.start_char,
|
|
1839
1942
|
start_column: heredoc.location.start_column,
|
|
1840
|
-
end_line:
|
|
1841
|
-
end_char:
|
|
1842
|
-
end_column:
|
|
1943
|
+
end_line: location.end_line,
|
|
1944
|
+
end_char: location.end_char,
|
|
1945
|
+
end_column: location.end_column
|
|
1843
1946
|
)
|
|
1844
1947
|
)
|
|
1845
1948
|
end
|
|
@@ -1847,10 +1950,42 @@ module SyntaxTree
|
|
|
1847
1950
|
# :call-seq:
|
|
1848
1951
|
# on_hshptn: (
|
|
1849
1952
|
# (nil | untyped) constant,
|
|
1850
|
-
# Array[[Label, untyped]] keywords,
|
|
1953
|
+
# Array[[Label | StringContent, untyped]] keywords,
|
|
1851
1954
|
# (nil | VarField) keyword_rest
|
|
1852
1955
|
# ) -> HshPtn
|
|
1853
1956
|
def on_hshptn(constant, keywords, keyword_rest)
|
|
1957
|
+
keywords =
|
|
1958
|
+
(keywords || []).map do |(label, value)|
|
|
1959
|
+
if label.is_a?(Label)
|
|
1960
|
+
[label, value]
|
|
1961
|
+
else
|
|
1962
|
+
tstring_beg_index =
|
|
1963
|
+
tokens.rindex do |token|
|
|
1964
|
+
token.is_a?(TStringBeg) &&
|
|
1965
|
+
token.location.start_char < label.location.start_char
|
|
1966
|
+
end
|
|
1967
|
+
|
|
1968
|
+
tstring_beg = tokens.delete_at(tstring_beg_index)
|
|
1969
|
+
|
|
1970
|
+
label_end_index =
|
|
1971
|
+
tokens.rindex do |token|
|
|
1972
|
+
token.is_a?(LabelEnd) &&
|
|
1973
|
+
token.location.start_char == label.location.end_char
|
|
1974
|
+
end
|
|
1975
|
+
|
|
1976
|
+
label_end = tokens.delete_at(label_end_index)
|
|
1977
|
+
|
|
1978
|
+
[
|
|
1979
|
+
DynaSymbol.new(
|
|
1980
|
+
parts: label.parts,
|
|
1981
|
+
quote: label_end.value[0],
|
|
1982
|
+
location: tstring_beg.location.to(label_end.location)
|
|
1983
|
+
),
|
|
1984
|
+
value
|
|
1985
|
+
]
|
|
1986
|
+
end
|
|
1987
|
+
end
|
|
1988
|
+
|
|
1854
1989
|
if keyword_rest
|
|
1855
1990
|
# We're doing this to delete the token from the list so that it doesn't
|
|
1856
1991
|
# confuse future patterns by thinking they have an extra ** on the end.
|
|
@@ -1863,7 +1998,7 @@ module SyntaxTree
|
|
|
1863
1998
|
keyword_rest = VarField.new(value: nil, location: token.location)
|
|
1864
1999
|
end
|
|
1865
2000
|
|
|
1866
|
-
parts = [constant, *keywords
|
|
2001
|
+
parts = [constant, *keywords.flatten(1), keyword_rest].compact
|
|
1867
2002
|
|
|
1868
2003
|
# If there's no constant, there may be braces, so we're going to look for
|
|
1869
2004
|
# those to get our bounds.
|
|
@@ -1880,7 +2015,7 @@ module SyntaxTree
|
|
|
1880
2015
|
|
|
1881
2016
|
HshPtn.new(
|
|
1882
2017
|
constant: constant,
|
|
1883
|
-
keywords: keywords
|
|
2018
|
+
keywords: keywords,
|
|
1884
2019
|
keyword_rest: keyword_rest,
|
|
1885
2020
|
location: parts[0].location.to(parts[-1].location)
|
|
1886
2021
|
)
|
|
@@ -1911,8 +2046,15 @@ module SyntaxTree
|
|
|
1911
2046
|
beginning = consume_keyword(:if)
|
|
1912
2047
|
ending = consequent || consume_keyword(:end)
|
|
1913
2048
|
|
|
1914
|
-
|
|
2049
|
+
if (keyword = find_keyword_between(:then, predicate, ending))
|
|
2050
|
+
tokens.delete(keyword)
|
|
2051
|
+
end
|
|
2052
|
+
|
|
2053
|
+
start_char =
|
|
2054
|
+
find_next_statement_start((keyword || predicate).location.end_char)
|
|
2055
|
+
|
|
1915
2056
|
statements.bind(
|
|
2057
|
+
self,
|
|
1916
2058
|
start_char,
|
|
1917
2059
|
start_char - line_counts[predicate.location.end_line - 1].start,
|
|
1918
2060
|
ending.location.start_char,
|
|
@@ -1946,7 +2088,7 @@ module SyntaxTree
|
|
|
1946
2088
|
IfNode.new(
|
|
1947
2089
|
predicate: predicate,
|
|
1948
2090
|
statements:
|
|
1949
|
-
Statements.new(
|
|
2091
|
+
Statements.new(body: [statement], location: statement.location),
|
|
1950
2092
|
consequent: nil,
|
|
1951
2093
|
location: statement.location.to(predicate.location)
|
|
1952
2094
|
)
|
|
@@ -1995,8 +2137,10 @@ module SyntaxTree
|
|
|
1995
2137
|
statements_start = token
|
|
1996
2138
|
end
|
|
1997
2139
|
|
|
1998
|
-
start_char =
|
|
2140
|
+
start_char =
|
|
2141
|
+
find_next_statement_start((token || statements_start).location.end_char)
|
|
1999
2142
|
statements.bind(
|
|
2143
|
+
self,
|
|
2000
2144
|
start_char,
|
|
2001
2145
|
start_char -
|
|
2002
2146
|
line_counts[statements_start.location.start_line - 1].start,
|
|
@@ -2121,12 +2265,19 @@ module SyntaxTree
|
|
|
2121
2265
|
token.location.start_char > beginning.location.start_char
|
|
2122
2266
|
end
|
|
2123
2267
|
|
|
2268
|
+
if braces
|
|
2269
|
+
opening = consume_token(TLamBeg)
|
|
2270
|
+
closing = consume_token(RBrace)
|
|
2271
|
+
else
|
|
2272
|
+
opening = consume_keyword(:do)
|
|
2273
|
+
closing = consume_keyword(:end)
|
|
2274
|
+
end
|
|
2275
|
+
|
|
2124
2276
|
# We need to do some special mapping here. Since ripper doesn't support
|
|
2125
|
-
# capturing lambda
|
|
2277
|
+
# capturing lambda vars, we need to normalize all of that here.
|
|
2126
2278
|
params =
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
# In this case we've gotten to the <3.2 parentheses wrapping a set of
|
|
2279
|
+
if params.is_a?(Paren)
|
|
2280
|
+
# In this case we've gotten to the parentheses wrapping a set of
|
|
2130
2281
|
# parameters case. Here we need to manually scan for lambda locals.
|
|
2131
2282
|
range = (params.location.start_char + 1)...params.location.end_char
|
|
2132
2283
|
locals = lambda_locals(source[range])
|
|
@@ -2148,27 +2299,31 @@ module SyntaxTree
|
|
|
2148
2299
|
|
|
2149
2300
|
node.comments.concat(params.comments)
|
|
2150
2301
|
node
|
|
2151
|
-
|
|
2152
|
-
#
|
|
2153
|
-
#
|
|
2154
|
-
#
|
|
2302
|
+
else
|
|
2303
|
+
# If there are no parameters, then we didn't have anything to base the
|
|
2304
|
+
# location information of off. Now that we have an opening of the
|
|
2305
|
+
# block, we can correct this.
|
|
2306
|
+
if params.empty?
|
|
2307
|
+
opening_location = opening.location
|
|
2308
|
+
location =
|
|
2309
|
+
Location.fixed(
|
|
2310
|
+
line: opening_location.start_line,
|
|
2311
|
+
char: opening_location.start_char,
|
|
2312
|
+
column: opening_location.start_column
|
|
2313
|
+
)
|
|
2314
|
+
|
|
2315
|
+
params = params.copy(location: location)
|
|
2316
|
+
end
|
|
2317
|
+
|
|
2318
|
+
# In this case we've gotten to the plain set of parameters. In this
|
|
2319
|
+
# case there cannot be lambda locals, so we will wrap the parameters
|
|
2320
|
+
# into a lambda var that has no locals.
|
|
2155
2321
|
LambdaVar.new(params: params, locals: [], location: params.location)
|
|
2156
|
-
when LambdaVar
|
|
2157
|
-
# In this case we've gotten to 3.2+ lambda var. In this case we don't
|
|
2158
|
-
# need to do anything and can just the value as given.
|
|
2159
|
-
params
|
|
2160
2322
|
end
|
|
2161
2323
|
|
|
2162
|
-
if braces
|
|
2163
|
-
opening = consume_token(TLamBeg)
|
|
2164
|
-
closing = consume_token(RBrace)
|
|
2165
|
-
else
|
|
2166
|
-
opening = consume_keyword(:do)
|
|
2167
|
-
closing = consume_keyword(:end)
|
|
2168
|
-
end
|
|
2169
|
-
|
|
2170
2324
|
start_char = find_next_statement_start(opening.location.end_char)
|
|
2171
2325
|
statements.bind(
|
|
2326
|
+
self,
|
|
2172
2327
|
start_char,
|
|
2173
2328
|
start_char - line_counts[opening.location.end_line - 1].start,
|
|
2174
2329
|
closing.location.start_char,
|
|
@@ -2353,23 +2508,30 @@ module SyntaxTree
|
|
|
2353
2508
|
|
|
2354
2509
|
# :call-seq:
|
|
2355
2510
|
# on_method_add_block: (
|
|
2356
|
-
# (Call | Command | CommandCall) call,
|
|
2511
|
+
# (Break | Call | Command | CommandCall, Next) call,
|
|
2357
2512
|
# Block block
|
|
2358
|
-
# ) -> MethodAddBlock
|
|
2513
|
+
# ) -> Break | MethodAddBlock
|
|
2359
2514
|
def on_method_add_block(call, block)
|
|
2360
2515
|
location = call.location.to(block.location)
|
|
2361
2516
|
|
|
2362
2517
|
case call
|
|
2518
|
+
when Break, Next, ReturnNode
|
|
2519
|
+
parts = call.arguments.parts
|
|
2520
|
+
|
|
2521
|
+
node = parts.pop
|
|
2522
|
+
copied =
|
|
2523
|
+
node.copy(block: block, location: node.location.to(block.location))
|
|
2524
|
+
|
|
2525
|
+
copied.comments.concat(call.comments)
|
|
2526
|
+
parts << copied
|
|
2527
|
+
|
|
2528
|
+
call.copy(location: location)
|
|
2363
2529
|
when Command, CommandCall
|
|
2364
2530
|
node = call.copy(block: block, location: location)
|
|
2365
2531
|
node.comments.concat(call.comments)
|
|
2366
2532
|
node
|
|
2367
2533
|
else
|
|
2368
|
-
MethodAddBlock.new(
|
|
2369
|
-
call: call,
|
|
2370
|
-
block: block,
|
|
2371
|
-
location: call.location.to(block.location)
|
|
2372
|
-
)
|
|
2534
|
+
MethodAddBlock.new(call: call, block: block, location: location)
|
|
2373
2535
|
end
|
|
2374
2536
|
end
|
|
2375
2537
|
|
|
@@ -2446,6 +2608,7 @@ module SyntaxTree
|
|
|
2446
2608
|
start_char = find_next_statement_start(constant.location.end_char)
|
|
2447
2609
|
|
|
2448
2610
|
bodystmt.bind(
|
|
2611
|
+
self,
|
|
2449
2612
|
start_char,
|
|
2450
2613
|
start_char - line_counts[constant.location.start_line - 1].start,
|
|
2451
2614
|
ending.location.start_char,
|
|
@@ -2592,19 +2755,40 @@ module SyntaxTree
|
|
|
2592
2755
|
# have a `nil` for the value instead of a `false`.
|
|
2593
2756
|
keywords&.map! { |(key, value)| [key, value || nil] }
|
|
2594
2757
|
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2758
|
+
# Here we're going to build up a list of all of the params so that we can
|
|
2759
|
+
# determine our location information.
|
|
2760
|
+
parts = []
|
|
2761
|
+
|
|
2762
|
+
requireds&.each { |required| parts << required.location }
|
|
2763
|
+
optionals&.each do |(key, value)|
|
|
2764
|
+
parts << key.location
|
|
2765
|
+
parts << value.location if value
|
|
2766
|
+
end
|
|
2767
|
+
|
|
2768
|
+
parts << rest.location if rest
|
|
2769
|
+
posts&.each { |post| parts << post.location }
|
|
2770
|
+
|
|
2771
|
+
keywords&.each do |(key, value)|
|
|
2772
|
+
parts << key.location
|
|
2773
|
+
parts << value.location if value
|
|
2774
|
+
end
|
|
2775
|
+
|
|
2776
|
+
if keyword_rest == :nil
|
|
2777
|
+
# When we get a :nil here, it means that we have **nil syntax, which
|
|
2778
|
+
# means this set of parameters accepts no more keyword arguments. In
|
|
2779
|
+
# this case we need to go and find the location of these two tokens.
|
|
2780
|
+
operator = consume_operator(:**)
|
|
2781
|
+
parts << operator.location.to(consume_keyword(:nil).location)
|
|
2782
|
+
elsif keyword_rest
|
|
2783
|
+
parts << keyword_rest.location
|
|
2784
|
+
end
|
|
2785
|
+
|
|
2786
|
+
parts << block.location if block && block != :&
|
|
2787
|
+
parts = parts.compact
|
|
2604
2788
|
|
|
2605
2789
|
location =
|
|
2606
2790
|
if parts.any?
|
|
2607
|
-
parts[0].
|
|
2791
|
+
parts[0].to(parts[-1])
|
|
2608
2792
|
else
|
|
2609
2793
|
Location.fixed(line: lineno, char: char_pos, column: current_column)
|
|
2610
2794
|
end
|
|
@@ -2701,7 +2885,7 @@ module SyntaxTree
|
|
|
2701
2885
|
)
|
|
2702
2886
|
|
|
2703
2887
|
statements.body << @__end__ if @__end__
|
|
2704
|
-
statements.bind(0, 0, source.length, last_column)
|
|
2888
|
+
statements.bind(self, 0, 0, source.length, last_column)
|
|
2705
2889
|
|
|
2706
2890
|
program = Program.new(statements: statements, location: location)
|
|
2707
2891
|
attach_comments(program, @comments)
|
|
@@ -3033,8 +3217,9 @@ module SyntaxTree
|
|
|
3033
3217
|
exceptions = exceptions[0] if exceptions.is_a?(Array)
|
|
3034
3218
|
|
|
3035
3219
|
last_node = variable || exceptions || keyword
|
|
3036
|
-
start_char = find_next_statement_start(last_node.
|
|
3220
|
+
start_char = find_next_statement_start(last_node.end_char)
|
|
3037
3221
|
statements.bind(
|
|
3222
|
+
self,
|
|
3038
3223
|
start_char,
|
|
3039
3224
|
start_char - line_counts[last_node.location.start_line - 1].start,
|
|
3040
3225
|
char_pos,
|
|
@@ -3055,7 +3240,7 @@ module SyntaxTree
|
|
|
3055
3240
|
start_char: keyword.location.end_char + 1,
|
|
3056
3241
|
start_column: keyword.location.end_column + 1,
|
|
3057
3242
|
end_line: last_node.location.end_line,
|
|
3058
|
-
end_char: last_node.
|
|
3243
|
+
end_char: last_node.end_char,
|
|
3059
3244
|
end_column: last_node.location.end_column
|
|
3060
3245
|
)
|
|
3061
3246
|
)
|
|
@@ -3153,6 +3338,7 @@ module SyntaxTree
|
|
|
3153
3338
|
start_char = find_next_statement_start(target.location.end_char)
|
|
3154
3339
|
|
|
3155
3340
|
bodystmt.bind(
|
|
3341
|
+
self,
|
|
3156
3342
|
start_char,
|
|
3157
3343
|
start_char - line_counts[target.location.start_line - 1].start,
|
|
3158
3344
|
ending.location.start_char,
|
|
@@ -3166,9 +3352,29 @@ module SyntaxTree
|
|
|
3166
3352
|
)
|
|
3167
3353
|
end
|
|
3168
3354
|
|
|
3169
|
-
#
|
|
3170
|
-
#
|
|
3171
|
-
#
|
|
3355
|
+
# Semicolons are tokens that get added to the token list but never get
|
|
3356
|
+
# attached to the AST. Because of this they only need to track their
|
|
3357
|
+
# associated location so they can be used for computing bounds.
|
|
3358
|
+
class Semicolon
|
|
3359
|
+
attr_reader :location
|
|
3360
|
+
|
|
3361
|
+
def initialize(location)
|
|
3362
|
+
@location = location
|
|
3363
|
+
end
|
|
3364
|
+
end
|
|
3365
|
+
|
|
3366
|
+
# :call-seq:
|
|
3367
|
+
# on_semicolon: (String value) -> Semicolon
|
|
3368
|
+
def on_semicolon(value)
|
|
3369
|
+
tokens << Semicolon.new(
|
|
3370
|
+
Location.token(
|
|
3371
|
+
line: lineno,
|
|
3372
|
+
char: char_pos,
|
|
3373
|
+
column: current_column,
|
|
3374
|
+
size: value.size
|
|
3375
|
+
)
|
|
3376
|
+
)
|
|
3377
|
+
end
|
|
3172
3378
|
|
|
3173
3379
|
# def on_sp(value)
|
|
3174
3380
|
# value
|
|
@@ -3186,18 +3392,13 @@ module SyntaxTree
|
|
|
3186
3392
|
statements.location.to(statement.location)
|
|
3187
3393
|
end
|
|
3188
3394
|
|
|
3189
|
-
Statements.new(
|
|
3190
|
-
self,
|
|
3191
|
-
body: statements.body << statement,
|
|
3192
|
-
location: location
|
|
3193
|
-
)
|
|
3395
|
+
Statements.new(body: statements.body << statement, location: location)
|
|
3194
3396
|
end
|
|
3195
3397
|
|
|
3196
3398
|
# :call-seq:
|
|
3197
3399
|
# on_stmts_new: () -> Statements
|
|
3198
3400
|
def on_stmts_new
|
|
3199
3401
|
Statements.new(
|
|
3200
|
-
self,
|
|
3201
3402
|
body: [],
|
|
3202
3403
|
location:
|
|
3203
3404
|
Location.fixed(line: lineno, char: char_pos, column: current_column)
|
|
@@ -3262,6 +3463,7 @@ module SyntaxTree
|
|
|
3262
3463
|
embexpr_end = consume_token(EmbExprEnd)
|
|
3263
3464
|
|
|
3264
3465
|
statements.bind(
|
|
3466
|
+
self,
|
|
3265
3467
|
embexpr_beg.location.end_char,
|
|
3266
3468
|
embexpr_beg.location.end_column,
|
|
3267
3469
|
embexpr_end.location.start_char,
|
|
@@ -3605,8 +3807,15 @@ module SyntaxTree
|
|
|
3605
3807
|
beginning = consume_keyword(:unless)
|
|
3606
3808
|
ending = consequent || consume_keyword(:end)
|
|
3607
3809
|
|
|
3608
|
-
|
|
3810
|
+
if (keyword = find_keyword_between(:then, predicate, ending))
|
|
3811
|
+
tokens.delete(keyword)
|
|
3812
|
+
end
|
|
3813
|
+
|
|
3814
|
+
start_char =
|
|
3815
|
+
find_next_statement_start((keyword || predicate).location.end_char)
|
|
3816
|
+
|
|
3609
3817
|
statements.bind(
|
|
3818
|
+
self,
|
|
3610
3819
|
start_char,
|
|
3611
3820
|
start_char - line_counts[predicate.location.end_line - 1].start,
|
|
3612
3821
|
ending.location.start_char,
|
|
@@ -3629,7 +3838,7 @@ module SyntaxTree
|
|
|
3629
3838
|
UnlessNode.new(
|
|
3630
3839
|
predicate: predicate,
|
|
3631
3840
|
statements:
|
|
3632
|
-
Statements.new(
|
|
3841
|
+
Statements.new(body: [statement], location: statement.location),
|
|
3633
3842
|
consequent: nil,
|
|
3634
3843
|
location: statement.location.to(predicate.location)
|
|
3635
3844
|
)
|
|
@@ -3641,17 +3850,18 @@ module SyntaxTree
|
|
|
3641
3850
|
beginning = consume_keyword(:until)
|
|
3642
3851
|
ending = consume_keyword(:end)
|
|
3643
3852
|
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
tokens.delete(keyword)
|
|
3650
|
-
end
|
|
3853
|
+
delimiter =
|
|
3854
|
+
find_keyword_between(:do, predicate, statements) ||
|
|
3855
|
+
find_token_between(Semicolon, predicate, statements)
|
|
3856
|
+
|
|
3857
|
+
tokens.delete(delimiter) if delimiter
|
|
3651
3858
|
|
|
3652
3859
|
# Update the Statements location information
|
|
3653
|
-
start_char =
|
|
3860
|
+
start_char =
|
|
3861
|
+
find_next_statement_start((delimiter || predicate).location.end_char)
|
|
3862
|
+
|
|
3654
3863
|
statements.bind(
|
|
3864
|
+
self,
|
|
3655
3865
|
start_char,
|
|
3656
3866
|
start_char - line_counts[predicate.location.end_line - 1].start,
|
|
3657
3867
|
ending.location.start_char,
|
|
@@ -3673,7 +3883,7 @@ module SyntaxTree
|
|
|
3673
3883
|
UntilNode.new(
|
|
3674
3884
|
predicate: predicate,
|
|
3675
3885
|
statements:
|
|
3676
|
-
Statements.new(
|
|
3886
|
+
Statements.new(body: [statement], location: statement.location),
|
|
3677
3887
|
location: statement.location.to(predicate.location)
|
|
3678
3888
|
)
|
|
3679
3889
|
end
|
|
@@ -3744,9 +3954,11 @@ module SyntaxTree
|
|
|
3744
3954
|
statements_start = token
|
|
3745
3955
|
end
|
|
3746
3956
|
|
|
3747
|
-
start_char =
|
|
3957
|
+
start_char =
|
|
3958
|
+
find_next_statement_start((token || statements_start).location.end_char)
|
|
3748
3959
|
|
|
3749
3960
|
statements.bind(
|
|
3961
|
+
self,
|
|
3750
3962
|
start_char,
|
|
3751
3963
|
start_char -
|
|
3752
3964
|
line_counts[statements_start.location.start_line - 1].start,
|
|
@@ -3768,17 +3980,18 @@ module SyntaxTree
|
|
|
3768
3980
|
beginning = consume_keyword(:while)
|
|
3769
3981
|
ending = consume_keyword(:end)
|
|
3770
3982
|
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
tokens.delete(keyword)
|
|
3777
|
-
end
|
|
3983
|
+
delimiter =
|
|
3984
|
+
find_keyword_between(:do, predicate, statements) ||
|
|
3985
|
+
find_token_between(Semicolon, predicate, statements)
|
|
3986
|
+
|
|
3987
|
+
tokens.delete(delimiter) if delimiter
|
|
3778
3988
|
|
|
3779
3989
|
# Update the Statements location information
|
|
3780
|
-
start_char =
|
|
3990
|
+
start_char =
|
|
3991
|
+
find_next_statement_start((delimiter || predicate).location.end_char)
|
|
3992
|
+
|
|
3781
3993
|
statements.bind(
|
|
3994
|
+
self,
|
|
3782
3995
|
start_char,
|
|
3783
3996
|
start_char - line_counts[predicate.location.end_line - 1].start,
|
|
3784
3997
|
ending.location.start_char,
|
|
@@ -3800,7 +4013,7 @@ module SyntaxTree
|
|
|
3800
4013
|
WhileNode.new(
|
|
3801
4014
|
predicate: predicate,
|
|
3802
4015
|
statements:
|
|
3803
|
-
Statements.new(
|
|
4016
|
+
Statements.new(body: [statement], location: statement.location),
|
|
3804
4017
|
location: statement.location.to(predicate.location)
|
|
3805
4018
|
)
|
|
3806
4019
|
end
|