parser 1.4.2 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -0
- data/CHANGELOG.md +167 -0
- data/CONTRIBUTING.md +10 -0
- data/Gemfile +4 -0
- data/README.md +56 -6
- data/Rakefile +66 -3
- data/doc/AST_FORMAT.md +3 -16
- data/lib/parser.rb +3 -0
- data/lib/parser/ast/node.rb +4 -4
- data/lib/parser/ast/processor.rb +1 -2
- data/lib/parser/base.rb +30 -2
- data/lib/parser/builders/default.rb +90 -70
- data/lib/parser/lexer.rl +98 -73
- data/lib/parser/lexer/explanation.rb +2 -11
- data/lib/parser/rewriter.rb +1 -2
- data/lib/parser/ruby18.y +2 -11
- data/lib/parser/ruby19.y +3 -14
- data/lib/parser/ruby20.y +3 -14
- data/lib/parser/ruby21.y +3 -14
- data/lib/parser/runner/ruby_parse.rb +1 -1
- data/lib/parser/source/buffer.rb +8 -18
- data/lib/parser/source/comment.rb +46 -0
- data/lib/parser/source/comment/associator.rb +70 -0
- data/lib/parser/source/map.rb +4 -0
- data/lib/parser/source/range.rb +2 -2
- data/lib/parser/version.rb +1 -1
- data/parser.gemspec +4 -3
- data/test/parse_helper.rb +6 -6
- data/test/test_lexer.rb +263 -15
- data/test/test_parser.rb +86 -48
- data/test/test_source_comment.rb +34 -0
- data/test/test_source_comment_associator.rb +48 -0
- data/test/test_source_range.rb +22 -16
- metadata +13 -5
- data/.jrubyrc +0 -1
data/lib/parser/ruby20.y
CHANGED
@@ -1149,7 +1149,6 @@ rule
|
|
1149
1149
|
diagnostic(:error, :class_in_def, val[0])
|
1150
1150
|
end
|
1151
1151
|
|
1152
|
-
@comments.push @lexer.clear_comments
|
1153
1152
|
@static_env.extend_static
|
1154
1153
|
}
|
1155
1154
|
bodystmt kEND
|
@@ -1160,7 +1159,6 @@ rule
|
|
1160
1159
|
val[4], val[5])
|
1161
1160
|
|
1162
1161
|
@static_env.unextend
|
1163
|
-
@lexer.clear_comments
|
1164
1162
|
}
|
1165
1163
|
| kCLASS tLSHFT expr term
|
1166
1164
|
{
|
@@ -1175,7 +1173,6 @@ rule
|
|
1175
1173
|
val[5], val[6])
|
1176
1174
|
|
1177
1175
|
@static_env.unextend
|
1178
|
-
@lexer.clear_comments
|
1179
1176
|
|
1180
1177
|
@def_level = val[4]
|
1181
1178
|
}
|
@@ -1185,7 +1182,6 @@ rule
|
|
1185
1182
|
diagnostic(:error, :module_in_def, val[0])
|
1186
1183
|
end
|
1187
1184
|
|
1188
|
-
@comments.push @lexer.clear_comments
|
1189
1185
|
@static_env.extend_static
|
1190
1186
|
}
|
1191
1187
|
bodystmt kEND
|
@@ -1194,26 +1190,22 @@ rule
|
|
1194
1190
|
val[3], val[4])
|
1195
1191
|
|
1196
1192
|
@static_env.unextend
|
1197
|
-
@lexer.clear_comments
|
1198
1193
|
}
|
1199
1194
|
| kDEF fname
|
1200
1195
|
{
|
1201
|
-
@comments.push @lexer.clear_comments
|
1202
1196
|
@def_level += 1
|
1203
1197
|
@static_env.extend_static
|
1204
1198
|
}
|
1205
1199
|
f_arglist bodystmt kEND
|
1206
1200
|
{
|
1207
1201
|
result = @builder.def_method(val[0], val[1],
|
1208
|
-
val[3], val[4], val[5]
|
1202
|
+
val[3], val[4], val[5])
|
1209
1203
|
|
1210
1204
|
@static_env.unextend
|
1211
1205
|
@def_level -= 1
|
1212
|
-
@lexer.clear_comments
|
1213
1206
|
}
|
1214
1207
|
| kDEF singleton dot_or_colon
|
1215
1208
|
{
|
1216
|
-
@comments.push @lexer.clear_comments
|
1217
1209
|
@lexer.state = :expr_fname
|
1218
1210
|
}
|
1219
1211
|
fname
|
@@ -1224,11 +1216,10 @@ rule
|
|
1224
1216
|
f_arglist bodystmt kEND
|
1225
1217
|
{
|
1226
1218
|
result = @builder.def_singleton(val[0], val[1], val[2],
|
1227
|
-
val[4], val[6], val[7], val[8]
|
1219
|
+
val[4], val[6], val[7], val[8])
|
1228
1220
|
|
1229
1221
|
@static_env.unextend
|
1230
1222
|
@def_level -= 1
|
1231
|
-
@lexer.clear_comments
|
1232
1223
|
}
|
1233
1224
|
| kBREAK
|
1234
1225
|
{
|
@@ -2284,9 +2275,7 @@ keyword_variable: kNIL
|
|
2284
2275
|
}
|
2285
2276
|
| tLABEL arg_value
|
2286
2277
|
{
|
2287
|
-
|
2288
|
-
key = @builder.symbol(val[0])
|
2289
|
-
result = @builder.pair(key, nil, val[1])
|
2278
|
+
result = @builder.pair_keyword(val[0], val[1])
|
2290
2279
|
}
|
2291
2280
|
| tDSTAR arg_value
|
2292
2281
|
{
|
data/lib/parser/ruby21.y
CHANGED
@@ -1150,7 +1150,6 @@ rule
|
|
1150
1150
|
diagnostic(:error, :class_in_def, val[0])
|
1151
1151
|
end
|
1152
1152
|
|
1153
|
-
@comments.push @lexer.clear_comments
|
1154
1153
|
@static_env.extend_static
|
1155
1154
|
}
|
1156
1155
|
bodystmt kEND
|
@@ -1161,7 +1160,6 @@ rule
|
|
1161
1160
|
val[4], val[5])
|
1162
1161
|
|
1163
1162
|
@static_env.unextend
|
1164
|
-
@lexer.clear_comments
|
1165
1163
|
}
|
1166
1164
|
| kCLASS tLSHFT expr term
|
1167
1165
|
{
|
@@ -1176,7 +1174,6 @@ rule
|
|
1176
1174
|
val[5], val[6])
|
1177
1175
|
|
1178
1176
|
@static_env.unextend
|
1179
|
-
@lexer.clear_comments
|
1180
1177
|
|
1181
1178
|
@def_level = val[4]
|
1182
1179
|
}
|
@@ -1186,7 +1183,6 @@ rule
|
|
1186
1183
|
diagnostic(:error, :module_in_def, val[0])
|
1187
1184
|
end
|
1188
1185
|
|
1189
|
-
@comments.push @lexer.clear_comments
|
1190
1186
|
@static_env.extend_static
|
1191
1187
|
}
|
1192
1188
|
bodystmt kEND
|
@@ -1195,26 +1191,22 @@ rule
|
|
1195
1191
|
val[3], val[4])
|
1196
1192
|
|
1197
1193
|
@static_env.unextend
|
1198
|
-
@lexer.clear_comments
|
1199
1194
|
}
|
1200
1195
|
| kDEF fname
|
1201
1196
|
{
|
1202
|
-
@comments.push @lexer.clear_comments
|
1203
1197
|
@def_level += 1
|
1204
1198
|
@static_env.extend_static
|
1205
1199
|
}
|
1206
1200
|
f_arglist bodystmt kEND
|
1207
1201
|
{
|
1208
1202
|
result = @builder.def_method(val[0], val[1],
|
1209
|
-
val[3], val[4], val[5]
|
1203
|
+
val[3], val[4], val[5])
|
1210
1204
|
|
1211
1205
|
@static_env.unextend
|
1212
1206
|
@def_level -= 1
|
1213
|
-
@lexer.clear_comments
|
1214
1207
|
}
|
1215
1208
|
| kDEF singleton dot_or_colon
|
1216
1209
|
{
|
1217
|
-
@comments.push @lexer.clear_comments
|
1218
1210
|
@lexer.state = :expr_fname
|
1219
1211
|
}
|
1220
1212
|
fname
|
@@ -1225,11 +1217,10 @@ rule
|
|
1225
1217
|
f_arglist bodystmt kEND
|
1226
1218
|
{
|
1227
1219
|
result = @builder.def_singleton(val[0], val[1], val[2],
|
1228
|
-
val[4], val[6], val[7], val[8]
|
1220
|
+
val[4], val[6], val[7], val[8])
|
1229
1221
|
|
1230
1222
|
@static_env.unextend
|
1231
1223
|
@def_level -= 1
|
1232
|
-
@lexer.clear_comments
|
1233
1224
|
}
|
1234
1225
|
| kBREAK
|
1235
1226
|
{
|
@@ -2301,9 +2292,7 @@ keyword_variable: kNIL
|
|
2301
2292
|
}
|
2302
2293
|
| tLABEL arg_value
|
2303
2294
|
{
|
2304
|
-
|
2305
|
-
key = @builder.symbol(val[0])
|
2306
|
-
result = @builder.pair(key, nil, val[1])
|
2295
|
+
result = @builder.pair_keyword(val[0], val[1])
|
2307
2296
|
}
|
2308
2297
|
| tDSTAR arg_value
|
2309
2298
|
{
|
data/lib/parser/source/buffer.rb
CHANGED
@@ -7,12 +7,11 @@ module Parser
|
|
7
7
|
attr_reader :name, :first_line
|
8
8
|
|
9
9
|
def self.recognize_encoding(string)
|
10
|
-
if string.empty?
|
11
|
-
return Encoding::BINARY
|
12
|
-
end
|
10
|
+
return Encoding::BINARY if string.empty?
|
13
11
|
|
14
|
-
#
|
15
|
-
|
12
|
+
# extract the first two lines in an efficient way
|
13
|
+
string =~ /(.*)\n?(.*\n)?/
|
14
|
+
first_line, second_line = $1, $2
|
16
15
|
|
17
16
|
[first_line, second_line].each do |line|
|
18
17
|
line.force_encoding(Encoding::ASCII_8BIT) if line
|
@@ -95,15 +94,7 @@ module Parser
|
|
95
94
|
|
96
95
|
def source_line(line)
|
97
96
|
unless @lines
|
98
|
-
@lines = @source.lines.map
|
99
|
-
# Line endings will be commonly present for all lines
|
100
|
-
# except the last one. It does not make sense to keep them.
|
101
|
-
if source_line.end_with? "\n"
|
102
|
-
source_line.chomp
|
103
|
-
else
|
104
|
-
source_line
|
105
|
-
end
|
106
|
-
end
|
97
|
+
@lines = @source.lines.map(&:chomp)
|
107
98
|
end
|
108
99
|
|
109
100
|
@lines[line - @first_line].dup
|
@@ -117,13 +108,11 @@ module Parser
|
|
117
108
|
|
118
109
|
@source.each_char do |char|
|
119
110
|
if char == "\n"
|
120
|
-
@line_begins
|
111
|
+
@line_begins.unshift [ @line_begins.length, index ]
|
121
112
|
end
|
122
113
|
|
123
114
|
index += 1
|
124
115
|
end
|
125
|
-
|
126
|
-
@line_begins = @line_begins.reverse
|
127
116
|
end
|
128
117
|
|
129
118
|
@line_begins
|
@@ -131,11 +120,12 @@ module Parser
|
|
131
120
|
|
132
121
|
def line_for(position)
|
133
122
|
if line_begins.respond_to? :bsearch
|
123
|
+
# Fast O(log n) variant for Ruby >=2.0.
|
134
124
|
line_begins.bsearch do |line, line_begin|
|
135
125
|
line_begin <= position
|
136
126
|
end
|
137
127
|
else
|
138
|
-
# Slower variant for
|
128
|
+
# Slower O(n) variant for Ruby <2.0.
|
139
129
|
line_begins.find do |line, line_begin|
|
140
130
|
line_begin <= position
|
141
131
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Parser
|
2
|
+
module Source
|
3
|
+
|
4
|
+
class Comment
|
5
|
+
attr_reader :text
|
6
|
+
|
7
|
+
attr_reader :location
|
8
|
+
alias_method :loc, :location
|
9
|
+
|
10
|
+
def self.associate(ast, comments)
|
11
|
+
associator = Associator.new(comments, ast)
|
12
|
+
associator.associate
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(location)
|
16
|
+
@location = location
|
17
|
+
@text = location.source.freeze
|
18
|
+
|
19
|
+
freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
def type
|
23
|
+
case text
|
24
|
+
when /^#/
|
25
|
+
:inline
|
26
|
+
when /^=begin/
|
27
|
+
:document
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def inline?
|
32
|
+
type == :inline
|
33
|
+
end
|
34
|
+
|
35
|
+
def document?
|
36
|
+
type == :document
|
37
|
+
end
|
38
|
+
|
39
|
+
def ==(other)
|
40
|
+
other.is_a?(Source::Comment) &&
|
41
|
+
@location == other.location
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Parser
|
2
|
+
module Source
|
3
|
+
|
4
|
+
class Comment::Associator
|
5
|
+
def initialize(comments, ast)
|
6
|
+
@comments = comments
|
7
|
+
@ast = ast
|
8
|
+
end
|
9
|
+
|
10
|
+
def associate
|
11
|
+
@mapping = Hash.new { |h, k| h[k] = [] }
|
12
|
+
@comment_num = 0
|
13
|
+
|
14
|
+
process(nil, @ast)
|
15
|
+
|
16
|
+
@mapping
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def process(upper_node, node)
|
22
|
+
if node.type == :begin
|
23
|
+
prev_node, next_node = nil, upper_node
|
24
|
+
else
|
25
|
+
while current_comment_between?(prev_node, node)
|
26
|
+
associate_and_advance_comment(node)
|
27
|
+
end
|
28
|
+
|
29
|
+
prev_node, next_node = nil, upper_node
|
30
|
+
end
|
31
|
+
|
32
|
+
node.children.each do |child|
|
33
|
+
if child.is_a?(AST::Node) && child.location.expression
|
34
|
+
prev_node, next_node = next_node, child
|
35
|
+
|
36
|
+
process(prev_node, child)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def current_comment
|
42
|
+
@comments[@comment_num]
|
43
|
+
end
|
44
|
+
|
45
|
+
def advance_comment
|
46
|
+
@comment_num += 1
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_comment_between?(prev_node, next_node)
|
50
|
+
comment_loc = current_comment.location
|
51
|
+
next_loc = next_node.location.expression
|
52
|
+
|
53
|
+
if prev_node.nil?
|
54
|
+
comment_loc.end_pos <= next_loc.begin_pos
|
55
|
+
else
|
56
|
+
prev_loc = prev_node.location.expression
|
57
|
+
|
58
|
+
comment_loc.begin_pos >= prev_loc.end_pos &&
|
59
|
+
comment_loc.end_pos <= next_loc.begin_pos
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def associate_and_advance_comment(node)
|
64
|
+
@mapping[node] << current_comment
|
65
|
+
advance_comment
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
data/lib/parser/source/map.rb
CHANGED
data/lib/parser/source/range.rb
CHANGED
@@ -46,12 +46,12 @@ module Parser
|
|
46
46
|
@source_buffer.source_line(line)
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
49
|
+
def source
|
50
50
|
@source_buffer.source[self.begin_pos...self.end_pos]
|
51
51
|
end
|
52
52
|
|
53
53
|
def is?(*what)
|
54
|
-
what.include?(
|
54
|
+
what.include?(source)
|
55
55
|
end
|
56
56
|
|
57
57
|
def to_a
|
data/lib/parser/version.rb
CHANGED
data/parser.gemspec
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
|
2
3
|
require File.expand_path('../lib/parser/version', __FILE__)
|
3
4
|
|
4
5
|
Gem::Specification.new do |spec|
|
@@ -6,20 +7,20 @@ Gem::Specification.new do |spec|
|
|
6
7
|
spec.version = Parser::VERSION
|
7
8
|
spec.authors = ['Peter Zotov']
|
8
9
|
spec.email = ['whitequark@whitequark.org']
|
9
|
-
spec.description =
|
10
|
+
spec.description = 'A Ruby parser written in pure Ruby.'
|
10
11
|
spec.summary = spec.description
|
11
12
|
spec.homepage = 'http://github.com/whitequark/parser'
|
12
13
|
spec.license = 'MIT'
|
13
14
|
spec.has_rdoc = 'yard'
|
14
15
|
|
15
|
-
spec.files = `git ls-files`.split
|
16
|
+
spec.files = `git ls-files`.split + %w(
|
16
17
|
lib/parser/lexer.rb
|
17
18
|
lib/parser/ruby18.rb
|
18
19
|
lib/parser/ruby19.rb
|
19
20
|
lib/parser/ruby20.rb
|
20
21
|
lib/parser/ruby21.rb
|
21
22
|
)
|
22
|
-
spec.executables = %
|
23
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
23
24
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
25
|
spec.require_paths = ['lib']
|
25
26
|
|
data/test/parse_helper.rb
CHANGED
@@ -34,7 +34,7 @@ module ParseHelper
|
|
34
34
|
parser
|
35
35
|
end
|
36
36
|
|
37
|
-
def with_versions(
|
37
|
+
def with_versions(versions)
|
38
38
|
(versions & ALL_VERSIONS).each do |version|
|
39
39
|
@diagnostics.clear
|
40
40
|
|
@@ -67,7 +67,7 @@ module ParseHelper
|
|
67
67
|
# )
|
68
68
|
# ~~~
|
69
69
|
def assert_parses(ast, code, source_maps='', versions=ALL_VERSIONS)
|
70
|
-
with_versions(
|
70
|
+
with_versions(versions) do |version, parser|
|
71
71
|
source_file = Parser::Source::Buffer.new('(assert_parses)')
|
72
72
|
source_file.source = code
|
73
73
|
|
@@ -92,10 +92,10 @@ module ParseHelper
|
|
92
92
|
raise "No entity with AST path #{ast_path} in #{parsed_ast.inspect}"
|
93
93
|
end
|
94
94
|
|
95
|
-
assert astlet.
|
96
|
-
"(#{version}) #{astlet.
|
95
|
+
assert astlet.location.respond_to?(map_field),
|
96
|
+
"(#{version}) #{astlet.location.inspect}.respond_to?(#{map_field.inspect}) for:\n#{parsed_ast.inspect}"
|
97
97
|
|
98
|
-
range = astlet.
|
98
|
+
range = astlet.location.send(map_field)
|
99
99
|
|
100
100
|
assert_source_range(begin_pos, end_pos, range, version, line.inspect)
|
101
101
|
end
|
@@ -111,7 +111,7 @@ module ParseHelper
|
|
111
111
|
# | ~~~ highlights (0)})
|
112
112
|
# ~~~
|
113
113
|
def assert_diagnoses(diagnostic, code, source_maps='', versions=ALL_VERSIONS)
|
114
|
-
with_versions(
|
114
|
+
with_versions(versions) do |version, parser|
|
115
115
|
source_file = Parser::Source::Buffer.new('(assert_diagnoses)')
|
116
116
|
source_file.source = code
|
117
117
|
|