parser 2.7.1.1 → 2.7.1.2
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/.gitignore +1 -0
- data/CHANGELOG.md +11 -1
- data/Rakefile +1 -0
- data/doc/AST_FORMAT.md +28 -1
- data/lib/parser.rb +1 -0
- data/lib/parser/all.rb +1 -0
- data/lib/parser/ast/processor.rb +4 -0
- data/lib/parser/builders/default.rb +44 -9
- data/lib/parser/current.rb +9 -0
- data/lib/parser/meta.rb +1 -1
- data/lib/parser/ruby28.y +2974 -0
- data/lib/parser/runner.rb +5 -0
- data/lib/parser/source/map/endless_definition.rb +23 -0
- data/lib/parser/source/tree_rewriter.rb +8 -11
- data/lib/parser/source/tree_rewriter/action.rb +75 -21
- data/lib/parser/version.rb +1 -1
- data/parser.gemspec +1 -0
- data/test/helper.rb +1 -0
- data/test/parse_helper.rb +2 -1
- data/test/test_current.rb +2 -0
- data/test/test_lexer.rb +12 -0
- data/test/test_parser.rb +95 -0
- data/test/test_source_tree_rewriter.rb +10 -0
- metadata +8 -5
data/lib/parser/runner.rb
CHANGED
@@ -111,6 +111,11 @@ module Parser
|
|
111
111
|
@parser_class = Parser::Ruby27
|
112
112
|
end
|
113
113
|
|
114
|
+
opts.on '--28', 'Parse as Ruby 2.8 would' do
|
115
|
+
require 'parser/ruby28'
|
116
|
+
@parser_class = Parser::Ruby28
|
117
|
+
end
|
118
|
+
|
114
119
|
opts.on '--mac', 'Parse as MacRuby 0.12 would' do
|
115
120
|
require 'parser/macruby'
|
116
121
|
@parser_class = Parser::MacRuby
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Parser
|
4
|
+
module Source
|
5
|
+
|
6
|
+
class Map::EndlessDefinition < Map
|
7
|
+
attr_reader :keyword
|
8
|
+
attr_reader :operator
|
9
|
+
attr_reader :name
|
10
|
+
attr_reader :assignment
|
11
|
+
|
12
|
+
def initialize(keyword_l, operator_l, name_l, assignment_l, body_l)
|
13
|
+
@keyword = keyword_l
|
14
|
+
@operator = operator_l
|
15
|
+
@name = name_l
|
16
|
+
@assignment = assignment_l
|
17
|
+
|
18
|
+
super(@keyword.join(body_l))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -221,20 +221,17 @@ module Parser
|
|
221
221
|
#
|
222
222
|
# @return [String]
|
223
223
|
#
|
224
|
-
|
225
|
-
source = @source_buffer.source
|
226
|
-
adjustment = 0
|
224
|
+
def process
|
225
|
+
source = @source_buffer.source
|
227
226
|
|
227
|
+
chunks = []
|
228
|
+
last_end = 0
|
228
229
|
@action_root.ordered_replacements.each do |range, replacement|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
source[begin_pos...end_pos] = replacement
|
233
|
-
|
234
|
-
adjustment += replacement.length - range.length
|
230
|
+
chunks << source[last_end...range.begin_pos] << replacement
|
231
|
+
last_end = range.end_pos
|
235
232
|
end
|
236
|
-
|
237
|
-
|
233
|
+
chunks << source[last_end...source.length]
|
234
|
+
chunks.join
|
238
235
|
end
|
239
236
|
|
240
237
|
##
|
@@ -7,7 +7,7 @@ module Parser
|
|
7
7
|
#
|
8
8
|
# Actions are arranged in a tree and get combined so that:
|
9
9
|
# children are strictly contained by their parent
|
10
|
-
# sibblings all disjoint from one another
|
10
|
+
# sibblings all disjoint from one another and ordered
|
11
11
|
# only actions with replacement==nil may have children
|
12
12
|
#
|
13
13
|
class TreeRewriter::Action
|
@@ -41,7 +41,7 @@ module Parser
|
|
41
41
|
reps = []
|
42
42
|
reps << [@range.begin, @insert_before] unless @insert_before.empty?
|
43
43
|
reps << [@range, @replacement] if @replacement
|
44
|
-
reps.concat(@children.
|
44
|
+
reps.concat(@children.flat_map(&:ordered_replacements))
|
45
45
|
reps << [@range.end, @insert_after] unless @insert_after.empty?
|
46
46
|
reps
|
47
47
|
end
|
@@ -69,24 +69,24 @@ module Parser
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def place_in_hierarchy(action)
|
72
|
-
family =
|
72
|
+
family = analyse_hierarchy(action)
|
73
73
|
|
74
74
|
if family[:fusible]
|
75
|
-
fuse_deletions(action, family[:fusible], [*family[:
|
75
|
+
fuse_deletions(action, family[:fusible], [*family[:sibbling_left], *family[:child], *family[:sibbling_right]])
|
76
76
|
else
|
77
77
|
extra_sibbling = if family[:parent] # action should be a descendant of one of the children
|
78
|
-
family[:parent]
|
78
|
+
family[:parent].do_combine(action)
|
79
79
|
elsif family[:child] # or it should become the parent of some of the children,
|
80
80
|
action.with(children: family[:child], enforcer: @enforcer)
|
81
81
|
.combine_children(action.children)
|
82
82
|
else # or else it should become an additional child
|
83
83
|
action
|
84
84
|
end
|
85
|
-
with(children: [*family[:
|
85
|
+
with(children: [*family[:sibbling_left], extra_sibbling, *family[:sibbling_right]])
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
# Assumes more_children all contained within
|
89
|
+
# Assumes `more_children` all contained within `@range`
|
90
90
|
def combine_children(more_children)
|
91
91
|
more_children.inject(self) do |parent, new_child|
|
92
92
|
parent.place_in_hierarchy(new_child)
|
@@ -100,22 +100,76 @@ module Parser
|
|
100
100
|
without_fusible.do_combine(fused_deletion)
|
101
101
|
end
|
102
102
|
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
def
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
103
|
+
# Similar to @children.bsearch_index || size
|
104
|
+
# except allows for a starting point
|
105
|
+
# and `bsearch_index` is only Ruby 2.3+
|
106
|
+
def bsearch_child_index(from = 0)
|
107
|
+
size = @children.size
|
108
|
+
(from...size).bsearch { |i| yield @children[i] } || size
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns the children in a hierarchy with respect to `action`:
|
112
|
+
# :sibbling_left, sibbling_right (for those that are disjoint from `action`)
|
113
|
+
# :parent (in case one of our children contains `action`)
|
114
|
+
# :child (in case `action` strictly contains some of our children)
|
115
|
+
# :fusible (in case `action` overlaps some children but they can be fused in one deletion)
|
116
|
+
# or raises a `CloberingError`
|
117
|
+
# In case a child has equal range to `action`, it is returned as `:parent`
|
118
|
+
# Reminder: an empty range 1...1 is considered disjoint from 1...10
|
119
|
+
def analyse_hierarchy(action)
|
120
|
+
r = action.range
|
121
|
+
# left_index is the index of the first child that isn't completely to the left of action
|
122
|
+
left_index = bsearch_child_index { |child| child.range.end_pos > r.begin_pos }
|
123
|
+
# right_index is the index of the first child that is completely on the right of action
|
124
|
+
start = left_index == 0 ? 0 : left_index - 1 # See "corner case" below for reason of -1
|
125
|
+
right_index = bsearch_child_index(start) { |child| child.range.begin_pos >= r.end_pos }
|
126
|
+
center = right_index - left_index
|
127
|
+
case center
|
128
|
+
when 0
|
129
|
+
# All children are disjoint from action, nothing else to do
|
130
|
+
when -1
|
131
|
+
# Corner case: if a child has empty range == action's range
|
132
|
+
# then it will appear to be both disjoint and to the left of action,
|
133
|
+
# as well as disjoint and to the right of action.
|
134
|
+
# Since ranges are equal, we return it as parent
|
135
|
+
left_index -= 1 # Fix indices, as otherwise this child would be
|
136
|
+
right_index += 1 # considered as a sibbling (both left and right!)
|
137
|
+
parent = @children[left_index]
|
116
138
|
else
|
117
|
-
@
|
139
|
+
overlap_left = @children[left_index].range.begin_pos <=> r.begin_pos
|
140
|
+
overlap_right = @children[right_index-1].range.end_pos <=> r.end_pos
|
141
|
+
|
142
|
+
# For one child to be the parent of action, we must have:
|
143
|
+
if center == 1 && overlap_left <= 0 && overlap_right >= 0
|
144
|
+
parent = @children[left_index]
|
145
|
+
else
|
146
|
+
# Otherwise consider all non disjoint elements (center) to be contained...
|
147
|
+
contained = @children[left_index...right_index]
|
148
|
+
fusible = check_fusible(action,
|
149
|
+
(contained.shift if overlap_left < 0), # ... but check first and last one
|
150
|
+
(contained.pop if overlap_right > 0) # ... for overlaps
|
151
|
+
)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
{
|
156
|
+
parent: parent,
|
157
|
+
sibbling_left: @children[0...left_index],
|
158
|
+
sibbling_right: @children[right_index...@children.size],
|
159
|
+
fusible: fusible,
|
160
|
+
child: contained,
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
# @param [Array(Action | nil)] fusible
|
165
|
+
def check_fusible(action, *fusible)
|
166
|
+
fusible.compact!
|
167
|
+
return if fusible.empty?
|
168
|
+
fusible.each do |child|
|
169
|
+
kind = action.insertion? || child.insertion? ? :crossing_insertions : :crossing_deletions
|
170
|
+
@enforcer.call(kind) { {range: action.range, conflict: child.range} }
|
118
171
|
end
|
172
|
+
fusible
|
119
173
|
end
|
120
174
|
|
121
175
|
# Assumes action.range == range && action.children.empty?
|
data/lib/parser/version.rb
CHANGED
data/parser.gemspec
CHANGED
data/test/helper.rb
CHANGED
data/test/parse_helper.rb
CHANGED
@@ -7,7 +7,7 @@ module ParseHelper
|
|
7
7
|
require 'parser/macruby'
|
8
8
|
require 'parser/rubymotion'
|
9
9
|
|
10
|
-
ALL_VERSIONS = %w(1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 mac ios)
|
10
|
+
ALL_VERSIONS = %w(1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 mac ios)
|
11
11
|
|
12
12
|
def setup
|
13
13
|
@diagnostics = []
|
@@ -27,6 +27,7 @@ module ParseHelper
|
|
27
27
|
when '2.5' then parser = Parser::Ruby25.new
|
28
28
|
when '2.6' then parser = Parser::Ruby26.new
|
29
29
|
when '2.7' then parser = Parser::Ruby27.new
|
30
|
+
when '2.8' then parser = Parser::Ruby28.new
|
30
31
|
when 'mac' then parser = Parser::MacRuby.new
|
31
32
|
when 'ios' then parser = Parser::RubyMotion.new
|
32
33
|
else raise "Unrecognized Ruby version #{version}"
|
data/test/test_current.rb
CHANGED
@@ -22,6 +22,8 @@ class TestCurrent < Minitest::Test
|
|
22
22
|
assert_equal Parser::Ruby26, Parser::CurrentRuby
|
23
23
|
when /^2\.7\.\d+/
|
24
24
|
assert_equal Parser::Ruby27, Parser::CurrentRuby
|
25
|
+
when /^2\.8\.\d+/
|
26
|
+
assert_equal Parser::Ruby28, Parser::CurrentRuby
|
25
27
|
else
|
26
28
|
flunk "Update test_current for #{RUBY_VERSION}"
|
27
29
|
end
|
data/test/test_lexer.rb
CHANGED
@@ -3569,6 +3569,18 @@ class TestLexer < Minitest::Test
|
|
3569
3569
|
:tIDENTIFIER, 're', [1, 3])
|
3570
3570
|
end
|
3571
3571
|
|
3572
|
+
def test_endless_method
|
3573
|
+
setup_lexer(28)
|
3574
|
+
|
3575
|
+
assert_scanned('def foo() = 42',
|
3576
|
+
:kDEF, "def", [0, 3],
|
3577
|
+
:tIDENTIFIER, "foo", [4, 7],
|
3578
|
+
:tLPAREN2, "(", [7, 8],
|
3579
|
+
:tRPAREN, ")", [8, 9],
|
3580
|
+
:tEQL, "=", [10, 11],
|
3581
|
+
:tINTEGER, 42, [12, 14])
|
3582
|
+
end
|
3583
|
+
|
3572
3584
|
def lex_numbered_parameter(input)
|
3573
3585
|
@lex.max_numparam_stack.push
|
3574
3586
|
|
data/test/test_parser.rb
CHANGED
@@ -29,6 +29,7 @@ class TestParser < Minitest::Test
|
|
29
29
|
SINCE_2_5 = SINCE_2_4 - %w(2.4)
|
30
30
|
SINCE_2_6 = SINCE_2_5 - %w(2.5)
|
31
31
|
SINCE_2_7 = SINCE_2_6 - %w(2.6)
|
32
|
+
SINCE_2_8 = SINCE_2_7 - %w(2.7)
|
32
33
|
|
33
34
|
# Guidelines for test naming:
|
34
35
|
# * Test structure follows structure of AST_FORMAT.md.
|
@@ -8933,6 +8934,22 @@ class TestParser < Minitest::Test
|
|
8933
8934
|
%q{ ~~~~~~~ location},
|
8934
8935
|
SINCE_2_7
|
8935
8936
|
)
|
8937
|
+
|
8938
|
+
assert_diagnoses(
|
8939
|
+
[:error, :pm_interp_in_var_name],
|
8940
|
+
%q{case a; in "#{a}": 1; end},
|
8941
|
+
%q{ ~~~~~~~ location},
|
8942
|
+
SINCE_2_7
|
8943
|
+
)
|
8944
|
+
end
|
8945
|
+
|
8946
|
+
def test_pattern_matching_invalid_lvar_name
|
8947
|
+
assert_diagnoses(
|
8948
|
+
[:error, :lvar_name, { name: :a? }],
|
8949
|
+
%q{case a; in a?:; end},
|
8950
|
+
%q{ ~~ location},
|
8951
|
+
SINCE_2_7
|
8952
|
+
)
|
8936
8953
|
end
|
8937
8954
|
|
8938
8955
|
def test_pattern_matching_keyword_variable
|
@@ -9427,4 +9444,82 @@ class TestParser < Minitest::Test
|
|
9427
9444
|
%{},
|
9428
9445
|
SINCE_1_9)
|
9429
9446
|
end
|
9447
|
+
|
9448
|
+
def test_endless_method
|
9449
|
+
assert_parses(
|
9450
|
+
s(:def_e, :foo,
|
9451
|
+
s(:args),
|
9452
|
+
s(:int, 42)),
|
9453
|
+
%q{def foo() = 42},
|
9454
|
+
%q{~~~ keyword
|
9455
|
+
| ~~~ name
|
9456
|
+
| ^ assignment
|
9457
|
+
|~~~~~~~~~~~~~~ expression},
|
9458
|
+
SINCE_2_8)
|
9459
|
+
|
9460
|
+
assert_parses(
|
9461
|
+
s(:def_e, :inc,
|
9462
|
+
s(:args, s(:arg, :x)),
|
9463
|
+
s(:send,
|
9464
|
+
s(:lvar, :x), :+,
|
9465
|
+
s(:int, 1))),
|
9466
|
+
%q{def inc(x) = x + 1},
|
9467
|
+
%q{~~~ keyword
|
9468
|
+
| ~~~ name
|
9469
|
+
| ^ assignment
|
9470
|
+
|~~~~~~~~~~~~~~~~~~ expression},
|
9471
|
+
SINCE_2_8)
|
9472
|
+
|
9473
|
+
assert_parses(
|
9474
|
+
s(:defs_e, s(:send, nil, :obj), :foo,
|
9475
|
+
s(:args),
|
9476
|
+
s(:int, 42)),
|
9477
|
+
%q{def obj.foo() = 42},
|
9478
|
+
%q{~~~ keyword
|
9479
|
+
| ^ operator
|
9480
|
+
| ~~~ name
|
9481
|
+
| ^ assignment
|
9482
|
+
|~~~~~~~~~~~~~~~~~~ expression},
|
9483
|
+
SINCE_2_8)
|
9484
|
+
|
9485
|
+
assert_parses(
|
9486
|
+
s(:defs_e, s(:send, nil, :obj), :inc,
|
9487
|
+
s(:args, s(:arg, :x)),
|
9488
|
+
s(:send,
|
9489
|
+
s(:lvar, :x), :+,
|
9490
|
+
s(:int, 1))),
|
9491
|
+
%q{def obj.inc(x) = x + 1},
|
9492
|
+
%q{~~~ keyword
|
9493
|
+
| ~~~ name
|
9494
|
+
| ^ operator
|
9495
|
+
| ^ assignment
|
9496
|
+
|~~~~~~~~~~~~~~~~~~~~~~ expression},
|
9497
|
+
SINCE_2_8)
|
9498
|
+
|
9499
|
+
assert_parses(
|
9500
|
+
s(:def_e, :foo,
|
9501
|
+
s(:forward_args),
|
9502
|
+
s(:send, nil, :bar,
|
9503
|
+
s(:forwarded_args))),
|
9504
|
+
%q{def foo(...) = bar(...)},
|
9505
|
+
%q{~~~ keyword
|
9506
|
+
| ~~~ name
|
9507
|
+
| ^ assignment
|
9508
|
+
|~~~~~~~~~~~~~~~~~~~~~~~ expression},
|
9509
|
+
SINCE_2_8)
|
9510
|
+
end
|
9511
|
+
|
9512
|
+
def test_endless_method_without_brackets
|
9513
|
+
assert_diagnoses(
|
9514
|
+
[:error, :unexpected_token, { :token => 'tEQL' }],
|
9515
|
+
%Q{def foo = 42},
|
9516
|
+
%q{ ^ location},
|
9517
|
+
SINCE_2_8)
|
9518
|
+
|
9519
|
+
assert_diagnoses(
|
9520
|
+
[:error, :unexpected_token, { :token => 'tEQL' }],
|
9521
|
+
%Q{def obj.foo = 42},
|
9522
|
+
%q{ ^ location},
|
9523
|
+
SINCE_2_8)
|
9524
|
+
end
|
9430
9525
|
end
|
@@ -151,6 +151,16 @@ DIAGNOSTIC
|
|
151
151
|
[:wrap, @hello, '[', ']']]
|
152
152
|
end
|
153
153
|
|
154
|
+
def test_inserts_on_empty_ranges
|
155
|
+
assert_actions_result 'puts({x}:hello[y], :world)',
|
156
|
+
[:insert_before, @hello.begin, '{'],
|
157
|
+
[:replace, @hello.begin, 'x'],
|
158
|
+
[:insert_after, @hello.begin, '}'],
|
159
|
+
[:insert_before, @hello.end, '['],
|
160
|
+
[:replace, @hello.end, 'y'],
|
161
|
+
[:insert_after, @hello.end, ']']
|
162
|
+
end
|
163
|
+
|
154
164
|
def test_replace_same_range
|
155
165
|
assert_actions_result 'puts(:hey, :world)',
|
156
166
|
[[:replace, @hello, ':hi'],
|
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.1.
|
4
|
+
version: 2.7.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- whitequark
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-04-
|
11
|
+
date: 2020-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ast
|
@@ -229,6 +229,8 @@ files:
|
|
229
229
|
- lib/parser/ruby26.y
|
230
230
|
- lib/parser/ruby27.rb
|
231
231
|
- lib/parser/ruby27.y
|
232
|
+
- lib/parser/ruby28.rb
|
233
|
+
- lib/parser/ruby28.y
|
232
234
|
- lib/parser/rubymotion.rb
|
233
235
|
- lib/parser/rubymotion.y
|
234
236
|
- lib/parser/runner.rb
|
@@ -242,6 +244,7 @@ files:
|
|
242
244
|
- lib/parser/source/map/condition.rb
|
243
245
|
- lib/parser/source/map/constant.rb
|
244
246
|
- lib/parser/source/map/definition.rb
|
247
|
+
- lib/parser/source/map/endless_definition.rb
|
245
248
|
- lib/parser/source/map/for.rb
|
246
249
|
- lib/parser/source/map/heredoc.rb
|
247
250
|
- lib/parser/source/map/index.rb
|
@@ -297,9 +300,9 @@ licenses:
|
|
297
300
|
- MIT
|
298
301
|
metadata:
|
299
302
|
bug_tracker_uri: https://github.com/whitequark/parser/issues
|
300
|
-
changelog_uri: https://github.com/whitequark/parser/blob/v2.7.1.
|
301
|
-
documentation_uri: https://www.rubydoc.info/gems/parser/2.7.1.
|
302
|
-
source_code_uri: https://github.com/whitequark/parser/tree/v2.7.1.
|
303
|
+
changelog_uri: https://github.com/whitequark/parser/blob/v2.7.1.2/CHANGELOG.md
|
304
|
+
documentation_uri: https://www.rubydoc.info/gems/parser/2.7.1.2
|
305
|
+
source_code_uri: https://github.com/whitequark/parser/tree/v2.7.1.2
|
303
306
|
post_install_message:
|
304
307
|
rdoc_options: []
|
305
308
|
require_paths:
|