1
#!/usr/bin/ruby
2
3
class MethodDef
4
  def initialize(returnType, signature, optional)
5
    @returnType = returnType
6
    @signature = signature
7
    @optional = optional
8
  end
9
10
  attr_reader :returnType, :signature, :optional
11
  attr_writer :optional
12
end
13
14
class SignalDef
15
  def initialize(signature)
16
    @signature = signature
17
  end
18
19
  attr_reader :signature
20
end
21
22
class Parser
23
    def initialize(filename)
24
      @file = File.new filename, "r"
25
      @signatures = Hash.new
26
      parse
27
    end
28
29
    attr_reader :signatures
30
31
    private
32
    def addSignal(signature)
33
      unless @signatures.include? signature
34
        @signatures[signature] = SignalDef.new(signature)
35
      end
36
    end
37
38
    def addMethod(returnType, signature, optional)
39
      if @signatures.include? signature
40
        if returnType != ''
41
          if @signatures[signature].returnType == ''
42
            optional = false if @signatures[signature].optional == false
43
            @signatures[signature] = MethodDef.new(returnType, signature, optional)
44
          elsif @signatures[signature].returnType != returnType
45
            fail "same signature '#{signature}' but differing return types: #{returnType} and #{@signatures[signature].returnType}"
46
          end
47
        elsif not optional and @signatures[signature].optional
48
          @signatures[signature].optional = false
49
        end
50
      else
51
        @signatures[signature] = MethodDef.new(returnType, signature, optional)
52
      end
53
    end
54
55
    PARSER_RETURN_TYPE   = 0
56
    PARSER_RETURN_VAR    = 1
57
    PARSER_METHOD_NAME   = 2
58
    PARSER_ARGUMENT_TYPE = 3
59
    PARSER_ARGUMENT_VAR  = 4
60
61
    PARSER2_CLASSNAME     = 0
62
    PARSER2_METHODPREFIX  = 1
63
64
    PARSER3_QMETAOBJECT   = 0
65
    PARSER3_SCOPEDELIMIT  = 1
66
    PARSER3_INVOKEMETHOD  = 2
67
    PARSER3_OPENPARENTH   = 3
68
    PARSER3_BACKENDOBJ    = 4
69
    PARSER3_METHODNAME    = 5
70
    PARSER3_CONNECTION    = 6
71
    PARSER3_RET_OR_ARG    = 7
72
    PARSER3_RET_OPEN      = 8
73
    PARSER3_RET_TYPE      = 9
74
    PARSER3_RET_NAME      = 10
75
    PARSER3_RET_CLOSE     = 11
76
    PARSER3_ARG           = 12
77
    PARSER3_ARG_OPEN      = 13
78
    PARSER3_ARG_TYPE      = 14
79
    PARSER3_ARG_NAME      = 15
80
    PARSER3_ARG_CLOSE     = 16
81
    PARSER3_CLOSE         = 17
82
    PARSER3_DONE          = 18
83
84
    PARSER4_OPENING_PAREN                  = 0
85
    PARSER4_SENDER                         = 1
86
    PARSER4_PRIVATE_SENDER                 = 2
87
    PARSER4_COMMA_1                        = 3
88
    PARSER4_SIGNAL_MACRO                   = 4
89
    PARSER4_SIGNAL_OPENING_PAREN           = 5
90
    PARSER4_SIGNAL_SIGNATURE               = 6
91
    PARSER4_SIGNAL_SIGNATURE_OPENING_PAREN = 7
92
    PARSER4_SIGNAL_SIGNATURE_CONST         = 8
93
    PARSER4_SIGNAL_SIGNATURE_TYPE1         = 9
94
    PARSER4_SIGNAL_SIGNATURE_TYPE2_1       = 10
95
    PARSER4_SIGNAL_SIGNATURE_TYPE2_2       = 11
96
    PARSER4_SIGNAL_CLOSING_PAREN           = 12
97
98
    def parse
99
      inbackendcall = false
100
      innamedescriptioncall = false
101
      ininvokemethodcall = false
102
      invokemethodcallOnBackendObject = false
103
      inconnect = false
104
      optionalmethod = false
105
      lasttoken = ';'
106
      thistoken = ';'
107
      returnType = String.new
108
      signature = String.new
109
      parserstate = PARSER_RETURN_TYPE
110
      depth = 0
111
      tokenize do |token|
112
        #STDERR.puts token
113
        lasttoken = thistoken
114
        thistoken = token
115
        token = token[1..-1] if token[0,9] == "pBACKEND_"
116
        if token[0,8] == "BACKEND_"
117
          fail if innamedescriptioncall
118
          fail if inbackendcall
