comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:c7db221de6e8
1 #!/usr/bin/env ruby
2
3 ##
4 # Copyright:: Copyright (c) 2009 Jacob Carlborg. All rights reserved.
5 # Author:: Jacob Carlborg
6 # Version:: Initial created: 2009
7 # License:: [revised BSD license]http://opensource.org/licenses/bsd-license.php or
8 # [Academic Free License v3.0]http://opensource.org/licenses/afl-3.0.php
9 #
10
11 require "rubygems"
12 gem "xml-simple"
13 require "xmlsimple"
14 require "optparse"
15
16 # Extensions to String
17 class String
18
19 # Passes each character to the supplied block
20 #
21 # === Example:
22 # "str".each_char { |c| puts c }
23 # # outputs below
24 # s
25 # t
26 # r
27 #
28 def each_char
29 if block_given?
30 scan(/./m) do |x|
31 yield x
32 end
33 else
34 scan(/./m)
35 end
36 end
37
38 # Indents the string
39 # levels:: how many levels/tabs to indent
40 #
41 # === Example:
42 # "str".indent #=> str
43 # "str".indent(3) #=> str
44 #
45 def indent (levels = 1)
46 "\t" * levels << self
47 end
48
49 # Appends a semicolon and a newline character on the string
50 # semicolon:: should a semicolon be appended
51 #
52 # === Example:
53 # "str".nl
54 # "str".nl(false)
55 # # outputs below
56 # str;
57 #
58 # str
59 #
60 #
61 def nl (semicolon = true)
62 if semicolon
63 if self[-1, 1] == "{" || self[-1, 1] == "}"
64 self << "\n"
65 else
66 self << ";\n"
67 end
68 else
69 self << "\n"
70 end
71 end
72
73 # Returns the index of the given character or length of the string if not found
74 # char:: the character to look for
75 # start:: the index where to start the search
76 #
77 # === Example:
78 # "012345789".index_of("1") #=> 1
79 # "0123 0123".index_of("3", 6) #=> 8
80 #
81 def index_of (char, start = 0)
82 return self.length if start >= self.length
83
84 i = 0
85
86 if start == 0
87 self.each_char do |c|
88 return i if char == c
89 i += 1
90 end
91 else
92 self[start + 1 .. -1].each_char do |c|
93 return i + start + 1 if char == c
94 i += 1
95 end
96 end
97
98 return self.length
99 end
100
101 # Returns true if the string is an Objective-C struct
102 #
103 # === Example:
104 # "{?=ii}".struct? #=> true
105 #
106 def struct?
107 self =~ /\{/
108 end
109 end
110
111 # Extensions that adds support for member access syntax
112 class Hash
113 def type
114 result = self["type"]
115 return result unless result.nil?
116 self[:type]
117 end
118
119 def type= (type)
120 self[:type] = type
121 end
122
123 def id
124 result = self["id"]
125 return result unless result.nil?
126 self[:id]
127 end
128
129 def id= (id)
130 self[:id] = id
131 end
132
133 def methods
134 result = self["methods"]
135 return result unless result.nil?
136 self[:methods]
137 end
138
139 def methods= (methods)
140 self[:methods] = methods
141 end
142
143 def method
144 result = self["method"]
145 return result unless result.nil?
146 self[:method]
147 end
148
149 def method= (method)
150 self[:method] = method
151 end
152
153 def method_missing (method, *args)
154 self.class.instance_eval do
155 define_method(method) do |*args|
156 if args.length > 0
157 self[method[0 ... -1]] = args[0]
158 self[eval(":#{method}"[0 ... -1])] = args[0]
159 else
160 result = self[method]
161 return result unless result.nil?
162 self[eval(":#{method}")]
163 end
164 end
165 end
166
167 if (method = method.id2name) =~ /=/
168 eval("self.#{method} (args.length < 2 ? args[0] : args)")
169 else
170 eval("self.#{method}")
171 end
172 end
173 end
174
175 # This Struct represents an Objective-C Framework
176 Framework = Struct.new(:name, :files) do
177 def initialize
178 self.files = []
179 self.name = ""
180 end
181 end
182
183 # This Struct represents a C/Objective-C header
184 HeaderFile = Struct.new(:name, :framework, :cftypes, :constants, :d_constants, :d_constants_static_this, :defines,
185 :enums, :functions, :function_wrappers, :imports, :path, :structs, :typedefs) do
186 def initialize
187 self.name = ""
188 self.cftypes = []
189 self.constants = []
190 self.defines = []
191 self.enums = []
192 self.framework = ""
193 self.functions = []
194 self.function_wrappers = []
195 self.imports = []
196 self.path = ""
197 self.structs = []
198 self.typedefs = []
199 end
200 end
201
202 # Performs the conversion of an xml metadata file to the D programming language
203 class ObjcToD
204 FIRST_YEAR = "2009"
205 VERSION = 1.0
206
207 attr_accessor :out_dir, :files
208
209 # Creates a new instance of the ObjcToD class
210 def initialize
211 @classes = {}
212 @copyright = nil
213 @d_constants = []
214 @d_constants_static_this = []
215 @files = []
216 @frameworks = []
217 @function_wrappers = []
218 @headers = []
219 @package = "dstep"
220 end
221
222 # Generates the D code from the xml metadata
223 def generate_code
224 @files.each do |dstep_file|
225 xml = XmlSimple.xml_in(dstep_file)
226
227 unless xml.framework.nil?
228 frameworks = xml.framework
229
230 frameworks.each do |frame|
231 framework = Framework.new
232 framework.name = frame.name
233
234 frame.file.each do |file|
235 header = HeaderFile.new
236 header.name = file.name
237 header.constants = constants(file.constant) unless file.constant.nil?
238 header.d_constants = d_constants unless file.constant.nil?
239 header.d_constants_static_this = d_constants_static_this unless file.constant.nil?
240 header.enums = enums(file.enum) unless file.enum.nil?
241 header.functions = functions(file.function) unless file.function.nil?
242 header.function_wrappers = function_wrappers unless file.function.nil?
243 header.imports = imports(file.import, file.name, framework.name) unless file.import.nil?
244 header.structs = structs(file.struct) unless file.struct.nil?
245
246 header.typedefs = typedefs(file.typedef) unless file.typedef.nil?
247
248 framework.files << header
249 end
250
251 @frameworks << framework
252 end
253 end
254
255 unless xml.file.nil?
256 files = xml.file
257
258 file.each do |file|
259 header = HeaderFile.new
260 header.name = file.name
261 header.constants = constants(file.constant) unless file.constant.nil?
262 header.d_constants = d_constants unless file.constant.nil?
263 header.d_constants_static_this = d_constants_static_this unless file.constant.nil?
264 header.enums = enums(file.enum) unless file.enum.nil?
265 header.functions = functions(file.function) unless file.function.nil?
266 header.function_wrappers = function_wrappers unless file.function.nil?
267 header.imports = imports(file.import, file.name) unless file.import.nil?
268 header.structs = structs(file.struct) unless file.struct.nil?
269 header.typedefs = typedefs(file.typedef) unless file.typedef.nil?
270
271 @headers << header
272 end
273 end
274
275 unless xml["class"].nil?
276 classes(xml["class"])
277 end
278 end
279 end
280
281 # Outputs the generate D code
282 def output_code
283 @frameworks.each do |framework|
284 framework_path = framework_path = "#{@out_dir}/#{@package}/#{framework.name}"
285
286 FileUtils.mkdir_p(framework_path) unless File.exist?(framework_path)
287
288 framework.files.each do |header|
289 file_path = "#{framework_path}/#{header.name}.d"
290
291 File.open(file_path, "w") do |file|
292 file << copyright
293 file << "module #{@package}.#{framework.name}.#{header.name};"
294 file << header.imports.nl(false)
295 file << header.defines
296 file << header.typedefs
297 file << header.cftypes
298 file << header.constants
299 file << header.d_constants
300 file << header.enums
301 file << header.structs
302
303 unless header.d_constants_static_this.nil?
304 file << "static this ()".nl(false)
305 file << "{".nl(false)
306 file << header.d_constants_static_this
307 file << "}".nl(false).nl(false)
308 end
309
310 classes = get_classes(header.name)
311
312 classes.each do |clazz, value|
313 file << value.code.nl(false)
314 @classes.delete(clazz)
315 end
316
317 file << header.functions
318 file << header.function_wrappers
319 end
320 end
321 end
322
323 package_path = "#{@out_dir}/#{@package}"
324 FileUtils.mkdir_p(package_path) unless File.exist?(package_path)
325
326 @headers.each do |header|
327 header_path = "#{package_path}/#{header.name}.d"
328
329 File.open(header_path, "w") do |file|
330 file << copyright
331 file << "module #{@package}.#{framework.name}.#{header.name};"
332 file << header.imports.nl(false)
333 file << header.defines
334 file << header.typedefs
335 file << header.cftypes
336 file << header.constants
337 file << header.d_constants
338 file << header.enums
339 file << header.structs
340
341 unless header.d_constants_static_this.nil?
342 file << "static this ()".nl(false)
343 file << "{".nl(false)
344 file << header.d_constants_static_this
345 file << "}".nl(false).nl(false)
346 end
347
348 classes = get_classes(header.name)
349
350 classes.each do |clazz, value|
351 file << value.code.nl(false)
352 @classes.delete(clazz)
353 end
354
355 file << header.functions
356 file << header.functions_wrappers
357 end
358 end
359
360 @classes.each do |clazz, value|
361 class_path = "#{package_path}/#{clazz}.d"
362
363 File.open(class_path, "w") do |file|
364 file << value.code unless value.nil?
365 end
366 end
367 end
368
369 # Creates and returns the copyright header that is included in the top of every file
370 def copyright
371 return @copyright unless @copyright.nil?
372
373 # Add an extra empty string in the begining because array indices begin with zero and months don't
374 months = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
375 initialt_year = "2009"
376
377 str = StringIO.new
378 date = Date.today
379 current_year = date.year.to_s
380 year = initialt_year
381 initial_created = "#{months[date.month]} #{date.day}, #{initialt_year}"
382
383 year << "-" << current_year unless initialt_year == current_year
384
385 str << "/**\n"
386 str << " * Copyright: Copyright (c) #{year} Jacob Carlborg. All rights reserved.\n"
387 str << " * Authors: Jacob Carlborg\n"
388 str << " * Version: Initial created: #{initial_created} \n"
389 str << " * License: $(LINK2 http://opensource.org/licenses/bsd-license.php, revised BSD license) or <br />\n"
390 str << " * $(LINK2 http://opensource.org/licenses/afl-3.0.php, Academic Free License v3.0)\n"
391 str << " */\n"
392
393 @copyright = str.string
394 end
395
396 # Gets name of the framework that the given class belongs to
397 def get_framework (class_name)
398 @frameworks.each do |framework|
399 framework.files.each do |file|
400 return framework.name if file.name == class_name
401 end
402 end
403
404 return []
405 end
406
407 # Gets the classes that belongs to the given file
408 def get_classes (name)
409 classes = @classes.find_all do |clazz, value|
410 value.file == name
411 end
412
413 return classes
414 end
415
416 # Generates the D code for the classes
417 def classes (classes)
418 classes.each do |clazz|
419 str = StringIO.new
420
421 str << "class "
422
423 if clazz == ""
424 str << clazz.name.nl(false)
425 else
426 str << clazz.name
427 str << " : "
428 str << clazz.parent.nl(false)
429 end
430
431 str << "{".nl(false)
432 str << methods(clazz.method, clazz.name)
433 str << "}".nl(false)
434
435 @classes[clazz.name] ||= {}
436 @classes[clazz.name].code = str.string
437 framework = get_framework(clazz.file)
438 @classes[clazz.name].framework = framework unless framework.nil?
439 @classes[clazz.name].file = clazz.file
440 end
441 end
442
443 # Generates the D code for the constants/globals
444 def constants (constants)
445 str = StringIO.new
446
447 str << "extern (C)".nl(false)
448 str << "{".nl(false)
449 str << "extern".indent.nl(false)
450 str << "{".indent.nl(false)
451
452 constants.each do |constant|
453 type = get_type(constant.type, constant["type64"], constant["declaredType"])
454 const = constant["const"] == "true"
455
456 if constant.type == "@"
457 @d_constants << { :name => constant.name.dup, :type => constant.declaredType.gsub("*", ""), :const => const }
458
459 if const
460 str << "const id".indent(2)
461 else
462 str << "id".indent(2)
463 end
464
465 str << " "
466 str << constant["name"]
467 str << "_".nl
468 else
469 if const
470 str << "const ".indent(2)
471 str << type
472 else
473 str << "id".indent(2)
474 end
475
476 str << " "
477 str << constant["name"].nl
478 end
479 end
480
481 str << "}".indent.nl(false)
482 str << "}".nl(false).nl(false)
483
484 str.string
485 end
486
487 # Generates the D code for the constants that D can't handle directly, like classes
488 def d_constants
489 str = StringIO.new
490
491 @d_constants.each do |constant|
492 # Deep copy constant
493 c = constant.dup
494 c.name = c.name.dup
495 c.type = c.type.dup
496
497 str << "const " if constant.const
498 str << constant.type
499 str << " "
500 str << constant.name.nl
501 @d_constants_static_this << c
502 end
503
504 str << "\n"
505 @d_constants.clear
506 str.string
507 end
508
509 # Generates the D code for the constants the has to be in a "static this"
510 def d_constants_static_this
511 str = StringIO.new
512
513 @d_constants_static_this.each do |constant|
514 str << constant.name.indent
515 str << " = new "
516 str << constant.type
517 str << "("
518 str << constant.name
519 str << "_)".nl
520 end
521
522 @d_constants_static_this.clear
523 str.string
524 end
525
526 # Generates the D code for the enums
527 def enums (enums)
528 str = StringIO.new
529
530 enums.each do |enum|
531 str << "enum"
532
533 if enum["name"] == ""
534 str << "\n{".nl
535
536 enum.member.each_with_index do |member, i|
537 str << member["name"].indent
538 str << " = "
539 str << member.value
540 str << ",".nl(false) unless i == enum.member.length - 1
541 end
542 else
543 str << enum["name"].nl
544 str << "{"
545
546 enum["member"].each_with_index do |member, i|
547 str << member["name"].indent
548 str << " = "
549 str << member.value
550 str << ",".nl(false) unless i == enum.member.length - 1
551 end
552 end
553
554 str << "\n}".nl.nl(false)
555 end
556
557 str.string
558 end
559
560 # Generates the D code for the function/method args
561 def args (args, variadic, method = false)
562 return "" if args.nil?
563
564 str = StringIO.new
565 if variadic
566 #p args
567 end
568 args.each do |arg|
569
570 if method || arg.type != "@"
571 type = get_type(arg.type, arg.type64, arg.declaredType)
572 else
573 type = "id"
574 end
575
576 str << type
577 str << " "
578 str << arg["name"]
579 str << ", "
580 end
581
582 if variadic
583 else
584 end
585
586 str << "..." if variadic
587
588 return str.string if variadic
589 return str.string[0 ... -2] unless variadic
590 end
591
592 # A helper function that generates the D code for the functions/methods
593 def build_function (function, method = false, static = false)
594 str = StringIO.new
595 variadic = function["variadic"] == "true"
596 return_type = ""
597 args = ""
598 original_type = function.returnValue[0].type
599
600 if !method && original_type == "@"
601 return_type = "id"
602 else
603 return_type = get_type(original_type, function.returnValue[0].type64, function.returnValue[0].declaredType)
604 args(function.arg, variadic) unless function.arg.nil?
605 end
606
607 str << "static " if static
608 str << return_type
609 str << " "
610 str << function["name"]
611 str << " ("
612 str << args(function["arg"], variadic, method)
613 str << ")".nl
614
615 str.string
616 end
617
618 # Generates the D code for the functions
619 def functions (functions)
620 str = StringIO.new
621
622 str << "extern (C)".nl(false)
623 str << "{".nl(false)
624
625 functions.each do |function|
626 wrapper_needed = false
627 original_type = function["returnValue"][0].type
628
629 if original_type == "@"
630 @function_wrappers << function
631 wrapper_needed = true
632 else
633 function["arg"].each do |arg|
634 if (arg.type || arg["type64"]) == "@"
635 @function_wrappers << function
636 wrapper_needed = true
637 end
638 end unless function["arg"].nil?
639 end
640
641 return_type = get_type(function["returnValue"][0].type, function["returnValue"][0]["type64"], function["returnValue"][0]["declaredType"])
642 variadic = function["variadic"] == "true"
643
644 str << functions_helper(function, wrapper_needed)
645 end
646
647 str << "}".nl(false).nl(false)
648 str.string
649 end
650
651 # A helper function that generates the D code for the functions
652 def functions_helper (function, wrapper_needed)
653 str = StringIO.new
654
655 if wrapper_needed
656 declaration = build_function(function)
657 index = declaration.index_of(" ")
658 index = declaration.index_of(" ", index)
659 str << (declaration[0 ... index] + "_" + declaration[index .. -1]).indent
660 else
661 str << build_function(function).indent
662 end
663
664 str.string
665 end
666
667 # Generates the D code for the functions that D can't handle without wrappers, like functions that takes
668 # objects as arguments or returns an object
669 def function_wrappers
670 str = StringIO.new
671
672 @function_wrappers.each do |function|
673 original_type = function["returnValue"][0].type
674 variadic = function["variadic"] == "true"
675 args = StringIO.new
676 out_args = []
677 return_type = get_type(original_type, function.returnValue[0].type64, function.returnValue[0].declaredType)
678
679 function["arg"].each do |arg|
680 args << arg["name"]
681
682 if arg.type == "@"
683 args << " !is null ? "
684 args << arg["name"]
685 args << ".id : null"
686 elsif arg.type == "^@"
687 type = get_type(arg.type, arg["type64"], arg["declaredType"])
688 out_args << { :type => type[4 .. -1], :name => arg["name"] }
689
690 args << " &"
691 args << arg["name"]
692 args << "_.id"
693 end
694
695 args << ", "
696 end unless function["arg"].nil?
697
698 str << build_function(function, true)[0 ... -2].nl(false)
699 str << "{".nl(false)
700
701 out_args.each do |arg|
702 str << arg.type.indent
703 str << " "
704 str << arg.name
705 str << "_ = new "
706 str << arg.type
707 str << "(false, false)".nl
708 end
709
710 str << "\n" if out_args.length > 0
711
712 if original_type == "@"
713 str << "id result = ".indent
714 str << function["name"]
715 elsif original_type != "v"
716 if out_args.length > 0
717 str << return_type.indent
718 str << " result = "
719 else
720 str << "return ".indent
721 end
722 end
723
724 if original_type == "v"
725 str << function["name"].indent
726 elsif original_type != "@"
727 str << "cast("
728 str << return_type
729 str << ") "
730 str << function["name"]
731 end
732
733 str << "_"
734 str << "("
735 str << args.string[0 ... -2] unless function["arg"].nil?
736 str << ")".nl
737 str << "\n" if out_args.length > 0
738
739 out_args.each do |arg|
740 str << arg.name.indent(2)
741 str << " = "
742 str << arg.name
743 str << "_".nl
744 end
745
746 if out_args.length > 0
747 str << "\n"
748 str << "return result".indent unless original_type == "v"
749 end
750
751 if original_type == "@"
752 str << "return result !is null ? new ".indent
753 str << return_type
754 str << "(result)".nl
755 elsif original_type != "v" && out_args.length > 0
756 str << "".nl
757 end
758
759 str << "}".nl(false).nl(false)
760 end
761
762 @function_wrappers.clear
763 str.string[0 ... -2]
764 end
765
766 # Generates the D code for the imports
767 def imports (imports, filename, framework_name = nil)
768 str = StringIO.new
769
770 imports.each do |import|
771 str << "import #{@package}."
772 str << import.gsub("/", ".").gsub("Frameworks", "").gsub(".framework", "").gsub("Headers", "").gsub(/\.{2,}/, ".").nl
773 end
774
775 str << "\n\n"
776 str = str.string.sort.to_s
777
778 return "\n\npublic:" << str if filename == framework_name
779 return str
780 end
781
782 # Generates the D code for the methods
783 def methods (methods, class_name)
784 str = StringIO.new
785
786 str << "this (bool init = true, bool alloc = true)".indent.nl(false)
787 str << "{".indent.nl(false)
788 str << "if (alloc && init)".indent(2).nl(false)
789 str << "{".indent(2).nl(false)
790 str << "id result = objc_msgSend(class_#{class_name}, sel_alloc)".indent(3).nl
791 str << "id result2".indent(3).nl.nl(false)
792 str << "if (result)".indent(3).nl(false)
793 str << "result2 = objc_msgSend(result, sel_init)".indent(4).nl.nl(false)
794 str << "if (result2)".indent(3).nl(false)
795 str << "this.id = result2".indent(4).nl.nl(false)
796 str << "else".indent(3).nl(false)
797 str << "this.id = result".indent(4).nl
798 str << "}".indent(2).nl(false).nl(false)
799 str << "else if (alloc)".indent(2).nl(false)
800 str << "this.id = objc_msgSend(this.id, sel_alloc)".indent(3).nl
801 str << "}".indent.nl(false).nl(false)
802
803 str << "this (id object)".indent.nl(false)
804 str << "{".indent.nl(false)
805 str << "this.id = object".indent(2).nl
806 str << "}".indent.nl(false).nl(false)
807
808 str << "this (id object)".indent.nl(false)
809 str << "{".indent.nl(false)
810 str << "this.id = object.id".indent(2).nl
811 str << "}".indent.nl(false).nl(false)
812
813 str << "static #{class_name} alloc ()".indent.nl(false)
814 str << "{".indent.nl(false)
815 str << "id result = objc_msgSend(class_#{class_name}, sel_alloc)".indent(2).nl
816 str << "return result !is null ? new #{class_name}(result) : null".indent(2).nl
817 str << "}".indent.nl(false).nl(false)
818
819 str << "#{class_name} init ()".indent.nl(false)
820 str << "{".indent.nl(false)
821 str << "id result = objc_msgSend(this.id, sel_init)".indent(2).nl
822 str << "return result is this.id ? this : (result !is null ? new #{class_name}(result) : null)".indent(2).nl
823 str << "}".indent.nl(false).nl(false)
824
825 methods.each do |method|
826 next if method.selector == ("alloc" || "init") && method.static == "true"
827
828 return_type = get_type(method["returnValue"][0].type, method["returnValue"][0]["type64"], method["returnValue"][0]["declaredType"])
829 variadic = method["variadic"] == "true"
830 static = method["static"] == "true"
831 this = "this.id" unless static
832 this = "class_#{class_name}" if static
833 args = ""
834 out_args = []
835 original_type = method.returnValue[0].type
836
837 if method.selector[0 ... 4] == "init" && return_type == "id"
838 return_type = class_name
839 method.returnValue[0].declaredType = class_name
840 end
841
842 method["arg"].each do |arg|
843 args << " &" if arg.type == "^@"
844 args << arg["name"]
845
846 if arg.type == "@"
847 args << " !is null ? "
848 args << arg["name"]
849 args << ".id : null"
850 elsif arg.type == "^@"
851 type = get_type(arg.type, arg.type, arg["declaredType"])
852 out_args << { :type => type[4 .. -1], :name => arg["name"] }
853 args << "_.id"
854 end
855
856 args << ", "
857 end unless method["arg"].nil?
858
859 declaration = build_function(method, true, static)[0 ... -2].indent.nl(false)
860 index = declaration.index_of(" ")
861
862 str << (declaration[0 .. index] + get_method_name(method["selector"]) + declaration[index .. -1]).gsub(/ +/, " ")
863 str << "{".indent.nl(false)
864
865 out_args.each do |arg|
866 str << arg.type.indent(2)
867 str << " "
868 str << arg.name
869 str << "_ = new "
870 str << arg.type
871 str << "(false, false)".nl
872 end
873
874 str << "\n" if out_args.length > 0
875
876
877 if method["returnValue"][0].type == "@"
878 str << "id result = objc_msgSend(#{this}, sel_".indent(2)
879 str << transform_selector(method["selector"])
880 str << ", " unless method.arg.nil?
881 str << args[0 ... -2] unless method.arg.nil?
882 str << ")".nl
883 str << "\n" if out_args.length > 0
884
885 out_args.each do |arg|
886 str << arg.name.indent(2)
887 str << " = "
888 str << arg.name
889 str << "_".nl
890 end
891
892 str << "\n" if out_args.length > 0
893
894 if method["returnValue"][0]["declaredType"] == class_name
895 str << "return result is this.id ? this : (return result ".indent(2)
896 else
897 str << "return result ".indent(2)
898 end
899
900 str << "!is null ? new "
901 str << return_type
902 str << "(result) : null"
903
904 if method["returnValue"][0]["declaredType"] == class_name
905 str << ")".nl
906 else
907 str << "".nl
908 end
909
910 str << "}".indent.nl(false).nl(false)
911 else
912 if original_type == "d" || original_type == "f"
913
914 str << "version (X86)".indent(2).nl(false)
915
916 if out_args.length > 0
917 str << return_type.indent(3)
918 str << " result "
919 else
920 if original_type == "d"
921 str << "return ".indent(3)
922 else
923 str << "return cast(".indent(3)
924 str << return_type
925 str << ") "
926 end
927 end
928
929 str << "objc_msgSend_fpret(#{this}, sel_"
930 str << transform_selector(method["selector"])
931 str << ", " unless method.arg.nil?
932
933 args = ""
934
935 method["arg"].each do |arg|
936 args << arg["name"]
937 args << ", "
938 end unless method["arg"].nil?
939
940 str << args[0 ... -2] if args.length > 0
941 str << ")".nl.nl(false)
942
943 str << "else".indent(2).nl(false)
944
945 if out_args.length > 0
946 str << return_type.indent(3)
947 str << " result "
948 else
949 if original_type == "d"
950 str << "return ".indent(3)
951 else
952 str << "return cast(".indent(3)
953 str << return_type
954 str << ") "
955 end
956 end
957
958 str << "objc_msgSend(#{this}, sel_"
959 str << transform_selector(method["selector"])
960 str << ", " unless method.arg.nil?
961
962 args = ""
963
964 method["arg"].each do |arg|
965 args << arg["name"]
966 args << ", "
967 end unless method["arg"].nil?
968
969 str << args[0 ... -2] if args.length > 0
970 str << ")".nl
971 str << "\n" if out_args.length > 0
972
973 out_args.each do |arg|
974 str << arg.name.indent(3)
975 str << " = "
976 str << arg.name
977 str << "_".nl
978 end
979
980 if out_args.length > 0
981 str << "\n"
982 str << "retrun result".indent(3).nl
983 end
984
985 str << "\n" if out_args.length > 0
986 str << "}".indent.nl(false).nl(false)
987 else
988 unless return_type == "void"
989 if out_args.length > 0
990 str << return_type.indent(2)
991 str << " result "
992 else
993 str << "return cast(".indent(2)
994 end
995
996 str << return_type
997 str << ") "
998 end
999
1000 str << "objc_msgSend(#{this}, sel_".indent(2) if return_type == "void"
1001 str << "objc_msgSend(#{this}, sel_" unless return_type == "void"
1002 str << transform_selector(method["selector"])
1003 str << ", " unless method.arg.nil?
1004
1005 args = ""
1006
1007 method["arg"].each do |arg|
1008 args << arg["name"]
1009 args << ", "
1010 end unless method["arg"].nil?
1011
1012 str << args[0 ... -2] if args.length > 0
1013 str << ")".nl
1014
1015 str << "\n" if out_args.length > 0
1016
1017 out_args.each do |arg|
1018 str << arg.name.indent(2)
1019 str << " = "
1020 str << arg.name
1021 str << "_".nl
1022 end
1023
1024 if out_args.length > 0
1025 str << "\n"
1026 str << "retrun result".indent(2).nl
1027 end
1028
1029 str << "}".indent.nl(false).nl(false)
1030 end
1031 end
1032 end
1033
1034 str.string[0 .. -2]
1035 end
1036
1037 # Generates the D code for the structs
1038 def structs (structs)
1039 str = StringIO.new
1040
1041 structs.each do |struct|
1042 str << "struct "
1043 str << struct.name.nl(false)
1044 str << "{".nl
1045
1046 struct.member.each do |member|
1047 type = get_type(member.type, member.type64, member.declaredType)
1048
1049 str << type.indent
1050 str << " "
1051 str << member.name.nl
1052 end unless struct.member.nil?
1053
1054 str << "}".nl
1055 str << "\n\n"
1056 end
1057
1058 str.string[0 .. -2]
1059 end
1060
1061 # Generates the D code for the typedefs
1062 def typedefs (typedefs)
1063 str = StringIO.new
1064
1065 typedefs.each do |typedef|
1066
1067 type = ""
1068 struct = false
1069
1070 if typedef.type.struct?
1071 type = get_struct_type(typedef.type, typedef["type64"], typedef["declaredType"])
1072 struct = true
1073 else
1074 type = get_type(typedef.type, typedef.type64, typedef.declaredType)
1075 end
1076
1077 # Special case
1078 type = "wchar" if typedef.name == "unichar"
1079
1080 if struct
1081 str << typedef["declaredType"].gsub("*", "").nl
1082 str << "alias "
1083 str << type[:name]
1084 str << " "
1085 str << typedef["name"].nl
1086 else
1087 str << "alias "
1088 str << type
1089 str << " "
1090 str << typedef.name.nl
1091 end
1092 end
1093
1094 str << "\n"
1095 str.string
1096 end
1097
1098 # Adds specific D extensions to classes, like opIndex and opApply
1099 def d_class_extensions (args)
1100
1101 end
1102
1103 # Checks if the declared type should be used instead of the type
1104 # type:: the type to check
1105 #
1106 def check_declared_type (type)
1107 case type
1108 when "unichar"; return "wchar"
1109 when "BOOL"; return "bool"
1110 when "CGFloat"; return type
1111 when "NSInteger"; return type
1112 when "NSUInteger"; return type
1113
1114 when "unichar*"; return "wchar*"
1115 when "BOOL*"; return "bool*"
1116 when "CGFloat*"; return type
1117 when "NSInteger*"; return type
1118 when "NSUInteger*"; return type
1119
1120 when "unichar**"; return "wchar**"
1121 when "BOOL**"; return "bool**"
1122 when "CGFloat**"; return type
1123 when "NSInteger**"; return type
1124 when "NSUInteger**"; return type
1125 else return nil;
1126 end
1127 end
1128
1129 # Gets the method name from the supplied selector
1130 # selector:: the selector to get the method name from
1131 #
1132 # === Example:
1133 # get_method_name("initWithContentsOfURL:options:error:") #=> initWithContentsOfURL
1134 #
1135 def get_method_name (selector)
1136 i = selector.index_of(":")
1137 selector[0 ... i]
1138 end
1139
1140 # Gets the D type from the encoded C/Objective-C type
1141 # type:: the type
1142 # type64:: the type for 64bit targets
1143 # declared_type:: the declared type
1144 #
1145 # === Example:
1146 # get_type("I", "Q", "NSUInteger") #=> NSUInteger
1147 # get_type("I", "I", "unsigned int") #=> uint
1148 #
1149 def get_type (type, type64, declared_type)
1150
1151 t = check_declared_type(declared_type)
1152 return t unless t.nil?
1153
1154 unless type64 == "" || type64.nil?
1155 return declared_type if type != type64
1156 end
1157
1158 case type
1159 when "c"; return "byte"
1160 when "i"; return "int"
1161 when "s"; return "short"
1162 when "l"; return "int"
1163 when "q"; return "long"
1164 when "C"; return "ubyte"
1165 when "I"; return "uint"
1166 when "S"; return "ushort"
1167 when "L"; return "uint"
1168 when "Q"; return "ulong"
1169 when "f"; return "float"
1170 when "d"; return "double"
1171 when "B"; return "bool"
1172 when "v"; return "void"
1173 when "*"; return "char*"
1174 when '#'; return "Class"
1175 when ":"; return "SEL"
1176 when "@"
1177 return declared_type.gsub(/\*+/, "") unless declared_type.nil?
1178 raise "No declared type given"
1179 else
1180 case type[0, 1]
1181 when "["
1182 str = ""
1183 t = type[1 ... -1]
1184 count = $1 if t =~ /(\d+)/
1185 str = get_type($', declared_type)
1186 str << "[#{count}]"
1187
1188 return str
1189 when "("
1190 resolved_types = []
1191 types = $2 if type =~ /\((.+)=(.+)\)/
1192
1193 types.each_char do |t|
1194 resolved_types << get_type(t, declared_type)
1195 end
1196
1197 return resolved_types
1198 when "^"
1199 if type == "^@"
1200 return "out " << get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup
1201 elsif type.length > 2 && type[0 ... 2] == "^^"
1202 return "out " << get_type(type[2 .. -1], type64[2 .. -1], declared_type).dup
1203 elsif type == "^?"
1204 tmp = cfp_to_dfp(type)
1205 return tmp unless tmp.nil?
1206 end
1207
1208 if !type.nil? && !type64.nil?
1209 return get_type(type[1 .. -1], type64[1 .. -1], declared_type).dup << "*"
1210 elsif !type.nil?
1211 return get_type(type[1 .. -1], type64, declared_type).dup << "*"
1212 end
1213 when "{"
1214 return declared_type
1215 end
1216 end
1217
1218 return declared_type
1219 end
1220
1221 # Gets the D type from the encoded C/Objective-C type when it's a struct
1222 # type:: the type
1223 # type64:: the type for 64bit targets
1224 # declared_type:: the declared type
1225 #
1226 # === Example
1227 # get_struct_type("{some=III}", "{some=III}", "struct some")
1228 # # outputs below
1229 # { :name => "some", :types => ["uint", "uint", "uint"] }
1230 #
1231 def get_struct_type (type, type64, declared_type)
1232
1233 return { :name => declared_type } if declared_type[0 ... 2] == "NS"
1234
1235 case type[0, 1]
1236 when "{"
1237 resolved_types = []
1238
1239 name = $1 if type =~ /\^{0,}\{(.{0,})=(.{0,})\}/
1240 types = $2
1241
1242 unless types.nil?
1243 types.each_char do |t|
1244 resolved_types << get_type(t, type64, declared_type)
1245 end
1246 end
1247
1248 return { :name => name, :types => resolved_types }
1249 when "^"
1250 get_struct_type(type[1 .. -1], type64, declared_type)
1251 hash = get_struct_type(type[1 .. -1], type64, declared_type)
1252 hash[:name].dup << "*"
1253 return hash
1254 end
1255 end
1256
1257 # C Function Pointer to D Function Pointer
1258 def cfp_to_dfp (fp)
1259 reg = /(\w+)\s*\(\*(\w*)\)\s*\((.*)\)/
1260
1261 return nil if fp !~ reg
1262
1263 return_type = $1
1264 name = $2
1265 arg_types = $3
1266
1267 return "#{return_type} function (#{arg_types})"
1268 end
1269
1270 # Is the supplied argument an "out" argument
1271 # arg:: the arg
1272 #
1273 # === Example:
1274 # out_arg?("out NSError") #=> true
1275 #
1276 def out_arg? (arg)
1277 return arg[0 .. 4] == "out "
1278 end
1279
1280 # Transform the supplied selector to a valid D representation
1281 # selector:: the selector to transform
1282 #
1283 # === Example:
1284 # transform_selector("initWithContentsOfURL:options:error:")
1285 # # outputs below
1286 # initWithContentsOfURL_options_error_
1287 #
1288 def transform_selector (selector)
1289 selector.gsub(/:/, "_")
1290 end
1291 end
1292
1293 # Prints the message to stderr, exits
1294 def die (*msg)
1295 $stderr.puts msg
1296 exit 1
1297 end
1298
1299 if __FILE__ == $0
1300 objc_to_d = ObjcToD.new
1301
1302 OptionParser.new do |opts|
1303 opts.banner = "Usage: #{File.basename(__FILE__)} [options] <dstep files...>"
1304 opts.separator ""
1305 opts.separator "Options:"
1306
1307 opts.on("-o", "--output DIRECTORY", "Place the output files in this directory'") do |opt|
1308 die "The specified directory \"#{opt}\" does not exists" if File.exist?(opt) == false
1309 die "Output directory cannot be specified more than once" if objc_to_d.out_dir
1310 objc_to_d.out_dir = opt
1311 end
1312
1313 help_msg = "Use the `-h' flag or for help."
1314
1315 opts.on("-h", "--help", "Show this message.") do
1316 puts opts, help_msg
1317 exit
1318 end
1319
1320 opts.on('-v', '--version', 'Show version.') do
1321 puts ObjcToD::VERSION
1322 exit
1323 end
1324
1325 opts.separator ""
1326
1327 if ARGV.empty?
1328 die opts.banner
1329 else
1330 begin
1331 opts.parse!(ARGV)
1332
1333 die "No output directory given" if objc_to_d.out_dir.nil?
1334
1335 ARGV.each do |file|
1336 objc_to_d.files << file
1337 end
1338
1339 objc_to_d.generate_code
1340 objc_to_d.output_code
1341 rescue => e
1342 msg = e.message
1343 msg = "Internal error" if msg.empty?
1344
1345 die msg, opts.banner, help_msg
1346 end
1347 end
1348 end
1349 end