| 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 |