parser 2.5.1.0 → 3.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/lib/parser.rb +4 -0
  3. data/lib/parser/all.rb +3 -0
  4. data/lib/parser/ast/processor.rb +49 -1
  5. data/lib/parser/base.rb +30 -6
  6. data/lib/parser/builders/default.rb +586 -29
  7. data/lib/parser/context.rb +17 -0
  8. data/lib/parser/current.rb +34 -7
  9. data/lib/parser/current_arg_stack.rb +46 -0
  10. data/lib/parser/diagnostic.rb +1 -1
  11. data/lib/parser/diagnostic/engine.rb +1 -2
  12. data/lib/parser/lexer.rb +23780 -0
  13. data/lib/parser/lexer/dedenter.rb +52 -49
  14. data/lib/parser/lexer/literal.rb +4 -0
  15. data/lib/parser/lexer/stack_state.rb +4 -0
  16. data/lib/parser/macruby.rb +6149 -0
  17. data/lib/parser/max_numparam_stack.rb +56 -0
  18. data/lib/parser/messages.rb +74 -44
  19. data/lib/parser/meta.rb +13 -3
  20. data/lib/parser/ruby18.rb +5667 -0
  21. data/lib/parser/ruby19.rb +6092 -0
  22. data/lib/parser/ruby20.rb +6527 -0
  23. data/lib/parser/ruby21.rb +6578 -0
  24. data/lib/parser/ruby22.rb +6613 -0
  25. data/lib/parser/ruby23.rb +6624 -0
  26. data/lib/parser/ruby24.rb +6694 -0
  27. data/lib/parser/ruby25.rb +6662 -0
  28. data/lib/parser/ruby26.rb +6676 -0
  29. data/lib/parser/ruby27.rb +7862 -0
  30. data/lib/parser/ruby28.rb +8047 -0
  31. data/lib/parser/ruby30.rb +8060 -0
  32. data/lib/parser/ruby31.rb +8075 -0
  33. data/lib/parser/rubymotion.rb +6086 -0
  34. data/lib/parser/runner.rb +36 -2
  35. data/lib/parser/runner/ruby_parse.rb +2 -2
  36. data/lib/parser/runner/ruby_rewrite.rb +2 -2
  37. data/lib/parser/source/buffer.rb +54 -29
  38. data/lib/parser/source/comment.rb +18 -5
  39. data/lib/parser/source/comment/associator.rb +34 -11
  40. data/lib/parser/source/map.rb +1 -1
  41. data/lib/parser/source/map/method_definition.rb +25 -0
  42. data/lib/parser/source/range.rb +20 -4
  43. data/lib/parser/source/tree_rewriter.rb +146 -16
  44. data/lib/parser/source/tree_rewriter/action.rb +137 -28
  45. data/lib/parser/static_environment.rb +14 -0
  46. data/lib/parser/tree_rewriter.rb +3 -3
  47. data/lib/parser/variables_stack.rb +36 -0
  48. data/lib/parser/version.rb +1 -1
  49. data/parser.gemspec +13 -21
  50. metadata +34 -98
  51. data/.gitignore +0 -32
  52. data/.travis.yml +0 -21
  53. data/.yardopts +0 -21
  54. data/CHANGELOG.md +0 -909
  55. data/CONTRIBUTING.md +0 -17
  56. data/Gemfile +0 -10
  57. data/README.md +0 -301
  58. data/Rakefile +0 -165
  59. data/doc/AST_FORMAT.md +0 -1718
  60. data/doc/CUSTOMIZATION.md +0 -37
  61. data/doc/INTERNALS.md +0 -21
  62. data/doc/css/.gitkeep +0 -0
  63. data/doc/css/common.css +0 -68
  64. data/lib/parser/lexer.rl +0 -2376
  65. data/lib/parser/macruby.y +0 -2198
  66. data/lib/parser/ruby18.y +0 -1934
  67. data/lib/parser/ruby19.y +0 -2175
  68. data/lib/parser/ruby20.y +0 -2353
  69. data/lib/parser/ruby21.y +0 -2357
  70. data/lib/parser/ruby22.y +0 -2364
  71. data/lib/parser/ruby23.y +0 -2370
  72. data/lib/parser/ruby24.y +0 -2395
  73. data/lib/parser/ruby25.y +0 -2392
  74. data/lib/parser/ruby26.y +0 -2392
  75. data/lib/parser/rubymotion.y +0 -2182
  76. data/test/bug_163/fixtures/input.rb +0 -5
  77. data/test/bug_163/fixtures/output.rb +0 -5
  78. data/test/bug_163/rewriter.rb +0 -20
  79. data/test/helper.rb +0 -52
  80. data/test/parse_helper.rb +0 -315
  81. data/test/racc_coverage_helper.rb +0 -133
  82. data/test/test_base.rb +0 -31
  83. data/test/test_current.rb +0 -27
  84. data/test/test_diagnostic.rb +0 -96
  85. data/test/test_diagnostic_engine.rb +0 -62
  86. data/test/test_encoding.rb +0 -99
  87. data/test/test_lexer.rb +0 -3537
  88. data/test/test_lexer_stack_state.rb +0 -78
  89. data/test/test_parse_helper.rb +0 -80
  90. data/test/test_parser.rb +0 -6968
  91. data/test/test_runner_rewrite.rb +0 -47
  92. data/test/test_source_buffer.rb +0 -162
  93. data/test/test_source_comment.rb +0 -36
  94. data/test/test_source_comment_associator.rb +0 -367
  95. data/test/test_source_map.rb +0 -15
  96. data/test/test_source_range.rb +0 -172
  97. data/test/test_source_rewriter.rb +0 -541
  98. data/test/test_source_rewriter_action.rb +0 -46
  99. data/test/test_source_tree_rewriter.rb +0 -173
  100. data/test/test_static_environment.rb +0 -45
  101. data/test/using_tree_rewriter/fixtures/input.rb +0 -3
  102. data/test/using_tree_rewriter/fixtures/output.rb +0 -3
  103. data/test/using_tree_rewriter/using_tree_rewriter.rb +0 -9
