-
Notifications
You must be signed in to change notification settings - Fork 4
/
SwiftingBridge.py
298 lines (218 loc) · 8.76 KB
/
SwiftingBridge.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
import sys
import subprocess
from os import devnull
out = None
f = None
definedTypes = []
def generateHeader(appPath, appName):
if appPath[-1:] == "/":
appPath = appPath[:-1]
if appPath[-4:] != ".app":
print "Invalid application path"
exit()
FNULL = open(devnull, "w")
sdef = subprocess.Popen(["sdef", appPath], stdout=subprocess.PIPE)
sdp = subprocess.Popen(["sdp", "-fh", "--basename", appName], stdin=sdef.stdout, stderr=FNULL)
sdp.communicate()
def isComment(line):
if line[:2] == "//":
out.write(line)
return True
elif line[:2] == "/*":
try:
out.write(line[line.index("/*"):line.index("*/")+2])
except ValueError:
out.write(line[line.index("/*"):])
while not "*/" in line:
line = f.readline()
out.write(line)
return True
def isImport(line):
if line[:7] == "#import":
frameworkName = line[9:line.index("/")]
out.write("import " + frameworkName + "\n")
return True
def isEnum(line):
if line[:4] == "enum":
enumName = line[5:line.index(" {")]
out.write("@objc enum " + enumName + ": ")
line = f.readline()
if "'" in line:
out.write("NSInteger ")
else:
print "Warning: Must give type to enum " + enumName
out.write("{\n")
while not "};" in line:
line = line.replace(",", "") #remove trailing commas
if "'" in line:
text = line[line.find("'") + 1:line.rfind("'")]
out.write("\tcase ")
out.write(line[1:line.find(" = ") + 3])
out.write("0x" + text.encode("hex"))
out.write(line[line.rfind("'")+1:])
else:
if " = " in line:
out.write("\tcase ")
out.write(line[1:])
else:
out.write(line)
line = f.readline()
out.write(line)
return True
def isInterface(line):
global definedTypes
if line[:10] == "@interface":
interfaceName = line[11:line[11:].index(" ") + 11]
out.write("@objc protocol " + interfaceName)
try:
super = line[line.index(":")+2:-1]
if super in definedTypes:
out.write(": " + super)
except ValueError:
super = None
out.write(" {\n")
if interfaceName in definedTypes:
print "Warning: protocol for type '" + interfaceName + "' defined multiple times"
definedTypes.append(interfaceName)
line = f.readline()
while not "@end" in line:
handleLine(line)
line = f.readline()
out.write("}\n")
if super != "SBApplication": #Assume everything else inherits from SBObject
out.write("extension SBObject: " + interfaceName + "{}\n")
else:
out.write("extension SBApplication: " + interfaceName + "{}\n")
return True
def parseType(type):
type = type.rstrip("* ")
type = escapedName(type)
typeDict = {
"id": "AnyObject",
"BOOL": "Bool",
"bool": "CBool",
"char": "CChar",
"signed char": "CChar",
"unsigned char": "CUnsignedChar",
"short": "CShort",
"unsigned short": "CUnsignedShort",
"int": "CInt",
"unsigned int": "CUnsignedInt",
"long": "CLong",
"unsigned long": "CUnsignedLong",
"long long": "CLongLong",
"unsigned long long": "CUnsignedLongLong",
"wchar_t": "CWideChar",
"char16_t": "CChar16",
"char32_t": "CChar32",
"float": "CFloat",
"double": "CDouble"
}
try:
return typeDict[type]
except KeyError:
return type
def escapedName(name):
#Objective-C escaped keywords end in _
#We must un-escape them first
if name[-1:] == "_":
name = name[:-1]
reservedWords = ["as", "in", "for", "class", "deinit", "enum", "extension", "func", "import", "init", "internal", "let", "operator", "private", "protocol", "public", "static", "struct", "subscript", "typealias", "var", "break", "case", "continue", "default", "do", "else", "fallthrough", "for", "if", "in", "return", "switch", "where", "while", "as", "dynamicType", "false", "is", "nil", "self", "Self", "super", "true", " associativity", "convenience", "dynamic", "didSet", "final", "get", "infix", "inout", "lazy", "left", "mutating", "none", "nonmutating", "optional", "override", "postfix", "precedence", "prefix", "Protocol", "required", "right", "set", "Type", "unowned", "weak", "willSet"]
if name in reservedWords:
name = "`" + name + "`"
return name
def isProperty(line):
if line[:9] == "@property":
typeIndex = 10
readonly = False
try:
attrs = line[line.index("(")+1:line.index(")")] #TODO: Split by ", "
typeIndex = line.index(")") + 2
#TODO: Anything but this
#not proud of this, but it works
attrs.index("readonly") #if this throws a ValueError, then the next line won't run
readonly = True
except ValueError:
attrs = None
nameIndex = line.rfind(" ")
name = line[nameIndex + 1:]
#handle pointers
if name[0] == "*":
name = name[1:]
type = line[line[:nameIndex].rfind(" ")+1:nameIndex]
type = parseType(type)
out.write("\toptional var " + name + ": " + type + " {get")
if readonly:
out.write("}\n")
else:
out.write(" set}\n")
return True
def isFunction(line):
if line[:3] == "- (":
returnTypeEnd = line.index(")")
returnType = line[3:returnTypeEnd]
returnType = parseType(returnType)
line = line[returnTypeEnd + 2:]
inparams = line.split(":")
funcName = inparams.pop(0)
outparams = []
for param in inparams:
type = param[param.index("(")+1:param.index(")")]
param = param[len(type)+2:]
type = parseType(type)
name = escapedName(param.split(" ")[0])
outparams.append((name, type))
out.write("\toptional func " + funcName + "(")
if len(outparams) > 0:
(param, type) = outparams.pop(0)
out.write(param + ": " + type) #handles that pesky comma issue
for (param, type) in outparams:
out.write(", " + param + ": " + type)
if returnType != "void":
out.write(") -> " + returnType + "\n")
else:
out.write(")\n")
return True
def isEmptyLine(line):
if line.rstrip() == "":
return True
def isJunkLine(line):
if line[:6] == "@class":
return True
if line[:12] == "typedef enum":
return True
def handleLine(line):
line = line.replace("'", '"') #replace single quotes with double quotes
line = line.lstrip() #remove whitespace
statements = line.split(";") #in case of statements (including code w/ comments) on single line
if not isComment(statements[0]):
if not isImport(statements[0]):
if not isEnum(statements[0]):
if not isInterface(statements[0]):
if not isProperty(statements[0]):
if not isFunction(statements[0]):
if not isEmptyLine(statements[0]):
if not isJunkLine(statements[0]):
print "Here there be dragons:"
print line
#handle any extra statements in same line
if len(statements) > 1:
handleLine(statements[1])
if len(sys.argv) <= 1:
appPath = raw_input("Application path: ")
appName = raw_input("Application name: ")
elif len(sys.argv) == 3:
appPath = sys.argv[1]
appName = sys.argv[2]
else:
print "Invalid number of arguments"
generateHeader(appPath, appName)
out = open(appName + ".swift", "w")
f = open(appName + ".h")
#this allows handleLine to process multiple lines in a single call
lastLine = f.readline()
while lastLine != "":
handleLine(lastLine)
lastLine = f.readline()
f.close()
out.close()