parser 1.1.0 → 1.2.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.
Files changed (48) hide show
  1. checksums.yaml +7 -7
  2. data/.gitignore +0 -1
  3. data/README.md +4 -2
  4. data/bin/{parse → ruby-parse} +2 -2
  5. data/bin/ruby-rewrite +6 -0
  6. data/{AST_FORMAT.md → doc/AST_FORMAT.md} +45 -29
  7. data/doc/CUSTOMIZATION.md +37 -0
  8. data/doc/INTERNALS.md +21 -0
  9. data/lib/parser.rb +14 -3
  10. data/lib/parser/ast/node.rb +6 -0
  11. data/lib/parser/ast/processor.rb +216 -0
  12. data/lib/parser/builders/default.rb +613 -215
  13. data/lib/parser/compatibility/slop.rb +12 -0
  14. data/lib/parser/lexer.rl +30 -10
  15. data/lib/parser/lexer/explanation.rb +1 -1
  16. data/lib/parser/lexer/literal.rb +5 -6
  17. data/lib/parser/ruby18.y +31 -24
  18. data/lib/parser/ruby19.y +26 -19
  19. data/lib/parser/ruby20.y +27 -20
  20. data/lib/parser/ruby21.y +27 -20
  21. data/lib/parser/runner.rb +198 -0
  22. data/lib/parser/runner/ruby_parse.rb +87 -0
  23. data/lib/parser/runner/ruby_rewrite.rb +13 -0
  24. data/lib/parser/source/buffer.rb +1 -0
  25. data/lib/parser/source/map.rb +20 -0
  26. data/lib/parser/source/map/block.rb +16 -0
  27. data/lib/parser/source/map/collection.rb +16 -0
  28. data/lib/parser/source/map/condition.rb +19 -0
  29. data/lib/parser/source/map/constant.rb +27 -0
  30. data/lib/parser/source/map/definition.rb +21 -0
  31. data/lib/parser/source/map/for.rb +17 -0
  32. data/lib/parser/source/map/keyword.rb +18 -0
  33. data/lib/parser/source/map/rescue_body.rb +19 -0
  34. data/lib/parser/source/map/send.rb +29 -0
  35. data/lib/parser/source/map/ternary.rb +16 -0
  36. data/lib/parser/source/map/variable.rb +26 -0
  37. data/lib/parser/source/range.rb +25 -24
  38. data/lib/parser/version.rb +3 -0
  39. data/parser.gemspec +4 -2
  40. data/test/parse_helper.rb +13 -10
  41. data/test/test_lexer.rb +32 -11
  42. data/test/test_parse_helper.rb +1 -0
  43. data/test/test_parser.rb +176 -128
  44. data/test/test_source_range.rb +18 -6
  45. metadata +161 -91
  46. data/bin/benchmark +0 -47
  47. data/bin/explain-parse +0 -14
  48. data/lib/parser/source/map/variable_assignment.rb +0 -15
