1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
25 # Path to the output file
26 var opt_output
= new OptionString("Output file", "-o")
28 # Shall `init` methods/constructors be wrapped as methods?
30 # By default, these methods/constructors are wrapped as extern constructors.
31 # So initializing an extern Objective-C object looks like:
33 # var o = new NSArray.init_with_array(some_other_array)
36 # If this option is set, the object must first be allocated and then initialized.
37 # This is closer to the Objective-C behavior:
40 # o.init_with_array(some_other_array)
42 var opt_init_as_methods
= new OptionBool(
43 "Wrap `init...` constructors as Nit methods instead of Nit constructors",
46 private var objc_to_nit_types
: Map[String, String] is lazy
do
47 var types
= new HashMap[String, String]
48 types
["char"] = "Byte"
49 types
["short"] = "Int"
50 types
["short int"] = "Int"
53 types
["long int"] = "Int"
54 types
["long long"] = "Int"
55 types
["long long int"] = "Int"
56 types
["float"] = "Float"
57 types
["double"] = "Float"
58 types
["long double"] = "Float"
60 types
["NSUInteger"] = "Int"
61 types
["NSInteger"] = "Int"
62 types
["CGFloat"] = "Float"
63 types
["BOOL"] = "Bool"
65 types
["id"] = "NSObject"
66 types
["constid"] = "NSObject"
67 types
["SEL"] = "NSObject"
68 types
["void"] = "Pointer"
75 redef fun knows_type
(objc_type
) do return super or
76 objc_to_nit_types
.keys
.has
(objc_type
)
85 # Generate Nit code to wrap `classes`
88 var classes
= model
.classes
90 # Open specified path or stdin
92 var path
= opt_output
.value
94 if path
.file_extension
!= "nit" then
95 print_error
"Warning: output file path does not end with '.nit'"
98 file
= new FileWriter.open
(path
)
105 # File generated by objcwrapper with the following command:
106 # {{{program_name}}} {{{args.join(" ")}}}
110 file
.write
"import cocoa::foundation\n"
111 for classe
in classes
do
112 write_class
(classe
, file
)
115 if path
!= null then file
.close
118 private fun write_class
(classe
: ObjcClass, file
: Writer)
120 # FIXME remove the redef when the base lib is generated by objcwrapper
122 if classe
.name
== "NSObject" then r
= "redef "
127 {{{r}}}extern class {{{classe.name}}} in "ObjC" `{ {{{classe.name}}} * `}
131 for super_name
in classe
.super_names
do file
.write
"""
132 super {{{super_name}}}
134 if classe
.super_names
.is_empty
and classe
.name
!= "NSObject" then file
.write
"""
138 # Constructor or constructors
139 write_constructors
(classe
, file
)
142 for attribute
in classe
.attributes
do
143 write_attribute
(attribute
, file
)
146 # Instance methods '-'
147 for method
in classe
.methods
do
148 if not model
.knows_all_types
(method
) then method
.is_commented
= true
150 if not opt_init_as_methods
.value
and method
.is_init
then continue
151 if method
.is_class_property
then continue
153 write_method_signature
(method
, file
)
154 write_objc_method_call
(method
, file
)
162 for method
in classe
.methods
do
163 if not method
.is_class_property
then continue
165 write_method_signature
(method
, file
)
166 write_objc_method_call
(method
, file
)
170 private fun write_constructors
(classe
: ObjcClass, file
: Writer)
172 if opt_init_as_methods
.value
then
173 # A single constructor for `alloc`
177 return [{{{classe.name}}} alloc];
183 # A constructor per `init...` method
184 for method
in classe
.methods
do
185 if not method
.is_init
then continue
187 if not model
.knows_all_types
(method
) then method
.is_commented
= true
189 write_method_signature
(method
, file
)
191 write_objc_init_call
(classe
.name
, method
, file
)
195 private fun write_attribute
(attribute
: ObjcAttribute, file
: Writer)
197 if not model
.knows_type
(attribute
.return_type
) then attribute
.is_commented
= true
199 write_attribute_getter
(attribute
, file
)
200 # TODO write_attribute_setter if there is no `readonly` annotation
203 private fun write_attribute_getter
(attribute
: ObjcAttribute, file
: Writer)
205 var nit_attr_name
= attribute
.name
.to_snake_case
206 var nit_attr_type
= attribute
.return_type
.objc_to_nit_type
208 var c
= attribute
.comment_str
213 {{{c}}} fun {{{nit_attr_name}}}: {{{nit_attr_type}}} in "ObjC" `{
214 {{{c}}} return [self {{{attribute.name}}}];
219 private fun write_attribute_setter
(attribute
: ObjcAttribute, file
: Writer)
221 var nit_attr_name
= attribute
.name
.to_snake_case
222 var nit_attr_type
= attribute
.return_type
.objc_to_nit_type
224 var c
= attribute
.comment_str
229 {{{c}}} fun {{{nit_attr_name}}}=(value: {{{nit_attr_type}}}) in "ObjC" `{
230 {{{c}}} return self.{{{attribute.name}}} = value;
235 private fun write_method_signature
(method
: ObjcMethod, file
: Writer)
237 var c
= method
.comment_str
239 # Build Nit method name
241 for param
in method
.params
do
242 name
+= param
.name
[0].to_upper
.to_s
+ param
.name
.substring_from
(1)
244 name
= name
.to_snake_case
246 if name
== "init" then name
= ""
248 name
= name
.to_nit_name
(property
=true, pointer
=true)
250 # If class method, prefix with class name
251 if method
.is_class_property
then name
= "{method.objc_class.name.to_snake_case}_{name}"
254 var fun_keyword
= "fun"
255 if not opt_init_as_methods
.value
and method
.is_init
then
260 var params
= new Array[String]
261 for param
in method
.params
do
262 if param
.is_single
then break
263 params
.add
"{param.nit_variable_name}: {param.return_type.objc_to_nit_type}"
266 var params_with_par
= ""
267 if params
.not_empty
then params_with_par
= "({params.join(", ")})"
271 if method
.return_type
!= "void" and fun_keyword
!= "new" then
272 ret
= ": {method.return_type.objc_to_nit_type}"
278 {{{c}}}{{{fun_keyword}}} {{{name}}}{{{params_with_par}}}{{{ret}}} in "ObjC" `{
282 # Write a combined call to alloc and to a constructor/method
283 private fun write_objc_init_call
(class_name
: String, method
: ObjcMethod, file
: Writer)
285 # Method name and other params
286 var params
= new Array[String]
287 for param
in method
.params
do
288 if not param
.is_single
then
289 params
.add
"{param.name}: {param.nit_variable_name}"
290 else params
.add param
.name
293 var c
= method
.comment_str
296 {{{c}}} return [[{{{class_name}}} alloc] {{{params.join(" ")}}}];
301 private fun write_objc_method_call
(method
: ObjcMethod, file
: Writer)
303 # Is there a value to return?
305 if method
.return_type
!= "void" then ret
= "return "
307 # Method name and other params
308 var params
= new Array[String]
309 for param
in method
.params
do
310 if not param
.is_single
then
311 params
.add
"{param.name}: {param.nit_variable_name}"
312 else params
.add param
.name
315 # Receiver, instance or class
317 if method
.is_class_property
then recv
= method
.objc_class
.name
319 var c
= method
.comment_str
322 {{{c}}} {{{ret}}}[{{{recv}}} {{{params.join(" ")}}}];
329 # Nit equivalent to this type
330 private fun objc_to_nit_type
: String
332 var types
= sys
.objc_to_nit_types
334 if types
.has_key
(self) then
341 # Convert to a safe Nit name for a `property`, a property in a subclass of `pointer` or a variable
342 private fun to_nit_name
(property
, pointer
: nullable Bool): String
345 name
= name
.to_snake_case
347 while not name
.is_empty
and name
.chars
.first
== '_' do name
= name
.substring_from
(1)
349 if keywords
.has
(name
) then name
= name
+ "0"
351 if property
== true then
352 if methods_in_object
.has
(name
) then name
= name
+ "0"
353 if pointer
== true and methods_in_pointer
.has
(name
) then name
= name
+ "0"
360 redef class ObjcProperty
361 private fun comment_str
: String do if is_commented
then
365 # Full documentation to be generated for the Nit code
366 private fun doc
: String is abstract
369 redef class ObjcMethod
370 private fun indent
: String do return if is_class_property
then "" else "\t"
372 redef fun comment_str
do return indent
+ super
376 var recv
= if is_class_property
then objc_class
.name
else "self"
377 return "{indent}# Wraps: `[{recv} {params.join(" ")}]`"
381 redef class ObjcAttribute
382 redef fun doc
do return "\t# Wraps: `{objc_class.name}.{name}`"
385 redef class ObjcParam
386 # `variable_name` mangled for the Nit language
387 private fun nit_variable_name
: String do return variable_name
.to_nit_name