diff sciprts/dgen.rb @ 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
line wrap: on
line diff
--- /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