parser 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,6 +17,9 @@ module Parser
17
17
  require 'parser/source/buffer'
18
18
  require 'parser/source/range'
19
19
 
20
+ require 'parser/source/rewriter'
21
+ require 'parser/source/rewriter/action'
22
+
20
23
  require 'parser/source/map'
21
24
  require 'parser/source/map/operator'
22
25
  require 'parser/source/map/collection'
@@ -47,6 +50,8 @@ module Parser
47
50
 
48
51
  require 'parser/base'
49
52
 
53
+ require 'parser/rewriter'
54
+
50
55
  ERRORS = {
51
56
  # Lexer errors
52
57
  :unicode_point_too_large => "invalid Unicode codepoint (too large)",
@@ -339,9 +339,6 @@ module Parser
339
339
  when :back_ref, :nth_ref
340
340
  message = ERRORS[:backref_assignment]
341
341
  diagnostic :error, message, node.src.expression
342
-
343
- else
344
- raise NotImplementedError, "build_assignable #{node.inspect}"
345
342
  end
346
343
  end
347
344
 
@@ -375,9 +372,6 @@ module Parser
375
372
  when :back_ref, :nth_ref
376
373
  message = ERRORS[:backref_assignment]
377
374
  diagnostic :error, message, lhs.src.expression
378
-
379
- else
380
- raise NotImplementedError, "build op_assign #{lhs.inspect}"
381
375
  end
382
376
  end
383
377
 
@@ -664,7 +658,7 @@ module Parser
664
658
  def condition(cond_t, cond, then_t,
665
659
  if_true, else_t, if_false, end_t)
666
660
  n(:if, [ check_condition(cond), if_true, if_false ],
667
- condition_map(cond_t, then_t, if_true, else_t, if_false, end_t))
661
+ condition_map(cond_t, cond, then_t, if_true, else_t, if_false, end_t))
668
662
  end
669
663
 
670
664
  def condition_mod(if_true, if_false, cond_t, cond)
@@ -687,7 +681,7 @@ module Parser
687
681
 
688
682
  def case(case_t, expr, when_bodies, else_t, else_body, end_t)
689
683
  n(:case, [ expr, *(when_bodies << else_body)],
690
- condition_map(case_t, nil, nil, else_t, else_body, end_t))
684
+ condition_map(case_t, expr, nil, nil, else_t, else_body, end_t))
691
685
  end
692
686
 
693
687
  # Loops
@@ -891,7 +885,17 @@ module Parser
891
885
  end
892
886
 
893
887
  def string_part_map(string_t)
894
- Source::Map::Collection.new(loc(string_t).begin, loc(string_t).end,
888
+ str_range = loc(string_t)
889
+
890
+ begin_l = Source::Range.new(str_range.source_buffer,
891
+ str_range.begin_pos,
892
+ str_range.begin_pos + 1)
893
+
894
+ end_l = Source::Range.new(str_range.source_buffer,
895
+ str_range.end_pos - 1,
896
+ str_range.end_pos)
897
+
898
+ Source::Map::Collection.new(begin_l, end_l,
895
899
  loc(string_t))
896
900
  end
897
901
 
@@ -1052,17 +1056,19 @@ module Parser
1052
1056
  join_exprs(pre_e, post_e))
1053
1057
  end
1054
1058
 
1055
- def condition_map(keyword_t, begin_t, body_e, else_t, else_e, end_t)
1059
+ def condition_map(keyword_t, cond_e, begin_t, body_e, else_t, else_e, end_t)
1056
1060
  if end_t
1057
1061
  end_l = loc(end_t)
1058
1062
  elsif else_e && else_e.src.expression
1059
1063
  end_l = else_e.src.expression
1060
- elsif else_t
1064
+ elsif loc(else_t)
1061
1065
  end_l = loc(else_t)
1062
1066
  elsif body_e.src.expression
1063
1067
  end_l = body_e.src.expression
1064
- else
1068
+ elsif loc(begin_t)
1065
1069
  end_l = loc(begin_t)
1070
+ else
1071
+ end_l = cond_e.src.expression
1066
1072
  end
1067
1073
 
