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.
- checksums.yaml +7 -7
- data/.gitignore +0 -1
- data/README.md +4 -2
- data/bin/{parse → ruby-parse} +2 -2
- data/bin/ruby-rewrite +6 -0
- data/{AST_FORMAT.md → doc/AST_FORMAT.md} +45 -29
- data/doc/CUSTOMIZATION.md +37 -0
- data/doc/INTERNALS.md +21 -0
- data/lib/parser.rb +14 -3
- data/lib/parser/ast/node.rb +6 -0
- data/lib/parser/ast/processor.rb +216 -0
- data/lib/parser/builders/default.rb +613 -215
- data/lib/parser/compatibility/slop.rb +12 -0
- data/lib/parser/lexer.rl +30 -10
- data/lib/parser/lexer/explanation.rb +1 -1
- data/lib/parser/lexer/literal.rb +5 -6
- data/lib/parser/ruby18.y +31 -24
- data/lib/parser/ruby19.y +26 -19
- data/lib/parser/ruby20.y +27 -20
- data/lib/parser/ruby21.y +27 -20
- data/lib/parser/runner.rb +198 -0
- data/lib/parser/runner/ruby_parse.rb +87 -0
- data/lib/parser/runner/ruby_rewrite.rb +13 -0
- data/lib/parser/source/buffer.rb +1 -0
- data/lib/parser/source/map.rb +20 -0
- data/lib/parser/source/map/block.rb +16 -0
- data/lib/parser/source/map/collection.rb +16 -0
- data/lib/parser/source/map/condition.rb +19 -0
- data/lib/parser/source/map/constant.rb +27 -0
- data/lib/parser/source/map/definition.rb +21 -0
- data/lib/parser/source/map/for.rb +17 -0
- data/lib/parser/source/map/keyword.rb +18 -0
- data/lib/parser/source/map/rescue_body.rb +19 -0
- data/lib/parser/source/map/send.rb +29 -0
- data/lib/parser/source/map/ternary.rb +16 -0
- data/lib/parser/source/map/variable.rb +26 -0
- data/lib/parser/source/range.rb +25 -24
- data/lib/parser/version.rb +3 -0
- data/parser.gemspec +4 -2
- data/test/parse_helper.rb +13 -10
- data/test/test_lexer.rb +32 -11
- data/test/test_parse_helper.rb +1 -0
- data/test/test_parser.rb +176 -128
- data/test/test_source_range.rb +18 -6
- metadata +161 -91
- data/bin/benchmark +0 -47
- data/bin/explain-parse +0 -14
- data/lib/parser/source/map/variable_assignment.rb +0 -15
@@ -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
|
data/lib/parser/source/range.rb
CHANGED
@@ -3,39 +3,43 @@ module Parser
|
|
3
3
|
|
4
4
|
class Range
|
5
5
|
attr_reader :source_buffer
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :begin_pos, :end_pos
|
7
7
|
|
8
|
-
def initialize(source_buffer,
|
9
|
-
@source_buffer
|
10
|
-
@
|
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
|
16
|
-
@
|
15
|
+
def begin
|
16
|
+
Range.new(@source_buffer, @begin_pos, @begin_pos)
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
|
19
|
+
def end
|
20
|
+
Range.new(@source_buffer, @end_pos, @end_pos)
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
+
def size
|
24
|
+
@end_pos - @begin_pos + 1
|
23
25
|
end
|
24
26
|
|
25
|
-
|
26
|
-
_, column = @source_buffer.decompose_position(@begin)
|
27
|
+
alias length size
|
27
28
|
|
28
|
-
|
29
|
+
def line
|
30
|
+
line, _ = @source_buffer.decompose_position(@begin_pos)
|
31
|
+
|
32
|
+
line
|
29
33
|
end
|
30
34
|
|
31
|
-
def
|
32
|
-
_, column = @source_buffer.decompose_position(@
|
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
|
-
|
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(@
|
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
|
-
|
52
|
-
|
53
|
-
|
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} #{@
|
62
|
+
"#<Source::Range #{@source_buffer.name} #{@begin_pos}..#{@end_pos}>"
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
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 =
|
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.
|
50
|
+
assert_equal begin_pos, range.begin_pos,
|
51
51
|
"(#{version}) begin of #{what}"
|
52
52
|
|
53
|
-
assert_equal end_pos, range.
|
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-
|
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
|
-
|
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 = "
|
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
|
-
:
|
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
|
-
:
|
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
|
-
:
|
1883
|
+
:tSTRING_BEG, '%<',
|
1884
|
+
:tSTRING_CONTENT, "blah",
|
1885
|
+
:tSTRING_END, '>')
|
1877
1886
|
end
|
1878
1887
|
|
1879
|
-
def
|
1888
|
+
def test_string_pct_pct
|
1880
1889
|
util_lex_token("%%blah%",
|
1881
|
-
:
|
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
|
-
:
|
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
|
-
:
|
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
|
-
:
|
2138
|
+
:tSTRING_BEG, "%=",
|
2139
|
+
:tSTRING_CONTENT, 'foo',
|
2140
|
+
:tSTRING_END, "=")
|
2124
2141
|
|
2125
2142
|
@lex.state = :expr_beg
|
2126
2143
|
util_lex_token("% = ",
|
2127
|
-
:
|
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
|
-
:
|
2184
|
+
:tSTRING_BEG, "%[",
|
2185
|
+
:tSTRING_CONTENT, '1',
|
2186
|
+
:tSTRING_END, ']')
|
2166
2187
|
|
2167
2188
|
@lex.state = :expr_arg
|
2168
2189
|
util_lex_token(" %=1=",
|