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.
@@ -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], @comments.pop)
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], @comments.pop)
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
- # TODO: Extract colon
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
  {
@@ -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], @comments.pop)
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], @comments.pop)
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
- # TODO: Extract colon
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
  {
@@ -107,7 +107,7 @@ module Parser
107
107
  @slop.on 'E', 'explain', 'Explain how the source is tokenized' do
108
108
  ENV['RACC_DEBUG'] = '1'
109
109
 
110
- Lexer.send :include, Lexer::Explanation
110
+ Lexer.send :prepend, Lexer::Explanation
111
111
  end
112
112
  end
113
113
 
@@ -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
- # TODO: Make this more efficient.
15
- first_line, second_line = string.lines.first(2)
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 do |source_line|
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 << [ @line_begins.length, index ]
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 <= Ruby 2.0.
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
@@ -19,6 +19,10 @@ module Parser
19
19
  @expression.line
20
20
  end
21
21
 
22
+ def column
23
+ @expression.column
24
+ end
25
+
22
26
  def with_expression(expression_l)
23
27
  with { |map| map.update_expression(expression_l) }
24
28
  end
@@ -46,12 +46,12 @@ module Parser
46
46
  @source_buffer.source_line(line)
47
47
  end
48
48
 
49
- def to_source
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?(to_source)
54
+ what.include?(source)
55
55
  end
56
56
 
57
57
  def to_a
@@ -1,3 +1,3 @@
1
1
  module Parser
2
- VERSION = '1.4.2'
2
+ VERSION = '2.0.0.beta1'
3
3
  end
@@ -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 = %q{A Ruby parser written in pure Ruby.}
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($/) + %w(
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 = %w(ruby-parse ruby-rewrite)
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
 
@@ -34,7 +34,7 @@ module ParseHelper
34
34
  parser
35
35
  end
36
36
 
37
- def with_versions(code, 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(code, versions) do |version, parser|
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.source_map.respond_to?(map_field),
96
- "(#{version}) #{astlet.source_map.inspect}.respond_to?(#{map_field.inspect}) for:\n#{parsed_ast.inspect}"
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.source_map.send(map_field)
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(code, versions) do |version, parser|
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