data/lib/parser/runner.rb CHANGED
@@ -14,9 +14,8 @@ module Parser
14
14
  end
15
15
 
16
16
  def initialize
17
- Parser::Builders::Default.modernize
18
-
19
17
  @option_parser = OptionParser.new { |opts| setup_option_parsing(opts) }
18
+ @legacy = {}
20
19
  @parser_class = nil
21
20
  @parser = nil
22
21
  @files = []
@@ -30,6 +29,7 @@ module Parser
30
29
 
31
30
  def execute(options)
32
31
  parse_options(options)
32
+ setup_builder_default
33
33
  prepare_parser
34
34
 
35
35
  process_all_input
@@ -37,6 +37,8 @@ module Parser
37
37
 
38
38
  private
39
39
 
40
+ LEGACY_MODES = %i[lambda procarg0 encoding index arg_inside_procarg0 forward_arg kwargs match_pattern].freeze
41
+
40
42
  def runner_name
41
43
  raise NotImplementedError, "implement #{self.class}##{__callee__}"
42
44
  end
@@ -106,6 +108,21 @@ module Parser
106
108
  @parser_class = Parser::Ruby26
107
109
  end
108
110
 
111
+ opts.on '--27', 'Parse as Ruby 2.7 would' do
112
+ require 'parser/ruby27'
113
+ @parser_class = Parser::Ruby27
114
+ end
115
+
116
+ opts.on '--30', 'Parse as Ruby 3.0 would' do
117
+ require 'parser/ruby30'
118
+ @parser_class = Parser::Ruby30
119
+ end
120
+
121
+ opts.on '--31', 'Parse as Ruby 3.1 would' do
122
+ require 'parser/ruby31'
123
+ @parser_class = Parser::Ruby31
124
+ end
125
+
109
126
  opts.on '--mac', 'Parse as MacRuby 0.12 would' do
110
127
  require 'parser/macruby'
111
128
  @parser_class = Parser::MacRuby
@@ -116,6 +133,17 @@ module Parser
116
133
  @parser_class = Parser::RubyMotion
117
134
  end
118
135
 
