parser 1.2.0 → 1.3.0

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