Mercurial > projects > dstep
view 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 source
#!/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