Mercurial > projects > dstep
view scripts/dgen.rb @ 13:4f583f7e242e
Added a constructor for every init method
author | Jacob Carlborg <doob@me.com> |
---|---|
date | Mon, 03 Aug 2009 15:21:59 +0200 |
parents | 07194b026fa4 |
children | 7ff919f595d5 |
line wrap: on
line source
#!/usr/bin/env ruby ## # Copyright:: Copyright (c) 2009 Jacob Carlborg. # Author:: Jacob Carlborg # Version:: Initial created: 2009 # License:: [Boost Software License 1.0]http://www.boost.org/LICENSE_1_0.txt # require "rubygems" gem "xml-simple" require "xmlsimple" require "optparse" require "date" # 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, :c_constants, :d_constants, :d_constants_static_this, :defines, :enums, :enums_gnu, :needs_type_encoding, :functions, :c_functions, :function_pointers, :function_wrappers, :imports, :path, :structs, :typedefs) do def initialize self.name = "" self.cftypes = [] self.constants = [] self.defines = [] self.enums = [] self.enums_gnu = [] self.needs_type_encoding = false self.framework = "" self.functions = [] self.c_functions = [] self.function_pointers = [] self.function_wrappers = [] self.imports = [] self.path = "" self.structs = [] self.typedefs = [] self.d_constants_static_this = [] 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 = {} @interfaces = {} @interfaces2 = [] @templates = {} @copyright = nil @c_constants = [] @d_constants = [] @d_constants_static_this = [] @files = [] @frameworks = [] @function_wrappers = [] @c_functions = [] @headers = [] @package = "dstep" @needs_bridge = false end # Generates the D code from the xml metadata def generate_code @files.each do |dstep_file| xml = XmlSimple.xml_in(dstep_file) @needs_bridge = !xml.protocol.nil? || !xml.category.nil? || !xml["class"].nil? interfaces(xml.protocol) unless xml.protocol.nil? templates(xml.category) unless xml.category.nil? classes(xml["class"]) unless xml["class"].nil? 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.c_constants = c_constants 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, header.enums_gnu, header.needs_type_encoding = enums(file.enum, header.name) unless file.enum.nil? header.functions = functions(file.function) unless file.function.nil? header.c_functions = c_functions unless file.function.nil? header.function_pointers = function_pointers(file.functionPointer) unless file.functionPointer.nil? header.function_wrappers = function_wrappers unless file.function.nil? header.imports = imports(header, 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 files.each do |file| header = HeaderFile.new header.name = file.name header.constants = constants(file.constant) unless file.constant.nil? header.c_constants = c_constants 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, header.enums_gnu, header.needs_type_encoding = enums(file.enum) unless file.enum.nil? header.functions = functions(file.function) unless file.function.nil? header.function_pointers = function_pointers(file.functionPointer) unless file.functionPointer.nil? header.c_functions = c_functions unless file.function.nil? header.function_wrappers = function_wrappers unless file.function.nil? header.imports = imports(header, 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 end end # Outputs the generate D code def output_code @frameworks.each do |framework| framework_path = framework_path = "#{@out_dir}/#{@package}/#{get_identifier(get_framework_name(framework.name.downcase))}" unless sub_framework?(framework.name.downcase) framework_path = framework_path = "#{@out_dir}/#{@package}/#{get_identifier(get_parent_framework_name(framework.name.downcase))}/#{get_identifier(get_framework_name(framework.name.downcase))}" if sub_framework?(framework.name.downcase) FileUtils.mkdir_p(framework_path) unless File.exist?(framework_path) framework.files.each do |header| file_path = "#{framework_path}/#{get_identifier(header.name)}" bindings_file_path = file_path + "_bindings.d" file_path << ".d" mod = "#{@package}.#{get_identifier(get_framework_name(framework.name.downcase))}.#{get_identifier(header.name)}" unless sub_framework?(framework.name.downcase) mod = "#{@package}.#{get_identifier(get_parent_framework_name(framework.name.downcase))}.#{get_identifier(get_framework_name(framework.name.downcase))}.#{get_identifier(header.name)}" if sub_framework?(framework.name.downcase) File.open(file_path, "w") do |file| file << copyright file << "module #{mod};" file << header.imports file << "import bindings = #{mod}_bindings".nl.nl(false) unless header.d_constants.nil? || header.function_wrappers.nil? file << header.defines file << header.typedefs file << header.cftypes file << header.function_pointers file << header.c_constants file << header.d_constants file << header.enums_gnu if header.needs_type_encoding file << header.enums file << header.structs unless header.d_constants_static_this.length == 0 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 interfaces = get_interfaces(header.name) interfaces.each do |interface, value| file << value.code.nl(false) @interfaces.delete(interface) end templates = get_templates(header.name) templates.each do |template, value| file << value.code.nl(false) @templates.delete(template) end file << header.function_wrappers file << header.c_functions end File.open(bindings_file_path, "w") do |file| file << "module " file << mod file << "_bindings;" file << header.imports.nl(false) file << header.constants file << header.functions end unless header.d_constants.nil? || header.function_wrappers.nil? 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}/#{get_identifier(header.name)}" bindings_file_path = header_path + "_bindings.d" header_path << ".d" mod = "#{@package}.#{get_identifier(header.name)}" File.open(header_path, "w") do |file| file << copyright file << "module #{mod};" file << header.imports file << "import bindings = #{mod}_bindings".nl.nl(false) unless header.d_constants.nil? || header.function_wrappers.nil? file << header.defines file << header.typedefs file << header.cftypes file << header.function_pointers file << header.c_constants file << header.d_constants file << header.enums_gnu if header.needs_type_encoding 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 interfaces = get_interfaces(header.name) interfaces.each do |interface, value| file << value.code.nl(false) @interfaces.delete(interface) end templates = get_templates(header.name) templates.each do |template, value| file << value.code.nl(false) @templates.delete(template) end file << header.function_wrappers file << header.c_functions end File.open(bindings_file_path, "w") do |file| file << "module " file << mod file << "_bindings;" file << header.imports.nl(false) file << header.constants file << header.functions end unless header.d_constants.nil? || header.function_wrappers.nil? end @classes.each do |clazz, value| class_path = "#{package_path}/#{get_identifier(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.\n" str << " * Authors: Jacob Carlborg\n" str << " * Version: Initial created: #{initial_created} \n" str << " * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.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 get_identifier(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 # Gets the interfaces that belongs to the given file def get_interfaces (name) interfaces = @interfaces.find_all do |interface, value| value.file == name end return interfaces end # Gets the templates that belongs to the given file def get_templates (name) templates = @templates.find_all do |template, value| value.file == name end return templates end # Generates the D code for the classes def classes (classes) classes.each do |clazz| str = StringIO.new protocols = [] str << "class " if clazz == "" str << get_identifier(clazz.name).nl(false) else str << get_identifier(clazz.name) unless clazz.parent == "" str << " : " str << get_identifier(clazz.parent) end unless clazz.protocols.length == 0 protocols = clazz.protocols.split(",") str2 = StringIO.new str2 << ", " unless clazz.parent.length == 0 str2 << " : " if clazz.parent.length == 0 protocols.each do |protocol| str2 << "I" str2 << get_identifier(protocol) str2 << ", " end str << str2.string[0 .. -3] end str << "\n" end str << "{".nl(false) str << "mixin ObjcWrap".indent.nl templates_for_class(clazz.name).each do |template, value| str << "mixin #{get_identifier("T" + template)}".indent.nl end str << "\n" str << methods(clazz.method, clazz.name) str << "\n" if protocols.length > 0 # implement the interfaces/protocols protocols.each_with_index do |protocol, i| interface = interface_for_protocol(protocol) unless interface.nil? str << methods(interface.method, interface.name) str << "\n" unless i == protocols.length - 1 end end 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 = get_identifier(clazz.file) end end # Generates the D code for the interfaces def interfaces (interfaces) interfaces.each do |interface| str = StringIO.new str << "interface " # prepend I to the interface name, because it can be the same as a class name if interface == "" str << get_identifier("I" + interface.name).nl(false) else str << get_identifier("I" + interface.name) unless interface.parent == "" str << " : " str << get_identifier(interface.parent) end unless interface.protocols.length == 0 protocols = interface.protocols protocols = protocols.split(",") str2 = StringIO.new str2 << ", " unless interface.parent.length == 0 str2 << " : " if interface.parent.length == 0 protocols.each do |protocol| str2 << "I" str2 << get_identifier(protocol) str2 << ", " end str << str2.string[0 .. -3] end str << "\n" end str << "{".nl(false) str << interface_methods(interface.method, interface.name) str << "}".nl(false) @interfaces[interface.name] ||= {} @interfaces[interface.name].code = str.string framework = get_framework(interface.file) @interfaces[interface.name].framework = framework unless framework.nil? @interfaces[interface.name].file = get_identifier(interface.file) @interfaces2 << interface end end # Generates the D code for the templates def templates (templates) templates.each do |template| str = StringIO.new str << "template " str << get_identifier("T" + template.name) str << " ()".nl(false) str << "{".nl(false) str << interface_methods(template.method, template.name) if template["class"] == "NSObject" str << methods(template.method, template.name) unless template["class"] == "NSObject" str << "}".nl(false) @templates[template.name] ||= {} @templates[template.name].code = str.string framework = get_framework(template.file) @templates[template.name].framework = framework unless framework.nil? @templates[template.name].file = get_identifier(template.file) @templates[template.name][:class] = get_identifier(template["class"]) end end # Generates the D code for the constants/globals def constants (constants) return "" if constants.length == 0 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 } str << "package ".indent(2) str << "const id" if const str << "id" unless const str << " " str << get_identifier(constant["name"]).nl else @c_constants << { :name => constant.name.dup, :type => type, :const => const } end end str << "}".indent.nl(false) str << "}".nl(false).nl(false) str.string end def c_constants return "" if @c_constants.length == 0 str = StringIO.new str << "extern (C)".nl(false) str << "{".nl(false) str << "extern".indent.nl(false) str << "{".indent.nl(false) @c_constants.each do |constant| if constant.const str << "const ".indent(2) str << constant.type else str << constant.type.indent(2) end str << " " str << get_identifier(constant.name).nl end str << "}".indent.nl(false) str << "}".nl(false).nl(false) @c_constants.clear str.string end # Generates the D code for the constants that D can't handle directly, like classes def d_constants return "" if @d_constants.length == 0 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 << get_identifier(constant.type) str << " " str << get_identifier(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 return "" if @d_constants_static_this.length == 0 str = StringIO.new @d_constants_static_this.each do |constant| str << constant.name.indent str << " = new " str << get_identifier(constant.type) str << "(" str << "bindings." str << get_identifier(constant.name) str << ")".nl end @d_constants_static_this.clear str.string end # Generates the D code for the enums def enums (enums, header_name) return "" if enums.length == 0 str = StringIO.new str_gnu = StringIO.new consts = [] enums.each do |enum| localConsts = [] str_gnu2 = StringIO.new str << "enum" needs_type_encoding = false if enum.name.length == 0 str << "\n{".nl(false) needs_type_encoding, localConsts = enum_helper(enum, str, str_gnu2, header_name) else str << " " str << get_identifier(enum["name"]).nl(false) str << "{".nl(false) needs_type_encoding, localConsts = enum_helper(enum, str, str_gnu2, header_name) end str << "\n}".nl(false).nl(false) # if needs_type_encoding # str_gnu << "// This is needed otherwise the enums will fail compiling with gdc\n" # str_gnu << "version (GNU)\n{\n" # str_gnu << "private\n".indent # str_gnu << "{\n".indent # str_gnu << str_gnu2.string # str_gnu << "\n" # str_gnu << "}".indent # str_gnu << "\n}".nl(false).nl(false) # end consts << localConsts end consts.flatten! if @needs_type_encoding && consts.length > 0 str_gnu << "// This is needed otherwise the enums will fail compiling with gdc\n" str_gnu << "version (GNU)\n{\n" str_gnu << "private\n".indent str_gnu << "{\n".indent consts.each do |const| str_gnu << "const __".indent(2) str_gnu << const.name str_gnu << ' = getOSType!("' str_gnu << const.value str_gnu << '")'.nl end str_gnu << "}".indent str_gnu << "\n}".nl(false).nl(false) end needs_type_encoding = @needs_type_encoding @needs_type_encoding = false return str.string, str_gnu.string, needs_type_encoding end def enum_helper (enum, str, str_gnu, header_name) needs_type_encoding = false consts = [] enum.member.each_with_index do |member, i| str << get_identifier(member.name).indent if member.value.length > 0 needs_type_encoding = true if member.value[0, 1] == "'" @needs_type_encoding = true if needs_type_encoding str << " = " if member.value[0, 1] == "'" str << 'getOSType!("' str << member.value[1 ... -1] str << '")' consts << { :name => get_identifier(member.name), :value => member.value[1 ... -1] } # str_gnu << "const __".indent(2) # str_gnu << get_identifier(member.name) # str_gnu << " = " # str_gnu << 'getOSType!("' # str_gnu << member.value[1 ... -1] # str_gnu << '")'.nl else str << member.value end end str << ",".nl(false) unless i == enum.member.length - 1 end return needs_type_encoding, consts end # Generates the D code for the function/method args def args (args, variadic, method = false) return "" if args.nil? str = StringIO.new args.each do |arg| if method || arg.type != "@" type = get_type(arg.type, arg.type64, arg.declaredType) else type = "id" end str << type str << " " unless arg.name.nil? str << get_identifier(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) return "" if functions.length == 0 str = StringIO.new wrapper_needed = false 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 str << functions_helper(function, wrapper_needed) else function.arg.each do |arg| if (arg.type || arg.type64) == "@" @function_wrappers << function wrapper_needed = true break end end unless function.arg.nil? str << functions_helper(function, wrapper_needed) if wrapper_needed @c_functions << function unless wrapper_needed end end str << "}" str.string end def function_pointers (function_pointers) return "" if function_pointers.length == 0 str = StringIO.new str << "extern (C)\n{".nl(false) function_pointers.each do |fp| str << "alias ".indent str << get_type(fp.returnValue[0].type, fp.returnValue[0].type64, fp.returnValue[0].declaredType) str << " function (" str << args(fp.arg, fp.variadic == "true") str << ") " str << get_identifier(fp.name).nl end str << "}".nl(false).nl(false) str.string end def c_functions return "" if @c_functions.length == 0 str = StringIO.new str << "extern (C)".nl(false) str << "{".nl(false) @c_functions.each do |function| str << functions_helper(function, false) end str << "}" @c_functions.clear str.string end # A helper function that generates the D code for the functions def functions_helper (function, wrapper_needed) str = StringIO.new str << ("private " + build_function(function)).indent if wrapper_needed str << build_function(function).indent unless wrapper_needed 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 return "" if @function_wrappers.length == 0 str = StringIO.new @function_wrappers.each do |function| return_type = get_type(function.returnValue[0].type, function.returnValue[0].type64, function.returnValue[0].declaredType) variadic = function.variadic == "true" str << build_function(function, true)[0 ... -2].nl(false) str << "{".nl(false) str << "return Bridge.invokeObjcFunction!(".indent str << return_type str << ", bindings." str << function.name function.arg.each do |arg| str << ", " str << get_type(arg.type, arg.type, arg.declaredType) end unless function.arg.nil? unless function.arg.nil? str << ")" str << "(" end function.arg.each_with_index do |arg, i| str << arg.name str << ", " unless i == function.arg.length - 1 end unless function.arg.nil? str << ")".nl str << "}".nl(false).nl(false) end @function_wrappers.clear str.string end # Generates the D code for the imports def imports (header, imports, filename, framework_name = nil) return "" if imports.length == 0 str = StringIO.new imports.each do |import| str << "import #{@package}." import = import.gsub("/", ".").gsub("Frameworks", "").gsub(".framework", "").gsub("Headers", "").gsub(/\.{2,}/, ".").nl splits = import.split('.') import = "" splits[0 .. -2].each do |s| import << get_identifier(s.downcase) import << '.' end str << import str << splits[-1 .. -1] end str << "import dstep.objc.bridge.TypeEncoding".nl if header.needs_type_encoding && !@needs_bridge if @needs_bridge str << "import dstep.objc.bridge.Bridge".nl str << "import dstep.objc.bridge.TypeEncoding".nl if header.needs_type_encoding str << "import dstep.objc.objc : id".nl end str << "\n\n" str = str.string.sort.to_s str << "\n" return "\n\npublic:" << str if filename == get_framework_name(framework_name) return str end # Generates the D code for the methods def methods (methods, class_name) return "" if methods.length == 0 str = StringIO.new methods.each do |method| return_type = get_type(method.returnValue[0].type, method.returnValue[0].type64, method.returnValue[0].declaredType) variadic = method.variadic == "true" static = method.classMethod == "true" index = 0 declaration = build_function(method, true, static)[0 ... -2].indent.nl(false) index = declaration.index_of(" ") if static index = declaration.index_of(" ", index) name = get_method_name(method.selector) str << (declaration[0 .. index] + name + declaration[index .. -1]).gsub(/ +/, " ") str << "{".indent.nl(false) if static str << "return invokeObjcSelfClass!(".indent(2) str << return_type else if return_type == class_name str << "id result = invokeObjcSelf!(".indent(2) str << "id" else str << "return invokeObjcSelf!(".indent(2) str << return_type end end str << ', "' str << method.selector str << '"' method.arg.each do |arg| str << ", " str << get_type(arg.type, arg.type, arg.declaredType) end unless method.arg.nil? unless method.arg.nil? str << ")" str << "(" end method.arg.each_with_index do |arg, i| str << get_identifier(arg.name) str << ", " unless i == method.arg.length - 1 end unless method.arg.nil? if return_type == class_name str << "return result is this.objcObject ? this : (result !is null ? new #{return_type}(result) : null);" else str << ")".nl end str << "}".indent.nl(false).nl(false) if name.length >= 4 && name[0 ... 4] == "init" && name != "initialize" str << ("this" + declaration[index .. -1]).gsub(/ +/, " ").indent str << "{".indent.nl(false) str << 'objcObject = Bridge.invokeObjcClassMethod!(id, "alloc")(objcClass)'.indent(2).nl str << 'id result = Bridge.invokeObjcMethod!(id, "'.indent(2) str << method.selector str << '"' method.arg.each do |arg| str << ", " str << get_type(arg.type, arg.type, arg.declaredType) end unless method.arg.nil? str << ")(objcObject" method.arg.each do |arg| str << ", " str << get_identifier(arg.name) end unless method.arg.nil? str << ")".nl.nl(false) str << "if (result)".indent(2).nl(false) str << "objcObject = ret".indent(3).nl.nl(false) str << "dObject = this".indent(2).nl str << "}".indent.nl(false).nl(false) end end str.string[0 .. -2] end # Generates the D code for the interface methods def interface_methods (methods, interface_name) return "" if methods.length == 0 str = StringIO.new methods.each do |method| return_type = get_type(method.returnValue[0].type, method.returnValue[0].type64, method.returnValue[0].declaredType) variadic = method.variadic == "true" static = method.classMethod == "true" index = 0 declaration = build_function(method, true, static)[0 ... -2].indent.nl(false) index = declaration.index_of(" ", index) index = declaration.index_of(" ", index + 1) if static str << ((declaration[0 .. index] + get_method_name(method["selector"]) + declaration[index .. -1]).gsub(/ +/, " "))[0 .. -2] str << ";\n" end str.string end # Generates the D code for the structs def structs (structs) return "" if structs.length == 0 str = StringIO.new structs.each do |struct| str << "struct " str << get_identifier(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 << get_identifier(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) return "" if typedefs.length == 0 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 if struct unless type.name == typedef.name declaredType = typedef.declaredType declaredType = declaredType[7 .. -1] if declaredType.length > 7 && declaredType[0 ... 6] == "struct" unless declaredType == typedef.name str << "alias " str << get_identifier(declaredType) str << " " str << get_identifier(typedef.name).nl end # str << "alias " # str << get_identifier(type.name) # str << "*" if typedef.declaredType =~ /\*{1}/ # str << " " # str << get_identifier(typedef.name).nl end else str << "alias " str << type str << " " str << get_identifier(typedef.name).nl end unless type.nil? 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" when "UniChar"; return "wchar" when "BOOL"; return "bool" when "CGFloat"; return type when "NSInteger"; return type when "NSUInteger"; return type when "IMP"; return type; when "unichar*" when "UniChar*"; return "wchar*" when "BOOL*"; return "bool*" when "CGFloat*"; return type when "NSInteger*"; return type when "NSUInteger*"; return type when "unichar**" 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(":") get_identifier(selector[0 ... i]) end def get_matching_close_char (char) case char when "{"; return "}" when "("; return ")" when "["; return "]" else char end 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) declared_type = "I" + $1 if declared_type =~ /\w+\s*<(.+)>/ declared_type.gsub!(/\(|\)/, "") return get_identifier(declared_type) if type.nil? && type64.nil? t = check_declared_type(declared_type) return t unless t.nil? unless type64 == "" || type64.nil? return get_identifier(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 "@" unless declared_type.nil? t = declared_type.gsub(/\*+/, "") return t == "id" ? "Object" : t end raise "No declared type given" else return declared_type if type =~ /\{/ case type[0, 1] when "[" str = "" t = type[1 ... -1] count = $1 if t =~ /(\d+)/ t = $' t64 = "" unless type64.nil? t64 = type64[1 ... -1] count = $1 if t64 =~ /(\d+)/ t64 = $' else t64 = t end str = get_type(t, t64, declared_type) str << "[#{count}]" return get_identifier(str) when "(" resolved_types = [] types = $2 if type =~ /\((.+)=(.+)\)/ types64 = $2 if type64 =~ /\((.+)=(.+)\)/ i = 0 while i < types.length t = types[i, 1] t64 = types64.nil? ? t : types64[i, 1] index = t =~ /(\(|\[|\{)/ unless index.nil? x = types.index(get_matching_close_char($1), index) t = types[index .. x] t64 = types64.nil? ? t : types64[index .. x] i += x - index end resolved_types << get_type(t, t64, declared_type) i += 1 end unless types.nil? get_identifier(resolved_types) when "^" t = type[1 .. -1] t64 = type64.nil? ? t : type64[1 .. -1] get_identifier(get_type(t, t64, declared_type).dup + "*") # if type == "^@" # return get_identifier(get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup + "*") # elsif type.length > 2 && type[0 ... 2] == "^^" # return get_identifier(get_type(type[2 .. -1], type64[2 .. -1], declared_type).dup + "**") # elsif type == "^?" # assuming function pointer # tmp = cfp_to_dfp(type) # return get_identifier(tmp) unless tmp.nil? # end # # if !type.nil? && !type64.nil? # t = get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup # # return get_identifier(t) if t =~ /\*/ # return get_identifier(t + "*") if t !~ /\*/ # elsif !type.nil? # t = get_type(type[1 .. -1], type64, declared_type).dup << "*" # # return get_identifier(t) if t =~ /\*/ # return get_identifier(t + "*") if t !~ /\*/ # end when "{" return get_identifier(declared_type) end end return get_identifier(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 = [] if type =~ /\^{0,}\{(.{0,})=(.{0,})\}/ name = get_identifier($1) types = get_identifier($2) elsif type =~ /\^{0,}\((.{0,})=(.{0,})\)/ name = get_identifier($1) types = get_identifier($2) end unless types.nil? types.each_char do |t| resolved_types << get_type(t, type64, declared_type) end end name = declared_type if name.nil? 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 << "*" unless hash.nil? 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 "#{get_identifier(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 # Gets the identifier, if it's a D keyoword it # will return string appended with a _ otherwise the string def get_identifier (str) return is_keyword?(str) ? str + "_" : str end # Returns true if the given string is a D(2) keyword def is_keyword? (str) case str when "abstract"; return true when "alias"; return true when "align"; return true when "asm"; return true when "assert"; return true when "auto"; return true when "body"; return true when "bool"; return true when "break"; return true when "byte"; return true when "case"; return true when "cast"; return true when "catch"; return true when "cdouble"; return true when "cent"; return true when "cfloat"; return true when "char"; return true when "class"; return true when "const"; return true when "continue"; return true when "creal"; return true when "dchar"; return true when "debug"; return true when "default"; return true when "delegate"; return true when "delete"; return true when "deprecated"; return true when "do"; return true when "double"; return true when "else"; return true when "enum"; return true when "export"; return true when "extern"; return true when "false"; return true when "final"; return true when "finally"; return true when "float"; return true when "for"; return true when "foreach"; return true when "foreach_reverse"; return true when "function"; return true when "goto"; return true when "idouble"; return true when "if"; return true when "ifloat"; return true when "import"; return true when "in"; return true when "inout"; return true when "int"; return true when "interface"; return true when "invariant"; return true when "ireal"; return true when "is"; return true when "lazy"; return true when "long"; return true when "macro"; return true when "mixin"; return true when "module"; return true when "new"; return true when "nothrow"; return true when "null"; return true when "out"; return true when "override"; return true when "package"; return true when "pragma"; return true when "private"; return true when "protected"; return true when "public"; return true when "pure"; return true when "real"; return true when "ref"; return true when "return"; return true when "scope"; return true when "shared"; return true when "short"; return true when "static"; return true when "struct"; return true when "super"; return true when "switch"; return true when "synchronized"; return true when "template"; return true when "this"; return true when "throw"; return true when "true"; return true when "try"; return true when "typedef"; return true when "typeid"; return true when "typeof"; return true when "ubyte"; return true when "ucent"; return true when "uint"; return true when "ulong"; return true when "union"; return true when "unittest"; return true when "ushort"; return true when "version"; return true when "void"; return true when "volatile"; return true when "wchar"; return true when "while"; return true when "with"; return true else return false; end end def templates_for_class (clazz) templates = @templates.find_all do |template, value| value[:class] == clazz end return templates end def interface_for_protocol (protocol) interface = @interfaces2.find do |interface| interface.name == protocol end return interface end def get_framework_name (framework) i = framework.rindex(".framework") return framework if i.nil? x = framework.rindex("/", i) framework[x + 1 ... i] end def sub_framework? (framework) i = framework.index("framework") return false if i.nil? !framework.index("framework", i + 1).nil? end def get_parent_framework_name (framework) return get_framework_name(framework) unless sub_framework?(framework) i = framework.index(".framework") return framework if i.nil? str = framework[0 ... i] x = str.rindex("/") return str if x.nil? str[x + 1 .. -1] 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