parser 1.2.0 → 1.3.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/.gitignore +2 -0
- data/.jrubyrc +1 -0
- data/.yardopts +4 -0
- data/Gemfile +2 -0
- data/README.md +3 -1
- data/doc/AST_FORMAT.md +93 -1
- data/lib/parser.rb +5 -0
- data/lib/parser/builders/default.rb +29 -19
- data/lib/parser/lexer.rl +3 -2
- data/lib/parser/lexer/explanation.rb +2 -2
- data/lib/parser/rewriter.rb +34 -0
- data/lib/parser/runner.rb +4 -5
- data/lib/parser/runner/ruby_parse.rb +76 -26
- data/lib/parser/runner/ruby_rewrite.rb +80 -0
- data/lib/parser/source/map.rb +4 -0
- data/lib/parser/source/range.rb +23 -4
- data/lib/parser/source/rewriter.rb +89 -0
- data/lib/parser/source/rewriter/action.rb +27 -0
- data/lib/parser/version.rb +1 -1
- data/parser.gemspec +3 -0
- data/test/parse_helper.rb +1 -1
- data/test/test_diagnostic.rb +5 -5
- data/test/test_parse_helper.rb +9 -9
- data/test/test_source_range.rb +2 -2
- data/test/test_source_rewriter.rb +81 -0
- data/test/test_source_rewriter_action.rb +44 -0
- metadata +40 -2
@@ -0,0 +1,89 @@
|
|
1
|
+
module Parser
|
2
|
+
module Source
|
3
|
+
|
4
|
+
class Rewriter
|
5
|
+
attr_accessor :diagnostics
|
6
|
+
|
7
|
+
def initialize(source_buffer)
|
8
|
+
@diagnostics = Diagnostic::Engine.new
|
9
|
+
@diagnostics.consumer = lambda do |diag|
|
10
|
+
$stderr.puts diag.render
|
11
|
+
end
|
12
|
+
|
13
|
+
@source_buffer = source_buffer
|
14
|
+
@queue = []
|
15
|
+
@clobber = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove(range)
|
19
|
+
append Rewriter::Action.new(range, "")
|
20
|
+
end
|
21
|
+
|
22
|
+
def insert_before(range, content)
|
23
|
+
append Rewriter::Action.new(range.begin, content)
|
24
|
+
end
|
25
|
+
|
26
|
+
def insert_after(range, content)
|
27
|
+
append Rewriter::Action.new(range.end, content)
|
28
|
+
end
|
29
|
+
|
30
|
+
def replace(range, content)
|
31
|
+
append Rewriter::Action.new(range, content)
|
32
|
+
end
|
33
|
+
|
34
|
+
def process
|
35
|
+
adjustment = 0
|
36
|
+
source = @source_buffer.source.dup
|
37
|
+
|
38
|
+
@queue.sort_by { |action| action.range.begin_pos }.
|
39
|
+
each do |action|
|
40
|
+
begin_pos = action.range.begin_pos + adjustment
|
41
|
+
end_pos = begin_pos + action.range.length
|
42
|
+
|
43
|
+
source[begin_pos...end_pos] = action.replacement
|
44
|
+
|
45
|
+
adjustment += (action.replacement.length - action.range.length)
|
46
|
+
end
|
47
|
+
|
48
|
+
source
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def append(action)
|
54
|
+
if (clobber_action = clobbered?(action.range))
|
55
|
+
# cannot replace 3 characters with "foobar"
|
56
|
+
diagnostic = Diagnostic.new(:error,
|
57
|
+
"cannot #{action}",
|
58
|
+
action.range)
|
59
|
+
@diagnostics.process(diagnostic)
|
60
|
+
|
61
|
+
# clobbered by: remove 3 characters
|
62
|
+
diagnostic = Diagnostic.new(:note,
|
63
|
+
"clobbered by: #{clobber_action}",
|
64
|
+
clobber_action.range)
|
65
|
+
@diagnostics.process(diagnostic)
|
66
|
+
else
|
67
|
+
clobber(action.range)
|
68
|
+
|
69
|
+
@queue << action
|
70
|
+
end
|
71
|
+
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
def clobber(range)
|
76
|
+
@clobber |= (2 ** range.size - 1) << range.begin_pos
|
77
|
+
end
|
78
|
+
|
79
|
+
def clobbered?(range)
|
80
|
+
if @clobber & ((2 ** range.size - 1) << range.begin_pos) != 0
|
81
|
+
@queue.find do |action|
|
82
|
+
action.range.to_a & range.to_a
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Parser
|
2
|
+
module Source
|
3
|
+
|
4
|
+
class Rewriter::Action
|
5
|
+
attr_reader :range, :replacement
|
6
|
+
|
7
|
+
def initialize(range, replacement="")
|
8
|
+
@range, @replacement = range, replacement
|
9
|
+
|
10
|
+
freeze
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
if @range.length == 0 && @replacement.empty?
|
15
|
+
"do nothing"
|
16
|
+
elsif @range.length == 0
|
17
|
+
"insert #{@replacement.inspect}"
|
18
|
+
elsif @replacement.empty?
|
19
|
+
"remove #{@range.length} character(s)"
|
20
|
+
else
|
21
|
+
"replace #{@range.length} character(s) with #{@replacement.inspect}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
data/lib/parser/version.rb
CHANGED
data/parser.gemspec
CHANGED
@@ -35,4 +35,7 @@ Gem::Specification.new do |spec|
|
|
35
35
|
spec.add_development_dependency 'json_pure' # for coveralls on 1.9.2
|
36
36
|
|
37
37
|
spec.add_development_dependency 'simplecov-sublime-ruby-coverage'
|
38
|
+
|
39
|
+
spec.add_development_dependency 'github-markup'
|
40
|
+
spec.add_development_dependency 'yard'
|
38
41
|
end
|
data/test/parse_helper.rb
CHANGED
data/test/test_diagnostic.rb
CHANGED
@@ -5,8 +5,8 @@ class TestDiagnostic < MiniTest::Unit::TestCase
|
|
5
5
|
@buffer = Parser::Source::Buffer.new('(string)')
|
6
6
|
@buffer.source = "if (this is some bad code + bugs)"
|
7
7
|
|
8
|
-
@range1 = Parser::Source::Range.new(@buffer, 0,
|
9
|
-
@range2 = Parser::Source::Range.new(@buffer, 4,
|
8
|
+
@range1 = Parser::Source::Range.new(@buffer, 0, 2) # if
|
9
|
+
@range2 = Parser::Source::Range.new(@buffer, 4, 8) # this
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_verifies_levels
|
@@ -29,11 +29,11 @@ class TestDiagnostic < MiniTest::Unit::TestCase
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def test_render
|
32
|
-
location = Parser::Source::Range.new(@buffer, 26,
|
32
|
+
location = Parser::Source::Range.new(@buffer, 26, 27)
|
33
33
|
|
34
34
|
highlights = [
|
35
|
-
Parser::Source::Range.new(@buffer, 21,
|
36
|
-
Parser::Source::Range.new(@buffer, 28,
|
35
|
+
Parser::Source::Range.new(@buffer, 21, 25),
|
36
|
+
Parser::Source::Range.new(@buffer, 28, 32)
|
37
37
|
]
|
38
38
|
|
39
39
|
diag = Parser::Diagnostic.new(:error, "code far too bad",
|
data/test/test_parse_helper.rb
CHANGED
@@ -25,29 +25,29 @@ class TestParseHelper < MiniTest::Unit::TestCase
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def test_parse_mapsation_description
|
28
|
-
assert_equal [[0,
|
28
|
+
assert_equal [[0, 4, 'expr', [], '~~~~ expr']],
|
29
29
|
parse_maps('~~~~ expr')
|
30
30
|
|
31
|
-
assert_equal [[0,
|
31
|
+
assert_equal [[0, 4, 'expr', [], '^~~~ expr']],
|
32
32
|
parse_maps('^~~~ expr')
|
33
33
|
|
34
|
-
assert_equal [[0,
|
34
|
+
assert_equal [[0, 4, 'expr', [], '^^^^ expr']],
|
35
35
|
parse_maps('^^^^ expr')
|
36
36
|
|
37
|
-
assert_equal [[2,
|
37
|
+
assert_equal [[2, 3, 'op', [], ' ^ op']],
|
38
38
|
parse_maps(' ^ op')
|
39
39
|
|
40
|
-
assert_equal [[2,
|
40
|
+
assert_equal [[2, 3, 'op', ['foo'], ' ~ op (foo)']],
|
41
41
|
parse_maps(' ~ op (foo)')
|
42
42
|
|
43
|
-
assert_equal [[2,
|
43
|
+
assert_equal [[2, 4, 'op', ['foo', 'bar'], ' ~~ op (foo.bar)']],
|
44
44
|
parse_maps(' ~~ op (foo.bar)')
|
45
45
|
|
46
|
-
assert_equal [[2,
|
46
|
+
assert_equal [[2, 4, 'op', ['foo/2', 'bar'], ' ~~ op (foo/2.bar)']],
|
47
47
|
parse_maps(' ~~ op (foo/2.bar)')
|
48
48
|
|
49
|
-
assert_equal [[0,
|
50
|
-
[5,
|
49
|
+
assert_equal [[0, 4, 'expr', [], '~~~~ expr'],
|
50
|
+
[5, 7, 'op', ['str', 'e_h'], ' ~~ op (str.e_h)']],
|
51
51
|
parse_maps(%{
|
52
52
|
|~~~~ expr
|
53
53
|
| ~~ op (str.e_h)
|
data/test/test_source_range.rb
CHANGED
@@ -14,7 +14,7 @@ class TestSourceRange < MiniTest::Unit::TestCase
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def test_size
|
17
|
-
sr = Parser::Source::Range.new(@sfile, 1,
|
17
|
+
sr = Parser::Source::Range.new(@sfile, 1, 3)
|
18
18
|
assert_equal 2, sr.size
|
19
19
|
end
|
20
20
|
|
@@ -41,7 +41,7 @@ class TestSourceRange < MiniTest::Unit::TestCase
|
|
41
41
|
sr = Parser::Source::Range.new(@sfile, 7, 8)
|
42
42
|
assert_equal 0, sr.begin.column
|
43
43
|
assert_equal 1, sr.end.column
|
44
|
-
assert_equal 0
|
44
|
+
assert_equal 0...1, sr.column_range
|
45
45
|
end
|
46
46
|
|
47
47
|
def test_begin_end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestSourceRewriter < MiniTest::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@buf = Parser::Source::Buffer.new('(rewriter)')
|
6
|
+
@buf.source = 'foo bar baz'
|
7
|
+
|
8
|
+
@rewriter = Parser::Source::Rewriter.new(@buf)
|
9
|
+
end
|
10
|
+
|
11
|
+
def range(from, len)
|
12
|
+
Parser::Source::Range.new(@buf, from, from + len)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_remove
|
16
|
+
assert_equal 'foo baz',
|
17
|
+
@rewriter.
|
18
|
+
remove(range(4, 3)).
|
19
|
+
process
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_insert_before
|
23
|
+
assert_equal 'foo quux bar baz',
|
24
|
+
@rewriter.
|
25
|
+
insert_before(range(4, 3), 'quux ').
|
26
|
+
process
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_insert_after
|
30
|
+
assert_equal 'foo bar quux baz',
|
31
|
+
@rewriter.
|
32
|
+
insert_after(range(4, 3), ' quux').
|
33
|
+
process
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_replace
|
37
|
+
assert_equal 'foo quux baz',
|
38
|
+
@rewriter.
|
39
|
+
replace(range(4, 3), 'quux').
|
40
|
+
process
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_composing_asc
|
44
|
+
assert_equal 'foo---bar---baz',
|
45
|
+
@rewriter.
|
46
|
+
replace(range(3, 1), '---').
|
47
|
+
replace(range(7, 1), '---').
|
48
|
+
process
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_composing_desc
|
52
|
+
assert_equal 'foo---bar---baz',
|
53
|
+
@rewriter.
|
54
|
+
replace(range(7, 1), '---').
|
55
|
+
replace(range(3, 1), '---').
|
56
|
+
process
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_clobber
|
60
|
+
diagnostics = []
|
61
|
+
@rewriter.diagnostics.consumer = lambda do |diag|
|
62
|
+
diagnostics << diag
|
63
|
+
end
|
64
|
+
|
65
|
+
@rewriter.
|
66
|
+
replace(range(3, 1), '---').
|
67
|
+
remove(range(3, 1))
|
68
|
+
|
69
|
+
assert_equal 2, diagnostics.count
|
70
|
+
|
71
|
+
assert_equal :error, diagnostics.first.level
|
72
|
+
assert_equal "cannot remove 1 character(s)",
|
73
|
+
diagnostics.first.message
|
74
|
+
assert_equal range(3, 1), diagnostics.first.location
|
75
|
+
|
76
|
+
assert_equal :note, diagnostics.last.level
|
77
|
+
assert_equal "clobbered by: replace 1 character(s) with \"---\"",
|
78
|
+
diagnostics.last.message
|
79
|
+
assert_equal range(3, 1), diagnostics.last.location
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestSourceRewriterAction < MiniTest::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@buf = Parser::Source::Buffer.new('(rewriter_action)')
|
6
|
+
@buf.source = 'foo bar baz'
|
7
|
+
end
|
8
|
+
|
9
|
+
def range(from, len)
|
10
|
+
Parser::Source::Range.new(@buf, from, from + len)
|
11
|
+
end
|
12
|
+
|
13
|
+
def action(range, replacement)
|
14
|
+
Parser::Source::Rewriter::Action.new(range, replacement)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_accessors
|
18
|
+
action = action(range(1, 10), "foo")
|
19
|
+
|
20
|
+
assert action.frozen?
|
21
|
+
assert_equal range(1, 10), action.range
|
22
|
+
assert_equal "foo", action.replacement
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_to_s_replace
|
26
|
+
action = action(range(3, 1), "foo")
|
27
|
+
assert_equal "replace 1 character(s) with \"foo\"", action.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_to_s_insert
|
31
|
+
action = action(range(3, 0), "foo")
|
32
|
+
assert_equal "insert \"foo\"", action.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_to_s_remove
|
36
|
+
action = action(range(3, 2), "")
|
37
|
+
assert_equal "remove 2 character(s)", action.to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_to_s_nop
|
41
|
+
action = action(range(3, 0), "")
|
42
|
+
assert_equal "do nothing", action.to_s
|
43
|
+
end
|
44
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Zotov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-04-
|
11
|
+
date: 2013-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ast
|
@@ -150,6 +150,34 @@ dependencies:
|
|
150
150
|
- - '>='
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: github-markup
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: yard
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
153
181
|
description: A Ruby parser written in pure Ruby.
|
154
182
|
email:
|
155
183
|
- whitequark@whitequark.org
|
@@ -160,7 +188,9 @@ extensions: []
|
|
160
188
|
extra_rdoc_files: []
|
161
189
|
files:
|
162
190
|
- .gitignore
|
191
|
+
- .jrubyrc
|
163
192
|
- .travis.yml
|
193
|
+
- .yardopts
|
164
194
|
- Gemfile
|
165
195
|
- LICENSE.txt
|
166
196
|
- README.md
|
@@ -185,6 +215,7 @@ files:
|
|
185
215
|
- lib/parser/lexer/explanation.rb
|
186
216
|
- lib/parser/lexer/literal.rb
|
187
217
|
- lib/parser/lexer/stack_state.rb
|
218
|
+
- lib/parser/rewriter.rb
|
188
219
|
- lib/parser/ruby18.y
|
189
220
|
- lib/parser/ruby19.y
|
190
221
|
- lib/parser/ruby20.y
|
@@ -207,6 +238,8 @@ files:
|
|
207
238
|
- lib/parser/source/map/ternary.rb
|
208
239
|
- lib/parser/source/map/variable.rb
|
209
240
|
- lib/parser/source/range.rb
|
241
|
+
- lib/parser/source/rewriter.rb
|
242
|
+
- lib/parser/source/rewriter/action.rb
|
210
243
|
- lib/parser/static_environment.rb
|
211
244
|
- lib/parser/syntax_error.rb
|
212
245
|
- lib/parser/version.rb
|
@@ -224,6 +257,8 @@ files:
|
|
224
257
|
- test/test_parser.rb
|
225
258
|
- test/test_source_buffer.rb
|
226
259
|
- test/test_source_range.rb
|
260
|
+
- test/test_source_rewriter.rb
|
261
|
+
- test/test_source_rewriter_action.rb
|
227
262
|
- test/test_static_environment.rb
|
228
263
|
- lib/parser/lexer.rb
|
229
264
|
- lib/parser/ruby18.rb
|
@@ -268,4 +303,7 @@ test_files:
|
|
268
303
|
- test/test_parser.rb
|
269
304
|
- test/test_source_buffer.rb
|
270
305
|
- test/test_source_range.rb
|
306
|
+
- test/test_source_rewriter.rb
|
307
|
+
- test/test_source_rewriter_action.rb
|
271
308
|
- test/test_static_environment.rb
|
309
|
+
has_rdoc:
|