119
          fail if ininvokemethodcall
120
          fail if inconnect
121
          inbackendcall = true
122
          if token[8,3] != "GET" # skip return arg
123
            parserstate = PARSER_METHOD_NAME
124
            returnType = ''
125
          else
126
            parserstate = PARSER_RETURN_TYPE
127
          end
128
        elsif token == 'NAMEDESCRIPTIONFROMINDEX'
129
          fail if innamedescriptioncall
130
          fail if inbackendcall
131
          fail if ininvokemethodcall
132
          fail if inconnect
133
          innamedescriptioncall = true
134
          parserstate = PARSER2_CLASSNAME
135
        elsif token == 'QMetaObject'
136
          fail if innamedescriptioncall
137
          fail if inbackendcall
138
          fail if ininvokemethodcall
139
          fail if inconnect
140
          ininvokemethodcall = true
141
          parserstate = PARSER3_SCOPEDELIMIT
142
          optionalmethod = (lasttoken[-1,1] == '=' or lasttoken == '(' or lasttoken == '!') ? true : false
143
        elsif token == 'connect'
144
          fail if innamedescriptioncall
145
          fail if inbackendcall
146
          fail if ininvokemethodcall
147
          fail if inconnect
148
          inconnect = true
149
          parserstate = PARSER4_OPENING_PAREN
150
        elsif inconnect
151
          #puts "state = #{parserstate}, token = #{token}"
152
          lastparserstate = parserstate
153
          case parserstate
154
          when PARSER4_OPENING_PAREN
155
            parserstate = PARSER4_SENDER if token == '('
156
          when PARSER4_SENDER
157
            # d->m_backendObject or only m_backendObject
158
            parserstate = PARSER4_COMMA_1 if token == 'm_backendObject'
159
            parserstate = PARSER4_PRIVATE_SENDER if token == 'd'
160
          when PARSER4_PRIVATE_SENDER
161
            parserstate = PARSER4_SENDER if token == '->'
162
          when PARSER4_COMMA_1
163
            parserstate = PARSER4_SIGNAL_MACRO if token == ','
164
          when PARSER4_SIGNAL_MACRO
165
            parserstate = PARSER4_SIGNAL_OPENING_PAREN if token == 'SIGNAL'
166
          when PARSER4_SIGNAL_OPENING_PAREN
167
            parserstate = PARSER4_SIGNAL_SIGNATURE if token == '('
168
          when PARSER4_SIGNAL_SIGNATURE
169
            signature = token
170
            parserstate = PARSER4_SIGNAL_SIGNATURE_OPENING_PAREN
171
          when PARSER4_SIGNAL_SIGNATURE_OPENING_PAREN
172
            case token
173
            when '('
174
              signature += '('
175
              parserstate = PARSER4_SIGNAL_SIGNATURE_CONST
176
            when '()'
177
              signature += '()'
178
              parserstate = PARSER4_SIGNAL_CLOSING_PAREN
179
            end
180
          when PARSER4_SIGNAL_SIGNATURE_CONST
181
            case token
182
            when 'const'
183
              signature += 'const '
184
              parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE1
185
            when ')'
186
              signature += ')'
187
              parserstate = PARSER4_SIGNAL_CLOSING_PAREN
188
            else
189
              signature += token
190
              parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE2_1
191
            end
192
          when PARSER4_SIGNAL_SIGNATURE_TYPE1
193
            case token
194
            when 'const'
195
            when ')'
196
            else
197
              signature += token
198
              parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE2_1
199
            end
200
          when PARSER4_SIGNAL_SIGNATURE_TYPE2_1
201
            case token
202
            when ','
203
              signature += ', '
204
              parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE1
205
            when ')'
206
              signature += ')'
207
              parserstate = PARSER4_SIGNAL_CLOSING_PAREN
208
            else
209
              signature += token
210
              parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE2_2
211
            end
212
          when PARSER4_SIGNAL_SIGNATURE_TYPE2_2
213
            case token
214
            when ','
215
              signature += ', '
216
              parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE1
217
            when ')'
218
              signature += ')'
219
              parserstate = PARSER4_SIGNAL_CLOSING_PAREN
220
            else
221
              signature += token
222
              parserstate = PARSER4_SIGNAL_SIGNATURE_TYPE2_1
223
            end
224
          when PARSER4_SIGNAL_CLOSING_PAREN
225
            addSignal(signature) if token == ')'
226
          end
227
          if parserstate == lastparserstate
228
            inconnect = false
229
            signature = String.new
230
          end
231
        elsif ininvokemethodcall
232
          case parserstate
233
          when PARSER3_BACKENDOBJ
234
            if token == ','
