syntax_tree 1.1.1 → 1.2.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/main.yml +1 -1
- data/CHANGELOG.md +10 -1
- data/Gemfile.lock +5 -4
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree.rb +312 -108
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4797ffd100da9b050f154cd0ac4a235b342c395e49747ee51574eb0e0f9fcf1a
|
4
|
+
data.tar.gz: 52babf707ae1bf42abaeb51b350644fc50f61fa1b74482abb776e863d2ec48dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62e28b7e6b25e81bdfb70817c02152bd640395149c1ac75fa553154ebb636c9c913dbf2ebda7a65f9687dd1bb69d51e0e3d8f5ffba172d0b1eadb5849e64369a
|
7
|
+
data.tar.gz: 6233b01960317929ce137305a08f45d3d756d741b5f6b6d037f2499377a53922f222543f433912cc853ee31726e2306527fbe29c75ad757e6125207b7476956c
|
data/.github/workflows/main.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [1.2.0] - 2022-01-09
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- Support for Ruby 3.1 syntax, including: blocks without names, hash keys without values, endless methods without parentheses, and new argument forwarding.
|
14
|
+
- Support for pinned expressions and variables within pattern matching.
|
15
|
+
- Support endless ranges as the final argument to a `when` clause.
|
16
|
+
|
9
17
|
## [1.1.1] - 2021-12-09
|
10
18
|
|
11
19
|
### Added
|
@@ -97,7 +105,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
97
105
|
|
98
106
|
- 🎉 Initial release! 🎉
|
99
107
|
|
100
|
-
[unreleased]: https://github.com/kddnewton/syntax_tree/compare/v1.
|
108
|
+
[unreleased]: https://github.com/kddnewton/syntax_tree/compare/v1.2.0...HEAD
|
109
|
+
[1.2.0]: https://github.com/kddnewton/syntax_tree/compare/v1.1.1...v1.2.0
|
101
110
|
[1.1.1]: https://github.com/kddnewton/syntax_tree/compare/v1.1.0...v1.1.1
|
102
111
|
[1.1.0]: https://github.com/kddnewton/syntax_tree/compare/v1.0.0...v1.1.0
|
103
112
|
[1.0.0]: https://github.com/kddnewton/syntax_tree/compare/v0.1.0...v1.0.0
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
syntax_tree (1.
|
4
|
+
syntax_tree (1.2.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -9,8 +9,8 @@ GEM
|
|
9
9
|
ast (2.4.2)
|
10
10
|
benchmark-ips (2.9.2)
|
11
11
|
docile (1.4.0)
|
12
|
-
minitest (5.
|
13
|
-
parser (3.0.
|
12
|
+
minitest (5.15.0)
|
13
|
+
parser (3.1.0.0)
|
14
14
|
ast (~> 2.4.1)
|
15
15
|
rake (13.0.6)
|
16
16
|
ruby_parser (3.18.1)
|
@@ -26,6 +26,7 @@ GEM
|
|
26
26
|
|
27
27
|
PLATFORMS
|
28
28
|
x86_64-darwin-19
|
29
|
+
x86_64-darwin-21
|
29
30
|
x86_64-linux
|
30
31
|
|
31
32
|
DEPENDENCIES
|
@@ -40,4 +41,4 @@ DEPENDENCIES
|
|
40
41
|
syntax_tree!
|
41
42
|
|
42
43
|
BUNDLED WITH
|
43
|
-
2.2.
|
44
|
+
2.2.31
|
data/lib/syntax_tree/version.rb
CHANGED
data/lib/syntax_tree.rb
CHANGED
@@ -1173,7 +1173,7 @@ class SyntaxTree < Ripper
|
|
1173
1173
|
# method(&expression)
|
1174
1174
|
#
|
1175
1175
|
class ArgBlock
|
1176
|
-
# [untyped] the expression being turned into a block
|
1176
|
+
# [nil | untyped] the expression being turned into a block
|
1177
1177
|
attr_reader :value
|
1178
1178
|
|
1179
1179
|
# [Location] the location of this node
|
@@ -1194,15 +1194,17 @@ class SyntaxTree < Ripper
|
|
1194
1194
|
|
1195
1195
|
def format(q)
|
1196
1196
|
q.text("&")
|
1197
|
-
q.format(value)
|
1197
|
+
q.format(value) if value
|
1198
1198
|
end
|
1199
1199
|
|
1200
1200
|
def pretty_print(q)
|
1201
1201
|
q.group(2, "(", ")") do
|
1202
1202
|
q.text("arg_block")
|
1203
1203
|
|
1204
|
-
|
1205
|
-
|
1204
|
+
if value
|
1205
|
+
q.breakable
|
1206
|
+
q.pp(value)
|
1207
|
+
end
|
1206
1208
|
|
1207
1209
|
q.pp(Comment::List.new(comments))
|
1208
1210
|
end
|
@@ -1221,17 +1223,34 @@ class SyntaxTree < Ripper
|
|
1221
1223
|
# (false | untyped) block
|
1222
1224
|
# ) -> Args
|
1223
1225
|
def on_args_add_block(arguments, block)
|
1224
|
-
|
1226
|
+
operator = find_token(Op, "&", consume: false)
|
1225
1227
|
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1228
|
+
# If we can't find the & operator, then there's no block to add to the list,
|
1229
|
+
# so we're just going to return the arguments as-is.
|
1230
|
+
return arguments unless operator
|
1231
|
+
|
1232
|
+
# Now we know we have an & operator, so we're going to delete it from the
|
1233
|
+
# list of tokens to make sure it doesn't get confused with anything else.
|
1234
|
+
tokens.delete(operator)
|
1235
|
+
|
1236
|
+
# Construct the location that represents the block argument.
|
1237
|
+
location = operator.location
|
1238
|
+
location = operator.location.to(block.location) if block
|
1239
|
+
|
1240
|
+
# If there are any arguments and the operator we found from the list is not
|
1241
|
+
# after them, then we're going to return the arguments as-is because we're
|
1242
|
+
# looking at an & that occurs before the arguments are done.
|
1243
|
+
if arguments.parts.any? && location.start_char < arguments.location.end_char
|
1244
|
+
return arguments
|
1245
|
+
end
|
1246
|
+
|
1247
|
+
# Otherwise, we're looking at an actual block argument (with or without a
|
1248
|
+
# block, which could be missing because it could be a bare & since 3.1.0).
|
1249
|
+
arg_block = ArgBlock.new(value: block, location: location)
|
1231
1250
|
|
1232
1251
|
Args.new(
|
1233
1252
|
parts: arguments.parts << arg_block,
|
1234
|
-
location: arguments.location.to(
|
1253
|
+
location: arguments.location.to(location)
|
1235
1254
|
)
|
1236
1255
|
end
|
1237
1256
|
|
@@ -1676,10 +1695,13 @@ class SyntaxTree < Ripper
|
|
1676
1695
|
parts += posts
|
1677
1696
|
|
1678
1697
|
if constant
|
1679
|
-
q.
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1698
|
+
q.group do
|
1699
|
+
q.format(constant)
|
1700
|
+
q.text("[")
|
1701
|
+
q.seplist(parts) { |part| q.format(part) }
|
1702
|
+
q.text("]")
|
1703
|
+
end
|
1704
|
+
|
1683
1705
|
return
|
1684
1706
|
end
|
1685
1707
|
|
@@ -1689,7 +1711,7 @@ class SyntaxTree < Ripper
|
|
1689
1711
|
q.seplist(parts) { |part| q.format(part) }
|
1690
1712
|
q.text("]")
|
1691
1713
|
else
|
1692
|
-
q.seplist(parts) { |part| q.format(part) }
|
1714
|
+
q.group { q.seplist(parts) { |part| q.format(part) } }
|
1693
1715
|
end
|
1694
1716
|
end
|
1695
1717
|
|
@@ -1896,7 +1918,7 @@ class SyntaxTree < Ripper
|
|
1896
1918
|
end
|
1897
1919
|
|
1898
1920
|
def format(q)
|
1899
|
-
if value
|
1921
|
+
if value&.is_a?(HashLiteral)
|
1900
1922
|
format_contents(q)
|
1901
1923
|
else
|
1902
1924
|
q.group { format_contents(q) }
|
@@ -1910,8 +1932,10 @@ class SyntaxTree < Ripper
|
|
1910
1932
|
q.breakable
|
1911
1933
|
q.pp(key)
|
1912
1934
|
|
1913
|
-
|
1914
|
-
|
1935
|
+
if value
|
1936
|
+
q.breakable
|
1937
|
+
q.pp(value)
|
1938
|
+
end
|
1915
1939
|
|
1916
1940
|
q.pp(Comment::List.new(comments))
|
1917
1941
|
end
|
@@ -1931,6 +1955,7 @@ class SyntaxTree < Ripper
|
|
1931
1955
|
|
1932
1956
|
def format_contents(q)
|
1933
1957
|
q.parent.format_key(q, key)
|
1958
|
+
return unless value
|
1934
1959
|
|
1935
1960
|
if key.comments.empty? && AssignFormatting.skip_indent?(value)
|
1936
1961
|
q.text(" ")
|
@@ -1947,7 +1972,10 @@ class SyntaxTree < Ripper
|
|
1947
1972
|
# :call-seq:
|
1948
1973
|
# on_assoc_new: (untyped key, untyped value) -> Assoc
|
1949
1974
|
def on_assoc_new(key, value)
|
1950
|
-
|
1975
|
+
location = key.location
|
1976
|
+
location = location.to(value.location) if value
|
1977
|
+
|
1978
|
+
Assoc.new(key: key, value: value, location: location)
|
1951
1979
|
end
|
1952
1980
|
|
1953
1981
|
# AssocSplat represents double-splatting a value into a hash (either a hash
|
@@ -2315,24 +2343,95 @@ class SyntaxTree < Ripper
|
|
2315
2343
|
end
|
2316
2344
|
end
|
2317
2345
|
|
2346
|
+
# PinnedBegin represents a pinning a nested statement within pattern matching.
|
2347
|
+
#
|
2348
|
+
# case value
|
2349
|
+
# in ^(statement)
|
2350
|
+
# end
|
2351
|
+
#
|
2352
|
+
class PinnedBegin
|
2353
|
+
# [untyped] the expression being pinned
|
2354
|
+
attr_reader :statement
|
2355
|
+
|
2356
|
+
# [Location] the location of this node
|
2357
|
+
attr_reader :location
|
2358
|
+
|
2359
|
+
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
2360
|
+
attr_reader :comments
|
2361
|
+
|
2362
|
+
def initialize(statement:, location:, comments: [])
|
2363
|
+
@statement = statement
|
2364
|
+
@location = location
|
2365
|
+
@comments = comments
|
2366
|
+
end
|
2367
|
+
|
2368
|
+
def child_nodes
|
2369
|
+
[statement]
|
2370
|
+
end
|
2371
|
+
|
2372
|
+
def format(q)
|
2373
|
+
q.group do
|
2374
|
+
q.text("^(")
|
2375
|
+
q.nest(1) do
|
2376
|
+
q.indent do
|
2377
|
+
q.breakable("")
|
2378
|
+
q.format(statement)
|
2379
|
+
end
|
2380
|
+
q.breakable("")
|
2381
|
+
q.text(")")
|
2382
|
+
end
|
2383
|
+
end
|
2384
|
+
end
|
2385
|
+
|
2386
|
+
def pretty_print(q)
|
2387
|
+
q.group(2, "(", ")") do
|
2388
|
+
q.text("pinned_begin")
|
2389
|
+
|
2390
|
+
q.breakable
|
2391
|
+
q.pp(statement)
|
2392
|
+
|
2393
|
+
q.pp(Comment::List.new(comments))
|
2394
|
+
end
|
2395
|
+
end
|
2396
|
+
|
2397
|
+
def to_json(*opts)
|
2398
|
+
{
|
2399
|
+
type: :pinned_begin,
|
2400
|
+
stmt: statement,
|
2401
|
+
loc: location,
|
2402
|
+
cmts: comments
|
2403
|
+
}.to_json(*opts)
|
2404
|
+
end
|
2405
|
+
end
|
2406
|
+
|
2318
2407
|
# :call-seq:
|
2319
|
-
# on_begin: (
|
2408
|
+
# on_begin: (untyped bodystmt) -> Begin | PinnedBegin
|
2320
2409
|
def on_begin(bodystmt)
|
2321
|
-
|
2322
|
-
end_char =
|
2323
|
-
if bodystmt.rescue_clause || bodystmt.ensure_clause ||
|
2324
|
-
bodystmt.else_clause
|
2325
|
-
bodystmt.location.end_char
|
2326
|
-
else
|
2327
|
-
find_token(Kw, "end").location.end_char
|
2328
|
-
end
|
2410
|
+
pin = find_token(Op, "^", consume: false)
|
2329
2411
|
|
2330
|
-
bodystmt.
|
2412
|
+
if pin && pin.location.start_char < bodystmt.location.start_char
|
2413
|
+
tokens.delete(pin)
|
2414
|
+
find_token(LParen)
|
2331
2415
|
|
2332
|
-
|
2333
|
-
|
2334
|
-
|
2335
|
-
|
2416
|
+
rparen = find_token(RParen)
|
2417
|
+
location = pin.location.to(rparen.location)
|
2418
|
+
|
2419
|
+
PinnedBegin.new(statement: bodystmt, location: location)
|
2420
|
+
else
|
2421
|
+
keyword = find_token(Kw, "begin")
|
2422
|
+
end_char =
|
2423
|
+
if bodystmt.rescue_clause || bodystmt.ensure_clause ||
|
2424
|
+
bodystmt.else_clause
|
2425
|
+
bodystmt.location.end_char
|
2426
|
+
else
|
2427
|
+
find_token(Kw, "end").location.end_char
|
2428
|
+
end
|
2429
|
+
|
2430
|
+
bodystmt.bind(keyword.location.end_char, end_char)
|
2431
|
+
location = keyword.location.to(bodystmt.location)
|
2432
|
+
|
2433
|
+
Begin.new(bodystmt: bodystmt, location: location)
|
2434
|
+
end
|
2336
2435
|
end
|
2337
2436
|
|
2338
2437
|
# Binary represents any expression that involves two sub-expressions with an
|
@@ -2423,12 +2522,29 @@ class SyntaxTree < Ripper
|
|
2423
2522
|
# :call-seq:
|
2424
2523
|
# on_binary: (untyped left, (Op | Symbol) operator, untyped right) -> Binary
|
2425
2524
|
def on_binary(left, operator, right)
|
2426
|
-
|
2427
|
-
|
2428
|
-
|
2429
|
-
|
2430
|
-
|
2431
|
-
|
2525
|
+
if operator.is_a?(Symbol)
|
2526
|
+
# Here, we're going to search backward for the token that's between the
|
2527
|
+
# two operands that matches the operator so we can delete it from the
|
2528
|
+
# list.
|
2529
|
+
index =
|
2530
|
+
tokens.rindex do |token|
|
2531
|
+
location = token.location
|
2532
|
+
|
2533
|
+
token.is_a?(Op) &&
|
2534
|
+
token.value == operator.to_s &&
|
2535
|
+
location.start_char > left.location.end_char &&
|
2536
|
+
location.end_char < right.location.start_char
|
2537
|
+
end
|
2538
|
+
|
2539
|
+
tokens.delete_at(index) if index
|
2540
|
+
else
|
2541
|
+
# On most Ruby implementations, operator is a Symbol that represents that
|
2542
|
+
# operation being performed. For instance in the example `1 < 2`, the
|
2543
|
+
# `operator` object would be `:<`. However, on JRuby, it's an `@op` node,
|
2544
|
+
# so here we're going to explicitly convert it into the same normalized
|
2545
|
+
# form.
|
2546
|
+
operator = tokens.delete(operator).value
|
2547
|
+
end
|
2432
2548
|
|
2433
2549
|
Binary.new(
|
2434
2550
|
left: left,
|
@@ -2578,7 +2694,7 @@ class SyntaxTree < Ripper
|
|
2578
2694
|
# def method(&block); end
|
2579
2695
|
#
|
2580
2696
|
class BlockArg
|
2581
|
-
# [Ident] the name of the block argument
|
2697
|
+
# [nil | Ident] the name of the block argument
|
2582
2698
|
attr_reader :name
|
2583
2699
|
|
2584
2700
|
# [Location] the location of this node
|
@@ -2599,15 +2715,17 @@ class SyntaxTree < Ripper
|
|
2599
2715
|
|
2600
2716
|
def format(q)
|
2601
2717
|
q.text("&")
|
2602
|
-
q.format(name)
|
2718
|
+
q.format(name) if name
|
2603
2719
|
end
|
2604
2720
|
|
2605
2721
|
def pretty_print(q)
|
2606
2722
|
q.group(2, "(", ")") do
|
2607
2723
|
q.text("blockarg")
|
2608
2724
|
|
2609
|
-
|
2610
|
-
|
2725
|
+
if name
|
2726
|
+
q.breakable
|
2727
|
+
q.pp(name)
|
2728
|
+
end
|
2611
2729
|
|
2612
2730
|
q.pp(Comment::List.new(comments))
|
2613
2731
|
end
|
@@ -2625,7 +2743,10 @@ class SyntaxTree < Ripper
|
|
2625
2743
|
def on_blockarg(name)
|
2626
2744
|
operator = find_token(Op, "&")
|
2627
2745
|
|
2628
|
-
|
2746
|
+
location = operator.location
|
2747
|
+
location = location.to(name.location) if name
|
2748
|
+
|
2749
|
+
BlockArg.new(name: name, location: location)
|
2629
2750
|
end
|
2630
2751
|
|
2631
2752
|
# bodystmt can't actually determine its bounds appropriately because it
|
@@ -3968,6 +4089,10 @@ class SyntaxTree < Ripper
|
|
3968
4089
|
[]
|
3969
4090
|
end
|
3970
4091
|
|
4092
|
+
def child_nodes
|
4093
|
+
[]
|
4094
|
+
end
|
4095
|
+
|
3971
4096
|
def format(q)
|
3972
4097
|
q.text(value)
|
3973
4098
|
end
|
@@ -4419,7 +4544,7 @@ class SyntaxTree < Ripper
|
|
4419
4544
|
# [Backtick | Const | Ident | Kw | Op] the name of the method
|
4420
4545
|
attr_reader :name
|
4421
4546
|
|
4422
|
-
# [nil | Paren] the parameter declaration for the method
|
4547
|
+
# [nil | Params | Paren] the parameter declaration for the method
|
4423
4548
|
attr_reader :paren
|
4424
4549
|
|
4425
4550
|
# [untyped] the expression to be executed by the method
|
@@ -4463,7 +4588,12 @@ class SyntaxTree < Ripper
|
|
4463
4588
|
end
|
4464
4589
|
|
4465
4590
|
q.format(name)
|
4466
|
-
|
4591
|
+
|
4592
|
+
if paren
|
4593
|
+
params = paren
|
4594
|
+
params = params.contents if params.is_a?(Paren)
|
4595
|
+
q.format(paren) unless params.empty?
|
4596
|
+
end
|
4467
4597
|
|
4468
4598
|
q.text(" =")
|
4469
4599
|
q.group do
|
@@ -4529,21 +4659,6 @@ class SyntaxTree < Ripper
|
|
4529
4659
|
# and normal method definitions.
|
4530
4660
|
beginning = find_token(Kw, "def")
|
4531
4661
|
|
4532
|
-
# If we don't have a bodystmt node, then we have a single-line method
|
4533
|
-
unless bodystmt.is_a?(BodyStmt)
|
4534
|
-
node =
|
4535
|
-
DefEndless.new(
|
4536
|
-
target: nil,
|
4537
|
-
operator: nil,
|
4538
|
-
name: name,
|
4539
|
-
paren: params,
|
4540
|
-
statement: bodystmt,
|
4541
|
-
location: beginning.location.to(bodystmt.location)
|
4542
|
-
)
|
4543
|
-
|
4544
|
-
return node
|
4545
|
-
end
|
4546
|
-
|
4547
4662
|
# If there aren't any params then we need to correct the params node
|
4548
4663
|
# location information
|
4549
4664
|
if params.is_a?(Params) && params.empty?
|
@@ -4559,18 +4674,35 @@ class SyntaxTree < Ripper
|
|
4559
4674
|
params = Params.new(location: location)
|
4560
4675
|
end
|
4561
4676
|
|
4562
|
-
ending = find_token(Kw, "end")
|
4563
|
-
bodystmt.bind(
|
4564
|
-
find_next_statement_start(params.location.end_char),
|
4565
|
-
ending.location.start_char
|
4566
|
-
)
|
4677
|
+
ending = find_token(Kw, "end", consume: false)
|
4567
4678
|
|
4568
|
-
|
4569
|
-
|
4570
|
-
|
4571
|
-
|
4572
|
-
|
4573
|
-
|
4679
|
+
if ending
|
4680
|
+
tokens.delete(ending)
|
4681
|
+
bodystmt.bind(
|
4682
|
+
find_next_statement_start(params.location.end_char),
|
4683
|
+
ending.location.start_char
|
4684
|
+
)
|
4685
|
+
|
4686
|
+
Def.new(
|
4687
|
+
name: name,
|
4688
|
+
params: params,
|
4689
|
+
bodystmt: bodystmt,
|
4690
|
+
location: beginning.location.to(ending.location)
|
4691
|
+
)
|
4692
|
+
else
|
4693
|
+
# In Ruby >= 3.1.0, this is a BodyStmt that wraps a single statement in
|
4694
|
+
# the statements list. Before, it was just the individual statement.
|
4695
|
+
statement = bodystmt.is_a?(BodyStmt) ? bodystmt.statements : bodystmt
|
4696
|
+
|
4697
|
+
DefEndless.new(
|
4698
|
+
target: nil,
|
4699
|
+
operator: nil,
|
4700
|
+
name: name,
|
4701
|
+
paren: params,
|
4702
|
+
statement: statement,
|
4703
|
+
location: beginning.location.to(bodystmt.location)
|
4704
|
+
)
|
4705
|
+
end
|
4574
4706
|
end
|
4575
4707
|
|
4576
4708
|
# Defined represents the use of the +defined?+ operator. It can be used with
|
@@ -4778,37 +4910,37 @@ class SyntaxTree < Ripper
|
|
4778
4910
|
end
|
4779
4911
|
|
4780
4912
|
beginning = find_token(Kw, "def")
|
4913
|
+
ending = find_token(Kw, "end", consume: false)
|
4781
4914
|
|
4782
|
-
|
4783
|
-
|
4784
|
-
|
4785
|
-
|
4786
|
-
|
4787
|
-
|
4788
|
-
name: name,
|
4789
|
-
paren: params,
|
4790
|
-
statement: bodystmt,
|
4791
|
-
location: beginning.location.to(bodystmt.location)
|
4792
|
-
)
|
4793
|
-
|
4794
|
-
return node
|
4795
|
-
end
|
4796
|
-
|
4797
|
-
ending = find_token(Kw, "end")
|
4915
|
+
if ending
|
4916
|
+
tokens.delete(ending)
|
4917
|
+
bodystmt.bind(
|
4918
|
+
find_next_statement_start(params.location.end_char),
|
4919
|
+
ending.location.start_char
|
4920
|
+
)
|
4798
4921
|
|
4799
|
-
|
4800
|
-
|
4801
|
-
|
4802
|
-
|
4922
|
+
Defs.new(
|
4923
|
+
target: target,
|
4924
|
+
operator: operator,
|
4925
|
+
name: name,
|
4926
|
+
params: params,
|
4927
|
+
bodystmt: bodystmt,
|
4928
|
+
location: beginning.location.to(ending.location)
|
4929
|
+
)
|
4930
|
+
else
|
4931
|
+
# In Ruby >= 3.1.0, this is a BodyStmt that wraps a single statement in
|
4932
|
+
# the statements list. Before, it was just the individual statement.
|
4933
|
+
statement = bodystmt.is_a?(BodyStmt) ? bodystmt.statements : bodystmt
|
4803
4934
|
|
4804
|
-
|
4805
|
-
|
4806
|
-
|
4807
|
-
|
4808
|
-
|
4809
|
-
|
4810
|
-
|
4811
|
-
|
4935
|
+
DefEndless.new(
|
4936
|
+
target: target,
|
4937
|
+
operator: operator,
|
4938
|
+
name: name,
|
4939
|
+
paren: params,
|
4940
|
+
statement: statement,
|
4941
|
+
location: beginning.location.to(bodystmt.location)
|
4942
|
+
)
|
4943
|
+
end
|
4812
4944
|
end
|
4813
4945
|
|
4814
4946
|
# DoBlock represents passing a block to a method call using the +do+ and +end+
|
@@ -8927,7 +9059,7 @@ class SyntaxTree < Ripper
|
|
8927
9059
|
end
|
8928
9060
|
|
8929
9061
|
class KeywordRestFormatter
|
8930
|
-
# [:nil | KwRestParam] the value of the parameter
|
9062
|
+
# [:nil | ArgsForward | KwRestParam] the value of the parameter
|
8931
9063
|
attr_reader :value
|
8932
9064
|
|
8933
9065
|
def initialize(value)
|
@@ -9042,7 +9174,7 @@ class SyntaxTree < Ripper
|
|
9042
9174
|
q.format(rest) if rest && rest.is_a?(ExcessedComma)
|
9043
9175
|
end
|
9044
9176
|
|
9045
|
-
if [Def, Defs].include?(q.parent.class)
|
9177
|
+
if [Def, Defs, DefEndless].include?(q.parent.class)
|
9046
9178
|
q.group(0, "(", ")") do
|
9047
9179
|
q.indent do
|
9048
9180
|
q.breakable("")
|
@@ -9142,8 +9274,8 @@ class SyntaxTree < Ripper
|
|
9142
9274
|
# (nil | ArgsForward | ExcessedComma | RestParam) rest,
|
9143
9275
|
# (nil | Array[Ident]) posts,
|
9144
9276
|
# (nil | Array[[Ident, nil | untyped]]) keywords,
|
9145
|
-
# (nil | :nil | KwRestParam) keyword_rest,
|
9146
|
-
# (nil | BlockArg) block
|
9277
|
+
# (nil | :nil | ArgsForward | KwRestParam) keyword_rest,
|
9278
|
+
# (nil | :& | BlockArg) block
|
9147
9279
|
# ) -> Params
|
9148
9280
|
def on_params(
|
9149
9281
|
requireds,
|
@@ -9161,7 +9293,7 @@ class SyntaxTree < Ripper
|
|
9161
9293
|
*posts,
|
9162
9294
|
*keywords&.flat_map { |(key, value)| [key, value || nil] },
|
9163
9295
|
(keyword_rest if keyword_rest != :nil),
|
9164
|
-
block
|
9296
|
+
(block if block != :&)
|
9165
9297
|
].compact
|
9166
9298
|
|
9167
9299
|
location =
|
@@ -9178,7 +9310,7 @@ class SyntaxTree < Ripper
|
|
9178
9310
|
posts: posts || [],
|
9179
9311
|
keywords: keywords || [],
|
9180
9312
|
keyword_rest: keyword_rest,
|
9181
|
-
block: block,
|
9313
|
+
block: (block if block != :&),
|
9182
9314
|
location: location
|
9183
9315
|
)
|
9184
9316
|
end
|
@@ -12901,10 +13033,70 @@ class SyntaxTree < Ripper
|
|
12901
13033
|
end
|
12902
13034
|
end
|
12903
13035
|
|
13036
|
+
# PinnedVarRef represents a pinned variable reference within a pattern
|
13037
|
+
# matching pattern.
|
13038
|
+
#
|
13039
|
+
# case value
|
13040
|
+
# in ^variable
|
13041
|
+
# end
|
13042
|
+
#
|
13043
|
+
# This can be a plain local variable like the example above. It can also be a
|
13044
|
+
# a class variable, a global variable, or an instance variable.
|
13045
|
+
class PinnedVarRef
|
13046
|
+
# [VarRef] the value of this node
|
13047
|
+
attr_reader :value
|
13048
|
+
|
13049
|
+
# [Location] the location of this node
|
13050
|
+
attr_reader :location
|
13051
|
+
|
13052
|
+
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
13053
|
+
attr_reader :comments
|
13054
|
+
|
13055
|
+
def initialize(value:, location:, comments: [])
|
13056
|
+
@value = value
|
13057
|
+
@location = location
|
13058
|
+
@comments = comments
|
13059
|
+
end
|
13060
|
+
|
13061
|
+
def child_nodes
|
13062
|
+
[value]
|
13063
|
+
end
|
13064
|
+
|
13065
|
+
def format(q)
|
13066
|
+
q.group do
|
13067
|
+
q.text("^")
|
13068
|
+
q.format(value)
|
13069
|
+
end
|
13070
|
+
end
|
13071
|
+
|
13072
|
+
def pretty_print(q)
|
13073
|
+
q.group(2, "(", ")") do
|
13074
|
+
q.text("pinned_var_ref")
|
13075
|
+
|
13076
|
+
q.breakable
|
13077
|
+
q.pp(value)
|
13078
|
+
|
13079
|
+
q.pp(Comment::List.new(comments))
|
13080
|
+
end
|
13081
|
+
end
|
13082
|
+
|
13083
|
+
def to_json(*opts)
|
13084
|
+
{ type: :pinned_var_ref, value: value, loc: location, cmts: comments }
|
13085
|
+
.to_json(*opts)
|
13086
|
+
end
|
13087
|
+
end
|
13088
|
+
|
12904
13089
|
# :call-seq:
|
12905
13090
|
# on_var_ref: ((Const | CVar | GVar | Ident | IVar | Kw) value) -> VarRef
|
12906
13091
|
def on_var_ref(value)
|
12907
|
-
|
13092
|
+
pin = find_token(Op, "^", consume: false)
|
13093
|
+
|
13094
|
+
if pin && pin.location.start_char == value.location.start_char - 1
|
13095
|
+
tokens.delete(pin)
|
13096
|
+
PinnedVarRef.new(value: value, location: pin.location.to(value.location))
|
13097
|
+
else
|
13098
|
+
VarRef.new(value: value, location: value.location)
|
13099
|
+
end
|
12908
13100
|
end
|
12909
13101
|
|
12910
13102
|
# VCall represent any plain named object with Ruby that could be either a
|
@@ -12976,6 +13168,10 @@ class SyntaxTree < Ripper
|
|
12976
13168
|
@comments = comments
|
12977
13169
|
end
|
12978
13170
|
|
13171
|
+
def child_nodes
|
13172
|
+
[]
|
13173
|
+
end
|
13174
|
+
|
12979
13175
|
def format(q)
|
12980
13176
|
end
|
12981
13177
|
|
@@ -13050,6 +13246,14 @@ class SyntaxTree < Ripper
|
|
13050
13246
|
separator = -> { q.group { q.comma_breakable } }
|
13051
13247
|
q.seplist(arguments.parts, separator) { |part| q.format(part) }
|
13052
13248
|
end
|
13249
|
+
|
13250
|
+
# Very special case here. If you're inside of a when clause and the
|
13251
|
+
# last argument to the predicate is and endless range, then you are
|
13252
|
+
# forced to use the "then" keyword to make it parse properly.
|
13253
|
+
last = arguments.parts.last
|
13254
|
+
if (last.is_a?(Dot2) || last.is_a?(Dot3)) && !last.right
|
13255
|
+
q.text(" then")
|
13256
|
+
end
|
13053
13257
|
end
|
13054
13258
|
end
|
13055
13259
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: syntax_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Newton
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -114,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
114
|
- !ruby/object:Gem::Version
|
115
115
|
version: '0'
|
116
116
|
requirements: []
|
117
|
-
rubygems_version: 3.
|
117
|
+
rubygems_version: 3.3.0.dev
|
118
118
|
signing_key:
|
119
119
|
specification_version: 4
|
120
120
|
summary: A parser based on ripper
|