136
+ opts.on '--legacy', "Parse with all legacy modes" do
137
+ @legacy = Hash.new(true)
138
+ end
139
+
140
+ LEGACY_MODES.each do |mode|
141
+ opt_name = "--legacy-#{mode.to_s.gsub('_', '-')}"
142
+ opts.on opt_name, "Parse with legacy mode for emit_#{mode}" do
143
+ @legacy[mode] = true
144
+ end
145
+ end
146
+
119
147
  opts.on '-w', '--warnings', 'Enable warnings' do |w|
120
148
  @warnings = w
121
149
  end
@@ -154,6 +182,12 @@ module Parser
154
182
  end
155
183
  end
156
184
 
185
+ def setup_builder_default
186
+ LEGACY_MODES.each do |mode|
187
+ Parser::Builders::Default.send(:"emit_#{mode}=", !@legacy[mode])
188
+ end
189
+ end
190
+
157
191
  def prepare_parser
158
192
  @parser = @parser_class.new
159
193
 
@@ -123,7 +123,7 @@ module Parser
123
123
  opts.on '--emit-ruby', 'Emit S-expressions as valid Ruby code' do
124
124
  @emit_ruby = true
125
125
  end
126
-
126
+
127
127
  opts.on '--emit-json', 'Emit S-expressions as valid JSON' do
128
128
  @emit_json = true
129
129
  end
@@ -146,7 +146,7 @@ module Parser
146
146
  if @emit_ruby
147
147
  puts ast.inspect
148
148
  elsif @emit_json
149
- puts JSON.generate(ast.to_sexp_array)
149
+ puts(ast ? JSON.generate(ast.to_sexp_array) : nil)
150
150
  else
151
151
  puts ast.to_s
152
152
  end
@@ -55,8 +55,8 @@ module Parser
55
55
  new_source = rewriter.rewrite(buffer, ast)
56
56
 
57
57
  new_buffer = Source::Buffer.new(initial_buffer.name +
58
- '|after ' + rewriter_class.name)
59
- new_buffer.source = new_source
58
+ '|after ' + rewriter_class.name,
59
+ source: new_source)
60
60
 
61
61
  @parser.reset
62
62
  new_ast = @parser.parse(new_buffer)
@@ -46,7 +46,7 @@ module Parser
46
46
  # magic encoding comment or UTF-8 BOM. `string` can be in any encoding.
47
47
  #
48
48
  # @param [String] string
49
- # @return [String|nil] encoding name, if recognized
49
+ # @return [String, nil] encoding name, if recognized
50
50
  #
51
51
  def self.recognize_encoding(string)
52
52
  return if string.empty?
@@ -102,7 +102,7 @@ module Parser
102
102
  end
103
103
  end
104
104
 
105
- def initialize(name, first_line = 1)
105
+ def initialize(name, first_line = 1, source: nil)
106
106
  @name = name.to_s
107
107
  @source = nil
108
108
  @first_line = first_line
@@ -114,8 +114,9 @@ module Parser
114
114
  @slice_source = nil
115
115
 
116
116
  # Cache for fast lookup
117
- @line_for_position = {}
118
- @column_for_position = {}
117
+ @line_index_for_position = {}
118
+
119
+ self.source = source if source
119
120
  end
120
121
 
121
122
  ##
@@ -205,9 +206,10 @@ module Parser
205
206
  # @return [[Integer, Integer]] `[line, column]`
206
207
  #
207
208
  def decompose_position(position)
208
- line_no, line_begin = line_for(position)
209
+ line_index = line_index_for_position(position)
210
+ line_begin = line_begins[line_index]
209
211
 
210
- [ @first_line + line_no, position - line_begin ]
212
+ [ @first_line + line_index , position - line_begin ]
211
213
  end
212
214
 
213
215
  ##
@@ -218,10 +220,7 @@ module Parser
218
220
  # @api private
219
221
  #
220
222
  def line_for_position(position)
221
- @line_for_position[position] ||= begin
222
- line_no, _ = line_for(position)
223
- @first_line + line_no
224
- end
223
+ line_index_for_position(position) + @first_line
225
224
  end
226
225
 
227
226
  ##
@@ -232,10 +231,8 @@ module Parser
232
231
  # @api private
233
232
  #
234
233
  def column_for_position(position)
