зеркало из https://github.com/github/ruby.git
153 строки
3.6 KiB
Ruby
Executable File
153 строки
3.6 KiB
Ruby
Executable File
# $Id$
|
|
|
|
require 'optparse'
|
|
|
|
def main
|
|
mode = nil
|
|
ids1src = nil
|
|
ids2src = nil
|
|
template = nil
|
|
output = nil
|
|
|
|
parser = @parser = OptionParser.new
|
|
parser.banner = "Usage: #{File.basename($0)} --mode=MODE [--ids1src=PATH] [--ids2src=PATH] [--output=PATH]"
|
|
parser.on('--mode=MODE', 'check, eventids1, or eventids2table.') {|m|
|
|
mode = m
|
|
}
|
|
parser.on('--ids1src=PATH', 'A source file of event-IDs 1 (parse.y).') {|path|
|
|
ids1src = path
|
|
}
|
|
parser.on('--ids2src=PATH', 'A source file of event-IDs 2 (eventids2.c).') {|path|
|
|
ids2src = path
|
|
}
|
|
parser.on('--output=PATH', 'An output file.') {|path|
|
|
output = path
|
|
}
|
|
parser.on('--help', 'Prints this message and quit.') {
|
|
puts parser.help
|
|
exit true
|
|
}
|
|
begin
|
|
parser.parse!
|
|
rescue OptionParser::ParseError => err
|
|
usage err.message
|
|
end
|
|
usage 'no mode given' unless mode
|
|
case mode
|
|
when 'check'
|
|
usage 'no --ids1src' unless ids1src
|
|
usage 'no --ids2src' unless ids2src
|
|
h = read_ids1_with_locations(ids1src)
|
|
check_arity h
|
|
ids2 = read_ids2(ids2src)
|
|
common = h.keys & ids2
|
|
unless common.empty?
|
|
abort "event crash: #{common.join(' ')}"
|
|
end
|
|
exit 0
|
|
when 'eventids1'
|
|
usage 'no --ids1src' unless ids1src
|
|
result = generate_eventids1(read_ids1(ids1src))
|
|
when 'eventids2table'
|
|
usage 'no --ids2src' unless ids2src
|
|
result = generate_eventids2_table(read_ids2(ids2src))
|
|
end
|
|
if output
|
|
File.open(output, 'w') {|f|
|
|
f.write result
|
|
}
|
|
else
|
|
puts result
|
|
end
|
|
end
|
|
|
|
def usage(msg)
|
|
$stderr.puts msg
|
|
$stderr.puts @parser.help
|
|
exit false
|
|
end
|
|
|
|
def generate_eventids1(ids)
|
|
buf = ""
|
|
ids.each do |id, arity|
|
|
buf << %Q[static ID ripper_id_#{id};\n]
|
|
end
|
|
buf << %Q[\n]
|
|
buf << %Q[static void\n]
|
|
buf << %Q[ripper_init_eventids1(VALUE self)\n]
|
|
buf << %Q[{\n]
|
|
buf << %Q[ VALUE h;\n]
|
|
buf << %Q[ ID id;\n]
|
|
ids.each do |id, arity|
|
|
buf << %Q[ ripper_id_#{id} = rb_intern_const("on_#{id}");\n]
|
|
end
|
|
buf << %Q[\n]
|
|
buf << %Q[ h = rb_hash_new();\n]
|
|
buf << %Q[ rb_define_const(self, "PARSER_EVENT_TABLE", h);\n]
|
|
ids.each do |id, arity|
|
|
buf << %Q[ id = rb_intern_const("#{id}");\n]
|
|
buf << %Q[ rb_hash_aset(h, ID2SYM(id), INT2NUM(#{arity}));\n]
|
|
end
|
|
buf << %Q[}\n]
|
|
buf
|
|
end
|
|
|
|
def generate_eventids2_table(ids)
|
|
buf = ""
|
|
buf << %Q[static void\n]
|
|
buf << %Q[ripper_init_eventids2_table(VALUE self)\n]
|
|
buf << %Q[{\n]
|
|
buf << %Q[ VALUE h = rb_hash_new();\n]
|
|
buf << %Q[ ID id;\n]
|
|
buf << %Q[ rb_define_const(self, "SCANNER_EVENT_TABLE", h);\n]
|
|
ids.each do |id|
|
|
buf << %Q[ id = rb_intern_const("#{id}");\n]
|
|
buf << %Q[ rb_hash_aset(h, ID2SYM(id), INT2NUM(1));\n]
|
|
end
|
|
buf << %Q[}\n]
|
|
buf
|
|
end
|
|
|
|
def read_ids1(path)
|
|
strip_locations(read_ids1_with_locations(path))
|
|
end
|
|
|
|
def strip_locations(h)
|
|
h.map {|event, list| [event, list.first[1]] }\
|
|
.sort_by {|event, arity| event.to_s }
|
|
end
|
|
|
|
def check_arity(h)
|
|
invalid = false
|
|
h.each do |event, list|
|
|
unless list.map {|line, arity| arity }.uniq.size == 1
|
|
invalid = true
|
|
locations = list.map {|line, a| "#{line}:#{a}" }.join(', ')
|
|
$stderr.puts "arity crash [event=#{event}]: #{locations}"
|
|
end
|
|
end
|
|
abort if invalid
|
|
end
|
|
|
|
def read_ids1_with_locations(path)
|
|
h = {}
|
|
File.open(path) {|f|
|
|
f.each do |line|
|
|
next if /\A\#\s*define\s+s?dispatch/ =~ line
|
|
next if /ripper_dispatch/ =~ line
|
|
line.scan(/dispatch(\d)\((\w+)/) do |arity, event|
|
|
(h[event] ||= []).push [f.lineno, arity.to_i]
|
|
end
|
|
end
|
|
}
|
|
h
|
|
end
|
|
|
|
def read_ids2(path)
|
|
File.open(path) {|f|
|
|
return f.read.scan(/ripper_id_(\w+)/).flatten.uniq.sort
|
|
}
|
|
end
|
|
|
|
main
|