parser 2.7.1.5 → 3.0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/lib/parser/all.rb +1 -0
  3. data/lib/parser/ast/processor.rb +3 -0
  4. data/lib/parser/base.rb +1 -0
  5. data/lib/parser/builders/default.rb +126 -13
  6. data/lib/parser/context.rb +4 -0
  7. data/lib/parser/current.rb +16 -7
  8. data/lib/parser/current_arg_stack.rb +5 -2
  9. data/lib/parser/lexer.rb +872 -802
  10. data/lib/parser/max_numparam_stack.rb +12 -4
  11. data/lib/parser/messages.rb +1 -0
  12. data/lib/parser/meta.rb +4 -3
  13. data/lib/parser/ruby18.rb +6 -2
  14. data/lib/parser/ruby27.rb +3763 -3704
  15. data/lib/parser/ruby28.rb +8047 -0
  16. data/lib/parser/ruby30.rb +3657 -3649
  17. data/lib/parser/ruby31.rb +8226 -0
  18. data/lib/parser/runner.rb +6 -1
  19. data/lib/parser/source/buffer.rb +50 -27
  20. data/lib/parser/source/comment.rb +13 -0
  21. data/lib/parser/source/comment/associator.rb +17 -4
  22. data/lib/parser/source/tree_rewriter.rb +27 -0
  23. data/lib/parser/static_environment.rb +4 -0
  24. data/lib/parser/variables_stack.rb +4 -0
  25. data/lib/parser/version.rb +1 -1
  26. data/parser.gemspec +1 -20
  27. metadata +8 -95
  28. data/.travis.yml +0 -41
  29. data/.yardopts +0 -21
  30. data/CHANGELOG.md +0 -1137
  31. data/CONTRIBUTING.md +0 -17
  32. data/Gemfile +0 -10
  33. data/README.md +0 -309
  34. data/Rakefile +0 -167
  35. data/ci/run_rubocop_specs +0 -14
  36. data/doc/AST_FORMAT.md +0 -2284
  37. data/doc/CUSTOMIZATION.md +0 -37
  38. data/doc/INTERNALS.md +0 -21
  39. data/doc/css/.gitkeep +0 -0
  40. data/doc/css/common.css +0 -68
  41. data/lib/parser/lexer.rl +0 -2550
  42. data/lib/parser/macruby.y +0 -2208
  43. data/lib/parser/ruby18.y +0 -1936
  44. data/lib/parser/ruby19.y +0 -2185
  45. data/lib/parser/ruby20.y +0 -2363
  46. data/lib/parser/ruby21.y +0 -2364
  47. data/lib/parser/ruby22.y +0 -2371
  48. data/lib/parser/ruby23.y +0 -2377
  49. data/lib/parser/ruby24.y +0 -2415
  50. data/lib/parser/ruby25.y +0 -2412
  51. data/lib/parser/ruby26.y +0 -2420
  52. data/lib/parser/ruby27.y +0 -2949
  53. data/lib/parser/ruby30.y +0 -3048
  54. data/lib/parser/rubymotion.y +0 -2192
  55. data/test/bug_163/fixtures/input.rb +0 -5
  56. data/test/bug_163/fixtures/output.rb +0 -5
  57. data/test/bug_163/rewriter.rb +0 -20
  58. data/test/helper.rb +0 -103
  59. data/test/parse_helper.rb +0 -328
  60. data/test/racc_coverage_helper.rb +0 -133
  61. data/test/test_ast_processor.rb +0 -32
  62. data/test/test_base.rb +0 -31
  63. data/test/test_current.rb +0 -31
  64. data/test/test_diagnostic.rb +0 -95
  65. data/test/test_diagnostic_engine.rb +0 -59
  66. data/test/test_encoding.rb +0 -99
  67. data/test/test_lexer.rb +0 -3617
  68. data/test/test_lexer_stack_state.rb +0 -78
  69. data/test/test_meta.rb +0 -12
  70. data/test/test_parse_helper.rb +0 -83
  71. data/test/test_parser.rb +0 -9986
  72. data/test/test_runner_parse.rb +0 -56
  73. data/test/test_runner_rewrite.rb +0 -47
  74. data/test/test_source_buffer.rb +0 -165
  75. data/test/test_source_comment.rb +0 -36
  76. data/test/test_source_comment_associator.rb +0 -399
  77. data/test/test_source_map.rb +0 -14
  78. data/test/test_source_range.rb +0 -192
  79. data/test/test_source_rewriter.rb +0 -541
  80. data/test/test_source_rewriter_action.rb +0 -46
  81. data/test/test_source_tree_rewriter.rb +0 -361
  82. data/test/test_static_environment.rb +0 -45
  83. data/test/using_tree_rewriter/fixtures/input.rb +0 -3
  84. data/test/using_tree_rewriter/fixtures/output.rb +0 -3
  85. data/test/using_tree_rewriter/using_tree_rewriter.rb +0 -9
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if(true)
4
- puts "Hello, world!"
5
- end
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if true
4
- puts "Hello, world!"
5
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Rewriter < Parser::Rewriter
4
- def on_if(node)
5
- # Crude, totally-not-usable-in-the-real-world code to remove optional
6
- # parens from control keywords.
7
- #
8
- # In a perfect test scenario we'd simply make this a no-op, to demonstrate
9
- # that the bug happens when any rewriter is loaded regardless of whether it
10
- # actually changes anything but that makes assertions much harder to get
11
- # right. It's much easier to just show that the file did, or did not
12
- # get changed.
13
- if node.children[0].type == :begin
14
- replace node.children[0].loc.begin, ' '
15
- remove node.children[0].loc.end
16
- end
17
-
18
- super
19
- end
20
- end
data/test/helper.rb DELETED
@@ -1,103 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'tempfile'
4
- require 'simplecov'
5
-
6
- if ENV.include?('COVERAGE') && SimpleCov.usable?
7
- require_relative 'racc_coverage_helper'
8
-
9
- RaccCoverage.start(
10
- %w(
11
- ruby18.y
12
- ruby19.y
13
- ruby20.y
14
- ruby21.y
15
- ruby22.y
16
- ruby23.y
17
- ruby24.y
18
- ruby25.y
19
- ruby26.y
20
- ruby27.y
21
- ruby30.y
22
- ),
23
- File.expand_path('../../lib/parser', __FILE__))
24
-
25
- # Report results faster.
26
- at_exit { RaccCoverage.stop }
27
-
28
- SimpleCov.start do
29
- self.formatter = SimpleCov::Formatter::MultiFormatter.new(
30
- SimpleCov::Formatter::HTMLFormatter
31
- )
32
-
33
- add_group 'Grammars' do |source_file|
34
- source_file.filename =~ %r{\.y$}
35
- end
36
-
37
- # Exclude the testsuite itself.
38
- add_filter '/test/'
39
-
40
- # Exclude generated files.
41
- add_filter do |source_file|
42
- source_file.filename =~ %r{/lib/parser/(lexer|ruby\d+|macruby|rubymotion)\.rb$}
43
- end
44
- end
45
- end
46
-
47
- # minitest/autorun must go after SimpleCov to preserve
48
- # correct order of at_exit hooks.
49
- require 'minitest/autorun'
50
-
51
- $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
52
- require 'parser'
53
-
54
- module NodeCollector
55
- extend self
56
- attr_accessor :callbacks, :nodes
57
- self.callbacks = []
58
- self.nodes = []
59
-
60
- def check
61
- @callbacks.each do |callback|
62
- @nodes.each { |node| callback.call(node) }
63
- end
64
- puts "#{callbacks.size} additional tests on #{nodes.size} nodes ran successfully"
65
- end
66
-
67
- Minitest.after_run { check }
68
- end
69
-
70
- def for_each_node(&block)
71
- NodeCollector.callbacks << block
72
- end
73
-
74
- class Parser::AST::Node
75
- def initialize(type, *)
76
- NodeCollector.nodes << self
77
- super
78
- end
79
- end
80
-
81
- # Special test extension that records a context of the parser
82
- # for any node that is created
83
- module NodeContextExt
84
- module NodeExt
85
- attr_reader :context
86
-
87
- def assign_properties(properties)
88
- super
89
-
90
- if (context = properties[:context])
91
- @context = context
92
- end
93
- end
94
- end
95
- Parser::AST::Node.prepend(NodeExt)
96
-
97
- module BuilderExt
98
- def n(type, children, source_map)
99
- super.updated(nil, nil, context: @parser.context.stack.dup)
100
- end
101
- end
102
- Parser::Builders::Default.prepend(BuilderExt)
103
- end
data/test/parse_helper.rb DELETED
@@ -1,328 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ParseHelper
4
- include AST::Sexp
5
-
6
- require 'parser/all'
7
- require 'parser/macruby'
8
- require 'parser/rubymotion'
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 3.0 mac ios)
11
-
12
- def setup
13
- @diagnostics = []
14
-
15
- super if defined?(super)
16
- end
17
-
18
- def parser_for_ruby_version(version)
19
- case version
20
- when '1.8' then parser = Parser::Ruby18.new
21
- when '1.9' then parser = Parser::Ruby19.new
22
- when '2.0' then parser = Parser::Ruby20.new
23
- when '2.1' then parser = Parser::Ruby21.new
24
- when '2.2' then parser = Parser::Ruby22.new
25
- when '2.3' then parser = Parser::Ruby23.new
26
- when '2.4' then parser = Parser::Ruby24.new
27
- when '2.5' then parser = Parser::Ruby25.new
28
- when '2.6' then parser = Parser::Ruby26.new
29
- when '2.7' then parser = Parser::Ruby27.new
30
- when '3.0' then parser = Parser::Ruby30.new
31
- when 'mac' then parser = Parser::MacRuby.new
32
- when 'ios' then parser = Parser::RubyMotion.new
33
- else raise "Unrecognized Ruby version #{version}"
34
- end
35
-
36
- parser.diagnostics.consumer = lambda do |diagnostic|
37
- @diagnostics << diagnostic
38
- end
39
-
40
- parser
41
- end
42
-
43
- def with_versions(versions)
44
- (versions & ALL_VERSIONS).each do |version|
45
- @diagnostics.clear
46
-
47
- parser = parser_for_ruby_version(version)
48
- yield version, parser
49
- end
50
- end
51
-
52
- def assert_source_range(expect_range, range, version, what)
53
- if expect_range == nil
54
- # Avoid "Use assert_nil if expecting nil from .... This will fail in Minitest 6.""
55
- assert_nil range,
56
- "(#{version}) range of #{what}"
57
- else
58
- assert range.is_a?(Parser::Source::Range),
59
- "(#{version}) #{range.inspect}.is_a?(Source::Range) for #{what}"
60
- assert_equal expect_range, range.to_range,
61
- "(#{version}) range of #{what}"
62
- end
63
- end
64
-
65
- # Use like this:
66
- # ~~~
67
- # assert_parses(
68
- # s(:send, s(:lit, 10), :+, s(:lit, 20))
69
- # %q{10 + 20},
70
- # %q{~~~~~~~ expression
71
- # | ^ operator
72
- # | ~~ expression (lit)
73
- # },
74
- # %w(1.8 1.9) # optional
75
- # )
76
- # ~~~
77
- def assert_parses(ast, code, source_maps='', versions=ALL_VERSIONS)
78
- with_versions(versions) do |version, parser|
79
- try_parsing(ast, code, parser, source_maps, version)
80
- end
81
-
82
- # Also try parsing with lexer set to use UTF-32LE internally
83
- with_versions(versions) do |version, parser|
84
- parser.instance_eval { @lexer.force_utf32 = true }
85
- try_parsing(ast, code, parser, source_maps, version)
86
- end
87
- end
88
-
89
- def try_parsing(ast, code, parser, source_maps, version)
90
- source_file = Parser::Source::Buffer.new('(assert_parses)', source: code)
91
-
92
- begin
93
- parsed_ast = parser.parse(source_file)
94
- rescue => exc
95
- backtrace = exc.backtrace
96
- Exception.instance_method(:initialize).bind(exc).
97
- call("(#{version}) #{exc.message}")
98
- exc.set_backtrace(backtrace)
99
- raise
100
- end
101
-
102
- if ast.nil?
103
- assert_nil parsed_ast, "(#{version}) AST equality"
104
- return
105
- end
106
-
107
- assert_equal ast, parsed_ast,
108
- "(#{version}) AST equality"
109
-
110
- parse_source_map_descriptions(source_maps) do |range, map_field, ast_path, line|
111
-
112
- astlet = traverse_ast(parsed_ast, ast_path)
113
-
114
- if astlet.nil?
115
- # This is a testsuite bug.
116
- raise "No entity with AST path #{ast_path} in #{parsed_ast.inspect}"
117
- end
118
-
119
- assert astlet.frozen?
120
-
121
- assert astlet.location.respond_to?(map_field),
122
- "(#{version}) #{astlet.location.inspect}.respond_to?(#{map_field.inspect}) for:\n#{parsed_ast.inspect}"
123
-
124
- found_range = astlet.location.send(map_field)
125
-
126
- assert_source_range(range, found_range, version, line.inspect)
127
- end
128
-
129
- assert parser.instance_eval { @lexer }.cmdarg.empty?,
130
- "(#{version}) expected cmdarg to be empty after parsing"
131
-
132
- assert_equal 0, parser.instance_eval { @lexer.instance_eval { @paren_nest } },
133
- "(#{version}) expected paren_nest to be 0 after parsing"
134
- end
135
-
136
- # Use like this:
137
- # ~~~
138
- # assert_diagnoses(
139
- # [:warning, :ambiguous_prefix, { prefix: '*' }],
140
- # %q{foo *bar},
141
- # %q{ ^ location
142
- # | ~~~ highlights (0)})
143
- # ~~~
144
- def assert_diagnoses(diagnostic, code, source_maps='', versions=ALL_VERSIONS)
145
- with_versions(versions) do |version, parser|
146
- source_file = Parser::Source::Buffer.new('(assert_diagnoses)', source: code)
147
-
148
- begin
149
- parser = parser.parse(source_file)
150
- rescue Parser::SyntaxError
151
- # do nothing; the diagnostic was reported
152
- end
153
-
154
- assert_equal 1, @diagnostics.count,
155
- "(#{version}) emits a single diagnostic, not\n" \
156
- "#{@diagnostics.map(&:render).join("\n")}"
157
-
158
- emitted_diagnostic = @diagnostics.first
159
-
160
- level, reason, arguments = diagnostic
161
- arguments ||= {}
162
- message = Parser::Messages.compile(reason, arguments)
163
-
164
- assert_equal level, emitted_diagnostic.level
165
- assert_equal reason, emitted_diagnostic.reason
166
- assert_equal arguments, emitted_diagnostic.arguments
167
- assert_equal message, emitted_diagnostic.message
168
-
169
- parse_source_map_descriptions(source_maps) do |range, map_field, ast_path, line|
170
-
171
- case map_field
172
- when 'location'
173
- assert_source_range range,
174
- emitted_diagnostic.location,
175
- version, 'location'
176
-
177
- when 'highlights'
178
- index = ast_path.first.to_i
179
-
180
- assert_source_range range,
181
- emitted_diagnostic.highlights[index],
182
- version, "#{index}th highlight"
183
-
184
- else
185
- raise "Unknown diagnostic range #{map_field}"
186
- end
187
- end
188
- end
189
- end
190
-
191
- # Use like this:
192
- # ~~~
193
- # assert_diagnoses_many(
194
- # [
195
- # [:warning, :ambiguous_literal],
196
- # [:error, :unexpected_token, { :token => :tLCURLY }]
197
- # ],
198
- # %q{m /foo/ {}},
199
- # SINCE_2_4)
200
- # ~~~
201
- def assert_diagnoses_many(diagnostics, code, versions=ALL_VERSIONS)
202
- with_versions(versions) do |version, parser|
203
- source_file = Parser::Source::Buffer.new('(assert_diagnoses_many)', source: code)
204
-
205
- begin
206
- parser = parser.parse(source_file)
207
- rescue Parser::SyntaxError
208
- # do nothing; the diagnostic was reported
209
- end
210
-
211
- assert_equal diagnostics.count, @diagnostics.count
212
-
213
- diagnostics.zip(@diagnostics) do |expected_diagnostic, actual_diagnostic|
214
- level, reason, arguments = expected_diagnostic
215
- arguments ||= {}
216
- message = Parser::Messages.compile(reason, arguments)
217
-
218
- assert_equal level, actual_diagnostic.level
219
- assert_equal reason, actual_diagnostic.reason
220
- assert_equal arguments, actual_diagnostic.arguments
221
- assert_equal message, actual_diagnostic.message
222
- end
223
- end
224
- end
225
-
226
- def refute_diagnoses(code, versions=ALL_VERSIONS)
227
- with_versions(versions) do |version, parser|
228
- source_file = Parser::Source::Buffer.new('(refute_diagnoses)', source: code)
229
-
230
- begin
231
- parser = parser.parse(source_file)
232
- rescue Parser::SyntaxError
233
- # do nothing; the diagnostic was reported
234
- end
235
-
236
- assert_empty @diagnostics,
237
- "(#{version}) emits no diagnostics, not\n" \
238
- "#{@diagnostics.map(&:render).join("\n")}"
239
- end
240
- end
241
-
242
- def assert_context(context, code, versions=ALL_VERSIONS)
243
- with_versions(versions) do |version, parser|
244
- source_file = Parser::Source::Buffer.new('(assert_context)', source: code)
245
-
246
- parsed_ast = parser.parse(source_file)
247
-
248
- nodes = find_matching_nodes(parsed_ast) { |node| node.type == :send && node.children[1] == :get_context }
249
- assert_equal 1, nodes.count, "there must exactly 1 `get_context()` call"
250
-
251
- node = nodes.first
252
- assert_equal context, node.context, "(#{version}) expect parsing context to match"
253
- end
254
- end
255
-
256
- SOURCE_MAP_DESCRIPTION_RE =
257
- /(?x)
258
- ^(?# $1 skip) ^(\s*)
259
- (?# $2 highlight) ([~\^]+|\!)
260
- \s+
261
- (?# $3 source_map_field) ([a-z_]+)
262
- (?# $5 ast_path) (\s+\(([a-z_.\/0-9]+)\))?
263
- $/
264
-
265
- def parse_source_map_descriptions(descriptions)
266
- unless block_given?
267
- return to_enum(:parse_source_map_descriptions, descriptions)
268
- end
269
-
270
- descriptions.each_line do |line|
271
- # Remove leading " |", if it exists.
272
- line = line.sub(/^\s*\|/, '').rstrip
273
-
274
- next if line.empty?
275
-
276
- if (match = SOURCE_MAP_DESCRIPTION_RE.match(line))
277
- if match[2] != '!'
278
- begin_pos = match[1].length
279
- end_pos = begin_pos + match[2].length
280
- range = begin_pos...end_pos
281
- end
282
- source_map_field = match[3]
283
-
284
- if match[5]
285
- ast_path = match[5].split('.')
286
- else
287
- ast_path = []
288
- end
289
-
290
- yield range, source_map_field, ast_path, line
291
- else
292
- raise "Cannot parse source map description line: #{line.inspect}."
293
- end
294
- end
295
- end
296
-
297
- def traverse_ast(ast, path)
298
- path.inject(ast) do |astlet, path_component|
299
- # Split "dstr/2" to :dstr and 1
300
- type_str, index_str = path_component.split('/')
301
-
302
- type = type_str.to_sym
303
-
304
- if index_str.nil?
305
- index = 0
306
- else
307
- index = index_str.to_i - 1
308
- end
309
-
310
- matching_children = \
311
- astlet.children.select do |child|
312
- AST::Node === child && child.type == type
313
- end
314
-
315
- matching_children[index]
316
- end
317
- end
318
-
319
- def find_matching_nodes(ast, &block)
320
- return [] unless ast.is_a?(AST::Node)
321
-
322
- result = []
323
- result << ast if block.call(ast)
324
- ast.children.each { |child| result += find_matching_nodes(child, &block) }
325
-
326
- result
327
- end
328
- end