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.
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Parser
2
- VERSION = '1.2.0'
2
+ VERSION = '1.3.0'
3
3
  end
@@ -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
@@ -175,7 +175,7 @@ module ParseHelper
175
175
 
176
176
  if (match = SOURCE_MAP_DESCRIPTION_RE.match(line))
177
177
  begin_pos = match[1].length
178
- end_pos = begin_pos + match[2].length - 1
178
+ end_pos = begin_pos + match[2].length
179
179
  source_map_field = match[3]
180
180
 
181
181
  if match[5]
@@ -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, 1) # if
9
- @range2 = Parser::Source::Range.new(@buffer, 4, 7) # this
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, 26)
32
+ location = Parser::Source::Range.new(@buffer, 26, 27)
33
33
 
34
34
  highlights = [
35
- Parser::Source::Range.new(@buffer, 21, 24),
36
- Parser::Source::Range.new(@buffer, 28, 31)
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",
@@ -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, 3, 'expr', [], '~~~~ expr']],
28
+ assert_equal [[0, 4, 'expr', [], '~~~~ expr']],
29
29
  parse_maps('~~~~ expr')
30
30
 
31
- assert_equal [[0, 3, 'expr', [], '^~~~ expr']],
31
+ assert_equal [[0, 4, 'expr', [], '^~~~ expr']],
32
32
  parse_maps('^~~~ expr')
33
33
 
34
- assert_equal [[0, 3, 'expr', [], '^^^^ expr']],
34
+ assert_equal [[0, 4, 'expr', [], '^^^^ expr']],
35
35
  parse_maps('^^^^ expr')
36
36
 
37
- assert_equal [[2, 2, 'op', [], ' ^ op']],
37
+ assert_equal [[2, 3, 'op', [], ' ^ op']],
38
38
  parse_maps(' ^ op')
39
39
 
40
- assert_equal [[2, 2, 'op', ['foo'], ' ~ op (foo)']],
40
+ assert_equal [[2, 3, 'op', ['foo'], ' ~ op (foo)']],
41
41
  parse_maps(' ~ op (foo)')
42
42
 
43
- assert_equal [[2, 3, 'op', ['foo', 'bar'], ' ~~ op (foo.bar)']],
43
+ assert_equal [[2, 4, 'op', ['foo', 'bar'], ' ~~ op (foo.bar)']],
44
44
  parse_maps(' ~~ op (foo.bar)')
45
45
 
46
- assert_equal [[2, 3, 'op', ['foo/2', 'bar'], ' ~~ op (foo/2.bar)']],
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, 3, 'expr', [], '~~~~ expr'],
50
- [5, 6, 'op', ['str', 'e_h'], ' ~~ op (str.e_h)']],
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)
@@ -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, 2)
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..1, sr.column_range
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.2.0
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-25 00:00:00.000000000 Z
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: