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