@@ -0,0 +1,16 @@
1
+ module Parser
2
+ module Source
3
+
4
+ class Map::Block < Map
5
+ attr_reader :begin
6
+ attr_reader :end
7
+
8
+ def initialize(begin_l, end_l, expression_l)
9
+ @begin, @end = begin_l, end_l
10
+
11
+ super(expression_l)
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Parser
2
+ module Source
3
+
4
+ class Map::Collection < Map
5
+ attr_reader :begin
6
+ attr_reader :end
7
+
8
+ def initialize(begin_l, end_l, expression_l)
9
+ @begin, @end = begin_l, end_l
10
+
11
+ super(expression_l)
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ module Parser
2
+ module Source
3
+
4
+ class Map::Condition < Map
5
+ attr_reader :keyword
6
+ attr_reader :begin
7
+ attr_reader :else
8
+ attr_reader :end
9
+
10
+ def initialize(keyword_l, begin_l, else_l, end_l, expression_l)
11
+ @keyword = keyword_l
12
+ @begin, @else, @end = begin_l, else_l, end_l
13
+
14
+ super(expression_l)
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ module Parser
2
+ module Source
3
+
4
+ class Map::Constant < Map
5
+ attr_reader :double_colon
6
+ attr_reader :name
7
+ attr_reader :operator
8
+
9
+ def initialize(double_colon, name, expression)
10
+ @double_colon, @name = double_colon, name
11
+
12
+ super(expression)
13
+ end
14
+
15
+ def with_operator(operator_l)
16
+ with { |map| map.update_operator(operator_l) }
17
+ end
18
+
19
+ protected
20
+
21
+ def update_operator(operator_l)
22
+ @operator = operator_l
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ module Parser
2
+ module Source
3
+
4
+ class Map::Definition < Map
5
+ attr_reader :keyword
6
+ attr_reader :operator
7
+ attr_reader :name
8
+ attr_reader :end
9
+
10
+ def initialize(keyword_l, operator_l, name_l, end_l)
11
+ @keyword = keyword_l
12
+ @operator = operator_l
13
+ @name = name_l
14
+ @end = end_l
15
+
16
+ super(@keyword.join(@end))
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module Parser
2
+ module Source
3
+
4
+ class Map::For < Map
5
+ attr_reader :keyword, :in
6
+ attr_reader :begin, :end
7
+
8
+ def initialize(keyword_l, in_l, begin_l, end_l, expression_l)
9
+ @keyword, @in = keyword_l, in_l
10
+ @begin, @end = begin_l, end_l
11
+
12
+ super(expression_l)
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module Parser
2
+ module Source
3
+
4
+ class Map::Keyword < Map
5
+ attr_reader :keyword
6
+ attr_reader :begin
7
+ attr_reader :end
8
+
9
+ def initialize(keyword_l, begin_l, end_l, expression_l)
10
+ @keyword = keyword_l
11
+ @begin, @end = begin_l, end_l
12
+
13
+ super(expression_l)
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ module Parser
2
+ module Source
3
+
4
+ class Map::RescueBody < Map
5
+ attr_reader :keyword
6
+ attr_reader :assoc
7
+ attr_reader :begin
8
+
9
+ def initialize(keyword_l, assoc_l, begin_l, expression_l)
10
+ @keyword = keyword_l
11
+ @assoc = assoc_l
12
+ @begin = begin_l
13
+
14
+ super(expression_l)
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ module Parser
2
+ module Source
3
+
4
+ class Map::Send < Map
5
+ attr_reader :selector
6
+ attr_reader :operator
7
+ attr_reader :begin
8
+ attr_reader :end
9
+
10
+ def initialize(selector_l, begin_l, end_l, expression_l)
11
+ @selector = selector_l
12
+ @begin, @end = begin_l, end_l
13
+
14
+ super(expression_l)
15
+ end
16
+
17
+ def with_operator(operator_l)
18
+ with { |map| map.update_operator(operator_l) }
19
+ end
20
+
21
+ protected
22
+
23
+ def update_operator(operator_l)
24
+ @operator = operator_l
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,16 @@
1
+ module Parser
2
+ module Source
3
+
4
+ class Map::Ternary < Map
5
+ attr_reader :question
6
+ attr_reader :colon
7
+
8
+ def initialize(question_l, colon_l, expression_l)
9
+ @question, @colon = question_l, colon_l
10
+
11
+ super(expression_l)
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ module Parser
2
+ module Source
3
+
4
+ class Map::Variable < Map
5
+ attr_reader :name
6
+ attr_reader :operator
7
+
8
+ def initialize(name_l, expression_l=name_l)
9
+ @name = name_l
10
+
11
+ super(expression_l)
12
+ end
13
+
14
+ def with_operator(operator_l)
15
+ with { |map| map.update_operator(operator_l) }
16
+ end
17
+
18
+ protected
19
+
20
+ def update_operator(operator_l)
21
+ @operator = operator_l
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -3,39 +3,43 @@ module Parser
3
3
 
4
4
  class Range
5
5
  attr_reader :source_buffer
6
- attr_reader :begin, :end
6
+ attr_reader :begin_pos, :end_pos
7
7
 
