parser 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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=",
|