parser 1.2.0 → 1.3.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.
@@ -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