Mercurial > projects > dstep
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 |