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