parser 2.4.0.2 → 2.5.0.0

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