235
- @column_for_position[position] ||= begin
236
- _, line_begin = line_for(position)
237
- position - line_begin
238
- end
234
+ line_index = line_index_for_position(position)
235
+ position - line_begins[line_index]
239
236
  end
240
237
 
241
238
  ##
@@ -276,15 +273,13 @@ module Parser
276
273
  # @raise [IndexError] if `lineno` is out of bounds
277
274
  #
278
275
  def line_range(lineno)
279
- index = lineno - @first_line + 1
280
- if index <= 0 || index > line_begins.size
276
+ index = lineno - @first_line
277
+ if index < 0 || index + 1 >= line_begins.size
281
278
  raise IndexError, 'Parser::Source::Buffer: range for line ' \
282
279
  "#{lineno} requested, valid line numbers are #{@first_line}.." \
283
- "#{@first_line + line_begins.size - 1}"
284
- elsif index == line_begins.size
285
- Range.new(self, line_begins[-index][1], @source.size)
280
+ "#{@first_line + line_begins.size - 2}"
286
281
  else
287
- Range.new(self, line_begins[-index][1], line_begins[-index - 1][1] - 1)
282
+ Range.new(self, line_begins[index], line_begins[index + 1] - 1)
288
283
  end
289
284
  end
290
285
 
@@ -301,27 +296,57 @@ module Parser
301
296
  # @return [Integer]
302
297
  #
303
298
  def last_line
304
- line_begins.size + @first_line - 1
299
+ line_begins.size + @first_line - 2
300
+ end
301
+
302
+ # :nodoc:
303
+ def freeze
304
+ source_lines; line_begins; source_range # build cache
305
+ super
306
+ end
307
+
308
+ # :nodoc:
309
+ def inspect
310
+ "#<#{self.class} #{name}>"
305
311
  end
306
312
 
307
313
  private
308
314
 
315
+ # @returns [0, line_begin_of_line_1, ..., source.size + 1]
309
316
  def line_begins
310
- unless @line_begins
311
- @line_begins, index = [ [ 0, 0 ] ], 0
312
-
317
+ @line_begins ||= begin
318
+ begins = [0]
319
+ index = 0
313
320
  while index = @source.index("\n".freeze, index)
314
321
  index += 1
315
- @line_begins.unshift [ @line_begins.length, index ]
322
+ begins << index
316
323
  end
324
+ begins << @source.size + 1
325
+ begins
317
326
  end
327
+ end
318
328
 
319
- @line_begins
329
+ # @returns 0-based line index of position
330
+ def line_index_for_position(position)
331
+ @line_index_for_position[position] || begin
332
+ index = bsearch(line_begins, position) - 1
333
+ @line_index_for_position[position] = index unless @line_index_for_position.frozen?
334
+ index
335
+ end
320
336
  end
321
337
 
322
- def line_for(position)
323
- line_begins.bsearch do |line, line_begin|
324
- line_begin <= position
338
+ if Array.method_defined?(:bsearch_index) # RUBY_VERSION >= 2.3
339
+ def bsearch(line_begins, position)
340
+ line_begins.bsearch_index do |line_begin|
341
+ position < line_begin
342
+ end || line_begins.size - 1 # || only for out of bound values
343
+ end
344
+ else
345
+ def bsearch(line_begins, position)
346
+ @line_range ||= 0...line_begins.size
347
+ @line_range.bsearch do |i|
348
+ position < line_begins[i]
349
+ end || line_begins.size - 1 # || only for out of bound values
325
350
  end
326
351
  end
327
352
  end
@@ -10,7 +10,7 @@ module Parser
10
10
  # @return [String]
11
11
  #
12
12
  # @!attribute [r] location
13
- # @return [Parser::Source::Map]
13
+ # @return [Parser::Source::Range]
14
14
  #
15
15
  # @api public
16
16
  #
@@ -24,8 +24,8 @@ module Parser
24
24
  # Associate `comments` with `ast` nodes by their corresponding node.
25
25
  #
26
26
  # @param [Parser::AST::Node] ast