1068
1074
  Source::Map::Condition.new(loc(keyword_t),
@@ -1084,11 +1090,10 @@ module Parser
1084
1090
  def rescue_body_map(keyword_t, exc_list_e, assoc_t,
1085
1091
  exc_var_e, then_t,
1086
1092
  compstmt_e)
1087
- end_l = compstmt_e.src.expression ||
1088
- loc(then_t) ||
1089
- exc_var_e.src.expression ||
1090
- exc_list_e.src.expression ||
1091
- loc(keyword_t)
1093
+ end_l = compstmt_e.src.expression || loc(then_t)
1094
+ end_l = exc_var_e.src.expression if end_l.nil? && exc_var_e
1095
+ end_l = exc_list_e.src.expression if end_l.nil? && exc_list_e
1096
+ end_l = loc(keyword_t) if end_l.nil?
1092
1097
 
1093
1098
  Source::Map::RescueBody.new(loc(keyword_t), loc(assoc_t), loc(then_t),
1094
1099
  loc(keyword_t).join(end_l))
@@ -1097,7 +1102,11 @@ module Parser
1097
1102
  def eh_keyword_map(compstmt_e, keyword_t, body_es,
1098
1103
  else_t, else_e)
1099
1104
  if synthesized_nil?(compstmt_e)
1100
- begin_l = loc(keyword_t)
1105
+ if keyword_t.nil?
1106
+ begin_l = body_es.first.src.expression
1107
+ else
1108
+ begin_l = loc(keyword_t)
1109
+ end
1101
1110
  else
1102
1111
  begin_l = compstmt_e.src.expression
1103
1112
  end
@@ -1132,7 +1141,8 @@ module Parser
1132
1141
  end
1133
1142
 
1134
1143
  def loc(token)
1135
- token[1] if token
1144
+ # Pass through `nil`s and return nil for tNL.
1145
+ token[1] if token && token[0]
1136
1146
  end
1137
1147
 
1138
1148
  def diagnostic(type, message, location, highlights=[])
@@ -240,7 +240,7 @@ class Parser::Lexer
240
240
  end
241
241
 
242
242
  def range(s = @ts, e = @te)
243
- Parser::Source::Range.new(@source_buffer, s, e - 1)
243
+ Parser::Source::Range.new(@source_buffer, s, e)
244
244
  end
245
245
 
246
246
  def emit(type, value = tok, s = @ts, e = @te)
@@ -993,7 +993,8 @@ class Parser::Lexer
993
993
  fnext expr_endfn; fbreak; };
994
994
 
995
995
  constant
996
- => { emit(:tCONSTANT) };
996
+ => { emit(:tCONSTANT)
997
+ fnext expr_endfn; fbreak; };
997
998
 
998
999
  bareword [?=!]?
