mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@2324 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			500 lines
		
	
	
	
		
			9.5 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			500 lines
		
	
	
	
		
			9.5 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
#!/usr/bin/env ruby
 | 
						|
# -*- ruby -*-
 | 
						|
# $Id$
 | 
						|
 | 
						|
require 'mkmf'
 | 
						|
require 'ftools'
 | 
						|
 | 
						|
$recursive = false
 | 
						|
$force     = false
 | 
						|
$conly     = true
 | 
						|
$inc_path  = []
 | 
						|
$infilename= nil
 | 
						|
$insert_require = true
 | 
						|
 | 
						|
def valid_ruby_code?(code)
 | 
						|
  begin
 | 
						|
    eval("BEGIN {return true}; #{code}")
 | 
						|
  rescue SyntaxError
 | 
						|
    return false
 | 
						|
  end
 | 
						|
  return false
 | 
						|
end
 | 
						|
 | 
						|
def print_usage
 | 
						|
  print <<EOF
 | 
						|
h2rb [-r] [-I <path>] [-d] [<filename>]
 | 
						|
EOF
 | 
						|
end
 | 
						|
 | 
						|
while( ARGV[0] )
 | 
						|
  case( ARGV[0] )
 | 
						|
  when "-r"
 | 
						|
    ARGV.shift
 | 
						|
    $recursive = true
 | 
						|
  when "-R"
 | 
						|
    ARGV.shift
 | 
						|
    $recursive = false
 | 
						|
  when "-l"
 | 
						|
    ARGV.shift
 | 
						|
    $insert_require = true
 | 
						|
  when "-L"
 | 
						|
    ARGV.shift
 | 
						|
    $insert_require = false
 | 
						|
  when "-c"
 | 
						|
    ARGV.shift
 | 
						|
    $conly = true
 | 
						|
  when "-C"
 | 
						|
    ARGV.shift
 | 
						|
    $conly = false
 | 
						|
  when "-f"
 | 
						|
    ARGV.shift
 | 
						|
    $force = true
 | 
						|
  when "-F"
 | 
						|
    ARGV.shift
 | 
						|
    $force = false
 | 
						|
  when "-I"
 | 
						|
    ARGV.shift
 | 
						|
    $inc_path << ARGV.shift
 | 
						|
  when "-d"
 | 
						|
    ARGV.shift
 | 
						|
    $DEBUG = true
 | 
						|
  when "-h","--help"
 | 
						|
    print_usage()
 | 
						|
    exit 0
 | 
						|
  when /-.*/
 | 
						|
    $stderr.print("unknown option '#{ARGV[0]}'.\n")
 | 
						|
    print_usage()
 | 
						|
    exit 0
 | 
						|
  else
 | 
						|
    $infilename = ARGV.shift
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
$inc_dir = File.join(CONFIG["prefix"], "lib", "ruby",
 | 
						|
		     CONFIG["MAJOR"] + "." + CONFIG["MINOR"],
 | 
						|
		     "dl")
 | 
						|
 | 
						|
class H2RBError < StandardError; end
 | 
						|
 | 
						|
 | 
						|
