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