673e306b90e415db618c787230281e6846dcc8b6
[nit.git] / contrib / objcwrapper / src / objc_generator.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 # Code generation
16 module objc_generator
17
18 import objc_model
19
20 class CodeGenerator
21 # Merge the calls to `alloc` and `init...` in a single constructor?
22 #
23 # If `true`, also the default behavior, initializing an extern Objective-C object looks like:
24 # ~~~nitish
25 # var o = new NSArray.init_with_array(some_other_array)
26 # ~~~
27 #
28 # If `false`, the object must first be allocated and then initialized.
29 # This is closer to the Objective-C behavior:
30 # ~~~nitish
31 # var o = new NSArray
32 # o.init_with_array(some_other_array)
33 # ~~~
34 var init_with_alloc = true is writable
35
36 fun generator(classes: Array[nullable ObjcClass]) do
37 for classe in classes do
38 var file = new FileWriter.open(classe.name + ".nit")
39 nit_class_generator(classe, file, init_with_alloc)
40 file.close
41 end
42 end
43
44 fun type_convertor(type_word: String): String do
45 var types = new HashMap[String, String]
46 types["char"] = "Byte"
47 types["short"] = "Int"
48 types["short int"] = "Int"
49 types["int"] = "Int"
50 types["long"] = "Int"
51 types["long int"] = "Int"
52 types["long long"] = "Int"
53 types["long long int"] = "Int"
54 types["float"] = "Float"
55 types["double"] = "Float"
56 types["long double"] = "Float"
57
58 types["NSUInteger"] = "Int"
59 types["BOOL"] = "Bool"
60 types["id"] = "NSObject"
61
62 if types.has_key(type_word) then
63 return types[type_word]
64 else
65 return type_word
66 end
67 end
68
69 fun nit_class_generator(classe: nullable ObjcClass, file: FileWriter, init_with_alloc: Bool) do
70 var commented_methods = new Array[ObjcMethod]
71 file.write "import cocoa::foundation\n\n"
72 file.write("extern class " + classe.name + """ in "ObjC" `{ """ + classe.name + """ * `}\n""")
73 for super_name in classe.super_names do
74 file.write(""" super """ + super_name + "\n")
75 end
76 if classe.super_names.is_empty then file.write(""" super NSObject\n""")
77 new_nit_generator(classe, file, init_with_alloc)
78 file.write("\n")
79 for attribute in classe.attributes do
80 nit_attribute_generator(attribute, file)
81 end
82 for method in classe.methods do
83 if method.is_commented then
84 commented_methods.add(method)
85 else
86 if init_with_alloc and method.params.first.name.has("init") then continue
87 file.write(""" """)
88 nit_method_generator(method, file, init_with_alloc)
89 file.write(""" in "ObjC" `{\n """)
90 objc_method_generator(method, file)
91 file.write(""" `}""")
92 if method != classe.methods.last then file.write("\n\n")
93 end
94 end
95 for commented_method in commented_methods do
96 if commented_method == commented_methods.first then file.write("\n")
97 file.write(""" #""")
98 nit_method_generator(commented_method, file, init_with_alloc)
99 if commented_method != commented_methods.last then file.write("\n")
100 end
101 file.write "\nend\n"
102 end
103
104 fun new_nit_generator(classe: nullable ObjcClass, file: FileWriter, init_with_alloc: Bool) do
105 if init_with_alloc then
106 for method in classe.methods do
107 if method.params.first.name.has("init") and not method.is_commented then
108 file.write """\n """
109 if method.params.first.name == "init" then
110 file.write "new"
111 else
112 nit_method_generator(method, file, init_with_alloc)
113 end
114 file.write """ in "ObjC" `{\n"""
115 new_alloc_init_objc_generator(classe.name, method, file)
116 file.write """ `}\n"""
117 end
118 end
119 else
120 file.write """\n new in "ObjC"`{\n"""
121 new_objc_generator(classe, file)
122 file.write """ `}\n"""
123 end
124 end
125
126 fun nit_attribute_generator(attribute: ObjcAttribute, file: FileWriter) do
127 nit_attribute_setter_generator(attribute, file)
128 file.write "\n"
129 end
130
131 fun nit_attribute_setter_generator(attribute: ObjcAttribute, file: FileWriter) do
132 file.write(""" fun """ + attribute.name.to_snake_case + ": " + type_convertor(attribute.return_type))
133 file.write """ in "ObjC" `{\n"""
134 objc_attribute_setter_generator(attribute, file)
135 file.write """ `}\n"""
136 end
137
138 fun nit_attribute_getter_generator(attribute: ObjcAttribute, file: FileWriter) do
139 file.write(""" fun """ + attribute.name.to_snake_case + "=(value: " + type_convertor(attribute.return_type) + ")")
140 file.write " in \"ObjC\" `\{\n"
141 objc_attribute_getter_generator(attribute, file)
142 file.write """ `}\n"""
143 end
144
145 fun nit_method_generator(method: ObjcMethod, file: FileWriter, init_with_alloc: Bool) do
146 var name = ""
147 for param in method.params do
148 name += param.name[0].to_upper.to_s + param.name.substring_from(1)
149 name = name.to_snake_case
150 end
151 if name.has("init") and init_with_alloc then
152 file.write "new "
153 else
154 if not init_with_alloc and name == "init" then name = "init_0"
155 file.write "fun "
156 end
157 file.write(name)
158 for param in method.params do
159 if param == method.params.first and not param.is_single then
160 file.write("(" + param.variable_name + ": " + type_convertor(param.return_type))
161 end
162 if param != method.params.first and not param.is_single then
163 file.write(", " + param.variable_name + ": " + type_convertor(param.return_type))
164 end
165 if param == method.params.last and not param.is_single then
166 file.write ")"
167 end
168 end
169 if method.return_type != "void" and not method.params.first.name.has("init") then file.write(": " + type_convertor(method.return_type))
170 end
171
172 fun new_alloc_init_objc_generator(classe_name: String, method: ObjcMethod, file: FileWriter) do
173 file.write(""" return [[""" + classe_name + " alloc] ")
174 for param in method.params do
175 if not param.is_single then
176 file.write(param.name + ":" + param.variable_name)
177 if not param == method.params.last then file.write(" ")
178 else
179 file.write param.name
180 end
181 end
182 file.write "];\n"
183 end
184
185 fun new_objc_generator(classe: nullable ObjcClass, file: FileWriter) do
186 file.write(""" return [""" + classe.name + " alloc];\n")
187 end
188
189 fun objc_attribute_setter_generator(attribute: ObjcAttribute, file: FileWriter) do
190 file.write(""" return [self """ + attribute.name + "];\n")
191 end
192
193 fun objc_attribute_getter_generator(attribute: ObjcAttribute, file: FileWriter) do
194 file.write(""" self.""" + attribute.name + " = value;\n")
195 end
196
197 fun objc_method_generator(method: ObjcMethod, file: FileWriter) do
198 if method.return_type != "void" then file.write("return ")
199 file.write "[self "
200 for param in method.params do
201 if not param.is_single then
202 file.write(param.name + ":" + param.variable_name)
203 if not param == method.params.last then file.write " "
204 else
205 file.write(param.name)
206 end
207 end
208 file.write "];\n"
209 end
210 end