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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -6
- data/CHANGELOG.md +35 -1
- data/Gemfile +2 -0
- data/README.md +1 -2
- data/Rakefile +2 -1
- data/bin/ruby-parse +2 -1
- data/bin/ruby-rewrite +2 -1
- data/lib/gauntlet_parser.rb +2 -0
- data/lib/parser.rb +16 -17
- data/lib/parser/all.rb +2 -0
- data/lib/parser/ast/node.rb +2 -0
- data/lib/parser/ast/processor.rb +2 -0
- data/lib/parser/base.rb +6 -12
- data/lib/parser/builders/default.rb +28 -47
- data/lib/parser/clobbering_error.rb +2 -0
- data/lib/parser/context.rb +42 -0
- data/lib/parser/current.rb +4 -20
- data/lib/parser/deprecation.rb +13 -0
- data/lib/parser/diagnostic.rb +3 -3
- data/lib/parser/diagnostic/engine.rb +2 -0
- data/lib/parser/lexer.rl +122 -60
- data/lib/parser/lexer/dedenter.rb +2 -0
- data/lib/parser/lexer/explanation.rb +2 -0
- data/lib/parser/lexer/literal.rb +4 -9
- data/lib/parser/lexer/stack_state.rb +4 -1
- data/lib/parser/macruby.y +32 -17
- data/lib/parser/messages.rb +14 -0
- data/lib/parser/meta.rb +2 -0
- data/lib/parser/rewriter.rb +30 -44
- data/lib/parser/ruby18.y +20 -13
- data/lib/parser/ruby19.y +32 -17
- data/lib/parser/ruby20.y +33 -18
- data/lib/parser/ruby21.y +32 -17
- data/lib/parser/ruby22.y +32 -17
- data/lib/parser/ruby23.y +32 -17
- data/lib/parser/ruby24.y +63 -46
- data/lib/parser/ruby25.y +72 -48
- data/lib/parser/rubymotion.y +33 -18
- data/lib/parser/runner.rb +4 -7
- data/lib/parser/runner/ruby_parse.rb +10 -0
- data/lib/parser/runner/ruby_rewrite.rb +2 -0
- data/lib/parser/source/buffer.rb +19 -24
- data/lib/parser/source/comment.rb +2 -0
- data/lib/parser/source/comment/associator.rb +2 -0
- data/lib/parser/source/map.rb +2 -0
- data/lib/parser/source/map/collection.rb +2 -0
- data/lib/parser/source/map/condition.rb +2 -0
- data/lib/parser/source/map/constant.rb +2 -0
- data/lib/parser/source/map/definition.rb +2 -0
- data/lib/parser/source/map/for.rb +2 -0
- data/lib/parser/source/map/heredoc.rb +2 -0
- data/lib/parser/source/map/keyword.rb +2 -0
- data/lib/parser/source/map/objc_kwarg.rb +2 -0
- data/lib/parser/source/map/operator.rb +2 -0
- data/lib/parser/source/map/rescue_body.rb +2 -0
- data/lib/parser/source/map/send.rb +2 -0
- data/lib/parser/source/map/ternary.rb +2 -0
- data/lib/parser/source/map/variable.rb +2 -0
- data/lib/parser/source/range.rb +81 -13
- data/lib/parser/source/rewriter.rb +48 -10
- data/lib/parser/source/rewriter/action.rb +2 -0
- data/lib/parser/source/tree_rewriter.rb +301 -0
- data/lib/parser/source/tree_rewriter/action.rb +133 -0
- data/lib/parser/static_environment.rb +2 -0
- data/lib/parser/syntax_error.rb +2 -0
- data/lib/parser/tree_rewriter.rb +133 -0
- data/lib/parser/version.rb +3 -1
- data/parser.gemspec +4 -1
- data/test/bug_163/fixtures/input.rb +2 -0
- data/test/bug_163/fixtures/output.rb +2 -0
- data/test/bug_163/rewriter.rb +2 -0
- data/test/helper.rb +7 -7
- data/test/parse_helper.rb +57 -10
- data/test/racc_coverage_helper.rb +2 -0
- data/test/test_base.rb +2 -0
- data/test/test_current.rb +2 -4
- data/test/test_diagnostic.rb +3 -1
- data/test/test_diagnostic_engine.rb +2 -0
- data/test/test_encoding.rb +61 -49
- data/test/test_lexer.rb +164 -77
- data/test/test_lexer_stack_state.rb +2 -0
- data/test/test_parse_helper.rb +8 -8
- data/test/test_parser.rb +613 -51
- data/test/test_runner_rewrite.rb +47 -0
- data/test/test_source_buffer.rb +22 -10
- data/test/test_source_comment.rb +2 -0
- data/test/test_source_comment_associator.rb +2 -0
- data/test/test_source_map.rb +2 -0
- data/test/test_source_range.rb +92 -45
- data/test/test_source_rewriter.rb +3 -1
- data/test/test_source_rewriter_action.rb +2 -0
- data/test/test_source_tree_rewriter.rb +177 -0
- data/test/test_static_environment.rb +2 -0
- data/test/using_tree_rewriter/fixtures/input.rb +3 -0
- data/test/using_tree_rewriter/fixtures/output.rb +3 -0
- data/test/using_tree_rewriter/using_tree_rewriter.rb +9 -0
- metadata +21 -10
- data/lib/parser/compatibility/ruby1_8.rb +0 -20
- data/lib/parser/compatibility/ruby1_9.rb +0 -32
- 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
|
data/lib/parser/version.rb
CHANGED
data/parser.gemspec
CHANGED
@@ -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.
|
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'
|
data/test/bug_163/rewriter.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -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
|
-
|
8
|
-
require_relative 'racc_coverage_helper'
|
9
|
+
require_relative 'racc_coverage_helper'
|
9
10
|
|
10
|
-
|
11
|
-
|
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
|
-
|
14
|
-
|
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[
|
data/test/parse_helper.rb
CHANGED
@@ -1,17 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ParseHelper
|
2
4
|
include AST::Sexp
|
3
5
|
|
4
|
-
|
5
|
-
|
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
|
-
|
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*)
|
data/test/test_base.rb
CHANGED
data/test/test_current.rb
CHANGED
@@ -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+/
|
data/test/test_diagnostic.rb
CHANGED
@@ -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)
|
data/test/test_encoding.rb
CHANGED
@@ -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
|
-
|
13
|
-
require 'parser/all'
|
13
|
+
require 'parser/all'
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
def test_default
|
16
|
+
assert_nil recognize('foobar')
|
17
|
+
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
24
|
+
def test_magic_comment
|
25
|
+
assert_equal Encoding::KOI8_R, recognize("# coding:koi8-r\nfoobar")
|
26
|
+
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
def test_shebang
|
29
|
+
assert_equal Encoding::KOI8_R, recognize("#!/bin/foo\n# coding:koi8-r\nfoobar")
|
30
|
+
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
def test_case
|
33
|
+
assert_equal Encoding::KOI8_R, recognize("# coding:KoI8-r\nfoobar")
|
34
|
+
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
def test_space
|
37
|
+
assert_equal Encoding::KOI8_R, recognize("# coding : koi8-r\nfoobar")
|
38
|
+
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
def test_empty
|
41
|
+
assert_nil recognize('')
|
42
|
+
end
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
def test_no_comment
|
45
|
+
assert_nil recognize(%{require 'cane/encoding_aware_iterator'})
|
46
|
+
end
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
def test_adjacent
|
49
|
+
assert_nil recognize('# codingkoi8-r')
|
50
|
+
assert_nil recognize('# coding koi8-r')
|
51
|
+
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
def test_utf8_mac
|
54
|
+
assert_equal Encoding::UTF8_MAC, recognize('# coding: utf8-mac')
|
55
|
+
end
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
63
|
-
|
64
|
-
end
|
62
|
+
assert_raises(ArgumentError) do
|
63
|
+
assert_nil recognize('# coding: utf-8-dicks')
|
65
64
|
end
|
65
|
+
end
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
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
|