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.
- 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
|