1# $Id: generate.rb 34802 2012-02-25 07:08:47Z naruse $
2
3require 'optparse'
4
5def main
6  mode = nil
7  ids1src = nil
8  ids2src = nil
9  output = nil
10
11  parser = @parser = OptionParser.new
12  parser.banner = "Usage: #{File.basename($0)} --mode=MODE [--ids1src=PATH] [--ids2src=PATH] [--output=PATH]"
13  parser.on('--mode=MODE', 'check, eventids1, or eventids2table.') {|m|
14    mode = m
15  }
16  parser.on('--ids1src=PATH', 'A source file of event-IDs 1 (parse.y).') {|path|
17    ids1src = path
18  }
19  parser.on('--ids2src=PATH', 'A source file of event-IDs 2 (eventids2.c).') {|path|
20    ids2src = path
21  }
22  parser.on('--output=PATH', 'An output file.') {|path|
23    output = path
24  }
25  parser.on('--help', 'Prints this message and quit.') {
26    puts parser.help
27    exit true
28  }
29  begin
30    parser.parse!
31  rescue OptionParser::ParseError => err
32    usage err.message
33  end
34  usage 'no mode given' unless mode
35  case mode
36  when 'check'
37    usage 'no --ids1src' unless ids1src
38    usage 'no --ids2src' unless ids2src
39    h = read_ids1_with_locations(ids1src)
40    check_arity h
41    ids2 = read_ids2(ids2src)
42    common = h.keys & ids2
43    unless common.empty?
44      abort "event crash: #{common.join(' ')}"
45    end
46    exit 0
47  when 'eventids1'
48    usage 'no --ids1src' unless ids1src
49    result = generate_eventids1(read_ids1(ids1src))
50  when 'eventids2table'
51    usage 'no --ids2src' unless ids2src
52    result = generate_eventids2_table(read_ids2(ids2src))
53  end
54  if output
55    File.open(output, 'w') {|f|
56      f.write result
57    }
58  else
59    puts result
60  end
61end
62
63def usage(msg)
64  $stderr.puts msg
65  $stderr.puts @parser.help
66  exit false
67end
68
69def generate_eventids1(ids)
70  buf = ""
71  ids.each do |id, arity|
72    buf << %Q[static ID ripper_id_#{id};\n]
73  end
74  buf << %Q[\n]
75  buf << %Q[static void\n]
76  buf << %Q[ripper_init_eventids1(void)\n]
77  buf << %Q[{\n]
78  ids.each do |id, arity|
79    buf << %Q[    ripper_id_#{id} = rb_intern_const("on_#{id}");\n]
80  end
81  buf << %Q[}\n]
82  buf << %Q[\n]
83  buf << %Q[static void\n]
84  buf << %Q[ripper_init_eventids1_table(VALUE self)\n]
85  buf << %Q[{\n]
86  buf << %Q[    VALUE h = rb_hash_new();\n]
87  buf << %Q[    ID id;\n]
88  buf << %Q[    rb_define_const(self, "PARSER_EVENT_TABLE", h);\n]
89  ids.each do |id, arity|
90    buf << %Q[    id = rb_intern_const("#{id}");\n]
91    buf << %Q[    rb_hash_aset(h, ID2SYM(id), INT2NUM(#{arity}));\n]
92  end
93  buf << %Q[}\n]
94  buf
95end
96
97def generate_eventids2_table(ids)
98  buf = ""
99  buf << %Q[static void\n]
100  buf << %Q[ripper_init_eventids2_table(VALUE self)\n]
101  buf << %Q[{\n]
102  buf << %Q[    VALUE h = rb_hash_new();\n]
103  buf << %Q[    ID id;\n]
104  buf << %Q[    rb_define_const(self, "SCANNER_EVENT_TABLE", h);\n]
105  ids.each do |id|
106    buf << %Q[    id = rb_intern_const("#{id}");\n]
107    buf << %Q[    rb_hash_aset(h, ID2SYM(id), INT2NUM(1));\n]
108  end
109  buf << %Q[}\n]
110  buf
111end
112
113def read_ids1(path)
114  strip_locations(read_ids1_with_locations(path))
115end
116
117def strip_locations(h)
118  h.map {|event, list| [event, list.first[1]] }\
119      .sort_by {|event, arity| event.to_s }
120end
121
122def check_arity(h)
123  invalid = false
124  h.each do |event, list|
125    unless list.map {|line, arity| arity }.uniq.size == 1
126      invalid = true
127      locations = list.map {|line, a| "#{line}:#{a}" }.join(', ')
128      $stderr.puts "arity crash [event=#{event}]: #{locations}"
129    end
130  end
131  abort if invalid
132end
133
134def read_ids1_with_locations(path)
135  h = {}
136  File.open(path) {|f|
137    f.each do |line|
138      next if /\A\#\s*define\s+s?dispatch/ =~ line
139      next if /ripper_dispatch/ =~ line
140      line.scan(/dispatch(\d)\((\w+)/) do |arity, event|
141        (h[event] ||= []).push [f.lineno, arity.to_i]
142      end
143    end
144  }
145  h
146end
147
148def read_ids2(path)
149  File.open(path) {|f|
150    return f.read.scan(/ripper_id_(\w+)/).flatten.uniq.sort
151  }
152end
153
154main
155