999
1000
  => { emit(:tIDENTIFIER)
@@ -20,9 +20,9 @@ module Parser
20
20
  from, to = range.begin.column, range.end.column
21
21
 
22
22
  line = range.source_line
23
- line[from..to] = "\e[4m#{line[from..to]}\e[0m"
23
+ line[from...to] = "\e[4m#{line[from...to]}\e[0m"
24
24
 
25
- tail_len = to - from
25
+ tail_len = to - from - 1
26
26
  tail = "~" * (tail_len >= 0 ? tail_len : 0)
27
27
  decoration = "#{" " * from}\e[1;31m^#{tail}\e[0m #{token} ".
28
28
  ljust(70) + info
@@ -0,0 +1,34 @@
1
+ module Parser
2
+ class Rewriter < Parser::AST::Processor
3
+ def rewrite(source_buffer, ast)
4
+ @source_rewriter = Source::Rewriter.new(source_buffer)
5
+
6
+ process(ast)
7
+
8
+ @source_rewriter.process
9
+ end
10
+
11
+ private
12
+
13
+ def assignment?(node)
14
+ [:lvasgn, :ivasgn, :gvasgn,
15
+ :cvasgn, :cvdecl, :cdecl].include?(node.type)
16
+ end
17
+
18
+ def remove(range)
19
+ @source_rewriter.remove(range)
20
+ end
21
+
22
+ def insert_before(range, content)
23
+ @source_rewriter.insert_before(range, content)
24
+ end
25
+
26
+ def insert_after(range, content)
27
+ @source_rewriter.insert_after(range, content)
28
+ end
29
+
30
+ def replace(range, content)
31
+ @source_rewriter.replace(range, content)
32
+ end
33
+ end
34
+ end
@@ -123,12 +123,11 @@ module Parser
123
123
  end
124
124
  end
125
125
 
126
- def process_all_input
127
- input_size = @files.size + @fragments.size
128
- if input_size > 1
129
- puts "Using #{@parser_class} to parse #{input_size} files."
130
- end
126
+ def input_size
127
+ @files.size + @fragments.size
128
+ end
131
129
 
130
+ def process_all_input
132
131
  parsing_time =
133
132
  Benchmark.measure do
134
133
  process_fragments
@@ -14,37 +14,79 @@ module Parser
14
14
  elsif node.src.expression.nil?
15
15
  puts "\e[31m[location info present but empty]\e[0m"
16
16
  else
17
- puts "\e[32m#{node.src.expression.source_line}\e[0m"
18
-
19
- hilight_line = ""
20
-
21
- print_line = lambda do |line|
22
- puts line.
23
- gsub(/[a-z_]+/) { |m| "\e[1;33m#{m}\e[0m" }.
24
- gsub(/~+/) { |m| "\e[1;35m#{m}\e[0m" }
17
+ source_line_no = nil
18
+ source_line = ""
19
+ hilight_line = ""
20
+
21
+ print_line = lambda do
22
+ unless hilight_line.empty?
23
+ puts hilight_line.
24
+ gsub(/[a-z_]+/) { |m| "\e[1;33m#{m}\e[0m" }.
25
+ gsub(/[~.]+/) { |m| "\e[1;35m#{m}\e[0m" }
26
+ hilight_line = ""
27
+ end
25
28
  end
26
29
 
27
- node.src.to_hash.each do |name, range|
28
- next if range.nil?
29
-
30
- length = range.length + 1 + name.length
31
- end_col = range.begin.column + length
32
- col_range = range.begin.column...end_col
30
+ print_source = lambda do |range|
31
+ source_line = range.source_line
32
+ puts "\e[32m#{source_line}\e[0m"
33
+ source_line
34
+ end
33
35
 
34
- if hilight_line.length < end_col
35
- hilight_line = hilight_line.ljust(end_col)
36
+ node.src.to_hash.
37
+ sort_by do |name, range|
38
+ [(range ? range.line : 0),
39
+ (name == :expression ? 1 : 0)]
40
+ end.
41
+ each do |name, range|
42
+ next if range.nil?
43
+
44
+ if source_line_no != range.line
45
+ print_line.call()
46
+ source_line = print_source.call(range)
47
+ source_line_no = range.line
48
+ end
49
+
50
+ beg_col = range.begin.column
51
+
52
+ if beg_col + range.length > source_line.length
53
+ multiline = true
54
+ range_length = source_line.length - beg_col + 3
55
+ else
56
+ multiline = false
57
+ range_length = range.length
58
+ end
59
+
60
+ length = range_length + 1 + name.length
61
+ end_col = beg_col + length
62
+
63
+ if beg_col > 0
64
+ col_range = (beg_col - 1)...end_col
65
+ else
66
+ col_range = beg_col...end_col
67
+ end
68
+
69
+ if hilight_line.length < end_col
70
+ hilight_line = hilight_line.ljust(end_col)
71
+ end
72
+
73
+ if hilight_line[col_range] =~ /^\s*$/
74
+ if multiline
75
+ tail = ('~' * (source_line.length - beg_col)) + '...'
76
+ else
77
+ tail = '~' * range_length
78
+ end
79
+
80
+ tail = ' ' + tail if beg_col > 0
81
+
82
+ hilight_line[col_range] = tail + " #{name}"
83
+ else
84
+ print_line.call
85
+ redo
86
+ end
36
87
  end
37
88
 
38
- if hilight_line[col_range] =~ /^\s*$/
39
- hilight_line[col_range] = '~' * range.length + " #{name}"
40
- else
41
- print_line.call(hilight_line)
42
- hilight_line = ""
43
- redo
44
- end
45
- end
46
-
47
- print_line.call(hilight_line) unless hilight_line.empty?
89
+ print_line.call
48
90
  end
49
91
 
50
92
  super
@@ -73,6 +115,14 @@ module Parser
73
115
  end
74
116
  end
75
117
 
118
+ def process_all_input
119
+ super
120
+
121
+ if input_size > 1
122
+ puts "Using #{@parser_class} to parse #{input_size} files."
123
+ end
124
+ end
125
+
76
126
  def process(buffer)
77
127
  ast = @parser.parse(buffer)
78
128
 
@@ -1,13 +1,93 @@
1
1
  require 'parser/runner'
2
+ require 'tempfile'
2
3
 
3
4
  module Parser
4
5
 
5
6
  class Runner::RubyRewrite < Runner
7
+ def initialize
8
+ super
9
+
10
+ @rewriters = []
11
+ end
12
+
6
13
  private
7
14
 
8
15
  def runner_name
9
16
  'ruby-rewrite'
10
17
  end
18
+
19
+ def setup_option_parsing
20
+ super
21
+
22
+ @slop.on 'l=', 'load=', 'Load a rewriter' do |file|
23
+ load_and_discover(file)
24
+ end
25
+ end
26
+
27
+ def load_and_discover(file)
28
+ load file
29
+
30
+ const_name = file.
31
+ sub(/\.rb$/, '').
32
+ gsub(/(^|_)([a-z])/) do |m|
33
+ "#{$2.upcase}"
34
+ end
35
+
36
+ @rewriters << Object.const_get(const_name)
37
+ end
38
+
39
+ def process(initial_buffer)
40
+ buffer = initial_buffer
41
+
42
+ @rewriters.each do |rewriter_class|
43
+ @parser.reset
44
+ ast = @parser.parse(buffer)
45
+
46
+ rewriter = rewriter_class.new
47
+ new_source = rewriter.rewrite(buffer, ast)
48
+
49
+ new_buffer = Source::Buffer.new(initial_buffer.name +
50
+ '|after ' + rewriter_class.name)
51
+ new_buffer.source = new_source
52
+
53
+ @parser.reset
54
+ new_ast = @parser.parse(new_buffer)
55
+
56
+ unless ast == new_ast
57
+ $stderr.puts "ASTs do not match:"
58
+
59
+ old = Tempfile.new('old')
60
+ old.write ast.inspect + "\n"; old.flush
61
+
62
+ new = Tempfile.new('new')
63
+ new.write new_ast.inspect + "\n"; new.flush
64
+
65
+ IO.popen("diff -u #{old.path} #{new.path}") do |io|
66
+ diff = io.read.
67
+ sub(/^---.*/, "--- #{buffer.name}").
68
+ sub(/^\+\+\+.*/, "+++ #{new_buffer.name}")
69
+
70
+ $stderr.write diff
71
+ end
72
+
73
+ exit 1
74
+ end
75
+
76
+ buffer = new_buffer
77
+ end
78
+
79
+ if File.exist?(buffer.name)
80
+ File.open(buffer.name, 'w') do |file|
81
+ file.write buffer.source
82
+ end
83
+ else
84
+ if input_size > 1
85
+ puts "Rewritten content of #{buffer.name}:"
86
+ end
87
+
88
+ puts buffer.source
89
+ end
90
+ end
11
91
  end
12
92
 
13
93
  end
@@ -15,6 +15,10 @@ module Parser
15
15
  freeze
16
16
  end
17
17
 
18
+ def line
19
+ @expression.line
20
+ end
21
+
18
22
  def with_expression(expression_l)
19
23
  with { |map| map.update_expression(expression_l) }
20
24
  end
@@ -21,7 +21,7 @@ module Parser
21
21
  end
22
22
 
23
23
  def size
24
- @end_pos - @begin_pos + 1
24
+ @end_pos - @begin_pos
25
25
  end
26
26
 
27
27
  alias length size
@@ -39,13 +39,25 @@ module Parser
39
39
  end
40
40
 
41
41
  def column_range
42
- self.begin.column..self.end.column
42
+ self.begin.column...self.end.column
43
43
  end
44
44
 
45
45
  def source_line
46
46
  @source_buffer.source_line(line)
47
47
  end
48
48
 
49
+ def to_source
50
+ source_line[column_range]
51
+ end
52
+
53
+ def is?(*what)
54
+ what.include?(to_source)
55
+ end
56
+
57
+ def to_a
58
+ (@begin_pos...@end_pos).to_a
59
+ end
60
+
49
61
  def to_s
50
62
  line, column = @source_buffer.decompose_position(@begin_pos)
51
63
 
@@ -55,11 +67,18 @@ module Parser
55
67
  def join(other)
56
68
  Range.new(@source_buffer,
57
69
  [@begin_pos, other.begin_pos].min,
58
- [@end_pos, other.end_pos].max)
70
+ [@end_pos, other.end_pos].max)
71
+ end
72
+
73
+ def ==(other)
74
+ other.is_a?(Range) &&
75
+ @source_buffer == other.source_buffer &&
76
+ @begin_pos == other.begin_pos &&
77
+ @end_pos == other.end_pos
59
78
  end
60
79
 
61
80
  def inspect
62
- "#<Source::Range #{@source_buffer.name} #{@begin_pos}..#{@end_pos}>"
81
+ "#<Source::Range #{@source_buffer.name} #{@begin_pos}...#{@end_pos}>"
63
82
  end
64
83
  end
65
84