class H2RB
 | 
						|
  def initialize(inc_dir = nil, inc_path = nil, insert_require = nil)
 | 
						|
    @inc_path = inc_path || []
 | 
						|
    @inc_dir  = inc_dir  || '.'
 | 
						|
    @indent = 0
 | 
						|
    @parsed_files = []
 | 
						|
    @insert_require = insert_require || false
 | 
						|
  end
 | 
						|
 | 
						|
  def find_path(file)
 | 
						|
    if( ! file )
 | 
						|
      return nil
 | 
						|
    end
 | 
						|
    if( File.exist?(file) )
 | 
						|
      if( file[0] == ?/ )
 | 
						|
	return file
 | 
						|
      else
 | 
						|
	return file
 | 
						|
      end
 | 
						|
    end
 | 
						|
    @inc_path.each{|path|
 | 
						|
    full = File.join(path, file)
 | 
						|
      if( File.exist?(full) )
 | 
						|
	return full
 | 
						|
      end
 | 
						|
    }
 | 
						|
    return nil
 | 
						|
  end
 | 
						|
 | 
						|
  def strip_comment(line)
 | 
						|
    if( @commented )
 | 
						|
      if( e = line.index("*/") )
 | 
						|
	line[0..(e+1)] = ""
 | 
						|
	@commented = false
 | 
						|
      else
 | 
						|
	line = ""
 | 
						|
      end
 | 
						|
    else
 | 
						|
      if( s = line.index("/*") )
 | 
						|
	if( e = line.index("*/") )
 | 
						|
	  line[s..(e+1)] = ""
 | 
						|
	else
 | 
						|
	  line[s..-1] = ""
 | 
						|
	  @commented = true
 | 
						|
	end
 | 
						|
      elsif( s = line.index("//") )
 | 
						|
	line[s..(-1)] = ""
 | 
						|
      end
 | 
						|
    end
 | 
						|
      
 | 
						|
    line.gsub!(/\s+$/,"")
 | 
						|
    return line
 | 
						|
  end
 | 
						|
  
 | 
						|
  def up_indent
 | 
						|
    @indent += 1
 | 
						|
  end
 | 
						|
  
 | 
						|
  def down_indent
 | 
						|
    @indent -= 1
 | 
						|
    if( @indent < 0 )
 | 
						|
      raise
 | 
						|
    end
 | 
						|
  end
 | 
						|
  
 | 
						|
  def indent
 | 
						|
    "  " * @indent
 | 
						|
  end
 | 
						|
 | 
						|
  def rescue_begin
 | 
						|
    line = "#{indent}begin"
 | 
						|
    up_indent
 | 
						|
    return line
 | 
						|
  end
 | 
						|
 | 
						|
  def rescue_nameerror
 | 
						|
    down_indent
 | 
						|
    line = [
 | 
						|
      "#{indent}rescue NameError => e",
 | 
						|
      "#{indent}  raise e if( $DEBUG )",
 | 
						|
      "#{indent}end"].join($/)
 | 
						|
    return line
 | 
						|
  end
 | 
						|
 | 
						|
  def parse_enum(line)
 | 
						|
    if( line =~ /enum\s+(\S+\s+)?\{(.+)\}/ )
 | 
						|
      enum_name  = $1
 | 
						|
      enum_block = $2
 | 
						|
      if( enum_name )
 | 
						|
	line = "#{indent}# -- enum #{enum_name}\n"
 | 
						|
      else
 | 
						|
	line = "#{indent}# -- enum\n"
 | 
						|
      end
 | 
						|
      enums = enum_block.split(/,/).collect{|e| e.strip}
 | 
						|
      i = 0
 | 
						|
      enums.each{|elem|
 | 
						|
	var,val = elem.split(/=/).collect{|e| e.strip}
 | 
						|
	if( val )
 | 
						|
	  i = val.to_i
 | 
						|
	end
 | 
						|
	line += "#{indent}#{var} = #{i.to_s}\n"
 | 
						|
	i += 1
 | 
						|
      }
 | 
						|
      line += "#{indent}# -- end of enum"
 | 
						|
      return line
 | 
						|
    else
 | 
						|
      return nil
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  def parse_define(line)
 | 
						|
    case line
 | 
						|
    when /^#\s*define\s+(\S+)\(\)/
 | 
						|
      line = nil
 | 
						|
    when /^#\s*define\s+(\S+)\((.+)\)\s+(.+)$/
 | 
						|
      if( @conly )
 | 
						|
	line = nil
 | 
						|
      else
 | 
						|
	defname = $1
 | 
						|
	defargs = $2
 | 
						|
	defval  = $3
 | 
						|
	if( !valid_ruby_code?(defval) )
 | 
						|
	  defval = "nil # #{defval}"
 | 
						|
	end
 | 
						|
	if( defname[0,1] =~ /^[A-Z]$/ )
 | 
						|
	  line = "#{indent}#{defname} = proc{|#{defargs}| #{defval}}"
 | 
						|
	else
 | 
						|
	  line = [
 | 
						|
	    "#{indent}def #{defname}(#{defargs})",
 | 
						|
	    "#{indent}  #{defval}",
 | 
						|
	    "#{indent}end"
 | 
						|
	  ].join("\n")
 | 
						|
	end
 | 
						|
      end
 | 
						|
    when /^#\s*define\s+(\S+)\((.+)\)$/
 | 
						|
      if( @conly )
 | 
						|
	line = nil
 | 
						|
      else
 | 
						|
	defname = $1
 | 
						|
	defargs = $2
 | 
						|
	defval  = nil
 | 
						|
	if( !valid_ruby_code?(defval) )
 | 
						|
	  defval = "nil # #{defval}"
 | 
						|
	end
 | 
						|
	if( defname[0,1] =~ /^[A-Z]$/ )
 | 
						|
	  line = "#{indent}#{defname} = proc{|#{defargs}| #{defval}}"
 | 
						|
	else
 | 
						|
	  line = [
 | 
						|
	    "#{indent}def #{defname}(#{defargs})",
 | 
						|
	    "#{indent}  #{defval}",
 | 
						|
	    "#{indent}end"
 | 
						|
	  ].join("\n")
 | 
						|
	end
 | 
						|
      end
 | 
						|
    when /^#\s*define\s+(\S+)\s+(.+)$/
 | 
						|
      defname = $1
 | 
						|
      defval  = $2
 | 
						|
      if( !valid_ruby_code?(defval) )
 | 
						|
	defval = "nil # #{defval}"
 | 
						|
      end
 | 
						|
      line = [rescue_begin, "#{indent}#{defname} = #{defval}", rescue_nameerror].join($/)
 | 
						|
    when /^#\s*define\s+(\S+)$/
 | 
						|
      defname = $1
 | 
						|
      line = "#{indent}#{defname} = nil"
 | 
						|
    else
 | 
						|
      line = nil
 | 
						|
    end
 | 
						|
    return line
 | 
						|
  end
 | 
						|
  
 | 
						|
  def parse_undef(line)
 | 
						|
    case line
 | 
						|
    when /^#\s*undef\s+([A-Z]\S+)$/
 | 
						|
      defname = $1
 | 
						|
      line = "#{indent}remove_const(:#{defname})"
 | 
						|
    when /^#\s*undef\s+(\S+)$/
 | 
						|
      defname = $1
 | 
						|
      line = "#{indent}#{defname} = nil"
 | 
						|
    else
 | 
						|
      line = nil
 | 
						|
    end
 | 
						|
    return line
 | 
						|
  end
 | 
						|
  
 | 
						|
  def parse_ifdef(line)
 | 
						|
    case line
 | 
						|
    when /^#\s*ifdef\s+(\S+)$/
 | 
						|
      defname = $1
 | 
						|
      line = [
 | 
						|
	rescue_begin,
 | 
						|
	"#{indent}if( defined?(#{defname}) && ! #{defname}.nil? )"].join($/)
 | 
						|
    else
 | 
						|
      line = nil
 | 
						|
    end
 | 
						|
    return line
 | 
						|
  end
 | 
						|
  
 | 
						|
  def parse_ifndef(line)
 | 
						|
    case line
 | 
						|
    when /^#\s*ifndef\s+(\S+)$/
 | 
						|
      defname = $1
 | 
						|
      line = [
 | 
						|
	rescue_begin,
 | 
						|
	"#{indent}if( ! defined?(#{defname}) || #{defname}.nil? )"].join($/)
 | 
						|
    else
 | 
						|
      line = nil
 | 
						|
    end
 | 
						|
    return line
 | 
						|
  end
 | 
						|
  
 | 
						|
  def parse_if(line)
 | 
						|
    case line
 | 
						|
    when /^#\s*if\s+(.+)$/
 | 
						|
      cond = $1
 | 
						|
      cond.gsub!(/defined(.+)/){ "defined?(#{$1}) && ! #{$1}.nil?" }
 | 
						|
      if( valid_ruby_code?(cond) )
 | 
						|
	line = "#{indent}if( #{cond} )"
 | 
						|
      else
 | 
						|
	line = "#{indent}if( false ) # #{cond}"
 | 
						|
      end
 | 
						|
      line = [rescue_begin, line].join($/)
 | 
						|
    else
 | 
						|
      line = nil
 | 
						|
    end
 | 
						|
    return line
 | 
						|
  end
 | 
						|
  
 | 
						|
  def parse_elif(line)
 | 
						|
    case line
 | 
						|
    when /^#\s*elif\s+(.+)$/
 | 
						|
      cond = $1
 | 
						|
      cond.gsub!("defined","defined?")
 | 
						|
      line = "#{indent}elsif( #{cond} )"
 | 
						|
    else
 | 
						|
      line = nil
 | 
						|
    end
 | 
						|
    return line
 | 
						|
  end
 | 
						|
  
 | 
						|
  def parse_else(line)
 | 
						|
    case line
 | 
						|
    when /^#\s*else\s*/
 | 
						|
      line = "#{indent}else"
 | 
						|
    else
 | 
						|
      line = nil
 | 
						|
    end
 | 
						|
    return line
 | 
						|
  end
 | 
						|
  
 | 
						|
  def parse_endif(line)
 | 
						|
    case line
 | 
						|
    when /^#\s*endif\s*$/
 | 
						|
      line = ["#{indent}end", rescue_nameerror].join($/)
 | 
						|
    else
 | 
						|
      line = nil
 | 
						|
    end
 | 
						|
    return line
 | 
						|
  end
 | 
						|
  
 | 
						|
  def parse_include(line)
 | 
						|
    if( ! @insert_require )
 | 
						|
      return nil
 | 
						|
    end
 | 
						|
 | 
						|
    file = nil
 | 
						|
    case line
 | 
						|
    when /^#\s*include "(.+)"$/
 | 
						|
      file = $1
 | 
						|
      line = "#{indent}require '#{file}'"
 | 
						|
    when /^#\s*include \<(.+)\>$/
 | 
						|
      file = $1
 | 
						|
      line = "#{indent}require '#{file}'"
 | 
						|
    else
 | 
						|
      line = nil
 | 
						|
    end
 | 
						|
    if( @recursive && file && (!@parsed_files.include?(file)) )
 | 
						|
      parse(file, @recursive, @force, @conly)
 | 
						|
    end
 | 
						|
    return line
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
  def open_files(infilename)
 | 
						|
    if( ! infilename )
 | 
						|
      return [$stdin, $stdout]
 | 
						|
    end
 | 
						|
 | 
						|
    old_infilename = infilename
 | 
						|
    infilename = find_path(infilename)
 | 
						|
    if( ! infilename )
 | 
						|
      $stderr.print("'#{old_infilename}' was not found.\n")
 | 
						|
      return [nil,nil]
 | 
						|
    end
 | 
						|
 | 
						|
    if( infilename )
 | 
						|
      if( infilename[0,1] == '/' )
 | 
						|
	outfilename = File.join(@inc_dir, infilename[1..-1] + ".rb")
 | 
						|
      else
 | 
						|
	outfilename = infilename + ".rb"
 | 
						|
      end
 | 
						|
      File.mkpath(File.dirname(outfilename))
 | 
						|
    else
 | 
						|
      outfilename = nil
 | 
						|
    end
 | 
						|
    
 | 
						|
    if( infilename )
 | 
						|
      fin    = File.open(infilename,"r")
 | 
						|
    else
 | 
						|
      fin    = $stdin
 | 
						|
    end
 | 
						|
    if( outfilename )
 | 
						|
      if( File.exist?(outfilename) && (!@force) )
 | 
						|
	$stderr.print("'#{outfilename}' have already existed.\n")
 | 
						|
	return [fin, nil]
 | 
						|
      end
 | 
						|
      fout   = File.open(outfilename,"w")
 | 
						|
    else
 | 
						|
      fout   = $stdout
 | 
						|
    end
 | 
						|
 | 
						|
    $stderr.print("#{infilename} -> #{outfilename}\n")
 | 
						|
    if( fout )
 | 
						|
      dir = File.dirname(outfilename)
 | 
						|
      if( dir[0,1] != "." && dir != "" )
 | 
						|
	fout.print("if( ! $LOAD_PATH.include?('#{dir}') )\n",
 | 
						|
		   "  $LOAD_PATH.push('#{dir}')\n",
 | 
						|
		   "end\n")
 | 
						|
      end
 | 
						|
    end
 | 
						|
    return [fin,fout]
 | 
						|
  end
 | 
						|
 | 
						|
  def parse(infilename = nil, recursive = false, force = false, conly = false)
 | 
						|
    @commented = false
 | 
						|
    @recursive = recursive
 | 
						|
    @force     = force
 | 
						|
    @conly     = conly
 | 
						|
    @parsed_files << infilename
 | 
						|
 | 
						|
    fin,fout = open_files(infilename)
 | 
						|
    if( !fin )
 | 
						|
      return
 | 
						|
    end
 | 
						|
 | 
						|
    begin
 | 
						|
      line_number = 0
 | 
						|
      pre_line    = nil
 | 
						|
      fin.each_line{|line|
 | 
						|
	line_number += 1
 | 
						|
	line.chop!
 | 
						|
	if( $DEBUG )
 | 
						|
	  $stderr.print("#{line_number}:(#{@indent}):", line, "\n")
 | 
						|
	end
 | 
						|
	
 | 
						|
	if( pre_line )
 | 
						|
	  line = pre_line + line
 | 
						|
	  pre_line = nil
 | 
						|
	end
 | 
						|
 | 
						|
	if( line[-1,1] == "\\" )
 | 
						|
	  pre_line = line[0..-2]
 | 
						|
	  next
 | 
						|
	end
 | 
						|
 | 
						|
	if( eidx = line.index("enum ") )
 | 
						|
	  pre_line = line[eidx .. -1]
 | 
						|
	  if( i = line.index("{") && j = line.index("}") )
 | 
						|
	    line = line[0..j]
 | 
						|
	    pre_line = nil
 | 
						|
	  else
 | 
						|
	    next
 | 
						|
	  end
 | 
						|
	end
 | 
						|
 | 
						|
	line = strip_comment(line)
 | 
						|
	case line
 | 
						|
	when /^enum\s/
 | 
						|
	  line = parse_enum(line)
 | 
						|
	when /^#\s*define\s/
 | 
						|
	  line = parse_define(line)
 | 
						|
	when /^#\s*undef\s/
 | 
						|
	  line = parse_undef(line)
 | 
						|
	when /^#\s*ifdef\s/
 | 
						|
	  line = parse_ifdef(line)
 | 
						|
	  up_indent
 | 
						|
	when /^#\s*ifndef\s/
 | 
						|
	  line = parse_ifndef(line)
 | 
						|
	  up_indent
 | 
						|
	when /^#\s*if\s/
 | 
						|
	  line = parse_if(line)
 | 
						|
	  up_indent
 | 
						|
	when /^#\s*elif\s/
 | 
						|
	  down_indent
 | 
						|
	  line = parse_elif(line)
 | 
						|
	  up_indent
 | 
						|
	when /^#\s*else/
 | 
						|
	  down_indent
 | 
						|
	  line = parse_else(line)
 | 
						|
	  up_indent
 | 
						|
	when /^#\s*endif/
 | 
						|
	  down_indent
 | 
						|
	  line = parse_endif(line)
 | 
						|
	when /^#\s*include\s/
 | 
						|
	  line = parse_include(line)
 | 
						|
	else
 | 
						|
	  line = nil
 | 
						|
	end
 | 
						|
	if( line && fout )
 | 
						|
	  fout.print(line, "     # #{line_number}",$/)
 | 
						|
	end
 | 
						|
      }
 | 
						|
    ensure
 | 
						|
      fin.close if fin
 | 
						|
      fout.close if fout
 | 
						|
    end
 | 
						|
  end
 | 
						|
end
 | 
						|
 | 
						|
h2rb = H2RB.new($inc_dir, $inc_path, $insert_require)
 | 
						|
h2rb.parse($infilename, $recursive, $force, $conly)
 |