parser 2.7.1.1 → 2.7.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|