235
              if invokemethodcallOnBackendObject
236
                parserstate += 1
237
              else
238
                ininvokemethodcall = false
239
              end
240
            elsif token =~ /backendObject/
241
              invokemethodcallOnBackendObject = true
242
            end
243
          when PARSER3_SCOPEDELIMIT
244
            if token == '::'
245
              parserstate += 1
246
            else
247
              ininvokemethodcall = false
248
            end
249
          when PARSER3_INVOKEMETHOD
250
            if token == 'invokeMethod'
251
              parserstate += 1
252
            else
253
              ininvokemethodcall = false
254
            end
255
          when PARSER3_OPENPARENTH
256
            fail if token != '('
257
            parserstate += 1
258
            invokemethodcallOnBackendObject = false
259
          when PARSER3_METHODNAME
260
            case token
261
            when ','
262
              signature += '('
263
              parserstate = PARSER3_CONNECTION
264
            else
265
              fail if signature.length > 0
266
              signature = token[1..-2]
267
            end
268
          when PARSER3_CONNECTION
269
            case token
270
            when ','
271
              parserstate = PARSER3_RET_OR_ARG
272
            when ')'
273
              parserstate = PARSER3_CLOSE
274
# the connection is optional
275
            when 'Q_RETURN_ARG'
276
              parserstate = PARSER3_RET_OPEN
277
            when 'Q_ARG'
278
              returnType = ''
279
              parserstate = PARSER3_ARG_OPEN
280
            end
281
          when PARSER3_RET_OR_ARG
282
            if token == 'Q_RETURN_ARG'
283
              parserstate = PARSER3_RET_OPEN
284
            elsif token == 'Q_ARG'
285
              returnType = ''
286
              parserstate = PARSER3_ARG_OPEN
287
            else
288
              fail "unexpected token '#{token}"
289
            end
290
          when PARSER3_RET_TYPE
291
            if token == ','
292
              parserstate += 1
293
            else
294
              if token == '*' or token == '&' or token == '<' or token == '>' or token == '::' or returnType.empty? or returnType[-1,1] == '<' or returnType[-2,2] == '::'
295
                returnType += token
296
              else
297
                returnType += ' ' + token
298
              end
299
            end
300
          when PARSER3_RET_NAME
301
            parserstate = PARSER3_RET_CLOSE if token == ')'
302
          when PARSER3_RET_CLOSE
303
            case token
304
            when ')'
305
              parserstate = PARSER3_CLOSE
306
            when ','
307
              parserstate = PARSER3_ARG
308
            end
309
          when PARSER3_ARG
310
            if token == 'Q_ARG'
311
              parserstate = PARSER3_ARG_OPEN
312
            else
313
              fail "unexpected token '#{token}"
314
            end
315
          when PARSER3_ARG_TYPE
316
            if token == ','
317
              parserstate += 1
318
            else
319
              signature += ' ' if signature[-1,1] =~ /\w/ and token[0,1] =~ /\w/
320
              signature += token
321
            end
322
          when PARSER3_ARG_NAME
323
            case token
324
            when '('
325
              depth += 1
326
            when ')'
327
              if depth == 0
328
                parserstate = PARSER3_ARG_CLOSE
329
              else
330
                depth -= 1
331
              end
332
            end
333
          when PARSER3_ARG_CLOSE
334
            case token
335
            when ','
336
              signature += ','
337
              parserstate = PARSER3_ARG
338
            when ')'
339
              parserstate = PARSER3_CLOSE
340
            else
341
              fail
342
            end
343
          when PARSER3_ARG_OPEN, PARSER3_RET_OPEN
344
            fail if token != '('
345
            parserstate += 1
346
          when PARSER3_CLOSE
347
            signature += ')'
348
            addMethod returnType, signature, optionalmethod
349
            ininvokemethodcall = false
350
            returnType = String.new
351
            signature = String.new
352
          end
353
        elsif innamedescriptioncall
354
          case parserstate
355
          when PARSER2_CLASSNAME
356
            parserstate = PARSER2_METHODPREFIX if token == ','
357
          when PARSER2_METHODPREFIX
358
            addMethod 'QSet<int>', token + 'Indexes()', false
359
            addMethod 'int', token + 'Name()', false
360
            addMethod 'int', token + 'Description()', false
361
            innamedescriptioncall = false
362
          end
363
        elsif inbackendcall
364
          next if token == '(' # skip (
365
          if token == ';'
366
            if signature.length > 0
367
              signature += ')'
368
              addMethod returnType, signature, false
369
              signature = String.new
370
              returnType = String.new
371
            end
372
            inbackendcall = false
373
          else
374
            if token == ','
