parser 2.4.0.2 → 2.5.0.0

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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -6
  3. data/CHANGELOG.md +35 -1
  4. data/Gemfile +2 -0
  5. data/README.md +1 -2
  6. data/Rakefile +2 -1
  7. data/bin/ruby-parse +2 -1
  8. data/bin/ruby-rewrite +2 -1
  9. data/lib/gauntlet_parser.rb +2 -0
  10. data/lib/parser.rb +16 -17
  11. data/lib/parser/all.rb +2 -0
  12. data/lib/parser/ast/node.rb +2 -0
  13. data/lib/parser/ast/processor.rb +2 -0
  14. data/lib/parser/base.rb +6 -12
  15. data/lib/parser/builders/default.rb +28 -47
  16. data/lib/parser/clobbering_error.rb +2 -0
  17. data/lib/parser/context.rb +42 -0
  18. data/lib/parser/current.rb +4 -20
  19. data/lib/parser/deprecation.rb +13 -0
  20. data/lib/parser/diagnostic.rb +3 -3
  21. data/lib/parser/diagnostic/engine.rb +2 -0
  22. data/lib/parser/lexer.rl +122 -60
  23. data/lib/parser/lexer/dedenter.rb +2 -0
  24. data/lib/parser/lexer/explanation.rb +2 -0
  25. data/lib/parser/lexer/literal.rb +4 -9
  26. data/lib/parser/lexer/stack_state.rb +4 -1
  27. data/lib/parser/macruby.y +32 -17
  28. data/lib/parser/messages.rb +14 -0
  29. data/lib/parser/meta.rb +2 -0
  30. data/lib/parser/rewriter.rb +30 -44
  31. data/lib/parser/ruby18.y +20 -13
  32. data/lib/parser/ruby19.y +32 -17
  33. data/lib/parser/ruby20.y +33 -18
  34. data/lib/parser/ruby21.y +32 -17
  35. data/lib/parser/ruby22.y +32 -17
  36. data/lib/parser/ruby23.y +32 -17
  37. data/lib/parser/ruby24.y +63 -46
  38. data/lib/parser/ruby25.y +72 -48
  39. data/lib/parser/rubymotion.y +33 -18
  40. data/lib/parser/runner.rb +4 -7
  41. data/lib/parser/runner/ruby_parse.rb +10 -0
  42. data/lib/parser/runner/ruby_rewrite.rb +2 -0
  43. data/lib/parser/source/buffer.rb +19 -24
  44. data/lib/parser/source/comment.rb +2 -0
  45. data/lib/parser/source/comment/associator.rb +2 -0
  46. data/lib/parser/source/map.rb +2 -0
  47. data/lib/parser/source/map/collection.rb +2 -0
  48. data/lib/parser/source/map/condition.rb +2 -0
  49. data/lib/parser/source/map/constant.rb +2 -0
  50. data/lib/parser/source/map/definition.rb +2 -0
  51. data/lib/parser/source/map/for.rb +2 -0
  52. data/lib/parser/source/map/heredoc.rb +2 -0
  53. data/lib/parser/source/map/keyword.rb +2 -0
  54. data/lib/parser/source/map/objc_kwarg.rb +2 -0
  55. data/lib/parser/source/map/operator.rb +2 -0
  56. data/lib/parser/source/map/rescue_body.rb +2 -0
  57. data/lib/parser/source/map/send.rb +2 -0
  58. data/lib/parser/source/map/ternary.rb +2 -0
  59. data/lib/parser/source/map/variable.rb +2 -0
  60. data/lib/parser/source/range.rb +81 -13
  61. data/lib/parser/source/rewriter.rb +48 -10
  62. data/lib/parser/source/rewriter/action.rb +2 -0
  63. data/lib/parser/source/tree_rewriter.rb +301 -0
  64. data/lib/parser/source/tree_rewriter/action.rb +133 -0
  65. data/lib/parser/static_environment.rb +2 -0
  66. data/lib/parser/syntax_error.rb +2 -0
  67. data/lib/parser/tree_rewriter.rb +133 -0
  68. data/lib/parser/version.rb +3 -1
  69. data/parser.gemspec +4 -1
  70. data/test/bug_163/fixtures/input.rb +2 -0
  71. data/test/bug_163/fixtures/output.rb +2 -0
  72. data/test/bug_163/rewriter.rb +2 -0
  73. data/test/helper.rb +7 -7
  74. data/test/parse_helper.rb +57 -10
  75. data/test/racc_coverage_helper.rb +2 -0
  76. data/test/test_base.rb +2 -0
  77. data/test/test_current.rb +2 -4
  78. data/test/test_diagnostic.rb +3 -1
  79. data/test/test_diagnostic_engine.rb +2 -0
  80. data/test/test_encoding.rb +61 -49
  81. data/test/test_lexer.rb +164 -77
  82. data/test/test_lexer_stack_state.rb +2 -0
  83. data/test/test_parse_helper.rb +8 -8
  84. data/test/test_parser.rb +613 -51
  85. data/test/test_runner_rewrite.rb +47 -0
  86. data/test/test_source_buffer.rb +22 -10
  87. data/test/test_source_comment.rb +2 -0
  88. data/test/test_source_comment_associator.rb +2 -0
  89. data/test/test_source_map.rb +2 -0
  90. data/test/test_source_range.rb +92 -45
  91. data/test/test_source_rewriter.rb +3 -1
  92. data/test/test_source_rewriter_action.rb +2 -0
  93. data/test/test_source_tree_rewriter.rb +177 -0
  94. data/test/test_static_environment.rb +2 -0
  95. data/test/using_tree_rewriter/fixtures/input.rb +3 -0
  96. data/test/using_tree_rewriter/fixtures/output.rb +3 -0
  97. data/test/using_tree_rewriter/using_tree_rewriter.rb +9 -0
  98. metadata +21 -10
  99. data/lib/parser/compatibility/ruby1_8.rb +0 -20
  100. data/lib/parser/compatibility/ruby1_9.rb +0 -32
  101. data/test/bug_163/test_runner_rewrite.rb +0 -35
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Parser
4
+
5
+ ##
6
+ # {Parser::TreeRewriter} offers a basic API that makes it easy to rewrite
7
+ # existing ASTs. It's built on top of {Parser::AST::Processor} and
8
+ # {Parser::Source::TreeRewriter}
9
+ #
10
+ # For example, assume you want to remove `do` tokens from a while statement.
11
+ # You can do this as following:
12
+ #
13
+ # require 'parser/current'
14
+ #
15
+ # class RemoveDo < Parser::TreeRewriter
16
+ # def on_while(node)
17
+ # # Check if the statement starts with "do"
18
+ # if node.location.begin.is?('do')
19
+ # remove(node.location.begin)
20
+ # end
21
+ # end
22
+ # end
23
+ #
24
+ # code = <<-EOF
25
+ # while true do
26
+ # puts 'hello'
27
+ # end
28
+ # EOF
29
+ #
30
+ # buffer = Parser::Source::Buffer.new('(example)')
31
+ # buffer.source = code
32
+ # rewriter = RemoveDo.new
33
+ #
34
+ # # Rewrite the AST, returns a String with the new form.
35
+ # puts rewriter.rewrite(buffer)
36
+ #
37
+ # This would result in the following Ruby code:
38
+ #
39
+ # while true
40
+ # puts 'hello'
41
+ # end
42
+ #
43
+ # Keep in mind that {Parser::TreeRewriter} does not take care of indentation when
44
+ # inserting/replacing code so you'll have to do this yourself.
45
+ #
46
+ # See also [a blog entry](http://whitequark.org/blog/2013/04/26/lets-play-with-ruby-code/)
47
+ # describing rewriters in greater detail.
48
+ #
49
+ # @api public
50
+ #
51
+ class TreeRewriter < Parser::AST::Processor
52
+ ##
53
+ # Rewrites the AST/source buffer and returns a String containing the new
54
+ # version.
55
+ #
56
+ # @param [Parser::Source::Buffer] source_buffer
57
+ # @param [Parser::AST::Node] ast
58
+ # @param [Symbol] crossing_deletions:, different_replacements:, swallowed_insertions:
59
+ # policy arguments for TreeRewriter (optional)
60
+ # @return [String]
61
+ #
62
+ def rewrite(source_buffer,
63
+ ast,
64
+ **policy)
65
+ @source_rewriter = Parser::Source::TreeRewriter.new(source_buffer, **policy)
66
+
67
+ process(ast)
68
+
69
+ @source_rewriter.process
70
+ end
71
+
72
+ ##
73
+ # Returns `true` if the specified node is an assignment node, returns false
74
+ # otherwise.
75
+ #
76
+ # @param [Parser::AST::Node] node
77
+ # @return [Boolean]
78
+ #
79
+ def assignment?(node)
80
+ [:lvasgn, :ivasgn, :gvasgn, :cvasgn, :casgn].include?(node.type)
81
+ end
82
+
83
+ ##
84
+ # Removes the source range.
85
+ #
86
+ # @param [Parser::Source::Range] range
87
+ #
88
+ def remove(range)
89
+ @source_rewriter.remove(range)
90
+ end
91
+
92
+ ##
93
+ # Wraps the given source range with the given values.
94
+ #
95
+ # @param [Parser::Source::Range] range
96
+ # @param [String] content
97
+ #
98
+ def wrap(range, before, after)
99
+ @source_rewriter.wrap(range, before, after)
100
+ end
101
+
102
+ ##
103
+ # Inserts new code before the given source range.
104
+ #
105
+ # @param [Parser::Source::Range] range
106
+ # @param [String] content
107
+ #
108
+ def insert_before(range, content)
109
+ @source_rewriter.insert_before(range, content)
110
+ end
111
+
112
+ ##
113
+ # Inserts new code after the given source range.
114
+ #
115
+ # @param [Parser::Source::Range] range
116
+ # @param [String] content
117
+ #
118
+ def insert_after(range, content)
119
+ @source_rewriter.insert_after(range, content)
120
+ end
121
+
122
+ ##
123
+ # Replaces the code of the source range `range` with `content`.
124
+ #
125
+ # @param [Parser::Source::Range] range
126
+ # @param [String] content
127
+ #
128
+ def replace(range, content)
129
+ @source_rewriter.replace(range, content)
130
+ end
131
+ end
132
+
133
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Parser
2
- VERSION = '2.4.0.2'
4
+ VERSION = '2.5.0.0'
3
5
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require File.expand_path('../lib/parser/version', __FILE__)
4
5
 