8
- def initialize(source_buffer, begin_, end_)
9
- @source_buffer = source_buffer
10
- @begin, @end = begin_, end_
8
+ def initialize(source_buffer, begin_pos, end_pos)
9
+ @source_buffer = source_buffer
10
+ @begin_pos, @end_pos = begin_pos, end_pos
11
11
 
12
12
  freeze
13
13
  end
14
14
 
15
- def size
16
- @end - @begin + 1
15
+ def begin
16
+ Range.new(@source_buffer, @begin_pos, @begin_pos)
17
17
  end
18
18
 
19
- def line
20
- line, _ = @source_buffer.decompose_position(@begin)
19
+ def end
20
+ Range.new(@source_buffer, @end_pos, @end_pos)
21
+ end
21
22
 
22
- line
23
+ def size
24
+ @end_pos - @begin_pos + 1
23
25
  end
24
26
 
25
- def begin_column
26
- _, column = @source_buffer.decompose_position(@begin)
27
+ alias length size
27
28
 
28
- column
29
+ def line
30
+ line, _ = @source_buffer.decompose_position(@begin_pos)
31
+
32
+ line
29
33
  end
30
34
 
31
- def end_column
32
- _, column = @source_buffer.decompose_position(@end)
35
+ def column
36
+ _, column = @source_buffer.decompose_position(@begin_pos)
33
37
 
34
38
  column
35
39
  end
36
40
 
37
41
  def column_range
38
- begin_column..end_column
42
+ self.begin.column..self.end.column
39
43
  end
40
44
 
41
45
  def source_line
@@ -43,22 +47,19 @@ module Parser
43
47
  end
44
48
 
45
49
  def to_s
46
- line, column = @source_buffer.decompose_position(@begin)
50
+ line, column = @source_buffer.decompose_position(@begin_pos)
51
+
47
52
  [@source_buffer.name, line, column + 1].join(':')
48
53
  end
49
54
 
50
55
  def join(other)
51
- if other.source_buffer == @source_buffer
52
- Range.new(@source_buffer,
53
- [@begin, other.begin].min,
54
- [@end, other.end].max)
55
- else
56
- raise ArgumentError, "Cannot join SourceRanges for different SourceFiles"
57
- end
56
+ Range.new(@source_buffer,
57
+ [@begin_pos, other.begin_pos].min,
58
+ [@end_pos, other.end_pos].max)
58
59
  end
59
60
 
60
61
  def inspect
61
- "#<Source::Range #{@source_buffer.name} #{@begin}..#{@end}>"
62
+ "#<Source::Range #{@source_buffer.name} #{@begin_pos}..#{@end_pos}>"
62
63
  end
63
64
  end
64
65
 
@@ -0,0 +1,3 @@
1
+ module Parser
2
+ VERSION = '1.2.0'
3
+ end
data/parser.gemspec CHANGED
@@ -1,8 +1,9 @@
1
1
  # encoding: utf-8
2
+ require File.expand_path('../lib/parser/version', __FILE__)
2
3
 
3
4
  Gem::Specification.new do |spec|
4
5
  spec.name = 'parser'
5
- spec.version = '1.1.0'
6
+ spec.version = Parser::VERSION
6
7
  spec.authors = ['Peter Zotov']
7
8
  spec.email = ['whitequark@whitequark.org']
8
9
  spec.description = %q{A Ruby parser written in pure Ruby.}
@@ -17,11 +18,12 @@ Gem::Specification.new do |spec|
17
18
  lib/parser/ruby20.rb
18
19
  lib/parser/ruby21.rb
19
20
  )
20
- spec.executables = %w()
21
+ spec.executables = %w(ruby-parse ruby-rewrite)
21
22
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
23
  spec.require_paths = ['lib']
23
24
 
24
25
  spec.add_dependency 'ast', '~> 1.0'
26
+ spec.add_dependency 'slop', '~> 3.4'
25
27
 
26
28
  spec.add_development_dependency 'bundler', '~> 1.2'
27
29
  spec.add_development_dependency 'rake', '~> 0.9'
