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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
|
#!/usr/bin/ruby
class MethodDef
def initialize(returnType, signature, optional)
@returnType = returnType
@signature = signature
@optional = optional
end
attr_reader :returnType, :signature, :optional
attr_writer :optional
end
class SignalDef
def initialize(signature)
@signature = signature
end
attr_reader :signature
end
class Parser
def initialize(filename)
@file = File.new filename, "r"
@signatures = Hash.new
parse
end
attr_reader :signatures
private
def addSignal(signature)
unless @signatures.include? signature
@signatures[signature] = SignalDef.new(signature)
end
end
def addMethod(returnType, signature, optional)
if @signatures.include? signature
if returnType != ''
if @signatures[signature].returnType == ''
optional = false if @signatures[signature].optional == false
@signatures[signature] = MethodDef.new(returnType, signature, optional)
elsif @signatures[signature].returnType != returnType
fail "same signature '#{signature}' but differing return types: #{returnType} and #{@signatures[signature].returnType}"
end
elsif not optional and @signatures[signature].optional
@signatures[signature].optional = false
end
else
@signatures[signature] = MethodDef.new(returnType, signature, optional)
end
end
PARSER_RETURN_TYPE = 0
PARSER_RETURN_VAR = 1
PARSER_METHOD_NAME = 2
PARSER_ARGUMENT_TYPE = 3
PARSER_ARGUMENT_VAR = 4
PARSER2_CLASSNAME = 0
PARSER2_METHODPREFIX = 1
PARSER3_QMETAOBJECT = 0
PARSER3_SCOPEDELIMIT = 1
PARSER3_INVOKEMETHOD = 2
PARSER3_OPENPARENTH = 3
PARSER3_BACKENDOBJ = 4
PARSER3_METHODNAME = 5
PARSER3_CONNECTION = 6
PARSER3_RET_OR_ARG = 7
PARSER3_RET_OPEN = 8
PARSER3_RET_TYPE = 9
PARSER3_RET_NAME = 10
PARSER3_RET_CLOSE = 11
PARSER3_ARG = 12
PARSER3_ARG_OPEN = 13
PARSER3_ARG_TYPE = 14
PARSER3_ARG_NAME = 15
PARSER3_ARG_CLOSE = 16
PARSER3_CLOSE = 17
PARSER3_DONE = 18
PARSER4_OPENING_PAREN = 0
PARSER4_SENDER = 1
PARSER4_PRIVATE_SENDER = 2
PARSER4_COMMA_1 = 3
PARSER4_SIGNAL_MACRO = 4
PARSER4_SIGNAL_OPENING_PAREN = 5
PARSER4_SIGNAL_SIGNATURE = 6
PARSER4_SIGNAL_SIGNATURE_OPENING_PAREN = 7
PARSER4_SIGNAL_SIGNATURE_CONST = 8
PARSER4_SIGNAL_SIGNATURE_TYPE1 = 9
PARSER4_SIGNAL_SIGNATURE_TYPE2_1 = 10
PARSER4_SIGNAL_SIGNATURE_TYPE2_2 = 11
PARSER4_SIGNAL_CLOSING_PAREN = 12
def parse
inbackendcall = false
innamedescriptioncall = false
ininvokemethodcall = false
invokemethodcallOnBackendObject = false
inconnect = false
optionalmethod = false
lasttoken = ';'
thistoken = ';'
returnType = String.new
signature = String.new
parserstate = PARSER_RETURN_TYPE
depth = 0
tokenize do |token|
#STDERR.puts token
lasttoken = thistoken
thistoken = token
token = token[1..-1] if token[0,9] == "pBACKEND_"
if token[0,8] == "BACKEND_"
fail if innamedescriptioncall
fail if inbackendcall
fail if ininvokemethodcall
fail if inconnect
inbackendcall = true
if token[8,3] != "GET" # skip return arg
parserstate = PARSER_METHOD_NAME
returnType = ''
else
parserstate = PARSER_RETURN_TYPE
end
elsif token == 'NAMEDESCRIPTIONFROMINDEX'
fail if innamedescriptioncall
fail if inbackendcall
fail if ininvokemethodcall
fail if inconnect
innamedescriptioncall = true
parserstate = PARSER2_CLASSNAME
elsif token == 'QMetaObject'
fail if innamedescriptioncall
fail if inbackendcall
fail if ininvokemethodcall
fail if inconnect
ininvokemethodcall = true
parserstate = PARSER3_SCOPEDELIMIT
optionalmethod = (lasttoken[-1,1] == '=' or lasttoken == '(' or lasttoken == '!') ? true : false
elsif token == 'connect'
fail if innamedescriptioncall
fail if inbackendcall
fail if ininvokemethodcall
fail if inconnect
inconnect = true
parserstate = PARSER4_OPENING_PAREN
elsif inconnect
#puts "state = #{parserstate}, token = #{token}"
lastparserstate = parserstate
case parserstate
when PARSER4_OPENING_PAREN
parserstate = PARSER4_SENDER if token == '('
when PARSER4_SENDER
# d->m_backendObject or only m_backendObject
parserstate = PARSER4_COMMA_1 if token == 'm_backendObject'
parserstate = PARSER4_PRIVATE_SENDER if token == 'd'
when PARSER4_PRIVATE_SENDER
parserstate = PARSER4_SENDER if token == '->'
when PARSER4_COMMA_1
parserstate = PARSER4_SIGNAL_MACRO if token == ','
when PARSER4_SIGNAL_MACRO
parserstate = PARSER4_SIGNAL_OPENING_PAREN if token == 'SIGNAL'
when PARSER4_SIGNAL_OPENING_PAREN
parserstate = PARSER4_SIGNAL_SIGNATURE if token == '('
when PARSER4_SIGNAL_SIGNATURE
signature = token
parserstate = PARSER4_SIGNAL_SIGNATURE_OPENING_PAREN
when PARSER4_SIGNAL_SIGNATURE_OPENING_PAREN
case token
when '('
signature += '('
parserstate = PARSER4_SIGNAL_SIGNATURE_CONST
when '()'
signature += '()'
parserstate = PARSER4_SIGNAL_CLOSING_PAREN
end
when PARSER4_SIGNAL_SIGNATURE_CONST
case token
when 'const'
signature += 'const '
parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE1
when ')'
signature += ')'
parserstate = PARSER4_SIGNAL_CLOSING_PAREN
else
signature += token
parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE2_1
end
when PARSER4_SIGNAL_SIGNATURE_TYPE1
case token
when 'const'
when ')'
else
signature += token
parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE2_1
end
when PARSER4_SIGNAL_SIGNATURE_TYPE2_1
case token
when ','
signature += ', '
parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE1
when ')'
signature += ')'
parserstate = PARSER4_SIGNAL_CLOSING_PAREN
else
signature += token
parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE2_2
end
when PARSER4_SIGNAL_SIGNATURE_TYPE2_2
case token
when ','
signature += ', '
parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE1
when ')'
signature += ')'
parserstate = PARSER4_SIGNAL_CLOSING_PAREN
else
signature += token
parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE2_1
end
when PARSER4_SIGNAL_CLOSING_PAREN
addSignal(signature) if token == ')'
end
if parserstate == lastparserstate
inconnect = false
signature = String.new
end
elsif ininvokemethodcall
case parserstate
when PARSER3_BACKENDOBJ
if token == ','
if invokemethodcallOnBackendObject
parserstate += 1
else
ininvokemethodcall = false
end
elsif token =~ /backendObject/
invokemethodcallOnBackendObject = true
end
when PARSER3_SCOPEDELIMIT
if token == '::'
parserstate += 1
else
ininvokemethodcall = false
end
when PARSER3_INVOKEMETHOD
if token == 'invokeMethod'
parserstate += 1
else
ininvokemethodcall = false
end
when PARSER3_OPENPARENTH
fail if token != '('
parserstate += 1
invokemethodcallOnBackendObject = false
when PARSER3_METHODNAME
case token
when ','
signature += '('
parserstate = PARSER3_CONNECTION
else
fail if signature.length > 0
signature = token[1..-2]
end
when PARSER3_CONNECTION
case token
when ','
parserstate = PARSER3_RET_OR_ARG
when ')'
parserstate = PARSER3_CLOSE
# the connection is optional
when 'Q_RETURN_ARG'
parserstate = PARSER3_RET_OPEN
when 'Q_ARG'
returnType = ''
parserstate = PARSER3_ARG_OPEN
end
when PARSER3_RET_OR_ARG
if token == 'Q_RETURN_ARG'
parserstate = PARSER3_RET_OPEN
elsif token == 'Q_ARG'
returnType = ''
parserstate = PARSER3_ARG_OPEN
else
fail "unexpected token '#{token}"
end
when PARSER3_RET_TYPE
if token == ','
parserstate += 1
else
if token == '*' or token == '&' or token == '<' or token == '>' or token == '::' or returnType.empty? or returnType[-1,1] == '<' or returnType[-2,2] == '::'
returnType += token
else
returnType += ' ' + token
end
end
when PARSER3_RET_NAME
parserstate = PARSER3_RET_CLOSE if token == ')'
when PARSER3_RET_CLOSE
case token
when ')'
parserstate = PARSER3_CLOSE
when ','
parserstate = PARSER3_ARG
end
when PARSER3_ARG
if token == 'Q_ARG'
parserstate = PARSER3_ARG_OPEN
else
fail "unexpected token '#{token}"
end
when PARSER3_ARG_TYPE
if token == ','
parserstate += 1
else
signature += ' ' if signature[-1,1] =~ /\w/ and token[0,1] =~ /\w/
signature += token
end
when PARSER3_ARG_NAME
case token
when '('
depth += 1
when ')'
if depth == 0
parserstate = PARSER3_ARG_CLOSE
else
depth -= 1
end
end
when PARSER3_ARG_CLOSE
case token
when ','
signature += ','
parserstate = PARSER3_ARG
when ')'
parserstate = PARSER3_CLOSE
else
fail
end
when PARSER3_ARG_OPEN, PARSER3_RET_OPEN
fail if token != '('
parserstate += 1
when PARSER3_CLOSE
signature += ')'
addMethod returnType, signature, optionalmethod
ininvokemethodcall = false
returnType = String.new
signature = String.new
end
elsif innamedescriptioncall
case parserstate
when PARSER2_CLASSNAME
parserstate = PARSER2_METHODPREFIX if token == ','
when PARSER2_METHODPREFIX
addMethod 'QSet<int>', token + 'Indexes()', false
addMethod 'int', token + 'Name()', false
addMethod 'int', token + 'Description()', false
innamedescriptioncall = false
end
elsif inbackendcall
next if token == '(' # skip (
if token == ';'
if signature.length > 0
signature += ')'
addMethod returnType, signature, false
signature = String.new
returnType = String.new
end
inbackendcall = false
else
if token == ','
if parserstate == PARSER_ARGUMENT_VAR
signature += ','
parserstate = PARSER_ARGUMENT_TYPE
else
parserstate += 1
end
next
end
case parserstate
when PARSER_RETURN_TYPE
returnType += token
when PARSER_RETURN_VAR
when PARSER_METHOD_NAME
next if token == ')'
fail if token[0,1] != '"' or token[-1,1] != '"'
fail if signature.length > 0
signature = token[1..-2] + '('
when PARSER_ARGUMENT_TYPE
signature += token
end
end
end
end
end
def tokenize
incomment = false
instring = false
laststring = ''
linenum = 0
@file.each_line do |line|
linenum += 1
line.strip!
next if line[0..1] == "//"
next if line[0,1] == "#" # ignore preprocessor statements
line.split(/(\b|\s+)/).each do |token|
#STDERR.puts "string: #{instring} comment: #{incomment} token: '#{token}'"
if instring
indexOfEscapedQuote = token.index '\\"'
if indexOfEscapedQuote != nil
laststring += token
next
end
indexOfQuote = token.index '"'
if indexOfQuote and indexOfQuote > 0
fail if token[indexOfQuote-1,1] == '\\'
laststring += token[0..indexOfQuote-1]
token = token[indexOfQuote..-1]
end
if token[0,2] == '""'
laststring += token[2..-1]
next
elsif token[0,1] == '"'
if laststring[-1,1] == '\\'
laststring[-1,1] = '"'
laststring += token[1..-1]
next
end
instring = false
yield laststring + '"'
token = token[1..-1]
else
laststring += token
next
end
end
token.strip!
next if token.empty?
if incomment
incomment = false if token[0..1] == "*/"
next
else
if token[0..1] == "/*"
incomment = true
next
end
break if token == "//"
end
doublequote = token.index '""'
if doublequote != nil
if doublequote > 0
yield token[0,doublequote]
end
yield '""'
if token.length > doublequote+2
token = token[doublequote+2..-1]
else
next
end
end
quote = token.index '"'
if quote != nil
laststring = token[quote..-1]
instring = true
if quote > 0
token = token[0,quote]
else
next
end
end
semicolon = token.index ';'
if not semicolon
tokenize2(token) { |i| yield i }
elsif semicolon > 0
tokenize2(token[0..semicolon-1]) { |i| yield i }
yield ';'
if token.length > semicolon + 1
tokenize2(token[semicolon+1..-1]) { |i| yield i }
end
elsif (semicolon == 0 and token.length > 1)
yield ';'
tokenize2(token[1..-1]) { |i| yield i }
else
yield token # a single ;
end
end
end
end
def tokenize2(token)
if token.length > 1
#STDERR.puts "long token: #{token}"
while token[0,1] == '(' or token[0,1] == ')' or token[0,1] == "'" or token[0,1] == '&'
yield token[0,1]
token = token[1..-1]
#STDERR.puts "less long token: #{token}"
end
return if token.empty?
if token.length == 1
yield token
return
elsif token[-1,1] == ',' or token[-1,1] == "'"
yield token[0..-2]
yield token[-1,1]
return
end
end
yield token
end
end
p = Parser.new ARGV[0]
p.signatures.each do |signature,method|
if method.class == SignalDef
puts "addSignal(\"#{signature}\");"
else
if method.optional
puts "addMethod(\"#{method.returnType}\", \"#{signature}\", true);"
else
puts "addMethod(\"#{method.returnType}\", \"#{signature}\");"
end
end
end
|