@@ -30,7 +31,9 @@ Gem::Specification.new do |spec|
30
31
  spec.test_files = spec.files.grep(%r{^test/})
31
32
  spec.require_paths = ['lib']
32
33
 
33
- spec.add_dependency 'ast', '~> 2.3'
34
+ spec.required_ruby_version = '>= 2.0.0'
35
+
36
+ spec.add_dependency 'ast', '~> 2.4.0'
34
37
 
35
38
  spec.add_development_dependency 'bundler', '~> 1.16'
36
39
  spec.add_development_dependency 'rake', '~> 10.0'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  if(true)
2
4
  puts "Hello, world!"
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  if true
2
4
  puts "Hello, world!"
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Rewriter < Parser::Rewriter
2
4
  def on_if(node)
3
5
  # Crude, totally-not-usable-in-the-real-world code to remove optional
@@ -1,18 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'tempfile'
2
4
  require 'minitest/test'
3
5
 
4
6
  require 'simplecov'
5
7
 
6
8
  if ENV.include?('COVERAGE') && SimpleCov.usable?
7
- if defined?(TracePoint)
8
- require_relative 'racc_coverage_helper'
9
+ require_relative 'racc_coverage_helper'
9
10
 
10
- RaccCoverage.start(%w(ruby18.y ruby19.y ruby20.y ruby21.y ruby22.y ruby23.y ruby24.y ruby25.y),
11
- File.expand_path('../../lib/parser', __FILE__))
11
+ RaccCoverage.start(%w(ruby18.y ruby19.y ruby20.y ruby21.y ruby22.y ruby23.y ruby24.y ruby25.y),
12
+ File.expand_path('../../lib/parser', __FILE__))
12
13
 