375
              if parserstate == PARSER_ARGUMENT_VAR
376
                signature += ','
377
                parserstate = PARSER_ARGUMENT_TYPE
378
              else
379
                parserstate += 1
380
              end
381
              next
382
            end
383
            case parserstate
384
            when PARSER_RETURN_TYPE
385
              returnType += token
386
            when PARSER_RETURN_VAR
387
            when PARSER_METHOD_NAME
388
              next if token == ')'
389
              fail if token[0,1] != '"' or token[-1,1] != '"'
390
              fail if signature.length > 0
391
              signature = token[1..-2] + '('
392
            when PARSER_ARGUMENT_TYPE
393
              signature += token
394
            end
395
          end
396
        end
397
      end
398
    end
399
400
    def tokenize
401
      incomment = false
402
      instring = false
403
      laststring = ''
404
      linenum = 0
405
      @file.each_line do |line|
406
        linenum += 1
407
        line.strip!
408
        next if line[0..1] == "//"
409
        next if line[0,1] == "#" # ignore preprocessor statements
410
        line.split(/(\b|\s+)/).each do |token|
411
          #STDERR.puts "string: #{instring} comment: #{incomment} token: '#{token}'"
412
          if instring
413
            indexOfEscapedQuote = token.index '\\"'
414
            if indexOfEscapedQuote != nil
415
              laststring += token
416
              next
417
            end
418
            indexOfQuote = token.index '"'
419
            if indexOfQuote and indexOfQuote > 0
420
              fail if token[indexOfQuote-1,1] == '\\'
421
              laststring += token[0..indexOfQuote-1]
422
              token = token[indexOfQuote..-1]
423
            end
424
            if token[0,2] == '""'
425
              laststring += token[2..-1]
426
              next
427
            elsif token[0,1] == '"'
428
              if laststring[-1,1] == '\\'
429
                laststring[-1,1] = '"'
430
                laststring += token[1..-1]
431
                next
432
              end
433
              instring = false
434
              yield laststring + '"'
435
              token = token[1..-1]
436
            else
437
              laststring += token
438
              next
439
            end
440
          end
441
          token.strip!
442
          next if token.empty?
443
          if incomment
444
            incomment = false if token[0..1] == "*/"
445
            next
446
          else
447
            if token[0..1] == "/*"
448
              incomment = true
449
              next
450
            end
451
            break if token == "//"
452
          end
453
          doublequote = token.index '""'
454
          if doublequote != nil
455
            if doublequote > 0
456
              yield token[0,doublequote]
457
            end
458
            yield '""'
459
            if token.length > doublequote+2
460
              token = token[doublequote+2..-1]
461
            else
462
              next
463
            end
464
          end
465
          quote = token.index '"'
466
          if quote != nil
467
            laststring = token[quote..-1]
468
            instring = true
469
            if quote > 0
470
              token = token[0,quote]
471
            else
472
              next
473
            end
474
          end
475
          semicolon = token.index ';'
476
          if not semicolon
477
            tokenize2(token) { |i| yield i }
478
          elsif semicolon > 0
479
            tokenize2(token[0..semicolon-1]) { |i| yield i }
480
            yield ';'
481
            if token.length > semicolon + 1
482
              tokenize2(token[semicolon+1..-1]) { |i| yield i }
483
            end
484
          elsif (semicolon == 0 and token.length > 1)
485
            yield ';'
486
            tokenize2(token[1..-1]) { |i| yield i }
487
          else
488
            yield token # a single ;
489
          end
490
        end
491
      end
492
    end
493
494
    def tokenize2(token)
495
      if token.length > 1
496
        #STDERR.puts "long token: #{token}"
497
        while token[0,1] == '(' or token[0,1] == ')' or token[0,1] == "'" or token[0,1] == '&'
498
          yield token[0,1]
499
          token = token[1..-1]
500
          #STDERR.puts "less long token: #{token}"
501
        end
502
        return if token.empty?
503
        if token.length == 1
504
          yield token
505
          return
506
        elsif token[-1,1] == ',' or token[-1,1] == "'"
507
          yield token[0..-2]
508
          yield token[-1,1]
509
          return
510
        end
511
      end
512
      yield token
513
    end
514
end
515
516
p = Parser.new ARGV[0]
517
p.signatures.each do |signature,method|
518
  if method.class == SignalDef
519
    puts "addSignal(\"#{signature}\");"
520
  else
521
    if method.optional
522
      puts "addMethod(\"#{method.returnType}\", \"#{signature}\", true);"
523
    else
524
      puts "addMethod(\"#{method.returnType}\", \"#{signature}\");"
525
    end
526
  end
527
end