27
- # @param [Array(Comment)] comments
28
- # @return [Hash(Parser::AST::Node, Array(Comment))]
27
+ # @param [Array<Comment>] comments
28
+ # @return [Hash<Parser::AST::Node, Array<Comment>>]
29
29
  # @see Parser::Source::Comment::Associator#associate
30
30
  # @deprecated Use {associate_locations}.
31
31
  #
@@ -39,8 +39,8 @@ module Parser
39
39
  # source.
40
40
  #
41
41
  # @param [Parser::AST::Node] ast
42
- # @param [Array(Comment)] comments
43
- # @return [Hash(Parser::Source::Map, Array(Comment))]
42
+ # @param [Array<Comment>] comments
43
+ # @return [Hash<Parser::Source::Map, Array<Comment>>]
44
44
  # @see Parser::Source::Comment::Associator#associate_locations
45
45
  #
46
46
  def self.associate_locations(ast, comments)
@@ -48,6 +48,19 @@ module Parser
48
48
  associator.associate_locations
49
49
  end
50
50
 
51
+ ##
52
+ # Associate `comments` with `ast` nodes using identity.
53
+ #
54
+ # @param [Parser::AST::Node] ast
55
+ # @param [Array<Comment>] comments
56
+ # @return [Hash<Parser::Source::Node, Array<Comment>>]
57
+ # @see Parser::Source::Comment::Associator#associate_by_identity
58
+ #
59
+ def self.associate_by_identity(ast, comments)
60
+ associator = Associator.new(ast, comments)
61
+ associator.associate_by_identity
62
+ end
63
+
51
64
  ##
52
65
  # @param [Parser::Source::Range] range
53
66
  #
@@ -47,7 +47,7 @@ module Parser
47
47
 
48
48
  ##
49
49
  # @param [Parser::AST::Node] ast
50
- # @param [Array(Parser::Source::Comment)] comments
50
+ # @param [Array<Parser::Source::Comment>] comments
51
51
  def initialize(ast, comments)
52
52
  @ast = ast
53
53
  @comments = comments
@@ -84,12 +84,24 @@ module Parser
84
84
  #
85
85
  # Note that {associate} produces unexpected result for nodes which are
86
86
  # equal but have distinct locations; comments for these nodes are merged.
87
+ # You may prefer using {associate_by_identity} or {associate_locations}.
87
88
  #
88
- # @return [Hash(Parser::AST::Node, Array(Parser::Source::Comment))]
89
+ # @return [Hash<Parser::AST::Node, Array<Parser::Source::Comment>>]
89
90
  # @deprecated Use {associate_locations}.
90
91
  #
91
92
  def associate
92
- @map_using_locations = false
93
+ @map_using = :eql
94
+ do_associate
95
+ end
96
+
97
+ ##
98
+ # Same as {associate}, but compares by identity, thus producing an unambiguous
99
+ # result even in presence of equal nodes.
100
+ #
101
+ # @return [Hash<Parser::Source::Node, Array<Parser::Source::Comment>>]
102
+ #
103
+ def associate_locations
104
+ @map_using = :location
93
105
  do_associate
94
106
  end
95
107
 
@@ -98,17 +110,31 @@ module Parser
98
110
  # the hash key, thus producing an unambiguous result even in presence
99
111
  # of equal nodes.
100
112
  #
101
- # @return [Hash(Parser::Source::Map, Array(Parser::Source::Comment))]
113
+ # @return [Hash<Parser::Source::Map, Array<Parser::Source::Comment>>]
102
114
  #
103
- def associate_locations
104
- @map_using_locations = true
115
+ def associate_by_identity
116
+ @map_using = :identity
105
117
  do_associate
106
118
  end
107
119
 
108
120
  private
109
121
 
122
+ POSTFIX_TYPES = Set[:if, :while, :while_post, :until, :until_post, :masgn].freeze
123
+ def children_in_source_order(node)
124
+ if POSTFIX_TYPES.include?(node.type)
125
+ # All these types have either nodes with expressions, or `nil`
126
+ # so a compact will do, but they need to be sorted.
127
+ node.children.compact.sort_by { |child| child.loc.expression.begin_pos }
128
+ else
129
+ node.children.select do |child|
130
+ child.is_a?(AST::Node) && child.loc && child.loc.expression
131
+ end
132
+ end
133
+ end
134
+
110
135
  def do_associate
