parser 2.7.0.2 → 2.7.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +14 -23
- data/CHANGELOG.md +33 -1
- data/README.md +6 -5
- data/doc/AST_FORMAT.md +41 -3
- data/lib/parser/ast/processor.rb +4 -0
- data/lib/parser/builders/default.rb +17 -7
- data/lib/parser/current.rb +4 -4
- data/lib/parser/lexer.rl +1 -1
- data/lib/parser/messages.rb +1 -0
- data/lib/parser/meta.rb +1 -0
- data/lib/parser/ruby27.y +25 -7
- data/lib/parser/source/range.rb +9 -0
- data/lib/parser/source/tree_rewriter.rb +42 -2
- data/lib/parser/source/tree_rewriter/action.rb +24 -8
- data/lib/parser/version.rb +1 -1
- data/parser.gemspec +7 -0
- data/test/test_parser.rb +133 -12
- data/test/test_source_comment_associator.rb +20 -20
- data/test/test_source_range.rb +15 -0
- data/test/test_source_tree_rewriter.rb +83 -3
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb7c9797808fcd8920ef34ccce6dae7f416d0fe3084a863fa3fec7fa30b086fc
|
4
|
+
data.tar.gz: 828a937688a35e277c7d435197ca2d023458de366a8e94fbaa67bdd6d3a04145
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 679bc103f3d8b2663a1ec2de6c3acac41c90c1d738b40bf4b5ed945b8e3f51e22359caa0789e55009f3bd2c45ac832d00182db9ba9be1f43b18e6fe0e0508278
|
7
|
+
data.tar.gz: b4f4d29766e23ca406644fb578e2134f28a9983d765d4aa45117dcb5a9ee012f27c2b8b65eccf11b7e3818b1543a62cc0483aa510a51352bc2260ad0fd650acd
|
data/.travis.yml
CHANGED
@@ -2,26 +2,17 @@ dist: trusty
|
|
2
2
|
language: ruby
|
3
3
|
matrix:
|
4
4
|
include:
|
5
|
-
- name: 2.
|
6
|
-
rvm: 2.
|
5
|
+
- name: 2.4.10 / Parser tests
|
6
|
+
rvm: 2.4.10
|
7
7
|
script: bundle exec rake test_cov
|
8
|
-
- name: 2.
|
9
|
-
rvm: 2.
|
8
|
+
- name: 2.5.8 / Parser tests
|
9
|
+
rvm: 2.5.8
|
10
10
|
script: bundle exec rake test_cov
|
11
|
-
- name: 2.
|
12
|
-
rvm: 2.
|
11
|
+
- name: 2.6.6 / Parser tests
|
12
|
+
rvm: 2.6.6
|
13
13
|
script: bundle exec rake test_cov
|
14
|
-
- name: 2.
|
15
|
-
rvm: 2.
|
16
|
-
script: bundle exec rake test_cov
|
17
|
-
- name: 2.5.7 / Parser tests
|
18
|
-
rvm: 2.5.7
|
19
|
-
script: bundle exec rake test_cov
|
20
|
-
- name: 2.6.5 / Parser tests
|
21
|
-
rvm: 2.6.5
|
22
|
-
script: bundle exec rake test_cov
|
23
|
-
- name: 2.7.0 / Parser tests
|
24
|
-
rvm: 2.7.0
|
14
|
+
- name: 2.7.1 / Parser tests
|
15
|
+
rvm: 2.7.1
|
25
16
|
script: bundle exec rake test_cov
|
26
17
|
- name: ruby-head / Parser tests
|
27
18
|
rvm: ruby-head
|
@@ -32,14 +23,14 @@ matrix:
|
|
32
23
|
- name: rbx-2 / Parser tests
|
33
24
|
rvm: rbx-2
|
34
25
|
script: bundle exec rake test_cov
|
35
|
-
- name: 2.5.
|
36
|
-
rvm: 2.5.
|
26
|
+
- name: 2.5.8 / Rubocop tests
|
27
|
+
rvm: 2.5.8
|
37
28
|
script: ./ci/run_rubocop_specs
|
38
|
-
- name: 2.6.
|
39
|
-
rvm: 2.6.
|
29
|
+
- name: 2.6.6 / Rubocop tests
|
30
|
+
rvm: 2.6.6
|
40
31
|
script: ./ci/run_rubocop_specs
|
41
|
-
- name: 2.7.
|
42
|
-
rvm: 2.7.
|
32
|
+
- name: 2.7.1 / Rubocop tests
|
33
|
+
rvm: 2.7.1
|
43
34
|
script: ./ci/run_rubocop_specs
|
44
35
|
allow_failures:
|
45
36
|
- rvm: ruby-head
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,41 @@
|
|
1
1
|
Changelog
|
2
2
|
=========
|
3
3
|
|
4
|
-
Not released (2020-
|
4
|
+
Not released (2020-04-15)
|
5
5
|
-------------------------
|
6
6
|
|
7
|
+
Features implemented:
|
8
|
+
* Add Source::Range#eql? and hash (#675) (Marc-André Lafortune)
|
9
|
+
* Source::TreeRewriter: Add #merge, #merge! and #empty? (#674) (Marc-André Lafortune)
|
10
|
+
|
11
|
+
v2.7.1.0 (2020-04-03)
|
12
|
+
---------------------
|
13
|
+
|
14
|
+
API modifications:
|
15
|
+
* Bump ruby versions to 2.4.10, 2.5.8, 2.6.6, 2.7.1. (#665) (Ilya Bylich)
|
16
|
+
|
17
|
+
Features implemented:
|
18
|
+
* ruby27.y: allow newlines inside braced pattern. (#664) (Ilya Bylich)
|
19
|
+
* ruby27.y: Allow trailing comma in hash pattern (#661) (Koichi ITO)
|
20
|
+
|
21
|
+
v2.7.0.5 (2020-03-20)
|
22
|
+
---------------------
|
23
|
+
|
24
|
+
Features implemented:
|
25
|
+
* ruby27.y: fix array pattern with tail source map (#659) (Vladimir Dementyev)
|
26
|
+
|
27
|
+
Bugs fixed:
|
28
|
+
* builder.rb: fix constant_pattern source map (#660) (Vladimir Dementyev)
|
29
|
+
|
30
|
+
v2.7.0.4 (2020-03-02)
|
31
|
+
---------------------
|
32
|
+
|
33
|
+
Bugs fixed:
|
34
|
+
* lexer.rl: allow spaces before comments-before-leading-dot. (#654) (Ilya Bylich)
|
35
|
+
|
36
|
+
v2.7.0.2 (2020-01-08)
|
37
|
+
---------------------
|
38
|
+
|
7
39
|
Bugs fixed:
|
8
40
|
* lexer.rl: fix paren_nest for curly braces (#646) (Ilya Bylich)
|
9
41
|
|
data/README.md
CHANGED
@@ -24,10 +24,11 @@ below for explanation of `emit_*` calls):
|
|
24
24
|
|
25
25
|
require 'parser/current'
|
26
26
|
# opt-in to most recent AST format:
|
27
|
-
Parser::Builders::Default.emit_lambda
|
28
|
-
Parser::Builders::Default.emit_procarg0
|
29
|
-
Parser::Builders::Default.emit_encoding
|
30
|
-
Parser::Builders::Default.emit_index
|
27
|
+
Parser::Builders::Default.emit_lambda = true
|
28
|
+
Parser::Builders::Default.emit_procarg0 = true
|
29
|
+
Parser::Builders::Default.emit_encoding = true
|
30
|
+
Parser::Builders::Default.emit_index = true
|
31
|
+
Parser::Builders::Default.emit_arg_inside_procarg0 = true
|
31
32
|
|
32
33
|
Parse a chunk of code:
|
33
34
|
|
@@ -234,7 +235,7 @@ Parser implements the MacRuby 0.12 and RubyMotion mid-2015 parsers precisely. Ho
|
|
234
235
|
|
235
236
|
## Known issues
|
236
237
|
|
237
|
-
Adding support for the following Ruby MRI features in Parser would needlessly complicate it, and as they all are very specific and rarely
|
238
|
+
Adding support for the following Ruby MRI features in Parser would needlessly complicate it, and as they all are very specific and rarely occurring corner cases, this is not done.
|
238
239
|
|
239
240
|
Parser has been extensively tested; in particular, it parses almost entire [Rubygems][rg] corpus. For every issue, a breakdown of affected gems is offered.
|
240
241
|
|
data/doc/AST_FORMAT.md
CHANGED
@@ -1870,6 +1870,27 @@ Format:
|
|
1870
1870
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ expression
|
1871
1871
|
~~~
|
1872
1872
|
|
1873
|
+
#### With empty else
|
1874
|
+
|
1875
|
+
Empty `else` differs from the missing (or _implicit_) `else` for pattern matching, since
|
1876
|
+
the latter one raises a `NoMatchingPattern` exception. Thus, we need a way to distinguish this
|
1877
|
+
two cases in the resulting AST.
|
1878
|
+
|
1879
|
+
Format:
|
1880
|
+
|
1881
|
+
~~~
|
1882
|
+
(case-match,
|
1883
|
+
(str "str")
|
1884
|
+
(in-pattern
|
1885
|
+
(match-var :foo)
|
1886
|
+
(lvar :bar))
|
1887
|
+
(empty-else))
|
1888
|
+
"case "str"; in foo; bar; else; end"
|
1889
|
+
~~~~ keyword ~~~~ else
|
1890
|
+
~~~ end
|
1891
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ expression
|
1892
|
+
~~~
|
1893
|
+
|
1873
1894
|
### In clause
|
1874
1895
|
|
1875
1896
|
Format:
|
@@ -2047,7 +2068,7 @@ so a single item match with comma gets interpreted as an array.
|
|
2047
2068
|
(array-pattern-with-tail
|
2048
2069
|
(match-var :foo))
|
2049
2070
|
"in foo,"
|
2050
|
-
|
2071
|
+
~~~~ expression
|
2051
2072
|
~~~
|
2052
2073
|
|
2053
2074
|
### Matching using hash pattern
|
@@ -2118,7 +2139,7 @@ Format:
|
|
2118
2139
|
"in X[^foo bar]"
|
2119
2140
|
~ begin (const-pattern)
|
2120
2141
|
~ end (const-pattern)
|
2121
|
-
|
2142
|
+
~~~~~~~~~~~~ expression (const-pattern)
|
2122
2143
|
~ name (const-pattern.const)
|
2123
2144
|
~ expression (const-pattern.const)
|
2124
2145
|
~~~
|
@@ -2136,7 +2157,24 @@ Format:
|
|
2136
2157
|
"in X[foo:, bar:]"
|
2137
2158
|
~ begin (const-pattern)
|
2138
2159
|
~ end (const-pattern)
|
2139
|
-
|
2160
|
+
~~~~~~~~~~~~~ expression (const-pattern)
|
2161
|
+
~ name (const-pattern.const)
|
2162
|
+
~ expression (const-pattern.const)
|
2163
|
+
~~~
|
2164
|
+
|
2165
|
+
#### With array pattern without elements
|
2166
|
+
|
2167
|
+
Format:
|
2168
|
+
|
2169
|
+
~~~
|
2170
|
+
(const-pattern
|
2171
|
+
(const nil :X)
|
2172
|
+
(array-pattern))
|
2173
|
+
"in X[]"
|
2174
|
+
~ begin (const-pattern)
|
2175
|
+
~ end (const-pattern)
|
2176
|
+
~~~ expression (const-pattern)
|
2140
2177
|
~ name (const-pattern.const)
|
2141
2178
|
~ expression (const-pattern.const)
|
2179
|
+
~~ expression (const-pattern.array_pattern)
|
2142
2180
|
~~~
|
data/lib/parser/ast/processor.rb
CHANGED
@@ -1213,6 +1213,7 @@ module Parser
|
|
1213
1213
|
#
|
1214
1214
|
|
1215
1215
|
def case_match(case_t, expr, in_bodies, else_t, else_body, end_t)
|
1216
|
+
else_body = n(:empty_else, nil, token_map(else_t)) if else_t && !else_body
|
1216
1217
|
n(:case_match, [ expr, *(in_bodies << else_body)],
|
1217
1218
|
condition_map(case_t, expr, nil, nil, else_t, else_body, end_t))
|
1218
1219
|
end
|
@@ -1313,9 +1314,11 @@ module Parser
|
|
1313
1314
|
end
|
1314
1315
|
|
1315
1316
|
def array_pattern(lbrack_t, elements, rbrack_t)
|
1317
|
+
return n(:array_pattern, nil, collection_map(lbrack_t, [], rbrack_t)) if elements.nil?
|
1318
|
+
|
1316
1319
|
trailing_comma = false
|
1317
1320
|
|
1318
|
-
|
1321
|
+
node_elements = elements.map do |element|
|
1319
1322
|
if element.type == :match_with_trailing_comma
|
1320
1323
|
trailing_comma = true
|
1321
1324
|
element.children.first
|
@@ -1326,17 +1329,22 @@ module Parser
|
|
1326
1329
|
end
|
1327
1330
|
|
1328
1331
|
node_type = trailing_comma ? :array_pattern_with_tail : :array_pattern
|
1329
|
-
|
1332
|
+
|
1333
|
+
n(node_type, node_elements,
|
1330
1334
|
collection_map(lbrack_t, elements, rbrack_t))
|
1331
1335
|
end
|
1332
1336
|
|
1333
|
-
def match_with_trailing_comma(match)
|
1334
|
-
n(:match_with_trailing_comma, [ match ],
|
1337
|
+
def match_with_trailing_comma(match, comma_t)
|
1338
|
+
n(:match_with_trailing_comma, [ match ], expr_map(match.loc.expression.join(loc(comma_t))))
|
1335
1339
|
end
|
1336
1340
|
|
1337
1341
|
def const_pattern(const, ldelim_t, pattern, rdelim_t)
|
1338
1342
|
n(:const_pattern, [const, pattern],
|
1339
|
-
|
1343
|
+
Source::Map::Collection.new(
|
1344
|
+
loc(ldelim_t), loc(rdelim_t),
|
1345
|
+
const.loc.expression.join(loc(rdelim_t))
|
1346
|
+
)
|
1347
|
+
)
|
1340
1348
|
end
|
1341
1349
|
|
1342
1350
|
def pin(pin_t, var)
|
@@ -1369,11 +1377,13 @@ module Parser
|
|
1369
1377
|
pair_keyword(label, value)
|
1370
1378
|
else
|
1371
1379
|
begin_t, parts, end_t = label
|
1380
|
+
label_loc = loc(begin_t).join(loc(end_t))
|
1372
1381
|
|
1373
1382
|
# quoted label like "label": value
|
1374
1383
|
if (var_name = static_string(parts))
|
1375
|
-
|
1376
|
-
|
1384
|
+
check_duplicate_pattern_key(var_name, label_loc)
|
1385
|
+
else
|
1386
|
+
diagnostic :error, :pm_interp_in_var_name, nil, label_loc
|
1377
1387
|
end
|
1378
1388
|
|
1379
1389
|
pair_quoted(begin_t, parts, end_t, value)
|
data/lib/parser/current.rb
CHANGED
@@ -48,7 +48,7 @@ module Parser
|
|
48
48
|
CurrentRuby = Ruby23
|
49
49
|
|
50
50
|
when /^2\.4\./
|
51
|
-
current_version = '2.4.
|
51
|
+
current_version = '2.4.10'
|
52
52
|
if RUBY_VERSION != current_version
|
53
53
|
warn_syntax_deviation 'parser/ruby24', current_version
|
54
54
|
end
|
@@ -57,7 +57,7 @@ module Parser
|
|
57
57
|
CurrentRuby = Ruby24
|
58
58
|
|
59
59
|
when /^2\.5\./
|
60
|
-
current_version = '2.5.
|
60
|
+
current_version = '2.5.8'
|
61
61
|
if RUBY_VERSION != current_version
|
62
62
|
warn_syntax_deviation 'parser/ruby25', current_version
|
63
63
|
end
|
@@ -66,7 +66,7 @@ module Parser
|
|
66
66
|
CurrentRuby = Ruby25
|
67
67
|
|
68
68
|
when /^2\.6\./
|
69
|
-
current_version = '2.6.
|
69
|
+
current_version = '2.6.6'
|
70
70
|
if RUBY_VERSION != current_version
|
71
71
|
warn_syntax_deviation 'parser/ruby26', current_version
|
72
72
|
end
|
@@ -75,7 +75,7 @@ module Parser
|
|
75
75
|
CurrentRuby = Ruby26
|
76
76
|
|
77
77
|
when /^2\.7\./
|
78
|
-
current_version = '2.7.
|
78
|
+
current_version = '2.7.1'
|
79
79
|
if RUBY_VERSION != current_version
|
80
80
|
warn_syntax_deviation 'parser/ruby27', current_version
|
81
81
|
end
|
data/lib/parser/lexer.rl
CHANGED
@@ -2473,7 +2473,7 @@ class Parser::Lexer
|
|
2473
2473
|
|
2474
2474
|
# Here we use '\n' instead of w_newline to not modify @newline_s
|
2475
2475
|
# and eventually properly emit tNL
|
2476
|
-
(w_space_comment '\n')+
|
2476
|
+
(c_space* w_space_comment '\n')+
|
2477
2477
|
=> {
|
2478
2478
|
if @version < 27
|
2479
2479
|
# Ruby before 2.7 doesn't support comments before leading dot.
|
data/lib/parser/messages.rb
CHANGED
@@ -70,6 +70,7 @@ module Parser
|
|
70
70
|
:circular_argument_reference => 'circular argument reference %{var_name}',
|
71
71
|
:pm_interp_in_var_name => 'symbol literal with interpolation is not allowed',
|
72
72
|
:lvar_name => "`%{name}' is not allowed as a local variable name",
|
73
|
+
:undefined_lvar => "no such local variable: `%{name}'",
|
73
74
|
:duplicate_variable_name => 'duplicate variable name %{name}',
|
74
75
|
:duplicate_pattern_key => 'duplicate hash pattern key %{name}',
|
75
76
|
|
data/lib/parser/meta.rb
CHANGED
data/lib/parser/ruby27.y
CHANGED
@@ -1784,7 +1784,7 @@ opt_block_args_tail:
|
|
1784
1784
|
# array patterns that end with comma
|
1785
1785
|
# like 1, 2,
|
1786
1786
|
# must be emitted as `array_pattern_with_tail`
|
1787
|
-
item = @builder.match_with_trailing_comma(val[0])
|
1787
|
+
item = @builder.match_with_trailing_comma(val[0], val[1])
|
1788
1788
|
result = @builder.array_pattern(nil, [ item ], nil)
|
1789
1789
|
}
|
1790
1790
|
| p_expr tCOMMA p_args
|
@@ -1841,7 +1841,8 @@ opt_block_args_tail:
|
|
1841
1841
|
}
|
1842
1842
|
| p_const tLPAREN2 rparen
|
1843
1843
|
{
|
1844
|
-
|
1844
|
+
pattern = @builder.array_pattern(val[1], nil, val[2])
|
1845
|
+
result = @builder.const_pattern(val[0], val[1], pattern, val[2])
|
1845
1846
|
}
|
1846
1847
|
| p_const p_lbracket p_args rbracket
|
1847
1848
|
{
|
@@ -1857,7 +1858,8 @@ opt_block_args_tail:
|
|
1857
1858
|
}
|
1858
1859
|
| p_const tLBRACK2 rbracket
|
1859
1860
|
{
|
1860
|
-
|
1861
|
+
pattern = @builder.array_pattern(val[1], nil, val[2])
|
1862
|
+
result = @builder.const_pattern(val[0], val[1], pattern, val[2])
|
1861
1863
|
}
|
1862
1864
|
| tLBRACK
|
1863
1865
|
{
|
@@ -1875,13 +1877,16 @@ opt_block_args_tail:
|
|
1875
1877
|
| tLBRACE
|
1876
1878
|
{
|
1877
1879
|
@pattern_hash_keys.push
|
1880
|
+
result = @lexer.in_kwarg
|
1881
|
+
@lexer.in_kwarg = false
|
1878
1882
|
}
|
1879
|
-
p_kwargs
|
1883
|
+
p_kwargs rbrace
|
1880
1884
|
{
|
1881
1885
|
@pattern_hash_keys.pop
|
1886
|
+
@lexer.in_kwarg = val[1]
|
1882
1887
|
result = @builder.hash_pattern(val[0], val[2], val[3])
|
1883
1888
|
}
|
1884
|
-
| tLBRACE
|
1889
|
+
| tLBRACE rbrace
|
1885
1890
|
{
|
1886
1891
|
result = @builder.hash_pattern(val[0], [], val[1])
|
1887
1892
|
}
|
@@ -1932,7 +1937,7 @@ opt_block_args_tail:
|
|
1932
1937
|
# array patterns that end with comma
|
1933
1938
|
# like [1, 2,]
|
1934
1939
|
# must be emitted as `array_pattern_with_tail`
|
1935
|
-
item = @builder.match_with_trailing_comma(val[0])
|
1940
|
+
item = @builder.match_with_trailing_comma(val[0], val[1])
|
1936
1941
|
result = [ item ]
|
1937
1942
|
}
|
1938
1943
|
| p_args_head p_arg tCOMMA
|
@@ -1940,7 +1945,7 @@ opt_block_args_tail:
|
|
1940
1945
|
# array patterns that end with comma
|
1941
1946
|
# like [1, 2,]
|
1942
1947
|
# must be emitted as `array_pattern_with_tail`
|
1943
|
-
last_item = @builder.match_with_trailing_comma(val[1])
|
1948
|
+
last_item = @builder.match_with_trailing_comma(val[1], val[2])
|
1944
1949
|
result = [ *val[0], last_item ]
|
1945
1950
|
}
|
1946
1951
|
|
@@ -1984,6 +1989,10 @@ opt_block_args_tail:
|
|
1984
1989
|
{
|
1985
1990
|
result = val[0]
|
1986
1991
|
}
|
1992
|
+
| p_kwarg tCOMMA
|
1993
|
+
{
|
1994
|
+
result = val[0]
|
1995
|
+
}
|
1987
1996
|
| p_kwrest
|
1988
1997
|
{
|
1989
1998
|
result = val[0]
|
@@ -2096,6 +2105,11 @@ opt_block_args_tail:
|
|
2096
2105
|
|
2097
2106
|
p_var_ref: tCARET tIDENTIFIER
|
2098
2107
|
{
|
2108
|
+
name = val[1][0]
|
2109
|
+
unless static_env.declared?(name)
|
2110
|
+
diagnostic :error, :undefined_lvar, { :name => name }, val[1]
|
2111
|
+
end
|
2112
|
+
|
2099
2113
|
lvar = @builder.accessible(@builder.ident(val[1]))
|
2100
2114
|
result = @builder.pin(val[0], lvar)
|
2101
2115
|
}
|
@@ -2891,6 +2905,10 @@ keyword_variable: kNIL
|
|
2891
2905
|
{
|
2892
2906
|
result = val[1]
|
2893
2907
|
}
|
2908
|
+
rbrace: opt_nl tRCURLY
|
2909
|
+
{
|
2910
|
+
result = val[1]
|
2911
|
+
}
|
2894
2912
|
trailer: | tNL | tCOMMA
|
2895
2913
|
|
2896
2914
|
term: tSEMI
|
data/lib/parser/source/range.rb
CHANGED
@@ -298,6 +298,15 @@ module Parser
|
|
298
298
|
(@end_pos <=> other.end_pos)
|
299
299
|
end
|
300
300
|
|
301
|
+
alias_method :eql?, :==
|
302
|
+
|
303
|
+
##
|
304
|
+
# Support for Ranges be used in as Hash indices and in Sets.
|
305
|
+
#
|
306
|
+
def hash
|
307
|
+
[@source_buffer, @begin_pos, @end_pos].hash
|
308
|
+
end
|
309
|
+
|
301
310
|
##
|
302
311
|
# @return [String] a human-readable representation of this range.
|
303
312
|
#
|
@@ -117,6 +117,43 @@ module Parser
|
|
117
117
|
@action_root = TreeRewriter::Action.new(all_encompassing_range, @enforcer)
|
118
118
|
end
|
119
119
|
|
120
|
+
##
|
121
|
+
# Returns true iff no (non trivial) update has been recorded
|
122
|
+
#
|
123
|
+
# @return [Boolean]
|
124
|
+
#
|
125
|
+
def empty?
|
126
|
+
@action_root.empty?
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Merges the updates of argument with the receiver.
|
131
|
+
# Policies of the receiver are used.
|
132
|
+
#
|
133
|
+
# @param [Rewriter] with
|
134
|
+
# @return [Rewriter] self
|
135
|
+
# @raise [ClobberingError] when clobbering is detected
|
136
|
+
#
|
137
|
+
def merge!(with)
|
138
|
+
raise 'TreeRewriter are not for the same source_buffer' unless
|
139
|
+
source_buffer == with.source_buffer
|
140
|
+
|
141
|
+
@action_root = @action_root.combine(with.action_root)
|
142
|
+
self
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# Returns a new rewriter that consists of the updates of the received
|
147
|
+
# and the given argument. Policies of the receiver are used.
|
148
|
+
#
|
149
|
+
# @param [Rewriter] with
|
150
|
+
# @return [Rewriter] merge of receiver and argument
|
151
|
+
# @raise [ClobberingError] when clobbering is detected
|
152
|
+
#
|
153
|
+
def merge(with)
|
154
|
+
dup.merge!(with)
|
155
|
+
end
|
156
|
+
|
120
157
|
##
|
121
158
|
# Replaces the code of the source range `range` with `content`.
|
122
159
|
#
|
@@ -203,10 +240,9 @@ module Parser
|
|
203
240
|
##
|
204
241
|
# Provides a protected block where a sequence of multiple rewrite actions
|
205
242
|
# are handled atomically. If any of the actions failed by clobbering,
|
206
|
-
# all the actions are rolled back.
|
243
|
+
# all the actions are rolled back. Transactions can be nested.
|
207
244
|
#
|
208
245
|
# @raise [RuntimeError] when no block is passed
|
209
|
-
# @raise [RuntimeError] when already in a transaction
|
210
246
|
#
|
211
247
|
def transaction
|
212
248
|
unless block_given?
|
@@ -256,6 +292,10 @@ module Parser
|
|
256
292
|
|
257
293
|
extend Deprecation
|
258
294
|
|
295
|
+
protected
|
296
|
+
|
297
|
+
attr_reader :action_root
|
298
|
+
|
259
299
|
private
|
260
300
|
|
261
301
|
ACTIONS = %i[accept warn raise].freeze
|
@@ -25,12 +25,18 @@ module Parser
|
|
25
25
|
freeze
|
26
26
|
end
|
27
27
|
|
28
|
-
# Assumes action.children.empty?
|
29
28
|
def combine(action)
|
30
|
-
return self
|
29
|
+
return self if action.empty? # Ignore empty action
|
31
30
|
do_combine(action)
|
32
31
|
end
|
33
32
|
|
33
|
+
def empty?
|
34
|
+
@insert_before.empty? &&
|
35
|
+
@insert_after.empty? &&
|
36
|
+
@children.empty? &&
|
37
|
+
(@replacement == nil || (@replacement.empty? && @range.empty?))
|
38
|
+
end
|
39
|
+
|
34
40
|
def ordered_replacements
|
35
41
|
reps = []
|
36
42
|
reps << [@range.begin, @insert_before] unless @insert_before.empty?
|
@@ -46,9 +52,11 @@ module Parser
|
|
46
52
|
|
47
53
|
protected
|
48
54
|
|
49
|
-
|
55
|
+
attr_reader :children
|
56
|
+
|
57
|
+
def with(range: @range, enforcer: @enforcer, children: @children, insert_before: @insert_before, replacement: @replacement, insert_after: @insert_after)
|
50
58
|
children = swallow(children) if replacement
|
51
|
-
self.class.new(range,
|
59
|
+
self.class.new(range, enforcer, children: children, insert_before: insert_before, replacement: replacement, insert_after: insert_after)
|
52
60
|
end
|
53
61
|
|
54
62
|
# Assumes range.contains?(action.range) && action.children.empty?
|
@@ -56,11 +64,11 @@ module Parser
|
|
56
64
|
if action.range == @range
|
57
65
|
merge(action)
|
58
66
|
else
|
59
|
-
|
67
|
+
place_in_hierarchy(action)
|
60
68
|
end
|
61
69
|
end
|
62
70
|
|
63
|
-
def
|
71
|
+
def place_in_hierarchy(action)
|
64
72
|
family = @children.group_by { |child| child.relationship_with(action) }
|
65
73
|
|
66
74
|
if family[:fusible]
|
@@ -69,7 +77,8 @@ module Parser
|
|
69
77
|
extra_sibbling = if family[:parent] # action should be a descendant of one of the children
|
70
78
|
family[:parent][0].do_combine(action)
|
71
79
|
elsif family[:child] # or it should become the parent of some of the children,
|
72
|
-
action.with(children: family[:child])
|
80
|
+
action.with(children: family[:child], enforcer: @enforcer)
|
81
|
+
.combine_children(action.children)
|
73
82
|
else # or else it should become an additional child
|
74
83
|
action
|
75
84
|
end
|
@@ -77,6 +86,13 @@ module Parser
|
|
77
86
|
end
|
78
87
|
end
|
79
88
|
|
89
|
+
# Assumes more_children all contained within @range
|
90
|
+
def combine_children(more_children)
|
91
|
+
more_children.inject(self) do |parent, new_child|
|
92
|
+
parent.place_in_hierarchy(new_child)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
80
96
|
def fuse_deletions(action, fusible, other_sibblings)
|
81
97
|
without_fusible = with(children: other_sibblings)
|
82
98
|
fused_range = [action, *fusible].map(&:range).inject(:join)
|
@@ -109,7 +125,7 @@ module Parser
|
|
109
125
|
insert_before: "#{action.insert_before}#{insert_before}",
|
110
126
|
replacement: action.replacement || @replacement,
|
111
127
|
insert_after: "#{insert_after}#{action.insert_after}",
|
112
|
-
)
|
128
|
+
).combine_children(action.children)
|
113
129
|
end
|
114
130
|
|
115
131
|
def call_enforcer_for_merge(action)
|
data/lib/parser/version.rb
CHANGED
data/parser.gemspec
CHANGED
@@ -13,6 +13,13 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.homepage = 'https://github.com/whitequark/parser'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
|
+
spec.metadata = {
|
17
|
+
'bug_tracker_uri' => 'https://github.com/whitequark/parser/issues',
|
18
|
+
'changelog_uri' => "https://github.com/whitequark/parser/blob/v#{spec.version}/CHANGELOG.md",
|
19
|
+
'documentation_uri' => "https://www.rubydoc.info/gems/parser/#{spec.version}",
|
20
|
+
'source_code_uri' => "https://github.com/whitequark/parser/tree/v#{spec.version}"
|
21
|
+
}
|
22
|
+
|
16
23
|
spec.files = `git ls-files`.split + %w(
|
17
24
|
lib/parser/lexer.rb
|
18
25
|
lib/parser/ruby18.rb
|
data/test/test_parser.rb
CHANGED
@@ -7644,12 +7644,26 @@ class TestParser < Minitest::Test
|
|
7644
7644
|
%q{},
|
7645
7645
|
SINCE_2_7)
|
7646
7646
|
|
7647
|
+
assert_parses(
|
7648
|
+
s(:send,
|
7649
|
+
s(:send, nil, :a), :foo),
|
7650
|
+
%Q{a #\n #\n.foo\n},
|
7651
|
+
%q{},
|
7652
|
+
SINCE_2_7)
|
7653
|
+
|
7647
7654
|
assert_parses(
|
7648
7655
|
s(:csend,
|
7649
7656
|
s(:send, nil, :a), :foo),
|
7650
7657
|
%Q{a #\n#\n&.foo\n},
|
7651
7658
|
%q{},
|
7652
7659
|
SINCE_2_7)
|
7660
|
+
|
7661
|
+
assert_parses(
|
7662
|
+
s(:csend,
|
7663
|
+
s(:send, nil, :a), :foo),
|
7664
|
+
%Q{a #\n #\n&.foo\n},
|
7665
|
+
%q{},
|
7666
|
+
SINCE_2_7)
|
7653
7667
|
end
|
7654
7668
|
|
7655
7669
|
def test_comments_before_leading_dot__before_27
|
@@ -8408,7 +8422,7 @@ class TestParser < Minitest::Test
|
|
8408
8422
|
nil,
|
8409
8423
|
s(:nil)),
|
8410
8424
|
%q{in x, then nil},
|
8411
|
-
%q{
|
8425
|
+
%q{ ~~ expression (in_pattern.array_pattern_with_tail)}
|
8412
8426
|
)
|
8413
8427
|
|
8414
8428
|
assert_parses_pattern_match(
|
@@ -8454,7 +8468,7 @@ class TestParser < Minitest::Test
|
|
8454
8468
|
nil,
|
8455
8469
|
s(:nil)),
|
8456
8470
|
%q{in x, y, then nil},
|
8457
|
-
%q{
|
8471
|
+
%q{ ~~~~~ expression (in_pattern.array_pattern_with_tail)}
|
8458
8472
|
)
|
8459
8473
|
|
8460
8474
|
assert_parses_pattern_match(
|
@@ -8661,6 +8675,18 @@ class TestParser < Minitest::Test
|
|
8661
8675
|
| ~ end (in_pattern.hash_pattern)}
|
8662
8676
|
)
|
8663
8677
|
|
8678
|
+
assert_parses_pattern_match(
|
8679
|
+
s(:in_pattern,
|
8680
|
+
s(:hash_pattern,
|
8681
|
+
s(:pair, s(:sym, :a), s(:int, 1))),
|
8682
|
+
nil,
|
8683
|
+
s(:true)),
|
8684
|
+
%q{in { a: 1, } then true},
|
8685
|
+
%q{ ~~~~~~~~~ expression (in_pattern.hash_pattern)
|
8686
|
+
| ~ begin (in_pattern.hash_pattern)
|
8687
|
+
| ~ end (in_pattern.hash_pattern)}
|
8688
|
+
)
|
8689
|
+
|
8664
8690
|
assert_parses_pattern_match(
|
8665
8691
|
s(:in_pattern,
|
8666
8692
|
s(:hash_pattern,
|
@@ -8732,6 +8758,67 @@ class TestParser < Minitest::Test
|
|
8732
8758
|
%q{in a: 1, _a:, ** then true},
|
8733
8759
|
%q{ ~~~~~~~~~~~~~ expression (in_pattern.hash_pattern)}
|
8734
8760
|
)
|
8761
|
+
|
8762
|
+
assert_parses_pattern_match(
|
8763
|
+
s(:in_pattern,
|
8764
|
+
s(:hash_pattern,
|
8765
|
+
s(:pair,
|
8766
|
+
s(:sym, :a),
|
8767
|
+
s(:int, 1))), nil,
|
8768
|
+
s(:false)),
|
8769
|
+
%q{
|
8770
|
+
in {a: 1
|
8771
|
+
}
|
8772
|
+
false
|
8773
|
+
},
|
8774
|
+
%q{}
|
8775
|
+
)
|
8776
|
+
|
8777
|
+
|
8778
|
+
assert_parses_pattern_match(
|
8779
|
+
s(:in_pattern,
|
8780
|
+
s(:hash_pattern,
|
8781
|
+
s(:pair,
|
8782
|
+
s(:sym, :a),
|
8783
|
+
s(:int, 2))), nil,
|
8784
|
+
s(:false)),
|
8785
|
+
%q{
|
8786
|
+
in {a:
|
8787
|
+
2}
|
8788
|
+
false
|
8789
|
+
},
|
8790
|
+
%q{}
|
8791
|
+
)
|
8792
|
+
|
8793
|
+
assert_parses_pattern_match(
|
8794
|
+
s(:in_pattern,
|
8795
|
+
s(:hash_pattern,
|
8796
|
+
s(:pair,
|
8797
|
+
s(:sym, :a),
|
8798
|
+
s(:hash_pattern,
|
8799
|
+
s(:match_var, :b))),
|
8800
|
+
s(:match_var, :c)), nil,
|
8801
|
+
s(:send, nil, :p,
|
8802
|
+
s(:lvar, :c))),
|
8803
|
+
%q{
|
8804
|
+
in a: {b:}, c:
|
8805
|
+
p c
|
8806
|
+
},
|
8807
|
+
%q{}
|
8808
|
+
)
|
8809
|
+
|
8810
|
+
assert_parses_pattern_match(
|
8811
|
+
s(:in_pattern,
|
8812
|
+
s(:hash_pattern,
|
8813
|
+
s(:match_var, :a)), nil,
|
8814
|
+
s(:true)),
|
8815
|
+
%q{
|
8816
|
+
in {a:
|
8817
|
+
}
|
8818
|
+
true
|
8819
|
+
},
|
8820
|
+
%q{}
|
8821
|
+
)
|
8735
8822
|
end
|
8736
8823
|
|
8737
8824
|
def test_pattern_matching_hash_with_string_keys
|
@@ -8839,6 +8926,15 @@ class TestParser < Minitest::Test
|
|
8839
8926
|
)
|
8840
8927
|
end
|
8841
8928
|
|
8929
|
+
def test_pattern_matching_hash_with_string_interpolation_keys
|
8930
|
+
assert_diagnoses(
|
8931
|
+
[:error, :pm_interp_in_var_name],
|
8932
|
+
%q{case a; in "#{a}": 1; end},
|
8933
|
+
%q{ ~~~~~~~ location},
|
8934
|
+
SINCE_2_7
|
8935
|
+
)
|
8936
|
+
end
|
8937
|
+
|
8842
8938
|
def test_pattern_matching_keyword_variable
|
8843
8939
|
assert_parses_pattern_match(
|
8844
8940
|
s(:in_pattern,
|
@@ -8970,7 +9066,7 @@ class TestParser < Minitest::Test
|
|
8970
9066
|
nil,
|
8971
9067
|
s(:true)),
|
8972
9068
|
%q{in A(1, 2) then true},
|
8973
|
-
%q{
|
9069
|
+
%q{ ~~~~~~~ expression (in_pattern.const_pattern)
|
8974
9070
|
| ~ begin (in_pattern.const_pattern)
|
8975
9071
|
| ~ end (in_pattern.const_pattern)
|
8976
9072
|
| ~ expression (in_pattern.const_pattern.const)
|
@@ -8986,7 +9082,7 @@ class TestParser < Minitest::Test
|
|
8986
9082
|
nil,
|
8987
9083
|
s(:true)),
|
8988
9084
|
%q{in A(x:) then true},
|
8989
|
-
%q{
|
9085
|
+
%q{ ~~~~~ expression (in_pattern.const_pattern)
|
8990
9086
|
| ~ begin (in_pattern.const_pattern)
|
8991
9087
|
| ~ end (in_pattern.const_pattern)
|
8992
9088
|
| ~ expression (in_pattern.const_pattern.const)
|
@@ -8997,13 +9093,15 @@ class TestParser < Minitest::Test
|
|
8997
9093
|
s(:in_pattern,
|
8998
9094
|
s(:const_pattern,
|
8999
9095
|
s(:const, nil, :A),
|
9000
|
-
|
9096
|
+
s(:array_pattern)),
|
9001
9097
|
nil,
|
9002
9098
|
s(:true)),
|
9003
9099
|
%q{in A() then true},
|
9004
|
-
%q{
|
9100
|
+
%q{ ~~~ expression (in_pattern.const_pattern)
|
9005
9101
|
| ~ begin (in_pattern.const_pattern)
|
9006
|
-
| ~ end (in_pattern.const_pattern)
|
9102
|
+
| ~ end (in_pattern.const_pattern)
|
9103
|
+
| ~ expression (in_pattern.const_pattern.const)
|
9104
|
+
| ~~ expression (in_pattern.const_pattern.array_pattern)}
|
9007
9105
|
)
|
9008
9106
|
|
9009
9107
|
assert_parses_pattern_match(
|
@@ -9016,7 +9114,7 @@ class TestParser < Minitest::Test
|
|
9016
9114
|
nil,
|
9017
9115
|
s(:true)),
|
9018
9116
|
%q{in A[1, 2] then true},
|
9019
|
-
%q{
|
9117
|
+
%q{ ~~~~~~~ expression (in_pattern.const_pattern)
|
9020
9118
|
| ~ begin (in_pattern.const_pattern)
|
9021
9119
|
| ~ end (in_pattern.const_pattern)
|
9022
9120
|
| ~ expression (in_pattern.const_pattern.const)
|
@@ -9032,7 +9130,7 @@ class TestParser < Minitest::Test
|
|
9032
9130
|
nil,
|
9033
9131
|
s(:true)),
|
9034
9132
|
%q{in A[x:] then true},
|
9035
|
-
%q{
|
9133
|
+
%q{ ~~~~~ expression (in_pattern.const_pattern)
|
9036
9134
|
| ~ begin (in_pattern.const_pattern)
|
9037
9135
|
| ~ end (in_pattern.const_pattern)
|
9038
9136
|
| ~ expression (in_pattern.const_pattern.const)
|
@@ -9043,13 +9141,14 @@ class TestParser < Minitest::Test
|
|
9043
9141
|
s(:in_pattern,
|
9044
9142
|
s(:const_pattern,
|
9045
9143
|
s(:const, nil, :A),
|
9046
|
-
|
9144
|
+
s(:array_pattern)),
|
9047
9145
|
nil,
|
9048
9146
|
s(:true)),
|
9049
9147
|
%q{in A[] then true},
|
9050
|
-
%q{
|
9148
|
+
%q{ ~~~ expression (in_pattern.const_pattern)
|
9051
9149
|
| ~ begin (in_pattern.const_pattern)
|
9052
|
-
| ~ end (in_pattern.const_pattern)
|
9150
|
+
| ~ end (in_pattern.const_pattern)
|
9151
|
+
| ~~ expression (in_pattern.const_pattern.array_pattern)}
|
9053
9152
|
)
|
9054
9153
|
end
|
9055
9154
|
|
@@ -9093,6 +9192,20 @@ class TestParser < Minitest::Test
|
|
9093
9192
|
)
|
9094
9193
|
end
|
9095
9194
|
|
9195
|
+
def test_pattern_matching_blank_else
|
9196
|
+
assert_parses(
|
9197
|
+
s(:case_match,
|
9198
|
+
s(:int, 1),
|
9199
|
+
s(:in_pattern,
|
9200
|
+
s(:int, 2), nil,
|
9201
|
+
s(:int, 3)),
|
9202
|
+
s(:empty_else)),
|
9203
|
+
%q{case 1; in 2; 3; else; end},
|
9204
|
+
%q{ ~~~~ else},
|
9205
|
+
SINCE_2_7
|
9206
|
+
)
|
9207
|
+
end
|
9208
|
+
|
9096
9209
|
def assert_pattern_matching_defines_local_variables(match_code, lvar_names, versions = SINCE_2_7)
|
9097
9210
|
code = "case 1; #{match_code}; then [#{lvar_names.join(', ')}]; end"
|
9098
9211
|
|
@@ -9295,6 +9408,14 @@ class TestParser < Minitest::Test
|
|
9295
9408
|
SINCE_2_7)
|
9296
9409
|
end
|
9297
9410
|
|
9411
|
+
def test_pattern_matching_required_bound_variable_before_pin
|
9412
|
+
assert_diagnoses(
|
9413
|
+
[:error, :undefined_lvar, { :name => 'a' }],
|
9414
|
+
%{case 0; in ^a; true; end},
|
9415
|
+
%{ ^ location},
|
9416
|
+
SINCE_2_7)
|
9417
|
+
end
|
9418
|
+
|
9298
9419
|
def test_parser_bug_645
|
9299
9420
|
assert_parses(
|
9300
9421
|
s(:block,
|
@@ -31,16 +31,16 @@ class TestSourceCommentAssociator < Minitest::Test
|
|
31
31
|
ast, associations = associate(<<-END)
|
32
32
|
#!/usr/bin/env ruby
|
33
33
|
# coding: utf-8
|
34
|
-
# class
|
35
|
-
# another class
|
34
|
+
# class preceding
|
35
|
+
# another class preceding
|
36
36
|
class Foo # class keyword line
|
37
|
-
# method foo
|
37
|
+
# method foo preceding
|
38
38
|
def foo
|
39
39
|
puts 'foo'
|
40
40
|
end # method foo decorating
|
41
|
-
# method bar
|
41
|
+
# method bar preceding
|
42
42
|
def bar
|
43
|
-
# expression
|
43
|
+
# expression preceding
|
44
44
|
1 + # 1 decorating
|
45
45
|
2
|
46
46
|
# method bar sparse
|
@@ -58,8 +58,8 @@ end # class decorating
|
|
58
58
|
|
59
59
|
assert_equal 6, associations.size
|
60
60
|
assert_equal [
|
61
|
-
'# class
|
62
|
-
'# another class
|
61
|
+
'# class preceding',
|
62
|
+
'# another class preceding',
|
63
63
|
'# class sparse',
|
64
64
|
'# class decorating'
|
65
65
|
], associations[klass_node].map(&:text)
|
@@ -67,16 +67,16 @@ end # class decorating
|
|
67
67
|
'# class keyword line'
|
68
68
|
], associations[klass_name_node].map(&:text)
|
69
69
|
assert_equal [
|
70
|
-
'# method foo
|
70
|
+
'# method foo preceding',
|
71
71
|
'# method foo decorating'
|
72
72
|
], associations[foo_node].map(&:text)
|
73
73
|
assert_equal [
|
74
|
-
'# method bar
|
74
|
+
'# method bar preceding',
|
75
75
|
'# method bar sparse',
|
76
76
|
'# method bar decorating'
|
77
77
|
], associations[bar_node].map(&:text)
|
78
78
|
assert_equal [
|
79
|
-
'# expression
|
79
|
+
'# expression preceding'
|
80
80
|
], associations[expr_node].map(&:text)
|
81
81
|
assert_equal [
|
82
82
|
'# 1 decorating'
|
@@ -112,16 +112,16 @@ end
|
|
112
112
|
ast, associations = associate_locations(<<-END)
|
113
113
|
#!/usr/bin/env ruby
|
114
114
|
# coding: utf-8
|
115
|
-
# class
|
116
|
-
# another class
|
115
|
+
# class preceding
|
116
|
+
# another class preceding
|
117
117
|
class Foo # class keyword line
|
118
|
-
# method foo
|
118
|
+
# method foo preceding
|
119
119
|
def foo
|
120
120
|
puts 'foo'
|
121
121
|
end # method foo decorating
|
122
|
-
# method bar
|
122
|
+
# method bar preceding
|
123
123
|
def bar
|
124
|
-
# expression
|
124
|
+
# expression preceding
|
125
125
|
1 + # 1 decorating
|
126
126
|
2
|
127
127
|
# method bar sparse
|
@@ -139,8 +139,8 @@ end # class decorating
|
|
139
139
|
|
140
140
|
assert_equal 6, associations.size
|
141
141
|
assert_equal [
|
142
|
-
'# class
|
143
|
-
'# another class
|
142
|
+
'# class preceding',
|
143
|
+
'# another class preceding',
|
144
144
|
'# class sparse',
|
145
145
|
'# class decorating'
|
146
146
|
], associations[klass_node.loc].map(&:text)
|
@@ -148,16 +148,16 @@ end # class decorating
|
|
148
148
|
'# class keyword line'
|
149
149
|
], associations[klass_name_node.loc].map(&:text)
|
150
150
|
assert_equal [
|
151
|
-
'# method foo
|
151
|
+
'# method foo preceding',
|
152
152
|
'# method foo decorating'
|
153
153
|
], associations[foo_node.loc].map(&:text)
|
154
154
|
assert_equal [
|
155
|
-
'# method bar
|
155
|
+
'# method bar preceding',
|
156
156
|
'# method bar sparse',
|
157
157
|
'# method bar decorating'
|
158
158
|
], associations[bar_node.loc].map(&:text)
|
159
159
|
assert_equal [
|
160
|
-
'# expression
|
160
|
+
'# expression preceding'
|
161
161
|
], associations[expr_node.loc].map(&:text)
|
162
162
|
assert_equal [
|
163
163
|
'# 1 decorating'
|
data/test/test_source_range.rb
CHANGED
@@ -169,4 +169,19 @@ class TestSourceRange < Minitest::Test
|
|
169
169
|
assert_equal 1, sr3.begin_pos
|
170
170
|
assert_equal 4, sr3.end_pos
|
171
171
|
end
|
172
|
+
|
173
|
+
def test_eql_and_hash
|
174
|
+
assert_equal false, @sr1_3.eql?(@sr3_3)
|
175
|
+
assert @sr1_3.hash != @sr3_3.hash
|
176
|
+
|
177
|
+
also_1_3 = @sr3_3.with(begin_pos: 1)
|
178
|
+
assert_equal true, @sr1_3.eql?(also_1_3)
|
179
|
+
assert_equal @sr1_3.hash, also_1_3.hash
|
180
|
+
|
181
|
+
buf2 = Parser::Source::Buffer.new('(string)')
|
182
|
+
buf2.source = "foobar\nbaz"
|
183
|
+
from_other_buf = Parser::Source::Range.new(buf2, 1, 3)
|
184
|
+
assert_equal false, @sr1_3.eql?(from_other_buf)
|
185
|
+
assert @sr1_3.hash != from_other_buf.hash
|
186
|
+
end
|
172
187
|
end
|
@@ -8,8 +8,10 @@ class TestSourceTreeRewriter < Minitest::Test
|
|
8
8
|
@buf.source = 'puts(:hello, :world)'
|
9
9
|
|
10
10
|
@hello = range(5, 6)
|
11
|
+
@ll = range(7, 2)
|
11
12
|
@comma_space = range(11,2)
|
12
13
|
@world = range(13,6)
|
14
|
+
@whole = range(0, @buf.source.length)
|
13
15
|
end
|
14
16
|
|
15
17
|
def range(from, len)
|
@@ -17,11 +19,11 @@ class TestSourceTreeRewriter < Minitest::Test
|
|
17
19
|
end
|
18
20
|
|
19
21
|
# Returns either:
|
20
|
-
# -
|
22
|
+
# - yield rewriter
|
21
23
|
# - [diagnostic, ...] (Diagnostics)
|
22
24
|
# - Parser::ClobberingError
|
23
25
|
#
|
24
|
-
def
|
26
|
+
def build(actions, **policy)
|
25
27
|
diagnostics = []
|
26
28
|
diags = -> { diagnostics.flatten.map(&:strip).join("\n") }
|
27
29
|
rewriter = Parser::Source::TreeRewriter.new(@buf, **policy)
|
@@ -30,7 +32,7 @@ class TestSourceTreeRewriter < Minitest::Test
|
|
30
32
|
rewriter.public_send(action, range, *args)
|
31
33
|
end
|
32
34
|
if diagnostics.empty?
|
33
|
-
rewriter
|
35
|
+
yield rewriter
|
34
36
|
else
|
35
37
|
diags.call
|
36
38
|
end
|
@@ -38,6 +40,15 @@ class TestSourceTreeRewriter < Minitest::Test
|
|
38
40
|
[::Parser::ClobberingError, diags.call]
|
39
41
|
end
|
40
42
|
|
43
|
+
# Returns either:
|
44
|
+
# - String (Normal operation)
|
45
|
+
# - [diagnostic, ...] (Diagnostics)
|
46
|
+
# - Parser::ClobberingError
|
47
|
+
#
|
48
|
+
def apply(actions, **policy)
|
49
|
+
build(actions, **policy) { |rewriter| rewriter.process }
|
50
|
+
end
|
51
|
+
|
41
52
|
# Expects ordered actions to be grouped together
|
42
53
|
def check_actions(expected, grouped_actions, **policy)
|
43
54
|
grouped_actions.permutation do |sequence|
|
@@ -170,4 +181,73 @@ DIAGNOSTIC
|
|
170
181
|
rewriter = Parser::Source::TreeRewriter.new(@buf)
|
171
182
|
assert_raises(IndexError) { rewriter.insert_before(range(0, 100), 'hola') }
|
172
183
|
end
|
184
|
+
|
185
|
+
def test_empty
|
186
|
+
rewriter = Parser::Source::TreeRewriter.new(@buf)
|
187
|
+
assert_equal true, rewriter.empty?
|
188
|
+
|
189
|
+
# This is a trivial wrap
|
190
|
+
rewriter.wrap(range(2,3), '', '')
|
191
|
+
assert_equal true, rewriter.empty?
|
192
|
+
|
193
|
+
# This is a trivial deletion
|
194
|
+
rewriter.remove(range(2,0))
|
195
|
+
assert_equal true, rewriter.empty?
|
196
|
+
|
197
|
+
rewriter.remove(range(2,3))
|
198
|
+
assert_equal false, rewriter.empty?
|
199
|
+
end
|
200
|
+
|
201
|
+
# splits array into two groups, yield all such possible pairs of groups
|
202
|
+
# each_split([1, 2, 3, 4]) yields [1, 2], [3, 4];
|
203
|
+
# then [1, 3], [2, 4]
|
204
|
+
# ...
|
205
|
+
# and finally [3, 4], [1, 2]
|
206
|
+
def each_split(array)
|
207
|
+
n = array.size
|
208
|
+
first_split_size = n.div(2)
|
209
|
+
splitting = (0...n).to_set
|
210
|
+
splitting.to_a.combination(first_split_size) do |indices|
|
211
|
+
yield array.values_at(*indices),
|
212
|
+
array.values_at(*(splitting - indices))
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Checks that `actions+extra` give the same result when
|
217
|
+
# made in order or from subgroups that are later merged.
|
218
|
+
# The `extra` actions are always added at the end of the second group.
|
219
|
+
#
|
220
|
+
def check_all_merge_possibilities(actions, extra, **policy)
|
221
|
+
expected = apply(actions + extra, **policy)
|
222
|
+
|
223
|
+
each_split(actions) do |actions_1, actions_2|
|
224
|
+
build(actions_1, **policy) do |rewriter_1|
|
225
|
+
build(actions_2 + extra, **policy) do |rewriter_2|
|
226
|
+
result = rewriter_1.merge(rewriter_2).process
|
227
|
+
assert_equal(expected, result,
|
228
|
+
"Group 1: #{actions_1.inspect}\n\n" +
|
229
|
+
"Group 2: #{(actions_2 + extra).inspect}"
|
230
|
+
)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def test_merge
|
237
|
+
check_all_merge_possibilities([
|
238
|
+
[:wrap, @whole, '<', '>'],
|
239
|
+
[:replace, @comma_space, ' => '],
|
240
|
+
[:wrap, @hello, '!', '!'],
|
241
|
+
# Following two wraps must have same value as they
|
242
|
+
# will be applied in different orders...
|
243
|
+
[:wrap, @hello.join(@world), '{', '}'],
|
244
|
+
[:wrap, @hello.join(@world), '{', '}'],
|
245
|
+
[:remove, @ll],
|
246
|
+
[:replace, @world, ':everybody'],
|
247
|
+
[:wrap, @world, '[', ']']
|
248
|
+
],
|
249
|
+
[ # ... but this one is always going to be applied last (extra)
|
250
|
+
[:wrap, @hello.join(@world), '@', '@'],
|
251
|
+
])
|
252
|
+
end
|
173
253
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.7.
|
4
|
+
version: 2.7.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- whitequark
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ast
|
@@ -295,7 +295,11 @@ files:
|
|
295
295
|
homepage: https://github.com/whitequark/parser
|
296
296
|
licenses:
|
297
297
|
- MIT
|
298
|
-
metadata:
|
298
|
+
metadata:
|
299
|
+
bug_tracker_uri: https://github.com/whitequark/parser/issues
|
300
|
+
changelog_uri: https://github.com/whitequark/parser/blob/v2.7.1.1/CHANGELOG.md
|
301
|
+
documentation_uri: https://www.rubydoc.info/gems/parser/2.7.1.1
|
302
|
+
source_code_uri: https://github.com/whitequark/parser/tree/v2.7.1.1
|
299
303
|
post_install_message:
|
300
304
|
rdoc_options: []
|
301
305
|
require_paths:
|