parser 2.7.0.2 → 2.7.1.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/.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:
|