data/test/parse_helper.rb CHANGED
@@ -45,12 +45,12 @@ module ParseHelper
45
45
 
46
46
  def assert_source_range(begin_pos, end_pos, range, version, what)
47
47
  assert range.is_a?(Parser::Source::Range),
48
- "(#{version}) is_a?(Source::Range) for #{what}"
48
+ "(#{version}) #{range.inspect}.is_a?(Source::Range) for #{what}"
49
49
 
50
- assert_equal begin_pos, range.begin,
50
+ assert_equal begin_pos, range.begin_pos,
51
51
  "(#{version}) begin of #{what}"
52
52
 
53
- assert_equal end_pos, range.end,
53
+ assert_equal end_pos, range.end_pos,
54
54
  "(#{version}) end of #{what}"
55
55
  end
56
56
 
@@ -86,10 +86,8 @@ module ParseHelper
86
86
  raise "No entity with AST path #{ast_path} in #{parsed_ast.inspect}"
87
87
  end
88
88
 
89
- next # TODO skip location checking
90
-
91
89
  assert astlet.source_map.respond_to?(map_field),
92
- "(#{version}) source_map.respond_to?(#{map_field.inspect}) for:\n#{parsed_ast.inspect}"
90
+ "(#{version}) #{astlet.source_map.inspect}.respond_to?(#{map_field.inspect}) for:\n#{parsed_ast.inspect}"
93
91
 
94
92
  range = astlet.source_map.send(map_field)
95
93
 
@@ -135,8 +133,6 @@ module ParseHelper
135
133
  parse_source_map_descriptions(source_maps) \
136
134
  do |begin_pos, end_pos, map_field, ast_path, line|
137
135
 
138
- next # TODO skip location checking
139
-
140
136
  case map_field
141
137
  when 'location'
142
138
  assert_source_range begin_pos, end_pos,
