summaryrefslogtreecommitdiff
path: root/ext/ripper/tools/generate.rb
diff options
context:
space:
mode:
Diffstat (limited to 'ext/ripper/tools/generate.rb')
-rwxr-xr-xext/ripper/tools/generate.rb180
1 files changed, 180 insertions, 0 deletions
diff --git a/ext/ripper/tools/generate.rb b/ext/ripper/tools/generate.rb
new file mode 100755
index 0000000000..a93d6cd839
--- /dev/null
+++ b/ext/ripper/tools/generate.rb
@@ -0,0 +1,180 @@
+# $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 <<header
+# This file is automatically generated from #{File.basename(template)} and parse.y.
+# DO NOT MODIFY!!!!!!
+
+header
+ File.foreach(template) do |line|
+ case line
+ when /\A\#include ids1/
+ comma = ''
+ ids1.each do |id, arity|
+ f.print comma; comma = ",\n"
+ f.print " #{id.intern.inspect} => #{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