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