111
136
  @mapping = Hash.new { |h, k| h[k] = [] }
137
+ @mapping.compare_by_identity if @map_using == :identity
112
138
  @comment_num = -1
113
139
  advance_comment
114
140
 
@@ -131,10 +157,7 @@ module Parser
131
157
  node_loc = node.location
132
158
  if @current_comment.location.line <= node_loc.last_line ||
133
159
  node_loc.is_a?(Map::Heredoc)
134
- node.children.each do |child|
135
- next unless child.is_a?(AST::Node) && child.loc && child.loc.expression
136
- visit(child)
137
- end
160
+ children_in_source_order(node).each { |child| visit(child) }
138
161
 
139
162
  process_trailing_comments(node)
140
163
  end
@@ -181,7 +204,7 @@ module Parser
181
204
  end
182
205
 
183
206
  def associate_and_advance_comment(node)
184
- key = @map_using_locations ? node.location : node
207
+ key = @map_using == :location ? node.location : node
185
208
  @mapping[key] << @current_comment
186
209
  advance_comment
187
210
  end
@@ -161,7 +161,7 @@ module Parser
161
161
  # # :expression => #<Source::Range (string) 0...6>
162
162
  # # }
163
163
  #
164
- # @return [Hash(Symbol, Parser::Source::Range)]
164
+ # @return [Hash<Symbol, Parser::Source::Range>]
165
165
  #
166
166
  def to_hash
167
167
  instance_variables.inject({}) do |hash, ivar|
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Parser
4
+ module Source
5
+
6
+ class Map::MethodDefinition < Map
7
+ attr_reader :keyword
8
+ attr_reader :operator
9
+ attr_reader :name
10
+ attr_reader :end
11
+ attr_reader :assignment
12
+
13
+ def initialize(keyword_l, operator_l, name_l, end_l, assignment_l, body_l)
14
+ @keyword = keyword_l
15
+ @operator = operator_l
16
+ @name = name_l
17
+ @end = end_l
18
+ @assignment = assignment_l
19
+
20
+ super(@keyword.join(end_l || body_l))
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -13,7 +13,7 @@ module Parser
13
13
  # ^^
14
14
  #
15
15
  # @!attribute [r] source_buffer
16
- # @return [Parser::Diagnostic::Engine]
16
+ # @return [Parser::Source::Buffer]
17
17
  #
18
18
  # @!attribute [r] begin_pos
19
19
  # @return [Integer] index of the first character in the range
@@ -112,11 +112,11 @@ module Parser
112
112
  # @raise RangeError
113
113
  #
114
114
  def column_range
115
- if self.begin.line != self.end.line
115
+ if line != last_line
116
116
  raise RangeError, "#{self.inspect} spans more than one line"
117
117
  end
118
118
 
119
- self.begin.column...self.end.column
119
+ column...last_column
120
120
  end
121
121
 
122
122
  ##
@@ -143,12 +143,19 @@ module Parser
143
143
  end
144
144
 
145
145
  ##
146
- # @return [Array(Integer)] a set of character indexes contained in this range.
146
+ # @return [Array<Integer>] a set of character indexes contained in this range.
147
147
  #
148
148
  def to_a
149
149
  (@begin_pos...@end_pos).to_a
150
150
  end
151
151
 
152
+ ##
153
+ # @return [Range] a Ruby range with the same `begin_pos` and `end_pos`
154
+ #
155
+ def to_range
156
+ self.begin_pos...self.end_pos
157
+ end
158
+
152
159
  ##
153
160
  # Composes a GNU/Clang-style string representation of the beginning of this
154
161
  # range.
@@ -298,6 +305,15 @@ module Parser
298
305
  (@end_pos <=> other.end_pos)
299
306
  end
300
307
 
308
+ alias_method :eql?, :==
309
+
310
+ ##
311
+ # Support for Ranges be used in as Hash indices and in Sets.
312
+ #
313
+ def hash
314
+ [@source_buffer, @begin_pos, @end_pos].hash
315
+ end
316
+
301
317
  ##
302
318
  # @return [String] a human-readable representation of this range.
303
319
  #