changeset 0:c7db221de6e8

First upload of dgen.rb and dstepgen.rb
author Jacob Carlborg <doob@me.com>
date Sat, 31 Jan 2009 17:22:44 +0100
parents
children 033d260cfc9b
files .hgignore sciprts/dgen.rb sciprts/dstepgen.rb
diffstat 3 files changed, 2748 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Sat Jan 31 17:22:44 2009 +0100
@@ -0,0 +1,11 @@
+syntax: glob
+*.DS_Store
+*.o
+dsss_objs
+dsss.last
+*.orig
+dsss_imports
+*.sh
+*.a
+*.rf
+*.dylib
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sciprts/dgen.rb	Sat Jan 31 17:22:44 2009 +0100
@@ -0,0 +1,1349 @@
+#!/usr/bin/env ruby
+
+##
+# Copyright:: Copyright (c) 2009 Jacob Carlborg. All rights reserved.
+# Author:: Jacob Carlborg
+# Version:: Initial created: 2009
+# License:: [revised BSD license]http://opensource.org/licenses/bsd-license.php or
+# 		    [Academic Free License v3.0]http://opensource.org/licenses/afl-3.0.php
+#
+
+require "rubygems"
+gem "xml-simple"
+require "xmlsimple"
+require "optparse"
+
+# Extensions to String
+class String	
+	
+	# Passes each character to the supplied block
+	#
+	# === Example:
+	# "str".each_char { |c| puts c }
+	# # outputs below
+	# s
+	# t
+	# r
+	#
+	def each_char
+		if block_given?
+			scan(/./m) do |x|
+				yield x
+			end
+		else
+			scan(/./m)
+		end
+	end
+	
+	# Indents the string
+	# levels:: how many levels/tabs to indent
+	# 
+	# === Example:
+	# "str".indent		#=>		str
+	# "str".indent(3)	#=>				str
+	# 
+	def indent (levels = 1)
+		"\t" * levels << self
+	end
+	
+	# Appends a semicolon and a newline character on the string
+	# semicolon:: should a semicolon be appended
+	#
+	# === Example:
+	# "str".nl
+	# "str".nl(false)
+	# # outputs below
+	# str;
+	#
+	# str
+	# 
+	# 
+	def nl (semicolon = true)
+		if semicolon
+			if self[-1, 1] == "{" || self[-1, 1] == "}"
+				self << "\n"
+			else
+				self << ";\n"
+			end
+		else
+			self << "\n"
+		end
+	end
+	
+	# Returns the index of the given character or length of the string if not found
+	# char:: the character to look for
+	# start:: the index where to start the search
+	# 
+	# === Example:
+	# "012345789".index_of("1")		#=> 1
+	# "0123 0123".index_of("3", 6)	#=> 8
+	# 
+	def index_of (char, start = 0)
+		return self.length if start >= self.length
+		
+		i = 0
+		
+		if start == 0
+			self.each_char do |c|
+				return i if char == c
+				i += 1			
+			end
+		else
+			self[start + 1 .. -1].each_char do |c|
+				return i + start + 1 if char == c
+				i += 1			
+			end
+		end
+		
+		return self.length
+	end
+	
+	# Returns true if the string is an Objective-C struct
+	# 
+	# === Example:
+	# "{?=ii}".struct?		#=> true
+	# 
+	def struct?
+		self =~ /\{/
+	end
+end
+
+# Extensions that adds support for member access syntax
+class Hash	
+	def type
+		result = self["type"]
+		return result unless result.nil?
+		self[:type]
+	end
+	
+	def type= (type)
+		self[:type] = type
+	end
+	
+	def id
+		result = self["id"]
+		return result unless result.nil?
+		self[:id]
+	end
+	
+	def id= (id)
+		self[:id] = id
+	end
+	
+	def methods
+		result = self["methods"]
+		return result unless result.nil?
+		self[:methods]
+	end
+	
+	def methods= (methods)
+		self[:methods] = methods
+	end
+	
+	def method
+		result = self["method"]
+		return result unless result.nil?
+		self[:method]
+	end
+	
+	def method= (method)
+		self[:method] = method
+	end
+	
+	def method_missing (method, *args)
+		self.class.instance_eval do
+			define_method(method) do |*args|
+				if args.length > 0
+					self[method[0 ... -1]] = args[0]
+					self[eval(":#{method}"[0 ... -1])] = args[0]
+				else
+					result = self[method]
+					return result unless result.nil?
+					self[eval(":#{method}")]
+				end
+			end
+		end
+		
+		if (method = method.id2name) =~ /=/
+			eval("self.#{method} (args.length < 2 ? args[0] : args)")
+		else
+			eval("self.#{method}")
+		end
+	end
+end
+
+# This Struct represents an Objective-C Framework
+Framework = Struct.new(:name, :files) do
+	def initialize
+		self.files = []
+		self.name = ""
+	end
+end
+
+# This Struct represents a C/Objective-C header
+HeaderFile = Struct.new(:name, :framework, :cftypes, :constants, :d_constants, :d_constants_static_this, :defines, 
+			  			:enums, :functions, :function_wrappers, :imports, :path, :structs, :typedefs) do
+	def initialize
+		self.name = ""
+		self.cftypes = []
+		self.constants = []
+		self.defines = []
+		self.enums = []
+		self.framework = ""
+		self.functions = []
+		self.function_wrappers = []
+		self.imports = []
+		self.path = ""
+		self.structs = []
+		self.typedefs = []
+	end	
+end
+
+# Performs the conversion of an xml metadata file to the D programming language
+class ObjcToD
+	FIRST_YEAR = "2009"
+	VERSION = 1.0
+	
+	attr_accessor :out_dir, :files
+	
+	# Creates a new instance of the ObjcToD class
+	def initialize
+		@classes = {}
+		@copyright = nil
+		@d_constants = []
+		@d_constants_static_this = []
+		@files = []
+		@frameworks = []
+		@function_wrappers = []
+		@headers = []
+		@package = "dstep"
+	end	
+	
+	# Generates the D code from the xml metadata
+	def generate_code
+		@files.each do |dstep_file|
+			xml = XmlSimple.xml_in(dstep_file)
+
+			unless xml.framework.nil?
+				frameworks = xml.framework
+
+				frameworks.each do |frame|
+					framework = Framework.new
+					framework.name = frame.name
+
+					frame.file.each do |file|
+						header = HeaderFile.new
+						header.name = file.name
+						header.constants = constants(file.constant) unless file.constant.nil?
+						header.d_constants = d_constants unless file.constant.nil?
+						header.d_constants_static_this = d_constants_static_this unless file.constant.nil?
+						header.enums = enums(file.enum) unless file.enum.nil?
+						header.functions = functions(file.function) unless file.function.nil?
+						header.function_wrappers = function_wrappers unless file.function.nil?
+						header.imports = imports(file.import, file.name, framework.name) unless file.import.nil?
+						header.structs = structs(file.struct) unless file.struct.nil?
+
+						header.typedefs = typedefs(file.typedef) unless file.typedef.nil?
+
+						framework.files << header
+					end
+
+					@frameworks << framework
+				end
+			end
+
+			unless xml.file.nil?
+				files = xml.file
+
+				file.each do |file|
+					header = HeaderFile.new
+					header.name = file.name
+					header.constants = constants(file.constant) unless file.constant.nil?
+					header.d_constants = d_constants unless file.constant.nil?
+					header.d_constants_static_this = d_constants_static_this unless file.constant.nil?
+					header.enums = enums(file.enum) unless file.enum.nil?
+					header.functions = functions(file.function) unless file.function.nil?
+					header.function_wrappers = function_wrappers unless file.function.nil?
+					header.imports = imports(file.import, file.name) unless file.import.nil?
+					header.structs = structs(file.struct) unless file.struct.nil?
+					header.typedefs = typedefs(file.typedef) unless file.typedef.nil?
+
+					@headers << header
+				end
+			end
+
+			unless xml["class"].nil?
+				classes(xml["class"])
+			end
+		end
+	end
+	
+	# Outputs the generate D code
+	def output_code
+		@frameworks.each do |framework|
+			framework_path = framework_path = "#{@out_dir}/#{@package}/#{framework.name}"
+			
+			FileUtils.mkdir_p(framework_path) unless File.exist?(framework_path)
+			
+			framework.files.each do |header|
+				file_path = "#{framework_path}/#{header.name}.d"
+				
+				File.open(file_path, "w") do |file|
+					file << copyright
+					file << "module #{@package}.#{framework.name}.#{header.name};"
+					file << header.imports.nl(false)
+					file << header.defines
+					file << header.typedefs
+					file << header.cftypes				
+					file << header.constants
+					file << header.d_constants
+					file << header.enums
+					file << header.structs
+					
+					unless header.d_constants_static_this.nil?
+						file << "static this ()".nl(false)
+						file << "{".nl(false)
+						file << header.d_constants_static_this
+						file << "}".nl(false).nl(false)
+					end
+					
+					classes = get_classes(header.name)
+					
+					classes.each do |clazz, value|
+						file << value.code.nl(false)
+						@classes.delete(clazz)
+					end
+					
+					file << header.functions
+					file << header.function_wrappers
+				end
+			end
+		end
+		
+		package_path = "#{@out_dir}/#{@package}"
+		FileUtils.mkdir_p(package_path) unless File.exist?(package_path)
+		
+		@headers.each do |header|
+			header_path = "#{package_path}/#{header.name}.d"
+			
+			File.open(header_path, "w") do |file|
+				file << copyright
+				file << "module #{@package}.#{framework.name}.#{header.name};"
+				file << header.imports.nl(false)
+				file << header.defines
+				file << header.typedefs
+				file << header.cftypes				
+				file << header.constants
+				file << header.d_constants
+				file << header.enums
+				file << header.structs
+				
+				unless header.d_constants_static_this.nil?
+					file << "static this ()".nl(false)
+					file << "{".nl(false)
+					file << header.d_constants_static_this
+					file << "}".nl(false).nl(false)
+				end
+				
+				classes = get_classes(header.name)
+				
+				classes.each do |clazz, value|
+					file << value.code.nl(false)
+					@classes.delete(clazz)
+				end
+
+				file << header.functions
+				file << header.functions_wrappers
+			end			
+		end
+		
+		@classes.each do |clazz, value|
+			class_path = "#{package_path}/#{clazz}.d"
+			
+			File.open(class_path, "w") do |file|
+				file << value.code unless value.nil?
+			end
+		end
+	end
+	
+	# Creates and returns the copyright header that is included in the top of every file
+	def copyright
+		return @copyright unless @copyright.nil?
+		
+		# Add an extra empty string in the begining because array indices begin with zero and months don't
+		months = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+		initialt_year = "2009"
+				
+		str = StringIO.new
+		date = Date.today
+		current_year = date.year.to_s
+		year = initialt_year
+		initial_created = "#{months[date.month]} #{date.day}, #{initialt_year}"
+
+		year << "-" << current_year unless initialt_year == current_year
+
+		str << "/**\n"
+		str << " * Copyright: Copyright (c) #{year} Jacob Carlborg. All rights reserved.\n"
+		str << " * Authors: Jacob Carlborg\n"
+		str << " * Version: Initial created: #{initial_created} \n"
+		str << " * License: $(LINK2 http://opensource.org/licenses/bsd-license.php, revised BSD license) or <br />\n"
+		str << " *	    	$(LINK2 http://opensource.org/licenses/afl-3.0.php, Academic Free License v3.0)\n"
+		str << " */\n"
+		
+		@copyright = str.string
+	end	
+	
+	# Gets name of the framework that the given class belongs to
+	def get_framework (class_name)
+		@frameworks.each do |framework|
+			framework.files.each do |file|
+				return framework.name if file.name == class_name
+			end
+		end
+		
+		return []
+	end	
+	
+	# Gets the classes that belongs to the given file
+	def get_classes (name)		
+		classes = @classes.find_all do |clazz, value|
+			value.file == name
+		end
+		
+		return classes
+	end	
+	
+	# Generates the D code for the classes
+	def classes (classes)
+		classes.each do |clazz|
+			str = StringIO.new
+			
+			str << "class "
+			
+			if clazz == ""
+				str << clazz.name.nl(false)
+			else
+				str << clazz.name
+				str << " : "
+				str << clazz.parent.nl(false)
+			end			
+			
+			str << "{".nl(false)
+			str << methods(clazz.method, clazz.name)
+			str << "}".nl(false)
+			
+			@classes[clazz.name] ||= {}
+			@classes[clazz.name].code = str.string
+			framework = get_framework(clazz.file)
+			@classes[clazz.name].framework = framework unless framework.nil?
+			@classes[clazz.name].file = clazz.file
+		end
+	end
+	
+	# Generates the D code for the constants/globals
+	def constants (constants)
+		str = StringIO.new
+		
+		str << "extern (C)".nl(false)
+		str << "{".nl(false)
+		str << "extern".indent.nl(false)
+		str << "{".indent.nl(false)
+		
+		constants.each do |constant|
+			type = get_type(constant.type, constant["type64"], constant["declaredType"])
+			const = constant["const"] == "true"
+			
+			if constant.type == "@"				
+				@d_constants << { :name => constant.name.dup, :type => constant.declaredType.gsub("*", ""), :const => const }
+				
+				if const
+					str << "const id".indent(2)
+				else
+					str << "id".indent(2)
+				end
+				
+				str << " "
+				str << constant["name"]
+				str << "_".nl
+			else
+				if const
+					str << "const ".indent(2)
+					str << type
+				else
+					str << "id".indent(2)
+				end
+				
+				str << " "
+				str << constant["name"].nl
+			end
+		end
+		
+		str << "}".indent.nl(false)
+		str << "}".nl(false).nl(false)
+		
+		str.string
+	end
+	
+    # Generates the D code for the constants that D can't handle directly, like classes
+	def d_constants
+		str = StringIO.new
+		
+		@d_constants.each do |constant|
+			# Deep copy constant
+			c = constant.dup
+			c.name = c.name.dup
+			c.type = c.type.dup
+			
+			str << "const " if constant.const
+			str << constant.type
+			str << " "			
+			str << constant.name.nl
+			@d_constants_static_this << c
+		end
+		
+		str << "\n"
+		@d_constants.clear
+		str.string
+	end
+	
+	# Generates the D code for the constants the has to be in a "static this"
+	def d_constants_static_this
+		str = StringIO.new
+		
+		@d_constants_static_this.each do |constant|
+			str << constant.name.indent
+			str << " = new "
+			str << constant.type
+			str << "("
+			str << constant.name
+			str << "_)".nl
+		end
+		
+		@d_constants_static_this.clear
+		str.string
+	end	
+	
+	# Generates the D code for the enums
+	def enums (enums)
+		str = StringIO.new
+		
+		enums.each do |enum|
+			str << "enum"
+								
+			if enum["name"] == ""
+				str << "\n{".nl
+				
+				enum.member.each_with_index do |member, i|
+					str << member["name"].indent
+					str << " = "
+					str << member.value
+					str << ",".nl(false) unless i == enum.member.length - 1
+				end				
+			else
+				str << enum["name"].nl
+				str << "{"
+				
+				enum["member"].each_with_index do |member, i|
+					str << member["name"].indent
+					str << " = "
+					str << member.value
+					str << ",".nl(false) unless i == enum.member.length - 1
+				end
+			end
+			
+			str << "\n}".nl.nl(false)
+		end
+		
+		str.string
+	end
+	
+	# Generates the D code for the function/method args
+	def args (args, variadic, method = false)
+		return "" if args.nil?
+		
+		str = StringIO.new
+		if variadic
+			#p args
+		end
+		args.each do |arg|
+			
+			if method || arg.type != "@"
+				type = get_type(arg.type, arg.type64, arg.declaredType)
+			else
+				type = "id"
+			end
+			
+			str << type
+			str << " "
+			str << arg["name"]
+			str << ", "
+		end
+		
+		if variadic
+		else
+		end
+		
+		str << "..." if variadic
+		
+		return str.string if variadic
+		return str.string[0 ... -2] unless variadic
+	end
+	
+	# A helper function that generates the D code for the functions/methods
+	def build_function (function, method = false, static = false)
+		str = StringIO.new
+		variadic = function["variadic"] == "true"
+		return_type  = ""
+		args = ""
+		original_type = function.returnValue[0].type
+		
+		if !method && original_type == "@"
+			return_type = "id"
+		else
+			return_type = get_type(original_type, function.returnValue[0].type64, function.returnValue[0].declaredType)
+			args(function.arg, variadic) unless function.arg.nil?
+		end
+		
+		str << "static " if static
+		str << return_type
+		str << " "
+		str << function["name"]
+		str << " ("
+		str << args(function["arg"], variadic, method)				
+		str << ")".nl
+		
+		str.string		
+	end
+	
+	# Generates the D code for the functions
+	def functions (functions)
+		str = StringIO.new
+		
+		str << "extern (C)".nl(false)
+		str << "{".nl(false)
+		
+		functions.each do |function|
+			wrapper_needed = false
+			original_type = function["returnValue"][0].type
+			
+			if original_type == "@"
+				@function_wrappers << function
+				wrapper_needed = true
+			else
+				function["arg"].each do |arg|
+					if (arg.type || arg["type64"]) == "@"
+						@function_wrappers << function
+						wrapper_needed = true
+					end
+				end unless function["arg"].nil?
+			end
+			
+			return_type = get_type(function["returnValue"][0].type, function["returnValue"][0]["type64"], function["returnValue"][0]["declaredType"])
+			variadic = function["variadic"] == "true"			
+
+			str << functions_helper(function, wrapper_needed)
+		end
+		
+		str << "}".nl(false).nl(false)
+		str.string
+	end
+	
+	# A helper function that generates the D code for the functions
+	def functions_helper (function, wrapper_needed)
+		str = StringIO.new
+
+		if wrapper_needed
+			declaration = build_function(function)
+			index = declaration.index_of(" ")
+			index = declaration.index_of(" ", index)
+			str << (declaration[0 ... index] + "_" + declaration[index .. -1]).indent
+		else
+			str << build_function(function).indent
+		end
+
+		str.string
+	end	
+	
+	# Generates the D code for the functions that D can't handle without wrappers, like functions that takes
+	# objects as arguments or returns an object
+	def function_wrappers
+		str = StringIO.new
+		
+		@function_wrappers.each do |function|
+			original_type = function["returnValue"][0].type
+			variadic = function["variadic"] == "true"
+			args = StringIO.new
+			out_args = []
+			return_type = get_type(original_type, function.returnValue[0].type64, function.returnValue[0].declaredType)
+			
+			function["arg"].each do |arg|
+				args << arg["name"]
+				
+				if arg.type == "@"
+					args << " !is null ? "
+					args << arg["name"]
+					args << ".id : null"
+				elsif arg.type == "^@"
+					type = get_type(arg.type, arg["type64"], arg["declaredType"])
+					out_args << { :type => type[4 .. -1], :name => arg["name"] }
+					
+					args << " &"
+					args << arg["name"]
+					args << "_.id"
+				end
+				
+				args << ", "
+			end unless function["arg"].nil?
+			
+			str << build_function(function, true)[0 ... -2].nl(false)
+			str << "{".nl(false)
+			
+			out_args.each do |arg|
+				str << arg.type.indent
+				str << " "
+				str << arg.name
+				str << "_ = new "
+				str << arg.type
+				str << "(false, false)".nl
+			end
+			
+			str << "\n" if out_args.length > 0
+			
+			if original_type == "@"
+				str << "id result = ".indent
+				str << function["name"]
+			elsif original_type != "v"
+				if out_args.length > 0					
+					str << return_type.indent				
+					str << " result = "
+				else
+					str << "return ".indent
+				end
+			end
+			
+			if original_type == "v"
+				str << function["name"].indent
+			elsif original_type != "@"
+				str << "cast("
+				str << return_type
+				str << ") "
+				str << function["name"]
+			end
+			
+			str << "_"
+			str << "("			
+			str << args.string[0 ... -2] unless function["arg"].nil?
+			str << ")".nl
+			str << "\n" if out_args.length > 0
+			
+			out_args.each do |arg|
+				str << arg.name.indent(2)
+				str << " = "
+				str << arg.name
+				str << "_".nl
+			end
+			
+			if out_args.length > 0
+				str << "\n"
+				str << "return result".indent unless original_type == "v"				
+			end
+
+			if original_type == "@"
+				str << "return result !is null ? new ".indent
+				str << return_type				
+				str << "(result)".nl
+			elsif original_type != "v" && out_args.length > 0
+				str << "".nl
+			end
+			
+			str << "}".nl(false).nl(false)
+		end
+		
+		@function_wrappers.clear		
+		str.string[0 ... -2]
+	end
+	
+	# Generates the D code for the imports
+	def imports (imports, filename, framework_name = nil)
+		str = StringIO.new
+		
+		imports.each do |import|			
+			str << "import #{@package}."
+			str << import.gsub("/", ".").gsub("Frameworks", "").gsub(".framework", "").gsub("Headers", "").gsub(/\.{2,}/, ".").nl
+		end
+		
+		str << "\n\n"
+		str = str.string.sort.to_s		
+		
+		return "\n\npublic:" << str if filename == framework_name
+		return str
+	end
+	
+	# Generates the D code for the methods
+	def methods (methods, class_name)
+		str = StringIO.new
+		
+		str << "this (bool init = true, bool alloc = true)".indent.nl(false)
+		str << "{".indent.nl(false)
+		str << "if (alloc && init)".indent(2).nl(false)
+		str << "{".indent(2).nl(false)
+		str << "id result = objc_msgSend(class_#{class_name}, sel_alloc)".indent(3).nl
+		str << "id result2".indent(3).nl.nl(false)
+		str << "if (result)".indent(3).nl(false)
+		str << "result2 = objc_msgSend(result, sel_init)".indent(4).nl.nl(false)
+		str << "if (result2)".indent(3).nl(false)
+		str << "this.id = result2".indent(4).nl.nl(false)
+		str << "else".indent(3).nl(false)
+		str << "this.id = result".indent(4).nl
+		str << "}".indent(2).nl(false).nl(false)
+		str << "else if (alloc)".indent(2).nl(false)
+		str << "this.id = objc_msgSend(this.id, sel_alloc)".indent(3).nl
+		str << "}".indent.nl(false).nl(false)
+		
+		str << "this (id object)".indent.nl(false)
+		str << "{".indent.nl(false)
+		str << "this.id = object".indent(2).nl
+		str << "}".indent.nl(false).nl(false)
+		
+		str << "this (id object)".indent.nl(false)
+		str << "{".indent.nl(false)
+		str << "this.id = object.id".indent(2).nl
+		str << "}".indent.nl(false).nl(false)
+		
+		str << "static #{class_name} alloc ()".indent.nl(false)
+		str << "{".indent.nl(false)
+		str << "id result = objc_msgSend(class_#{class_name}, sel_alloc)".indent(2).nl
+		str << "return result !is null ? new #{class_name}(result) : null".indent(2).nl
+		str << "}".indent.nl(false).nl(false)
+		
+		str << "#{class_name} init ()".indent.nl(false)
+		str << "{".indent.nl(false)
+		str << "id result = objc_msgSend(this.id, sel_init)".indent(2).nl
+		str << "return result is this.id ? this : (result !is null ? new #{class_name}(result) : null)".indent(2).nl
+		str << "}".indent.nl(false).nl(false)
+		
+		methods.each do |method|		
+			next if method.selector == ("alloc" || "init") && method.static == "true"
+				
+			return_type = get_type(method["returnValue"][0].type, method["returnValue"][0]["type64"], method["returnValue"][0]["declaredType"])
+			variadic = method["variadic"] == "true"
+			static = method["static"] == "true"
+			this = "this.id" unless static
+			this = "class_#{class_name}" if static
+			args = ""
+			out_args = []
+			original_type = method.returnValue[0].type
+			
+			if method.selector[0 ... 4] == "init" && return_type == "id"
+				return_type = class_name
+				method.returnValue[0].declaredType = class_name
+			end
+			
+			method["arg"].each do |arg|
+				args << " &" if arg.type == "^@"
+				args << arg["name"]
+				
+				if arg.type == "@"
+					args << " !is null ? "
+					args << arg["name"]
+					args << ".id : null"
+				elsif arg.type == "^@"
+					type = get_type(arg.type, arg.type, arg["declaredType"])
+					out_args << { :type => type[4 .. -1], :name => arg["name"] }
+					args << "_.id"
+				end
+
+				args << ", "
+			end unless method["arg"].nil?
+			
+			declaration = build_function(method, true, static)[0 ... -2].indent.nl(false)
+			index = declaration.index_of(" ")
+			
+			str << (declaration[0 .. index] + get_method_name(method["selector"]) + declaration[index .. -1]).gsub(/ +/, " ")
+			str << "{".indent.nl(false)
+			
+			out_args.each do |arg|
+				str << arg.type.indent(2)
+				str << " "
+				str << arg.name
+				str << "_ = new "
+				str << arg.type
+				str << "(false, false)".nl
+			end
+			
+			str << "\n" if out_args.length > 0
+			
+			
+			if method["returnValue"][0].type == "@"
+				str << "id result = objc_msgSend(#{this}, sel_".indent(2)
+				str << transform_selector(method["selector"])
+				str << ", " unless method.arg.nil?		
+				str << args[0 ... -2] unless method.arg.nil?
+				str << ")".nl					
+				str << "\n" if out_args.length > 0	
+				
+				out_args.each do |arg|
+					str << arg.name.indent(2)
+					str << " = "
+					str << arg.name
+					str << "_".nl
+				end
+				
+				str << "\n" if out_args.length > 0
+				
+				if method["returnValue"][0]["declaredType"] == class_name
+					str << "return result is this.id ? this : (return result ".indent(2)
+				else
+					str << "return result ".indent(2)
+				end
+				
+				str << "!is null ? new "
+				str << return_type					
+				str << "(result) : null"
+				
+				if method["returnValue"][0]["declaredType"] == class_name
+					str << ")".nl
+				else
+					str << "".nl
+				end
+				
+				str << "}".indent.nl(false).nl(false)				
+			else
+				if original_type == "d" || original_type == "f"
+					
+					str << "version (X86)".indent(2).nl(false)
+					
+					if out_args.length > 0
+						str << return_type.indent(3)
+						str << " result "
+					else						
+						if original_type == "d"
+							str << "return ".indent(3)
+						else
+							str << "return cast(".indent(3)
+							str << return_type
+							str << ") "
+						end							
+					end
+					
+					str << "objc_msgSend_fpret(#{this}, sel_"
+					str << transform_selector(method["selector"])
+					str << ", " unless method.arg.nil?
+
+					args = ""
+
+					method["arg"].each do |arg| 
+						args << arg["name"]
+						args << ", "
+					end unless method["arg"].nil?
+
+					str << args[0 ... -2] if args.length > 0
+					str << ")".nl.nl(false)
+					
+					str << "else".indent(2).nl(false)
+					
+					if out_args.length > 0
+						str << return_type.indent(3)
+						str << " result "
+					else						
+						if original_type == "d"
+							str << "return ".indent(3)
+						else
+							str << "return cast(".indent(3)
+							str << return_type
+							str << ") "
+						end							
+					end
+					
+					str << "objc_msgSend(#{this}, sel_"
+					str << transform_selector(method["selector"])
+					str << ", " unless method.arg.nil?
+
+					args = ""
+
+					method["arg"].each do |arg| 
+						args << arg["name"]
+						args << ", "
+					end unless method["arg"].nil?
+
+					str << args[0 ... -2] if args.length > 0
+					str << ")".nl
+					str << "\n" if out_args.length > 0					
+
+					out_args.each do |arg|
+						str << arg.name.indent(3)
+						str << " = "
+						str << arg.name
+						str << "_".nl
+					end
+
+					if out_args.length > 0
+						str << "\n"
+						str << "retrun result".indent(3).nl
+					end
+
+					str << "\n" if out_args.length > 0			
+					str << "}".indent.nl(false).nl(false)					
+				else
+					unless return_type == "void"					
+						if out_args.length > 0
+							str << return_type.indent(2)
+							str << " result "
+						else
+							str << "return cast(".indent(2)
+						end
+
+						str << return_type
+						str << ") "
+					end
+
+					str << "objc_msgSend(#{this}, sel_".indent(2) if return_type == "void"
+					str << "objc_msgSend(#{this}, sel_" unless return_type == "void"
+					str << transform_selector(method["selector"])
+					str << ", " unless method.arg.nil?
+
+					args = ""
+
+					method["arg"].each do |arg| 
+						args << arg["name"]
+						args << ", "
+					end unless method["arg"].nil?
+
+					str << args[0 ... -2] if args.length > 0
+					str << ")".nl
+
+					str << "\n" if out_args.length > 0
+
+					out_args.each do |arg|
+						str << arg.name.indent(2)
+						str << " = "
+						str << arg.name
+						str << "_".nl
+					end
+
+					if out_args.length > 0
+						str << "\n"
+						str << "retrun result".indent(2).nl
+					end
+							
+					str << "}".indent.nl(false).nl(false)
+				end				
+			end
+		end
+		
+		str.string[0 .. -2]
+	end
+	
+	# Generates the D code for the structs
+	def structs (structs)
+		str = StringIO.new
+		
+		structs.each do |struct|
+			str << "struct "
+			str << struct.name.nl(false)
+			str << "{".nl
+			
+			struct.member.each do |member|
+				type = get_type(member.type, member.type64, member.declaredType)
+				
+				str << type.indent
+				str << " "
+				str << member.name.nl
+			end unless struct.member.nil?
+			
+			str << "}".nl
+			str << "\n\n"
+		end
+		
+		str.string[0 .. -2]
+	end
+	
+	# Generates the D code for the typedefs
+	def typedefs (typedefs)
+		str = StringIO.new
+
+		typedefs.each do |typedef|
+			
+			type = ""
+			struct = false
+			
+			if typedef.type.struct?
+				type = get_struct_type(typedef.type, typedef["type64"], typedef["declaredType"])
+				struct = true
+			else
+				type = get_type(typedef.type, typedef.type64, typedef.declaredType)
+			end
+			
+			# Special case
+			type = "wchar" if typedef.name == "unichar"				
+			
+			if struct
+				str << typedef["declaredType"].gsub("*", "").nl
+				str << "alias "				
+				str << type[:name]
+				str << " "
+				str << typedef["name"].nl
+			else
+				str << "alias "
+				str << type
+				str << " "
+				str << typedef.name.nl
+			end
+		end
+
+		str << "\n"
+		str.string
+	end
+	
+	# Adds specific D extensions to classes, like opIndex and opApply
+	def d_class_extensions (args)
+		
+	end
+	
+	# Checks if the declared type should be used instead of the type
+	# type:: the type to check
+	#
+	def check_declared_type (type)		
+		case type
+			when "unichar"; return "wchar"
+			when "BOOL"; return "bool"
+			when "CGFloat"; return type
+			when "NSInteger"; return type
+			when "NSUInteger"; return type
+				
+			when "unichar*"; return "wchar*"
+			when "BOOL*"; return "bool*"
+			when "CGFloat*"; return type
+			when "NSInteger*"; return type
+			when "NSUInteger*"; return type
+				
+			when "unichar**"; return "wchar**"
+			when "BOOL**"; return "bool**"
+			when "CGFloat**"; return type
+			when "NSInteger**"; return type
+			when "NSUInteger**"; return type
+			else return nil;
+		end
+	end
+	
+	# Gets the method name from the supplied selector
+	# selector:: the selector to get the method name from
+	#
+	# === Example:
+	# get_method_name("initWithContentsOfURL:options:error:")       #=> initWithContentsOfURL
+	#
+	def get_method_name (selector)
+		i = selector.index_of(":")
+		selector[0 ... i]
+	end
+	
+	# Gets the D type from the encoded C/Objective-C type
+	# type:: the type
+	# type64:: the type for 64bit targets
+	# declared_type:: the declared type
+	#
+	# === Example:
+	# get_type("I", "Q", "NSUInteger")      #=> NSUInteger
+	# get_type("I", "I", "unsigned int")    #=> uint
+	#
+	def get_type (type, type64, declared_type)
+				
+		t = check_declared_type(declared_type)
+		return t unless t.nil?
+		
+		unless type64 == "" || type64.nil?
+			return declared_type if type != type64
+		end
+		
+		case type
+		when "c"; return "byte"
+		when "i"; return "int"
+		when "s"; return "short"
+		when "l"; return "int"
+		when "q"; return "long"
+		when "C"; return "ubyte"
+		when "I"; return "uint"
+		when "S"; return "ushort"
+		when "L"; return "uint"
+		when "Q"; return "ulong"
+		when "f"; return "float"
+		when "d"; return "double"
+		when "B"; return "bool"
+		when "v"; return "void"
+		when "*"; return "char*"
+		when '#'; return "Class"
+		when ":"; return "SEL"					
+		when "@"
+			return declared_type.gsub(/\*+/, "") unless declared_type.nil?
+			raise "No declared type given"
+		else
+			case type[0, 1]
+			when "["
+				str = ""
+				t = type[1 ... -1]
+				count = $1 if t =~ /(\d+)/
+				str = get_type($', declared_type)
+				str << "[#{count}]"
+			
+				return str
+			when "("
+				resolved_types = []
+				types = $2 if type =~ /\((.+)=(.+)\)/
+				
+				types.each_char do |t|
+					resolved_types << get_type(t, declared_type)
+				end
+				
+				return resolved_types
+			when "^"				
+				if type == "^@"
+					return "out " << get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup
+				elsif type.length > 2 && type[0 ... 2] == "^^"
+					return "out " << get_type(type[2 .. -1], type64[2 .. -1], declared_type).dup
+				elsif type == "^?"
+					tmp = cfp_to_dfp(type)
+					return tmp unless tmp.nil?
+				end
+				
+				if !type.nil? && !type64.nil?
+					return get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup << "*"
+				elsif !type.nil?
+					return get_type(type[1 .. -1], type64, declared_type).dup << "*"
+				end			
+			when "{"
+				return declared_type
+			end
+		end
+		
+		return declared_type
+	end
+	
+	# Gets the D type from the encoded C/Objective-C type when it's a struct
+	# type:: the type
+	# type64:: the type for 64bit targets
+	# declared_type:: the declared type
+	#
+	# === Example
+	# get_struct_type("{some=III}", "{some=III}", "struct some")
+	# # outputs below
+    # { :name => "some", :types => ["uint", "uint", "uint"] }
+	#
+	def get_struct_type (type, type64, declared_type)
+		
+		return { :name => declared_type } if declared_type[0 ... 2] == "NS"
+		
+		case type[0, 1]
+		when "{"
+			resolved_types = []
+		
+			name = $1 if type =~ /\^{0,}\{(.{0,})=(.{0,})\}/
+			types = $2
+		
+			unless types.nil?
+				types.each_char do |t|
+					resolved_types << get_type(t, type64, declared_type)
+				end
+			end
+		
+			return { :name => name, :types => resolved_types }
+		when "^"
+			get_struct_type(type[1 .. -1], type64, declared_type)
+			hash = get_struct_type(type[1 .. -1], type64, declared_type)
+			hash[:name].dup << "*"
+			return hash
+		end
+	end
+	
+	# C Function Pointer to D Function Pointer
+	def cfp_to_dfp (fp)
+		reg = /(\w+)\s*\(\*(\w*)\)\s*\((.*)\)/
+		
+		return nil if fp !~ reg
+		
+		return_type = $1
+		name = $2
+		arg_types = $3
+		
+		return "#{return_type} function (#{arg_types})"
+	end
+	
+	# Is the supplied argument an "out" argument
+	# arg:: the arg
+	#
+	# === Example:
+	# out_arg?("out NSError")       #=> true
+	#
+	def out_arg? (arg)
+		return arg[0 .. 4] == "out "
+	end	
+	
+	# Transform the supplied selector to a valid D representation
+	# selector:: the selector to transform
+	#
+	# === Example:
+	# transform_selector("initWithContentsOfURL:options:error:")
+	# # outputs below
+	# initWithContentsOfURL_options_error_
+	#
+	def transform_selector (selector)
+		selector.gsub(/:/, "_")
+	end	
+end
+
+# Prints the message to stderr, exits
+def die (*msg)
+	$stderr.puts msg
+	exit 1
+end
+
+if __FILE__ == $0
+	objc_to_d = ObjcToD.new
+	
+	OptionParser.new do |opts|
+		opts.banner = "Usage: #{File.basename(__FILE__)} [options] <dstep files...>"
+		opts.separator ""
+		opts.separator "Options:"
+		
+		opts.on("-o", "--output DIRECTORY", "Place the output files in this directory'") do |opt|
+			die "The specified directory \"#{opt}\" does not exists" if File.exist?(opt) == false
+			die "Output directory cannot be specified more than once" if objc_to_d.out_dir
+			objc_to_d.out_dir = opt
+		end		
+		
+		help_msg = "Use the `-h' flag or for help."	
+			
+		opts.on("-h", "--help", "Show this message.") do
+			puts opts, help_msg
+			exit
+		end
+		
+		opts.on('-v', '--version', 'Show version.') do
+			puts ObjcToD::VERSION
+			exit
+		end
+		
+		opts.separator ""
+		
+		if ARGV.empty?			
+			die opts.banner
+		else
+			begin
+				opts.parse!(ARGV)
+				
+				die "No output directory given" if objc_to_d.out_dir.nil?
+				
+				ARGV.each do |file|
+					objc_to_d.files << file
+				end
+				
+				objc_to_d.generate_code
+				objc_to_d.output_code
+			rescue => e
+				msg = e.message
+				msg = "Internal error" if msg.empty?
+				
+				die msg, opts.banner, help_msg
+			end
+		end
+	end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sciprts/dstepgen.rb	Sat Jan 31 17:22:44 2009 +0100
@@ -0,0 +1,1388 @@
+#!/usr/bin/env ruby
+
+# Copyright (c) 2008-2009, Jacob Carlborg. All rights reserved.
+# Copyright (c) 2006-2007, Apple Inc. All rights reserved.
+# Copyright (c) 2005-2006 FUJIMOTO Hisakuni
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+# 3.  Neither the name of Apple Inc. ("Apple") nor the names of
+#     its contributors may be used to endorse or promote products derived
+#     from this software without specific prior written permission.
+# 
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+ 
+require "rubygems"
+gem "builder", "~> 2.0"
+require "builder"
+require "tmpdir"
+require "optparse"
+include Builder
+
+$KCODE = "UTF8"
+
+# Extensions that adds support for member access syntax
+class Hash	
+	def type
+		result = self["type"]
+		return result unless result.nil?
+		self[:type]
+	end
+	
+	def type= (type)
+		self[:type] = type
+	end
+	
+	def id
+		result = self["id"]
+		return result unless result.nil?
+		self[:id]
+	end
+	
+	def id= (id)
+		self[:id] = id
+	end
+	
+	def methods
+		result = self["methods"]
+		return result unless result.nil?
+		self[:methods]
+	end
+	
+	def methods= (methods)
+		self[:methods] = methods
+	end
+	
+	def method_missing (method, *args)
+		self.class.instance_eval do
+			define_method(method) do |*args|
+				if args.length > 0
+					self[method[0 ... -1]] = args[0]
+					self[eval(":#{method}"[0 ... -1])] = args[0]
+				else
+					result = self[method]
+					return result unless result.nil?
+					self[eval(":#{method}")]
+				end
+			end
+		end
+		
+		if (method = method.id2name) =~ /=/
+			eval("self.#{method} (args.length < 2 ? args[0] : args)")
+		else
+			eval("self.#{method}")
+		end
+	end
+end
+
+# This Struct represents an Objective-C Framework
+Framework = Struct.new(:name, :headers) do
+	def initialize
+		self.headers = []
+		self.name = ""
+	end
+end
+
+# This Struct represents a C/Objective-C header
+HeaderFile = Struct.new(:name, :framework, :cftypes, :constants, :defines, :enums, :externs, :functions,
+			  			:imports, :inline_functions, :opaques, :path, :structs, :typedefs) do
+	def initialize
+		self.name = ""
+		self.cftypes = []
+		self.constants = []
+		self.defines = []
+		self.enums = []
+		self.externs = []
+		self.framework = ""
+		self.functions = []
+		self.imports = []
+		self.inline_functions = []
+		self.opaques = []
+		self.path = ""
+		self.structs = []
+		self.typedefs = []
+	end	
+end
+
+# This class scans the headers
+class HeaderScaner
+	CPP = ['/usr/bin/cpp-4.0', '/usr/bin/cpp-3.3', '/usr/bin/cpp3'].find { |x| File.exist?(x) }
+	raise "cpp not found" if CPP.nil?
+	CPPFLAGS = "-D__APPLE_CPP__ -include /usr/include/AvailabilityMacros.h"
+	CPPFLAGS << "-D__GNUC__" unless /\Acpp-4/.match(File.basename(CPP))
+	
+	attr_accessor :frameworks, :headers, :do_64bit
+	
+	def initialize
+		@extern_name = 'extern'
+		@frameworks = []
+		@file_content = nil
+		@headers = []
+		@classes = {}
+		@informal_protocols = {}
+		@function_pointer_types = {}
+		@do_64bit = false
+	end
+	
+	def classes
+		@classes
+	end
+	
+	def protocols
+		@informal_protocols
+	end
+	
+	def cftypes (header)
+		re = /typedef\s+(const\s+)?(struct\s*\w+\s*\*\s*)([^\s]+Ref)\s*;/
+		@cpp_result.scan(re).each do |m|
+			header.cftypes << { :name => m[2], :type => m[1], :const => m[0] =~ /const/ ? true : false}
+		end
+	end
+	
+	def constants (header)
+		tmp = header.externs.map do |i|
+			constant?(i, true)
+		end
+		
+		header.constants = tmp.flatten.compact
+	end
+	
+	def defines (header)
+		re = /#define\s+([^\s]+)\s+(\([^)]+\)|[^\s]+)\s*$/
+		@file_content.scan(re).each do |m|
+			next unless !m[0].include?('(') && m[1] != '\\'
+			header.defines <<  { :name => m[0], :value => m[1] }
+		end
+	end
+	
+	def enums (header)
+		re = /\benum\b\s*(\w+\s+)?\{([^}]*)\}/
+		@cpp_result.scan(re).each do |m|
+			enum = { :name => m[0], :members => [] }
+			
+			m[1].split(",").map do |i|
+				name, value = i.split("=", 2).map do |x|
+					x.strip
+				end
+				
+				enum[:members] << { :name => name, :value => value } unless name.empty? || name[0] == ?#
+			end
+			
+			header.enums << enum
+		end
+	end
+	
+	def function_pointer_types (header)
+		re = /typedef\s+([\w\s]+)\s*\(\s*\*\s*(\w+)\s*\)\s*\(([^)]*)\)\s*;/
+		data = @cpp_result.scan(re)
+		re = /typedef\s+([\w\s]+)\s*\(([^)]+)\)\s*;/
+		data |= @cpp_result.scan(re).map do |m|
+			ary = m[0].split(/(\w+)$/)
+			ary[1] << " *"
+			ary << m[1]
+			ary
+		end
+		
+		data.each do |m|
+			name = m[1]
+			args = m[2].split(",").map do |x|
+				if x.include?(" ")
+					ptr = x.sub!(/\[\]\s*$/, "")
+					x = x.sub(/\w+\s*$/, "").strip
+					ptr ? x + "*" : x
+				else
+					x.strip
+				end
+			end
+			
+			type = "#{m[0]}(*)(#{args.join(', ')})"
+			@function_pointer_types[name] = type
+		end
+	end
+	
+	def typedefs (header)
+		re = /^\s*typedef\s+(.+)\s+([\w\*]+)\s*;$/
+		data = @cpp_result
+		data.scan(re).each do |m|
+			var = get_var_info(m[0] + " " + m[1])
+			header.typedefs << get_var_info(m[0] + " " + m[1]) if var
+		end
+	end
+	
+	def externs (header)
+		re = /^\s*#{@extern_name}\s+\b(.*)\s*;.*$/
+		header.externs = @cpp_result.scan(re).map do |m|
+			m[0].strip
+		end
+	end
+	
+	def imports (header)
+		tmp = []
+			
+		@file_content.each do |line|
+			if line =~ /#(include|import) <(.+)\.h>/
+				next if $2 == header.name
+				tmp << $2
+			elsif line =~ /@class(\s+)(\w+)(,|;)/
+				next if $2 == header.name
+				
+				if $3 == ";"
+					tmp << header.framework + "/" + $2 unless header.framework == ""
+					tmp << $2 if header.framework == ""
+				elsif
+					str = line[6 + $1.length ... -2]
+					str.gsub!(" ", "")
+					arr = str.split(",")
+					
+					arr.each do |s|
+						tmp << header.framework + "/" + s unless header.framework == ""
+						tmp << s if header.framework == ""
+					end
+				end
+			end
+		end
+		
+		header.imports = tmp.compact.uniq
+	end
+	
+	def informal_protocols (header)
+		self.methods(header)
+	end	
+	
+	def methods (header)
+		interface_re = /^@(interface|protocol)\s+(\w+)\s*:?\s*(\w*)\s*(\([^)]+\))?/
+		
+		end_re = /^@end/
+		body_re = /^[-+]\s*(\([^)]+\))?\s*([^:\s;]+)/
+		args_re = /\w+\s*:/
+		prop_re = /^@property\s*(\([^)]+\))?\s*([^;]+);$/
+		current_interface = current_category = nil
+		i = 0
+		parent = nil	
+		
+		@cpp_result.each_line do |line|
+			size = line.size
+			line.strip!
+			
+			if md = interface_re.match(line)
+				parent = nil
+				current_interface = md[1] == "protocol" ? "NSObject" : md[2]
+				current_category = md[4].delete("()").strip if md[4]
+				parent = md[3] unless md[3] == ""
+				
+			elsif end_re.match(line)
+				current_interface = current_category = nil
+				
+			elsif current_interface && md = prop_re.match(line)
+				# Parsing Objective-C 2.0 properties
+				if (a = md[2].split(/\s/)).length >= 2 && /^\w+$/.match(name = a[-1]) && (type = a[0 .. -2].join(" ")).index(",").nil?
+					getter, setter = name, "set#{name[0].chr.upcase + name[1 .. -1]}"
+					readonly = false
+					
+					if attributes = md[1]
+						if md = /getter\s*=\s*(\w+)/.match(attributes)
+							getter = md[1]
+						end
+						
+						if md = /setter\s*=\s*(\w+)/.match(attributes)
+							setter = md[1]
+						end
+						
+						readonly = true if attributes.index("readonly")
+					end
+					
+					typeinfo = VarInfo.new(type, "", "")
+					
+					@classes[current_interface] ||= {}					
+					methods = (@classes[current_interface].methods ||= [])
+					methods << MethodInfo.new(typeinfo, getter, false, [], line)
+					
+					unless readonly
+						methods << MethodInfo.new(VarInfo.new("void", "", ""), setter + ":", false, [typeinfo], line)
+					end
+				end
+				
+			elsif current_interface && (line[0] == ?+ || line[0] == ?-)
+				mtype = line[0]
+				data = @cpp_result[i .. -1]
+				body_md = body_re.match(data)
+				
+				next if body_md.nil?
+				
+				rettype = body_md[1] ? body_md[1].delete("()") : "id"
+				retval = VarInfo.new(rettype, "", "")
+				args = []
+				selector = ""
+				data = data[0 .. data.index(";")]
+				args_data = []
+				
+				data.scan(args_re) do |x|
+					args_data << [$`, x, $']
+				end
+				
+				variadic = false
+				args_data.each_with_index do |ary, n|
+					before, argname, argtype = ary
+					arg_nameless = (n > 0 && /\)\s*$/.match(before))
+					argname = ":" if arg_nameless
+					realargname = nil
+					
+					if n < args_data.length - 1
+						argtype.sub!(args_data[n + 1][2], "")
+						
+						if arg_nameless
+							argtype.sub!(/(\w+\s*)?\w+\s*:\s*$/, "")
+						else
+							unless argtype.sub!(/(\w+)\s+\w+:\s*$/)	{ |s| realargname = $1; "" }
+								# maybe the next argument is nameless
+								argtype.sub!(/\w+\s*:\s*$/, "")
+							end
+						end
+					else
+						argtype.sub!(/\s+__attribute__\(\(.+\)\)/, "")
+						
+						if arg_nameless
+							argtype.sub!(/\w+\s*;$/, "")
+						else
+							unless argtype.sub!(/(\w+)\s*;$/) { |s| realargname = $1; "" }
+								variadic = argtype.sub!(/,\s*\.\.\.\s*;/, "") != nil
+								argtype.sub!(/\w+\s*$/, "") if variadic
+							end
+						end
+					end
+					
+					selector << argname
+					realargname ||= argname.sub(/:/, "")
+					args << VarInfo.new(argtype, realargname, "") unless argtype.empty?
+				end
+				
+				selector = body_md[2] if selector.empty?
+				args << VarInfo.new("...", "vararg", "") if variadic
+				method = MethodInfo.new(retval, selector, line[0] == ?+, args, data)
+				
+				if current_category && current_interface == "NSObject"
+					(@informal_protocols[current_category] ||= []) << method
+				end
+				
+				@classes[current_interface] ||= {}
+				
+				if header.name == current_interface				
+					@classes[current_interface].file = header.name
+				else
+					@classes[current_interface].file ||= header.name
+				end
+				
+				unless parent == current_interface || parent =~ /\s+/ || parent.nil?
+					@classes[current_interface].parent = parent
+				end				
+				
+				(@classes[current_interface].methods ||= []) << method
+			end
+			i += size
+		end
+	end	
+	
+	def structs (header)
+		re = /typedef\s+struct\s*\w*\s*((\w+)|\{([^{}]*(\{[^}]+\})?)*\}\s*([^\s]+))\s*(__attribute__\(.+\))?\s*;/
+		i = 0
+		body = nil
+		@cpp_result.scan(re).each do |m|
+			struct = { :name => m[4], :members => [] }
+			
+			unless struct[:name].nil?
+				if struct[:name][0, 1] == "*"
+					struct[:name].sub!("*", "")
+				end
+			end
+			
+			return_type = nil
+			stripped_return_type = nil
+			body = m[2]
+			
+			if m[2]
+				m[2].split(/,|;/).map do |i|				
+					str, bytes = i.split(":", 2).map do |x|
+						x.strip
+					end
+	
+					var = get_var_info(str, true)
+	
+					if var				
+						if var.return_type == "***dummy***"
+							var.return_type = return_type
+							var.stripped_return_type = stripped_return_type
+						else
+							return_type = var.return_type
+							stripped_return_type = var.stripped_return_type
+						end
+	
+						struct[:members] << { :name => var.name, :bytes => bytes, :declaredType => var.return_type, :type => "", :type64 => "" } unless str.empty? || str[0] == ?#
+						
+						names = []
+						
+						tmp = struct[:members].collect do |member|
+							unless names.include?(member[:name])
+								names << member[:name]
+								member
+							end
+						end
+						
+						struct[:members] = tmp.compact
+					end
+				end
+			end
+			
+			header.structs << struct if body
+		end
+	end
+	
+	def functions (header, inline = false)
+		if inline
+			inline_func_re = /(inline|__inline__)\s+((__attribute__\(\([^)]*\)\)\s+)?([\w\s\*<>]+)\s*\(([^)]*)\)\s*)\{/
+			res = @cpp_result.scan(inline_func_re)
+			res.each do |x|
+				x.delete_at(0)
+				x.delete_at(1)
+			end
+		else
+			skip_inline_re = /(static)?\s(inline|__inline__)[^{;]+(;|\{([^{}]*(\{[^}]+\})?)*\})\s*/
+			func_re = /(^([\w\s\*<>]+)\s*\(([^)]*)\)\s*)(__attribute__[^;]+)?;/
+			res = @cpp_result.gsub(skip_inline_re, '').scan(func_re)
+		end
+		
+		funcs = res.map do |m|
+			orig, base, args = m			
+			base.sub!(/^.*extern\s/, "")
+			func = constant?(base)
+			
+			if func
+				args = args.strip.split(",").map do |i|
+					constant?(i)
+				end
+				
+				next if args.any? do |x|
+					x.nil?
+				end
+				
+				args = [] if args.size == 1 && args[0].return_type == "void"	
+				FunctionInfo.new(func, args, orig, inline)
+			end
+		end.compact
+		
+		if inline
+			header.inline_functions = funcs
+		else
+			header.functions = funcs
+		end
+	end
+	
+	def prepare (path)		
+		@file_content = File.read(path)
+		@file_content.gsub!(%r{(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)}, "");
+		@complete_cpp_result, @cpp_result = do_cpp(path, false, true, "")
+	end	
+	
+	def scan (frameworks, headers)
+		@frameworks = frameworks
+		@headers = headers
+		
+		@frameworks.each do |framework|			
+			framework.headers.each do |header|
+				prepare(header.path)
+				
+				imports(header)
+				cftypes(header)
+				externs(header)
+				constants(header)
+				enums(header)
+				structs(header)
+				typedefs(header)
+				functions(header)
+				functions(header, true)
+				defines(header)
+				methods(header)
+				function_pointer_types(header)
+			end
+		end
+		
+		@headers.each do |header|
+			prepare(header.path)
+			
+			imports(header)
+			cftypes(header)
+			externs(header)
+			constants(header)
+			enums(header)
+			structs(header)
+			typedefs(header)
+			functions(header)
+			functions(header, true)
+			defines(header)
+			methods(header)
+			function_pointer_types(header)
+		end
+	end
+	
+	def get_var_info (str, multi = false)
+		str.strip!
+		
+		return nil if str.empty?
+		
+		if str == "..."
+			VarInfo.new("...", "...", str)
+		else
+			str = "***dummy*** " + str if str[-1].chr == '*' || str.index(/\s/).nil?	
+			tokens = multi ? str.split(',') : [str]
+			part = tokens.first
+			re = /^([^()]*)\b(\w+)\b\s*(\[[^\]]*\])*$/
+			m = re.match(part)	
+	
+			if m
+				return nil if m[1].split(/\s+/).any? do |x|
+					['end', 'typedef'].include?(x)
+				end
+				
+				m = m.to_a[1..-1].compact.map do |i|
+					i.strip
+				end
+				
+				m[0] += m[2] if m.size == 3
+				m[0] = 'void' if m[1] == 'void'
+				
+				var = begin
+					VarInfo.new(m[0], m[1], part)
+				rescue
+					return nil
+				end
+				
+				if tokens.size > 1
+					[var, *tokens[1..-1].map { |x| constant?(m[0] + x.strip.sub(/^\*+/, '')) }]
+				else
+					var
+				end				
+			end
+		end
+	end
+	
+	def constant? (str, multi = false)
+		str.strip!
+		return nil if str.empty?
+		
+		if str == '...'
+			VarInfo.new('...', '...', str)
+		else
+			str << " dummy" if str[-1].chr == '*' or str.index(/\s/).nil?
+			tokens = multi ? str.split(',') : [str]
+			part = tokens.first
+			re = /^([^()]*)\b(\w+)\b\s*(\[[^\]]*\])*$/
+			m = re.match(part)
+			
+			if m
+				return nil if m[1].split(/\s+/).any? do |x|
+					['end', 'typedef'].include?(x)
+				end
+				
+				m = m.to_a[1..-1].compact.map do |i|
+					i.strip
+				end
+				
+				m[0] += m[2] if m.size == 3
+				m[0] = 'void' if m[1] == 'void'
+				
+				var = begin
+					VarInfo.new(m[0], m[1], part)
+				rescue
+					return nil
+				end
+				
+				if tokens.size > 1
+					[var, *tokens[1..-1].map { |x| constant?(m[0] + x.strip.sub(/^\*+/, '')) }]
+				else
+					var
+				end
+			end
+		end
+	end	
+	
+	class VarInfo
+		
+		attr_reader :name, :orig, :const, :type_modifier
+		attr_accessor :octype, :resolved_type, :resolved_type64, :return_type, :stripped_return_type
+		
+		def initialize (type, name, orig)
+			@return_type = type.clone
+			@name = name
+			@orig = orig
+			@const = false
+			@type_modifier = ""
+			
+			@return_type.gsub!(/\[[^\]]*\]/, "*")
+			
+			if @return_type =~ /\bconst\b/
+				@const = true
+				@return_type.gsub!("const", "")
+			end
+			
+			if @return_type =~ /\b(in|out|inout|oneway|bycopy|byref)\b/
+				
+				case $1
+				when "in"
+					@type_modifier << "n"
+				when "out"
+					@type_modifier << "o"
+				when "inout"
+					@type_modifier << "N"
+				when "oneway"
+					@type_modifier << "w"
+				when "bycopy"
+					@type_modifier << "c"
+				when "byref"
+					@type_modifier << "r"
+				end
+				
+				@return_type.gsub!("#{$1}", "")
+			end
+			
+			@return_type.gsub!(/\s?\*\s?/, "*")
+			@return_type.gsub!(/^\(|\)$/, "")
+			@return_type.strip!
+			
+			t = type.gsub(/\b(__)?const\b/, "")
+			t.gsub!(/<[^>]*>/, '')
+			t.gsub!(/\b(in|out|inout|oneway|const)\b/, "")
+			t.gsub!(/\b__private_extern__\b/, "")
+			t.gsub!(/^\s*\(?\s*/, "")
+			t.gsub!(/\s*\)?\s*$/, "")
+			
+			raise "Empty type (was '#{type}')" if t.empty?
+			
+			@stripped_return_type = t
+		end
+		
+		def function_pointer? (function_pointer_types)
+			type = @function_pointer_types[@stripped_return_type] || @stripped_return_type
+			@function_pointer_type ||= FunctionPointerInfo.new_from_type(type)
+		end
+		
+		def <=>(x)
+			self.name <=> x.name
+		end					
+		
+		def hash
+			@name.hash
+		end
+		
+		def eql? (o)
+			@name == o.name
+		end
+	end
+ 
+	class FunctionInfo < VarInfo
+		
+		attr_reader :args, :argc
+		
+		def initialize (function, args, orig, inline = false)
+			super(function.return_type, function.name, orig)
+			
+			@args = args
+			@argc = @args.size
+			@variadic = false
+			
+			if @args[-1] && @args[-1].return_type == "..."
+				@argc -= 1
+				@variadic = true
+				@args.pop
+			end
+			
+			@inline = inline
+			self
+		end
+		
+		def variadic?
+			@variadic
+		end
+		
+		def inline?
+			@inline
+		end
+	end
+	
+	class FunctionPointerInfo < FunctionInfo
+		def initialize (return_type, arg_types, orig)
+			args = arg_types.map do |x|
+				VarInfo.new(x, "", "")
+			end
+			
+			super(VarInfo.new(return_type, "", ""), args, orig)
+		end
+		
+		def self.new_from_type (type)
+			@cache ||= {}
+			info = @cache[type]
+			
+			return info if info
+			
+			tokens = type.split(/\(\*\)/)
+			return nil if tokens.size != 2
+			
+			return_type = tokens.first.strip
+			rest = tokens.last.sub(/^\s*\(\s*/, "").sub(/\s*\)\s*$/, "")
+			
+			arg_types = rest.split(/,/).map do |x|
+				x.strip
+			end
+			
+			@cache[type] = self.new(return_type, arg_types, type)
+		end
+	end
+	
+	class MethodInfo < FunctionInfo
+		
+		attr_reader :selector
+		
+		def initialize (method, selector, is_class, args, orig)
+			super(method, args, orig)
+			
+			@selector = selector
+			@is_class = is_class
+			self
+		end
+		
+		def class_method?
+			@is_class
+		end
+		
+		def <=>(o)
+			@selector <=> o.selector
+		end
+		
+		def hash
+			@selector.hash
+		end
+		
+		def eql? (o)
+			@selector == o.selector
+		end
+	end
+	
+	def do_cpp (path, fails_on_error = true, do_64 = true, flags = "")
+	    f_on = false
+	    err_file = '/tmp/.cpp.err'
+	    cpp_line = "#{CPP} #{CPPFLAGS} #{flags} #{do_64 ? '-D__LP64__' : ''} \"#{path}\" 2>#{err_file}"
+	    complete_result = `#{cpp_line}`
+		
+		if $?.to_i != 0 && fails_on_error
+			$stderr.puts File.read(err_file)
+			File.unlink(err_file)
+			raise "#{CPP} returned #{$?.to_int/256} exit status\nline was: #{cpp_line}"
+		end
+		
+		result = complete_result.select do |s|
+			# First pass to only grab non-empty lines and the pre-processed lines
+			# only from the target header (and not the entire pre-processing result).
+			next if s.strip.empty?
+			m = %r{^#\s*\d+\s+"([^"]+)"}.match(s)
+			f_on = (File.basename(m[1]) == File.basename(path)) if m
+			f_on
+		end.select do |s|
+			# Second pass to ignore all pro-processor comments that were left.
+			/^#/.match(s) == nil
+		end.join
+		
+		File.unlink(err_file)
+		return [complete_result, result]
+	end
+end
+
+class DStepGenerator
+	
+	VERSION = 1.0
+	
+	attr_accessor :out_file, :scaner
+	
+	def initialize
+		@do_64bit = false
+		@frameworks = []
+		@framework_paths = []
+		@headers = []
+		@import_directives = ""
+		@informal_protocols = []
+		@classes = []
+		@scaner = HeaderScaner.new
+		@scaner.do_64bit = @do_64bit
+	end
+	
+	def do_64bit
+		return @do_64bit
+	end
+	
+	def do_64bit= (do_64bit)
+		@do_64bit = do_64bit
+		@scaner.do_64bit = do_64bit
+	end	
+	
+	def collect		
+		scaner.scan(@frameworks, @headers)
+		@classes = scaner.classes
+		@informal_protocols = scaner.protocols
+	end
+	
+	def handle_framework (framework)
+		val = framework.name
+		path = framework_path(val)
+		
+		raise "Can't locate framework '#{val}'" if path.nil?
+		@framework_paths << File.dirname(path)
+		raise "Can't find framework '#{val}'" if path.nil?
+		
+		parent_path, name = path.scan(/^(.+)\/(\w+)\.framework\/?$/)[0]
+		
+		if @private
+			headers_path = File.join(path, "PrivateHeaders")
+			raise "Can't locate private framework headers at '#{headers_path}'" unless File.exist?(headers_path)
+			
+			headers = Dir.glob(File.join(headers_path, "**", "*.h"))
+			public_headers_path = File.join(path, "Headers")
+			public_headers = if File.exist?(public_headers_path)
+				HeaderScaner::CPPFLAGS << " -I#{public_headers_path} "
+				Dir.glob(File.join8(headers_path, "**", "*.h"))
+			else
+				[]
+			end
+		else
+			headers_path = File.join(path, "Headers")
+			raise "Can't locate public framework headers at '#{headers_path}'" unless File.exist?(headers_path)
+			public_headers = headers = Dir.glob(File.join(headers_path, "**", "*.h"))
+		end
+		
+		# We can't just "#import <x/x.h>" as the main Framework header might not include _all_ headers.
+			    # So we are tricking this by importing the main header first, then all headers.
+			    header_basenames = (headers | public_headers).map do |x|
+			    	x.sub(/#{headers_path}\/*/, "")
+			    end
+			
+		if idx = header_basenames.index("#{name}.h")
+			header_basenames.delete_at(idx)
+			header_basenames.unshift("#{name}.h")
+		end
+		
+		@import_directives = header_basenames.map do |x|
+			"#import <#{name}/#{x}>"
+		end.join("\n")
+		
+		@import_directives << "\n"
+		
+		@compiler_flags ||= "-F\"#{parent_path}\" -framework #{name}"
+		@cpp_flags ||= ""
+		@cpp_flags << "-F\"#{parent_path}\" "
+		
+		headers.each do |header|
+			header_file = HeaderFile.new
+			header_file.path = header
+			header_file.name = File.basename(header, File.extname(header))
+			header_file.framework = framework.name
+			
+			framework.headers << header_file
+		end
+		
+		# Memorize the dependencies.
+		@dependencies = DStepGenerator.dependencies_of_framework(path)
+	end
+	
+	def framework_path (val)
+		return path if File.exist?(val)
+		
+		val += ".framework" unless /\.framework$/.match(val)
+		paths = ["/System/Library/Frameworks", "/Library/Frameworks", "#{ENV['HOME']}/Library/Frameworks"]
+		paths << "/System/Library/PrivateFrameworks" if @private
+		
+		paths.each do |dir|
+			path = File.join(dir, val)
+			return path if File.exist?(path)
+		end
+		
+		return nil
+	end
+	
+	def self.dependencies_of_framework (path)
+		@dependencies ||= {}
+		name = File.basename(path, ".framework")
+		path = File.join(path, name)
+		deps = @dependencies[path]
+		
+		if deps.nil?
+			deps = `otool -L "#{path}"`.scan(/\t([^\s]+)/).map do |m|
+				dpath = m[0]
+				next if File.basename(dpath) == name
+				next if dpath.include?("PrivateFrameworks")
+				next unless dpath.sub!(/\.framework\/Versions\/\w+\/\w+$/, "")
+				dpath + ".framework"
+			end.compact
+			
+			@dependencies[path] = deps
+		end
+		
+		return deps
+	end
+	
+	def compile_and_execute (code, enable_64bit = false)
+		compiler_line = "gcc "				
+		src = File.new(unique_tmp_path("src", ".m"), "w")
+		src << code
+		src.close
+		
+		arch_flag = if enable_64bit
+					"-arch x86_64 -arch ppc64"
+				else
+					"-arch i386 -arch ppc"
+				end
+		
+		compiler_line << arch_flag
+		
+		bin = unique_tmp_path "bin"
+		log = unique_tmp_path "log"
+		
+		line = "#{compiler_line} -o #{bin} #{src.path} #{@compiler_flags}> #{log}"
+		
+		unless system(line)
+			msg = "Cannot compile Objective-C code ...aborting\nCommand was: #{line}\n\nLog:\n#{File.read(log)}\n\n"
+			$stderr << msg
+			File.delete src.path
+			raise msg
+		end
+		
+		result = `#{bin}`
+		
+		unless $?.success?
+			raise "Cannot execute compiled Objective-C code ... aborting\nCommand was: #{line}\nBinary is: #{bin}"
+		end
+		
+		File.delete bin
+		File.delete log
+		File.delete src.path
+		
+		return result
+	end
+	
+	def unique_tmp_path (base, extension = "", dir = Dir.tmpdir)
+		i = 0
+		loop do
+			path = File.join(dir, "#{base}-#{Process.pid}-#{i}#{extension}")
+			return path unless File.exists?(path)
+			i += 1
+		end
+	end
+	
+	def add_header (path)
+		header = HeaderFile.new
+		header.path = path
+		header.name = File.basename(path, File.extname(path))
+		@import_directives << "#include <#{path}>\n"
+		@headers << header
+	end
+	
+	def add_framework (name)
+		framework = Framework.new
+		framework.name = name
+		handle_framework(framework)
+		#@import_directives << "#import <#{framework.name}/#{framework.name}.h>\n"
+		@frameworks << framework
+	end
+	
+	def collect_header_types (header, enable_64bit)
+		types = []
+		
+		header.cftypes.each do |cftype|
+			types << cftype[:type]
+		end
+		
+		header.constants.each do |constant|
+			types << constant.stripped_return_type
+		end
+		
+		header.structs.each do |struct|
+			types << struct[:name]
+			
+			struct[:members].each do |member|
+				types << member[:declaredType]
+			end
+		end
+		
+		header.typedefs.each do |typedef|
+			types << typedef.stripped_return_type
+		end
+		
+		header.functions.each do |function|
+			types << function.stripped_return_type
+			
+			function.args.each do |arg|
+				types << arg.stripped_return_type
+			end
+		end
+		
+		types
+	end
+	
+	def collect_classes_types (enable_64bit)
+		types = []
+		
+		@classes.each do |clazz, value|
+			value.methods.each do |method|
+				types << method.stripped_return_type
+				
+				method.args.each do |arg|
+					types << arg.stripped_return_type
+				end
+			end
+		end
+		
+		types
+	end
+	
+	def collect_informal_protocols_types (enable_64bit)
+		types = []
+		
+		@informal_protocols.each do |name, methods|
+			methods.each do |method|
+				types << method.stripped_return_type
+				
+				method.args.each do |arg|
+					types << arg.stripped_return_type
+				end
+			end
+		end
+		
+		types
+	end
+	
+	def resolve_header_types (header, enable_64bit, resolved_types, x)
+		i = x
+		
+		header.cftypes.each do |cftype|
+			cftype[enable_64bit ? :type64 : :type] = resolved_types[i]
+			i += 1
+		end
+		
+		header.constants.each do |constant|
+			constant.resolved_type = resolved_types[i] unless enable_64bit
+			constant.resolved_type64 = resolved_types[i] if enable_64bit
+			i += 1
+		end
+		
+		header.structs.each do |struct|
+			struct[enable_64bit ? :type64 : :type] = resolved_types[i]
+			i += 1
+			
+			struct[:members].each do |member|
+				member[enable_64bit ? :type64 : :type] = resolved_types[i]
+				i += 1
+			end
+		end
+		
+		header.typedefs.each do |typedef|
+			typedef.resolved_type = resolved_types[i] unless enable_64bit
+			typedef.resolved_type64 = resolved_types[i] if enable_64bit
+			i += 1
+		end
+		
+		header.functions.each do |function|
+			function.resolved_type = resolved_types[i] unless enable_64bit
+			function.resolved_type64 = resolved_types[i] if enable_64bit
+			i += 1
+			
+			function.args.each do |arg|
+				arg.resolved_type = resolved_types[i] unless enable_64bit
+				arg.resolved_type64 = resolved_types[i] if enable_64bit
+				i += 1
+			end
+		end
+		
+		i
+	end
+	
+	def resolve_methods_types (enable_64bit, resolved_types, x)
+		i = x
+		
+		@classes.each do |clazz, value|
+			value.methods.each do |method|
+				method.resolved_type = resolved_types[i] unless enable_64bit
+				method.resolved_type64 = resolved_types[i] if enable_64bit
+				i += 1
+				
+				method.args.each do |arg|
+					arg.resolved_type = resolved_types[i] unless enable_64bit
+					arg.resolved_type64 = resolved_types[i] if enable_64bit
+					i += 1
+				end
+			end
+		end
+		
+		i
+	end
+	
+	def resolve_informal_protocols_types (enable_64bit, resolved_types, x)
+		i = x
+		
+		@informal_protocols.each do |name, methods|
+			methods.each do |method|
+				method.resolved_type = resolved_types[i] unless enable_64bit
+				method.resolved_type64 = resolved_types[i] if enable_64bit
+				i += 1
+				
+				method.args.each do |arg|
+					arg.resolved_type = resolved_types[i] unless enable_64bit
+					arg.resolved_type64 = resolved_types[i] if enable_64bit
+					i += 1
+				end
+			end
+		end
+		
+		i
+	end
+	
+	def resolve_types (enable_64bit = false)
+		code = "#include <stdio.h>\n"
+		code << @import_directives
+		types = []
+		
+		@frameworks.each do |framework|
+			framework.headers.each do |header|
+				types << collect_header_types(header, enable_64bit)
+			end
+		end
+
+		@headers.each do |header|
+			types << collect_header_types(header, enable_64bit)
+		end
+		
+		types << collect_classes_types(enable_64bit)
+		types << collect_informal_protocols_types(enable_64bit)
+		
+		code << "int main () \n{\n"
+		types.flatten!
+		
+		types.each do |type|
+			code << '	printf("%s\n", ' + "@encode(#{type}));\n"
+		end
+		
+		code << "	return 0;\n}"
+		
+		resolved_types = []
+		
+		compile_and_execute(code, enable_64bit).split("\n").each do |line|
+			resolved_types << line
+		end
+		
+		i = 0
+		
+		@frameworks.each do |framework|
+			framework.headers.each do |header|
+				i = resolve_header_types(header, enable_64bit, resolved_types, i)
+			end
+		end
+
+		@headers.each do |header|
+			i = resolve_header_types(header, enable_64bit, resolved_types, i)
+		end
+		
+		i = resolve_methods_types(enable_64bit, resolved_types, i)
+		i = resolve_informal_protocols_types(enable_64bit, resolved_types, i)
+	end
+	
+	def generate_header (xml, header)
+		xml.file :name => header.name do
+			header.imports.each	do |import|
+				xml.import import
+			end
+			
+			header.defines.each do |define|
+				xml.define define
+			end
+			
+			header.cftypes.each do |cftype|
+				xml.cftype cftype
+			end
+			
+			header.constants.each do |constant|
+				xml.constant :name => constant.name, :declaredType => constant.return_type, :type => constant.resolved_type, :type64 => constant.resolved_type64, :const => constant.const
+			end
+			
+			header.enums.each do |enum|
+				xml.enum :name => enum[:name] do
+					enum[:members].each do |member|
+						xml.member member
+					end
+				end
+			end
+			
+			header.structs.each do |struct|
+				xml.struct :name => struct[:name], :type => struct[:type], :type64 => struct[:type64] do
+					struct[:members].each do |member|
+						xml.member member
+					end
+				end	
+			end
+			
+			header.typedefs.each do |typedef|
+				xml.typedef :name => typedef.name, :declaredType => typedef.return_type, :type => typedef.resolved_type, :type64 => typedef.resolved_type64, :const => typedef.const
+			end
+			
+			header.functions.each do |function|
+				xml.function :name => function.name, :inline => function.inline?, :variadic => function.variadic? do
+					function.args.each do |arg|
+						xml.arg :name => arg.name, :declaredType => arg.return_type, :type => arg.resolved_type, :type64 => arg.resolved_type64, :const => arg.const, :typeModifier => arg.type_modifier
+					end
+					
+					xml.returnValue :declaredType => function.return_type, :type => function.resolved_type, :type64 => function.resolved_type64, :const => function.const, :typeModifier => function.type_modifier
+				end
+			end
+		end
+	end
+	
+	def generate_classes (xml)
+		@classes.each do |clazz, value|
+			xml.class :name => clazz, :parent => value.parent, :file => value.file do
+				value.methods.each do |method|
+					xml.method :selector => method.selector, :classMethod => method.class_method?, :variadic => method.variadic? do
+						method.args.each do |arg|
+							xml.arg :name => arg.name, :declaredType => arg.return_type, :type => arg.resolved_type, :type64 => arg.resolved_type64, :const => arg.const, :typeModifier => arg.type_modifier
+						end
+						
+						xml.returnValue :declaredType => method.return_type, :type => method.resolved_type, :type64 => method.resolved_type64, :const => method.const, :typeModifier => method.type_modifier
+					end
+				end
+			end
+		end
+	end
+	
+	def generate_informal_protocols (xml)
+		@informal_protocols.each do |name, methods|
+			xml.informalProtocol :name => name do
+				methods.each do |method|
+					xml.method :selector => method.selector, :classMethod => method.class_method?, :variadic => method.variadic? do
+						method.args.each do |arg|
+							xml.arg :name => arg.name, :declaredType => arg.return_type, :type => arg.resolved_type, :type64 => arg.resolved_type64, :const => arg.const, :typeModifier => arg.type_modifier
+						end
+						
+						xml.returnValue :declaredType => method.return_type, :type => method.resolved_type, :type64 => method.resolved_type64, :const => method.const, :typeModifier => method.type_modifier
+					end
+				end
+			end
+		end
+	end
+	
+	def generate
+		resolve_types
+		resolve_types(true) if @do_64bit
+		
+		file = STDOUT if @out_file == nil
+		file = File.open @out_file, "w" unless @out_file == nil
+		
+		xml = XmlMarkup.new(:target => file, :indent => 4)
+		xml.instruct!
+
+		xml.dstep :xmlns => "http://www.dsource.org/projects/dstep", "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation" => "http://www.dsource.org/projects/dstep/trunk/scripts/dstep.xsd"   do
+			@frameworks.each do |framework|
+				xml.framework :name => framework.name do
+					framework.headers.each do |header|
+						generate_header(xml, header)
+					end
+				end
+			end
+			
+			@headers.each do |header|
+				generate_header(xml, header)
+			end
+			
+			generate_classes(xml)
+			generate_informal_protocols(xml)
+		end
+		
+		file.close unless file == STDOUT
+	end	
+end
+
+def die (*msg)
+	$stderr.puts msg
+	exit 1
+end
+
+if __FILE__ == $0
+	dstep_gen = DStepGenerator.new
+	
+	OptionParser.new do |opts|
+		opts.banner = "Usage: #{File.basename(__FILE__)} [options] <headers...>"
+		opts.separator ""
+		opts.separator "Options:"
+		
+		opts.on("-f", "--framework FRAMEWORK", "Generate metadata for the given framework.") do |opt|
+			dstep_gen.add_framework(opt)
+		end
+		
+		opts.on(nil, "--64-bit", "Write 64-bit annotations.") do
+			dstep_gen.do_64bit = true
+		end
+		
+		opts.on("-o", "--output FILE", "Write output to the given file.") do |opt|
+			die "Output file can't be specified more than once" if dstep_gen.out_file
+			dstep_gen.out_file = opt
+		end
+		
+		# opts.on("-d", "--output-dir PATH", "Write ouptut to the given paht, use this with the --framework option") do |opt|
+		# 	die "Output directory can't be specified more than once" if dstep_gen.out_dir
+		# 	dstep_gen.out_dir = opt
+		# end
+		
+		
+		
+		help_msg = "Use the `-h' flag or for help."	
+			
+		opts.on("-h", "--help", "Show this message.") do
+			puts opts, help_msg
+			exit
+		end
+		
+		opts.on('-v', '--version', 'Show version.') do
+			puts DStepGenerator::VERSION
+			exit
+		end
+		
+		opts.separator ""
+		
+		if ARGV.empty?			
+			die opts.banner
+		else
+			begin
+				opts.parse!(ARGV)
+				
+				ARGV.each do |header|
+					dstep_gen.add_header(header)
+				end
+				
+				dstep_gen.collect
+				dstep_gen.generate
+			rescue => e
+				msg = e.message
+				msg = "Internal error" if msg.empty?
+				
+				die msg, opts.banner, help_msg
+			end
+		end
+	end
+end
\ No newline at end of file