@@ -162,7 +158,7 @@ module ParseHelper
162
158
  ^(?# $1 skip) ^(\s*)
163
159
  (?# $2 highlight) ([~\^]+)
164
160
  \s+
165
- (?# $3 source_map_field) ([a-z]+)
161
+ (?# $3 source_map_field) ([a-z_]+)
166
162
  (?# $5 ast_path) (\s+\(([a-z_.\/0-9]+)\))?
167
163
  $/
168
164
 
@@ -199,7 +195,14 @@ module ParseHelper
199
195
  path.inject(ast) do |astlet, path_component|
200
196
  # Split "dstr/2" to :dstr and 1
201
197
  type_str, index_str = path_component.split('/')
202
- type, index = type_str.to_sym, index_str.to_i - 1
198
+
199
+ type = type_str.to_sym
200
+
201
+ if index_str.nil?
202
+ index = 0
203
+ else
204
+ index = index_str.to_i - 1
205
+ end
203
206
 
204
207
  matching_children = \
205
208
  astlet.children.select do |child|
data/test/test_lexer.rb CHANGED
@@ -27,7 +27,7 @@ class TestLexer < MiniTest::Unit::TestCase
27
27
 
28
28
  def util_escape(expected, input)
29
29
  source_buffer = Parser::Source::Buffer.new('(util_escape)')
30
- source_buffer.source = "%Q[\\#{input}]"
30
+ source_buffer.source = "\"\\#{input}\""
31
31
 
32
32
  @lex.reset
33
33
  @lex.source_buffer = source_buffer
@@ -1277,6 +1277,9 @@ class TestLexer < MiniTest::Unit::TestCase
1277
1277
  util_bad_token "1_e1"
1278
1278
  util_bad_token "1_.1"
1279
1279
  util_bad_token "1__1"
1280
+
1281
+ util_bad_token "1end"
1282
+ util_bad_token "1.1end"
1280
1283
  end
1281
1284
 
1282
1285
  def test_plus_unary_number
@@ -1802,7 +1805,9 @@ class TestLexer < MiniTest::Unit::TestCase
1802
1805
 
1803
1806
  def test_string_double_nested_curlies
1804
1807
  util_lex_token('%{nest{one{two}one}nest}',
1805
- :tSTRING, "nest{one{two}one}nest")
1808
+ :tSTRING_BEG, '%{',
1809
+ :tSTRING_CONTENT, "nest{one{two}one}nest",
1810
+ :tSTRING_END, '}')
1806
1811
  end
1807
1812
 
1808
1813
  def test_string_double_no_interp
@@ -1820,7 +1825,9 @@ class TestLexer < MiniTest::Unit::TestCase
1820
1825
 
1821
1826
  def test_string_pct_Q
1822
1827
  util_lex_token("%Q[s1 s2]",
1823
- :tSTRING, "s1 s2")
1828
+ :tSTRING_BEG, '%Q[',
1829
+ :tSTRING_CONTENT, "s1 s2",
1830
+ :tSTRING_END, ']')
1824
1831
  end
1825
1832
 
1826
1833
  def test_string_pct_W
@@ -1873,12 +1880,16 @@ class TestLexer < MiniTest::Unit::TestCase
1873
1880
 
1874
1881
  def test_string_pct_angle
1875
1882
  util_lex_token("%<blah>",
1876
- :tSTRING, "blah")
1883
+ :tSTRING_BEG, '%<',
1884
+ :tSTRING_CONTENT, "blah",
1885
+ :tSTRING_END, '>')
1877
1886
  end
1878
1887
 
1879
- def test_string_pct_other
1888
+ def test_string_pct_pct
1880
1889
  util_lex_token("%%blah%",
1881
- :tSTRING, "blah")
1890
+ :tSTRING_BEG, '%',
1891
+ :tSTRING_CONTENT, "blah",
1892
+ :tSTRING_END, '%')
1882
1893
  end
1883
1894
 
1884
1895
  def test_string_pct_w
@@ -1968,12 +1979,16 @@ class TestLexer < MiniTest::Unit::TestCase
1968
1979
 
1969
1980
  def test_symbol_double
1970
1981
  util_lex_token(":\"symbol\"",
1971
- :tSYMBOL, "symbol")
1982
+ :tSYMBEG, ":\"",
1983
+ :tSTRING_CONTENT, "symbol",
1984
+ :tSTRING_END, "\"")
1972
1985
  end
1973
1986
 
1974
1987
  def test_symbol_single
1975
1988
  util_lex_token(":'symbol'",
1976
- :tSYMBOL, "symbol")
1989
+ :tSYMBEG, ":'",
1990
+ :tSTRING_CONTENT, "symbol",
1991
+ :tSTRING_END, "'")
1977
1992
  end
1978
1993
 
1979
1994
  def test_ternary
@@ -2120,11 +2135,15 @@ class TestLexer < MiniTest::Unit::TestCase
2120
2135
  def test_bug_expr_beg_percent
2121
2136
  @lex.state = :expr_beg
2122
2137
  util_lex_token("%=foo=",
2123
- :tSTRING, "foo")
2138
+ :tSTRING_BEG, "%=",
2139
+ :tSTRING_CONTENT, 'foo',
2140
+ :tSTRING_END, "=")
2124
2141
 
2125
2142
  @lex.state = :expr_beg
2126
2143
  util_lex_token("% = ",
2127
- :tSTRING, "=")
2144
+ :tSTRING_BEG, "% ",
2145
+ :tSTRING_CONTENT, '=',
2146
+ :tSTRING_END, ' ')
2128
2147
  end
2129
2148
 
2130
2149
  def test_bug_expr_beg_document
@@ -2162,7 +2181,9 @@ class TestLexer < MiniTest::Unit::TestCase
2162
2181
 
2163
2182
  @lex.state = :expr_arg
2164
2183
  util_lex_token(" %[1]",
2165
- :tSTRING, "1")
2184
+ :tSTRING_BEG, "%[",
2185
+ :tSTRING_CONTENT, '1',
2186
+ :tSTRING_END, ']')
2166
2187
 
2167
2188
  @lex.state = :expr_arg
2168
2189
  util_lex_token(" %=1=",