13
- # Report results faster.
14
- at_exit { RaccCoverage.stop }
15
- end
14
+ # Report results faster.
15
+ at_exit { RaccCoverage.stop }
16
16
 
17
17
  SimpleCov.start do
18
18
  self.formatter = SimpleCov::Formatter::MultiFormatter[
@@ -1,17 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ParseHelper
2
4
  include AST::Sexp
3
5
 
4
- if RUBY_VERSION == '1.8.7'
5
- require 'parser/ruby18'
6
-
7
- ALL_VERSIONS = %w(1.8)
8
- else
9
- require 'parser/all'
10
- require 'parser/macruby'
11
- require 'parser/rubymotion'
6
+ require 'parser/all'
7
+ require 'parser/macruby'
8
+ require 'parser/rubymotion'
12
9
 
13
- ALL_VERSIONS = %w(1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 mac ios)
14
- end
10
+ ALL_VERSIONS = %w(1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 mac ios)
15
11
 
16
12
  def setup
17
13
  @diagnostics = []
@@ -185,6 +181,42 @@ module ParseHelper
185
181
  end
186
182
  end
187
183
 
184
+ # Use like this:
185
+ # ~~~
186
+ # assert_diagnoses_many(
187
+ # [
188
+ # [:warning, :ambiguous_literal],
189
+ # [:error, :unexpected_token, { :token => :tLCURLY }]
190
+ # ],
191
+ # %q{m /foo/ {}},
192
+ # SINCE_2_4)
193
+ # ~~~
194
+ def assert_diagnoses_many(diagnostics, code, versions=ALL_VERSIONS)
195
+ with_versions(versions) do |version, parser|
196
+ source_file = Parser::Source::Buffer.new('(assert_diagnoses_many)')
197
+ source_file.source = code
198
+
199
+ begin
200
+ parser = parser.parse(source_file)
201
+ rescue Parser::SyntaxError
202
+ # do nothing; the diagnostic was reported
203
+ end
204
+
205
+ assert_equal diagnostics.count, @diagnostics.count
206
+
207
+ diagnostics.zip(@diagnostics) do |expected_diagnostic, actual_diagnostic|
208
+ level, reason, arguments = expected_diagnostic
209
+ arguments ||= {}
210
+ message = Parser::MESSAGES[reason] % arguments
211
+
212
+ assert_equal level, actual_diagnostic.level
213
+ assert_equal reason, actual_diagnostic.reason
214
+ assert_equal arguments, actual_diagnostic.arguments
215
+ assert_equal message, actual_diagnostic.message
216
+ end
217
+ end
218
+ end
219
+
188
220
  def refute_diagnoses(code, versions=ALL_VERSIONS)
189
221
  with_versions(versions) do |version, parser|
190
222
  source_file = Parser::Source::Buffer.new('(refute_diagnoses)')
@@ -202,6 +234,21 @@ module ParseHelper
202
234
  end
203
235
  end
204
236
 
237
+ def assert_context(context, code, versions=ALL_VERSIONS)
238
+ with_versions(versions) do |version, parser|
239
+ source_file = Parser::Source::Buffer.new('(assert_context)')
240
+ source_file.source = code
241
+
242
+ begin
243
+ parser.parse(source_file)
244
+ rescue Parser::SyntaxError
245
+ # do nothing; the diagnostic was reported
246
+ end
247
+
248
+ assert_equal parser.context.stack, context, "(#{version}) parsing context"
249
+ end
250
+ end
251
+
205
252
  SOURCE_MAP_DESCRIPTION_RE =
206
253
  /(?x)
207
254
  ^(?# $1 skip) ^(\s*)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'racc/grammarfileparser'
2
4
 
3
5
  # Unfortunately, Ruby's Coverage module ignores module_eval statements,
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
  require 'parser/current'
3
5
 
@@ -1,13 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
  require 'parser/current'
3
5
 
4
6
  class TestCurrent < Minitest::Test
5
7
  def test_current
6
8
  case RUBY_VERSION
7
- when '1.8.7'
8
- assert_equal Parser::Ruby18, Parser::CurrentRuby
9
- when '1.9.2', '1.9.3'
10
- assert_equal Parser::Ruby19, Parser::CurrentRuby
11
9
  when '2.0.0'
12
10
  assert_equal Parser::Ruby20, Parser::CurrentRuby
13
11
  when /^2\.1\.\d+/
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
 
3
5
  class TestDiagnostic < Minitest::Test
@@ -18,7 +20,7 @@ class TestDiagnostic < Minitest::Test
18
20
  end
19
21
 
20
22
  def test_freezes
21
- string = 'foo'
23
+ string = 'foo'.dup
22
24
  highlights = [@range2]
23
25
 
24
26
  diag = Parser::Diagnostic.new(:error, :escape_eof, @range1, highlights)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'helper'
2
4
 
3
5
  class TestDiagnosticEngine < Minitest::Test
@@ -1,4 +1,5 @@
1
1
  # encoding: binary
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'helper'
4
5
 
@@ -9,70 +10,81 @@ class TestEncoding < Minitest::Test
9
10
  Parser::Source::Buffer.recognize_encoding(string)
10
11
  end
11
12
 
12
- if defined?(Encoding)
13
- require 'parser/all'
13
+ require 'parser/all'
14
14
 
15
- def test_default
16
- assert_nil recognize('foobar')
17
- end
15
+ def test_default
16
+ assert_nil recognize('foobar')
17
+ end
18
18
 
19
- def test_bom
20
- assert_equal Encoding::UTF_8, recognize("\xef\xbb\xbf\nfoobar")
21
- assert_equal Encoding::UTF_8, recognize("\xef\xbb\xbf# coding:koi8-r\nfoobar")
22
- end
19
+ def test_bom
20
+ assert_equal Encoding::UTF_8, recognize("\xef\xbb\xbf\nfoobar")
21
+ assert_equal Encoding::UTF_8, recognize("\xef\xbb\xbf# coding:koi8-r\nfoobar")
22
+ end
23
23
 
24
- def test_magic_comment
25
- assert_equal Encoding::KOI8_R, recognize("# coding:koi8-r\nfoobar")
26
- end
24
+ def test_magic_comment
25
+ assert_equal Encoding::KOI8_R, recognize("# coding:koi8-r\nfoobar")
26
+ end
27
27
 
28
- def test_shebang
29
- assert_equal Encoding::KOI8_R, recognize("#!/bin/foo\n# coding:koi8-r\nfoobar")
30
- end
28
+ def test_shebang
29
+ assert_equal Encoding::KOI8_R, recognize("#!/bin/foo\n# coding:koi8-r\nfoobar")
30
+ end
31
31
 
32
- def test_case
33
- assert_equal Encoding::KOI8_R, recognize("# coding:KoI8-r\nfoobar")
34
- end
32
+ def test_case
33
+ assert_equal Encoding::KOI8_R, recognize("# coding:KoI8-r\nfoobar")
34
+ end
35
35
 
36
- def test_space
37
- assert_equal Encoding::KOI8_R, recognize("# coding : koi8-r\nfoobar")
38
- end
36
+ def test_space
37
+ assert_equal Encoding::KOI8_R, recognize("# coding : koi8-r\nfoobar")
38
+ end
39
39
 
40
- def test_empty
41
- assert_nil recognize('')
42
- end
40
+ def test_empty
41
+ assert_nil recognize('')
42
+ end
43
43
 
44
- def test_no_comment
45
- assert_nil recognize(%{require 'cane/encoding_aware_iterator'})
46
- end
44
+ def test_no_comment
45
+ assert_nil recognize(%{require 'cane/encoding_aware_iterator'})
46
+ end
47
47
 
48
- def test_adjacent
49
- assert_nil recognize('# codingkoi8-r')
50
- assert_nil recognize('# coding koi8-r')
51
- end
48
+ def test_adjacent
49
+ assert_nil recognize('# codingkoi8-r')
50
+ assert_nil recognize('# coding koi8-r')
51
+ end
52
52
 
53
- def test_utf8_mac
54
- assert_equal Encoding::UTF8_MAC, recognize('# coding: utf8-mac')
55
- end
53
+ def test_utf8_mac
54
+ assert_equal Encoding::UTF8_MAC, recognize('# coding: utf8-mac')
55
+ end
56
56
 
57
- def test_suffix
58
- assert_equal Encoding::UTF_8, recognize('# coding: utf-8-dos')
59
- assert_equal Encoding::UTF_8, recognize('# coding: utf-8-unix')
60
- assert_equal Encoding::UTF_8, recognize('# coding: utf-8-mac')
57
+ def test_suffix
58
+ assert_equal Encoding::UTF_8, recognize('# coding: utf-8-dos')
59
+ assert_equal Encoding::UTF_8, recognize('# coding: utf-8-unix')
60
+ assert_equal Encoding::UTF_8, recognize('# coding: utf-8-mac')
61
61
 
62
- assert_raises(ArgumentError) do
63
- assert_nil recognize('# coding: utf-8-dicks')
64
- end
62
+ assert_raises(ArgumentError) do
63
+ assert_nil recognize('# coding: utf-8-dicks')
65
64
  end
65
+ end
66
66
 
67
- def test_parse_18_invalid_enc
68
- ast = Parser::Ruby18.parse("# encoding:feynman-diagram\n1")
69
- assert_equal ast, s(:int, 1)
70
- end
67
+ def test_parse_18_invalid_enc
68
+ ast = Parser::Ruby18.parse("# encoding:feynman-diagram\n1")
69
+ assert_equal ast, s(:int, 1)
70
+ end
71
71
 
72
- def test_parse_19_invalid_enc
73
- assert_raises(ArgumentError) do
74
- Parser::Ruby19.parse("# encoding:feynman-diagram\n1")
75
- end
72
+ def test_parse_19_invalid_enc
73
+ assert_raises(ArgumentError) do
74
+ Parser::Ruby19.parse("# encoding:feynman-diagram\n1")
76
75
  end
77
76
  end
77
+
78
+ def test_ending_comment
79
+ assert_nil recognize('foo # coding: koi8-r')
80
+ end
81
+
82
+ def test_wrong_prefix
83
+ assert_nil recognize('# decoding: koi8-r')
84
+ end
85
+
86
+ def test_no_spaces
87
+ assert_equal Encoding::KOI8_R, recognize('#encoding:koi8-r')
88
+ assert_equal Encoding::KOI8_R, recognize('#coding:koi8-r')
89
+ end
78
90
  end