# $Id$ require 'stringio' 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] [--template=PATH] [--output=PATH]" parser.on('--mode=MODE', '"ripper/core" or "eventids1".') {|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('--template=PATH', 'A template file of ripper/core.rb.') {|path| template = path } parser.on('--output=PATH', 'An output file.') {|path| output = path } parser.on('--help', 'Prints this message and quit.') { puts parser.help exit 0 } begin parser.parse! rescue OptionParser::ParseError => err usage err.message end usage 'no mode given' unless mode case mode when 'ripper/core', 'ripper/core.rb' usage 'no --ids1src' unless ids1src usage 'no --ids2src' unless ids2src usage 'no --template' unless template result = generate_ripper_core(template, read_ids1(ids1src), read_ids2(ids2src)) when 'eventids1', 'eventids1.c' usage 'no --ids1src' unless ids1src result = generate_eventids1(read_ids1(ids1src)) 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 1 end def generate_ripper_core(template, ids1, ids2) f = StringIO.new f.print <
#{arity}" end f.puts when /\A\#include ids2/ comma = '' ids2.each do |id| f.print comma; comma = ",\n" f.print " #{id.intern.inspect} => 1" end f.puts when /\A\#include handlers1/ ids1.each do |id, arity| f.puts f.puts " def on_#{id}#{paramdecl(arity)}" f.puts " #{arity == 0 ? 'nil' : 'a'}" f.puts " end" end when /\A\#include handlers2/ ids2.each do |id| f.puts f.puts " def on_#{id}(token)" f.puts " token" f.puts " end" end when /\A\#include (.*)/ raise "unknown operation: #include #{$1}" else f.print line end end f.string end def paramdecl(n) return '' if n == 0 '(' + ('a'..'z').to_a[0, n].join(', ') + ')' end def generate_eventids1(ids) f = StringIO.new ids.each do |id, arity| f.puts "static ID ripper_id_#{id};" end f.puts f.puts 'static void' f.puts 'ripper_init_eventids1()' f.puts '{' ids.each do |id, arity| f.puts %Q[ ripper_id_#{id} = rb_intern("on_#{id}");] end f.puts '}' f.string end def read_ids1(path) check_arity path ids = {} File.open(path) {|f| f.each do |line| next if /\A\#\s*define\s+s?dispatch/ =~ line next if /ripper_dispatch/ =~ line if a = line.scan(/dispatch(\d)\((\w+)/) a.each do |arity, event| ids[event] = arity.to_i end end end } ids.to_a.sort_by {|event, arity| event.to_s } end def check_arity(path) invalid = false table = {} File.open(path) {|f| File.foreach(path) do |line| next if /\A\#\s*define\s+s?dispatch\d/ =~ line next if /ripper_dispatch\d/ =~ line line.scan(/dispatch(\d)\((\w+)/) do |num, ev| num = num.to_i if table.key?(ev) locations, arity = *table[ev] unless num == arity invalid = true $stderr.puts "arity differ [event=#{ev}]: #{f.lineno}=#{num}; #{locations.join(',')}=#{arity}" end locations.push f.lineno else table[ev] = [[f.lineno], num.to_i] end end end } exit 1 if invalid end def read_ids2(path) File.open(path) {|f| return f.read.scan(/ripper_id_(\w+)/).flatten.uniq.sort } end main