blob: 2cc4e9496e9fe47d7ff7a0b4db27a12ab4e1043f [file] [log] [blame]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001# a glorified C pre-processor parser
2
3import sys, re, string
4from utils import *
5from defaults import *
6
7debugTokens = False
8debugDirectiveTokenizer = False
9debugLineParsing = False
10debugCppExpr = False
11debugOptimIf01 = False
12
13#####################################################################################
14#####################################################################################
15##### #####
16##### C P P T O K E N S #####
17##### #####
18#####################################################################################
19#####################################################################################
20
21# the list of supported C-preprocessor tokens
22# plus a couple of C tokens as well
23tokEOF = "\0"
24tokLN = "\n"
25tokSTRINGIFY = "#"
26tokCONCAT = "##"
27tokLOGICAND = "&&"
28tokLOGICOR = "||"
29tokSHL = "<<"
30tokSHR = ">>"
31tokEQUAL = "=="
32tokNEQUAL = "!="
33tokLT = "<"
34tokLTE = "<="
35tokGT = ">"
36tokGTE = ">="
37tokELLIPSIS = "..."
38tokSPACE = " "
39tokDEFINED = "defined"
40tokLPAREN = "("
41tokRPAREN = ")"
42tokNOT = "!"
43tokPLUS = "+"
44tokMINUS = "-"
45tokMULTIPLY = "*"
46tokDIVIDE = "/"
47tokMODULUS = "%"
48tokBINAND = "&"
49tokBINOR = "|"
50tokBINXOR = "^"
51tokCOMMA = ","
52tokLBRACE = "{"
53tokRBRACE = "}"
54tokARROW = "->"
55tokINCREMENT = "++"
56tokDECREMENT = "--"
57tokNUMBER = "<number>"
58tokIDENT = "<ident>"
59tokSTRING = "<string>"
60
61class Token:
62 """a simple class to hold information about a given token.
63 each token has a position in the source code, as well as
64 an 'id' and a 'value'. the id is a string that identifies
65 the token's class, while the value is the string of the
66 original token itself.
67
68 for example, the tokenizer concatenates a series of spaces
69 and tabs as a single tokSPACE id, whose value if the original
70 spaces+tabs sequence."""
71
72 def __init__(self):
73 self.id = None
74 self.value = None
75 self.lineno = 0
76 self.colno = 0
77
78 def set(self,id,val=None):
79 self.id = id
80 if val:
81 self.value = val
82 else:
83 self.value = id
84 return None
85
86 def copyFrom(self,src):
87 self.id = src.id
88 self.value = src.value
89 self.lineno = src.lineno
90 self.colno = src.colno
91
92 def __repr__(self):
93 if self.id == tokIDENT:
94 return "(ident %s)" % self.value
95 if self.id == tokNUMBER:
96 return "(number %s)" % self.value
97 if self.id == tokSTRING:
98 return "(string '%s')" % self.value
99 if self.id == tokLN:
100 return "<LN>"
101 if self.id == tokEOF:
102 return "<EOF>"
103 if self.id == tokSPACE and self.value == "\\":
104 # this corresponds to a trailing \ that was transformed into a tokSPACE
105 return "<\\>"
106
107 return self.id
108
109 def __str__(self):
110 if self.id == tokIDENT:
111 return self.value
112 if self.id == tokNUMBER:
113 return self.value
114 if self.id == tokSTRING:
115 return self.value
116 if self.id == tokEOF:
117 return "<EOF>"
118 if self.id == tokSPACE:
119 if self.value == "\\": # trailing \
120 return "\\\n"
121 else:
122 return self.value
123
124 return self.id
125
126class BadExpectedToken(Exception):
127 def __init__(self,msg):
128 print msg
129
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800130
131#####################################################################################
132#####################################################################################
133##### #####
134##### C P P T O K E N I Z E R #####
135##### #####
136#####################################################################################
137#####################################################################################
138
139# list of long symbols, i.e. those that take more than one characters
140cppLongSymbols = [ tokCONCAT, tokLOGICAND, tokLOGICOR, tokSHL, tokSHR, tokELLIPSIS, tokEQUAL,\
141 tokNEQUAL, tokLTE, tokGTE, tokARROW, tokINCREMENT, tokDECREMENT ]
142
143class CppTokenizer:
144 """an abstract class used to convert some input text into a list
145 of tokens. real implementations follow and differ in the format
146 of the input text only"""
147
148 def __init__(self):
149 """initialize a new CppTokenizer object"""
150 self.eof = False # end of file reached ?
151 self.text = None # content of current line, with final \n stripped
152 self.line = 0 # number of current line
153 self.pos = 0 # current character position in current line
154 self.len = 0 # length of current line text
155 self.held = Token()
156
157 def setLineText(self,line):
158 """set the content of the (next) current line. should be called
159 by fillLineText() in derived classes"""
160 self.text = line
161 self.len = len(line)
162 self.pos = 0
163
164 def fillLineText(self):
165 """refresh the content of 'line' with a new line of input"""
166 # to be overriden
167 self.eof = True
168
169 def markPos(self,tok):
170 """mark the position of the current token in the source file"""
171 if self.eof or self.pos > self.len:
172 tok.lineno = self.line + 1
173 tok.colno = 0
174 else:
175 tok.lineno = self.line
176 tok.colno = self.pos
177
178 def peekChar(self):
179 """return the current token under the cursor without moving it"""
180 if self.eof:
181 return tokEOF
182
183 if self.pos > self.len:
184 self.pos = 0
185 self.line += 1
186 self.fillLineText()
187 if self.eof:
188 return tokEOF
189
190 if self.pos == self.len:
191 return tokLN
192 else:
193 return self.text[self.pos]
194
195 def peekNChar(self,n):
196 """try to peek the next n chars on the same line"""
197 if self.pos + n > self.len:
198 return None
199 return self.text[self.pos:self.pos+n]
200
201 def skipChar(self):
202 """increment the token cursor position"""
203 if not self.eof:
204 self.pos += 1
205
206 def skipNChars(self,n):
207 if self.pos + n <= self.len:
208 self.pos += n
209 else:
210 while n > 0:
211 self.skipChar()
212 n -= 1
213
214 def nextChar(self):
215 """retrieve the token at the current cursor position, then skip it"""
216 result = self.peekChar()
217 self.skipChar()
218 return result
219
220 def getEscape(self):
221 # try to get all characters after a backslash (\)
222 result = self.nextChar()
223 if result == "0":
224 # octal number ?
225 num = self.peekNChar(3)
226 if num != None:
227 isOctal = True
228 for d in num:
229 if not d in "01234567":
230 isOctal = False
231 break
232 if isOctal:
233 result += num
234 self.skipNChars(3)
235 elif result == "x" or result == "X":
236 # hex number ?
237 num = self.peekNChar(2)
238 if num != None:
239 isHex = True
240 for d in num:
241 if not d in "012345678abcdefABCDEF":
242 isHex = False
243 break
244 if isHex:
245 result += num
246 self.skipNChars(2)
247 elif result == "u" or result == "U":
248 # unicode char ?
249 num = self.peekNChar(4)
250 if num != None:
251 isHex = True
252 for d in num:
253 if not d in "012345678abcdefABCDEF":
254 isHex = False
255 break
256 if isHex:
257 result += num
258 self.skipNChars(4)
259
260 return result
261
262 def nextRealToken(self,tok):
263 """return next CPP token, used internally by nextToken()"""
264 c = self.nextChar()
265 if c == tokEOF or c == tokLN:
266 return tok.set(c)
267
268 if c == '/':
269 c = self.peekChar()
270 if c == '/': # C++ comment line
271 self.skipChar()
272 while 1:
273 c = self.nextChar()
274 if c == tokEOF or c == tokLN:
275 break
276 return tok.set(tokLN)
277 if c == '*': # C comment start
278 self.skipChar()
279 value = "/*"
280 prev_c = None
281 while 1:
282 c = self.nextChar()
283 if c == tokEOF:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800284 return tok.set(tokEOF,value)
285 if c == '/' and prev_c == '*':
286 break
287 prev_c = c
288 value += c
289
290 value += "/"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800291 return tok.set(tokSPACE,value)
292 c = '/'
293
294 if c.isspace():
295 while 1:
296 c2 = self.peekChar()
297 if c2 == tokLN or not c2.isspace():
298 break
299 c += c2
300 self.skipChar()
301 return tok.set(tokSPACE,c)
302
303 if c == '\\':
304 if debugTokens:
305 print "nextRealToken: \\ found, next token is '%s'" % repr(self.peekChar())
306 if self.peekChar() == tokLN: # trailing \
307 # eat the tokLN
308 self.skipChar()
309 # we replace a trailing \ by a tokSPACE whose value is
310 # simply "\\". this allows us to detect them later when
311 # needed.
312 return tok.set(tokSPACE,"\\")
313 else:
314 # treat as a single token here ?
315 c +=self.getEscape()
316 return tok.set(c)
317
318 if c == "'": # chars
319 c2 = self.nextChar()
320 c += c2
321 if c2 == '\\':
322 c += self.getEscape()
323
324 while 1:
325 c2 = self.nextChar()
326 if c2 == tokEOF:
327 break
328 c += c2
329 if c2 == "'":
330 break
331
332 return tok.set(tokSTRING, c)
333
334 if c == '"': # strings
335 quote = 0
336 while 1:
337 c2 = self.nextChar()
338 if c2 == tokEOF:
339 return tok.set(tokSTRING,c)
340
341 c += c2
342 if not quote:
343 if c2 == '"':
344 return tok.set(tokSTRING,c)
345 if c2 == "\\":
346 quote = 1
347 else:
348 quote = 0
349
350 if c >= "0" and c <= "9": # integers ?
351 while 1:
352 c2 = self.peekChar()
353 if c2 == tokLN or (not c2.isalnum() and c2 != "_"):
354 break
355 c += c2
356 self.skipChar()
357 return tok.set(tokNUMBER,c)
358
359 if c.isalnum() or c == "_": # identifiers ?
360 while 1:
361 c2 = self.peekChar()
362 if c2 == tokLN or (not c2.isalnum() and c2 != "_"):
363 break
364 c += c2
365 self.skipChar()
366 if c == tokDEFINED:
367 return tok.set(tokDEFINED)
368 else:
369 return tok.set(tokIDENT,c)
370
371 # check special symbols
372 for sk in cppLongSymbols:
373 if c == sk[0]:
374 sklen = len(sk[1:])
375 if self.pos + sklen <= self.len and \
376 self.text[self.pos:self.pos+sklen] == sk[1:]:
377 self.pos += sklen
378 return tok.set(sk)
379
380 return tok.set(c)
381
382 def nextToken(self,tok):
383 """return the next token from the input text. this function
384 really updates 'tok', and does not return a new one"""
385 self.markPos(tok)
386 self.nextRealToken(tok)
387
388 def getToken(self):
389 tok = Token()
390 self.nextToken(tok)
391 if debugTokens:
392 print "getTokens: %s" % repr(tok)
393 return tok
394
395 def toTokenList(self):
396 """convert the input text of a CppTokenizer into a direct
397 list of token objects. tokEOF is stripped from the result"""
398 result = []
399 while 1:
400 tok = Token()
401 self.nextToken(tok)
402 if tok.id == tokEOF:
403 break
404 result.append(tok)
405 return result
406
407class CppLineTokenizer(CppTokenizer):
408 """a CppTokenizer derived class that accepts a single line of text as input"""
409 def __init__(self,line,lineno=1):
410 CppTokenizer.__init__(self)
411 self.line = lineno
412 self.setLineText(line)
413
414
415class CppLinesTokenizer(CppTokenizer):
416 """a CppTokenizer derived class that accepts a list of texdt lines as input.
417 the lines must not have a trailing \n"""
418 def __init__(self,lines=[],lineno=1):
419 """initialize a CppLinesTokenizer. you can later add lines using addLines()"""
420 CppTokenizer.__init__(self)
421 self.line = lineno
422 self.lines = lines
423 self.index = 0
424 self.count = len(lines)
425
426 if self.count > 0:
427 self.fillLineText()
428 else:
429 self.eof = True
430
431 def addLine(self,line):
432 """add a line to a CppLinesTokenizer. this can be done after tokenization
433 happens"""
434 if self.count == 0:
435 self.setLineText(line)
436 self.index = 1
437 self.lines.append(line)
438 self.count += 1
439 self.eof = False
440
441 def fillLineText(self):
442 if self.index < self.count:
443 self.setLineText(self.lines[self.index])
444 self.index += 1
445 else:
446 self.eof = True
447
448
449class CppFileTokenizer(CppTokenizer):
450 def __init__(self,file,lineno=1):
451 CppTokenizer.__init__(self)
452 self.file = file
453 self.line = lineno
454
455 def fillLineText(self):
456 line = self.file.readline()
457 if len(line) > 0:
458 if line[-1] == '\n':
459 line = line[:-1]
460 if len(line) > 0 and line[-1] == "\r":
461 line = line[:-1]
462 self.setLineText(line)
463 else:
464 self.eof = True
465
466# Unit testing
467#
468class CppTokenizerTester:
469 """a class used to test CppTokenizer classes"""
470 def __init__(self,tokenizer=None):
471 self.tokenizer = tokenizer
472 self.token = Token()
473
474 def setTokenizer(self,tokenizer):
475 self.tokenizer = tokenizer
476
477 def expect(self,id):
478 self.tokenizer.nextToken(self.token)
479 tokid = self.token.id
480 if tokid == id:
481 return
482 if self.token.value == id and (tokid == tokIDENT or tokid == tokNUMBER):
483 return
484 raise BadExpectedToken, "### BAD TOKEN: '%s' expecting '%s'" % (self.token.id,id)
485
486 def expectToken(self,id,line,col):
487 self.expect(id)
488 if self.token.lineno != line:
489 raise BadExpectedToken, "### BAD LINENO: token '%s' got '%d' expecting '%d'" % (id,self.token.lineno,line)
490 if self.token.colno != col:
491 raise BadExpectedToken, "### BAD COLNO: '%d' expecting '%d'" % (self.token.colno,col)
492
493 def expectTokenVal(self,id,value,line,col):
494 self.expectToken(id,line,col)
495 if self.token.value != value:
496 raise BadExpectedToken, "### BAD VALUE: '%s' expecting '%s'" % (self.token.value,value)
497
498 def expectList(self,list):
499 for item in list:
500 self.expect(item)
501
502def test_CppTokenizer():
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800503 tester = CppTokenizerTester()
504
505 tester.setTokenizer( CppLineTokenizer("#an/example && (01923_xy)") )
506 tester.expectList( ["#", "an", "/", "example", tokSPACE, tokLOGICAND, tokSPACE, tokLPAREN, "01923_xy", \
507 tokRPAREN, tokLN, tokEOF] )
508
509 tester.setTokenizer( CppLineTokenizer("FOO(BAR) && defined(BAZ)") )
510 tester.expectList( ["FOO", tokLPAREN, "BAR", tokRPAREN, tokSPACE, tokLOGICAND, tokSPACE,
511 tokDEFINED, tokLPAREN, "BAZ", tokRPAREN, tokLN, tokEOF] )
512
513 tester.setTokenizer( CppLinesTokenizer( ["/*", "#", "*/"] ) )
514 tester.expectList( [ tokSPACE, tokLN, tokEOF ] )
515
516 tester.setTokenizer( CppLinesTokenizer( ["first", "second"] ) )
517 tester.expectList( [ "first", tokLN, "second", tokLN, tokEOF ] )
518
519 tester.setTokenizer( CppLinesTokenizer( ["first second", " third"] ) )
520 tester.expectToken( "first", 1, 0 )
521 tester.expectToken( tokSPACE, 1, 5 )
522 tester.expectToken( "second", 1, 6 )
523 tester.expectToken( tokLN, 1, 12 )
524 tester.expectToken( tokSPACE, 2, 0 )
525 tester.expectToken( "third", 2, 2 )
526
527 tester.setTokenizer( CppLinesTokenizer( [ "boo /* what the", "hell */" ] ) )
528 tester.expectList( [ "boo", tokSPACE ] )
529 tester.expectTokenVal( tokSPACE, "/* what the\nhell */", 1, 4 )
530 tester.expectList( [ tokLN, tokEOF ] )
531
532 tester.setTokenizer( CppLinesTokenizer( [ "an \\", " example" ] ) )
533 tester.expectToken( "an", 1, 0 )
534 tester.expectToken( tokSPACE, 1, 2 )
535 tester.expectTokenVal( tokSPACE, "\\", 1, 3 )
536 tester.expectToken( tokSPACE, 2, 0 )
537 tester.expectToken( "example", 2, 1 )
538 tester.expectToken( tokLN, 2, 8 )
539
540 return True
541
542
543#####################################################################################
544#####################################################################################
545##### #####
546##### C P P E X P R E S S I O N S #####
547##### #####
548#####################################################################################
549#####################################################################################
550
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800551class CppExpr:
552 """a class that models the condition of #if directives into
553 an expression tree. each node in the tree is of the form (op,arg) or (op,arg1,arg2)
554 where "op" is a string describing the operation"""
555
556 unaries = [ "!", "~" ]
557 binaries = [ "+", "-", "<", "<=", ">=", ">", "&&", "||", "*", "/", "%", "&", "|", "^", "<<", ">>", "==", "!=" ]
558 precedences = { "||": 1,
559 "&&": 2,
560 "|": 3,
561 "^": 4,
562 "&": 5,
563 "==":6, "!=":6,
564 "<":7, "<=":7, ">":7, ">=":7,
565 "<<":8, ">>":8,
566 "+":9, "-":9,
567 "*":10, "/":10, "%":10,
568 "!":11, "~":12
569 }
570
Elliott Hughes40596aa2013-11-05 14:54:29 -0800571 re_cpp_constant = re.compile(r"((\d|\w|_)+)")
572
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800573 def __init__(self, tokens):
574 """initialize a CppExpr. 'tokens' must be a CppToken list"""
575 self.tok = tokens
576 self.n = len(tokens)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800577 self.i = 0
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800578 if debugCppExpr:
579 print "CppExpr: trying to parse %s" % repr(tokens)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800580 self.expr = self.parseExpression(0)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800581 if debugCppExpr:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800582 print "CppExpr: got " + repr(self.expr)
583 if self.i != self.n:
584 print 'crap at end of input (%d != %d)' % (self.i, self.n)
585 raise
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800586
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800587
Elliott Hughes40596aa2013-11-05 14:54:29 -0800588 def throw(self, exception, msg):
589 if self.i < self.n:
590 tok = self.tok[self.i]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800591 print "%d:%d: %s" % (tok.lineno,tok.colno,msg)
592 else:
593 print "EOF: %s" % msg
Elliott Hughes40596aa2013-11-05 14:54:29 -0800594 raise exception(msg)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800595
Elliott Hughes40596aa2013-11-05 14:54:29 -0800596
597 def skip_spaces(self):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800598 """skip spaces in input token list"""
Elliott Hughes40596aa2013-11-05 14:54:29 -0800599 while self.i < self.n:
600 t = self.tok[self.i]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800601 if t.id != tokSPACE and t.id != tokLN:
602 break
Elliott Hughes40596aa2013-11-05 14:54:29 -0800603 self.i += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800604
Elliott Hughes40596aa2013-11-05 14:54:29 -0800605
606 def expectId(self, id):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800607 """check that a given token id is at the current position, then skip over it"""
Elliott Hughes40596aa2013-11-05 14:54:29 -0800608 self.skip_spaces()
609 if self.i >= self.n or self.tok[self.i].id != id:
610 self.throw(BadExpectedToken,self.i,"### expecting '%s' in expression, got '%s'" % (id, self.tok[self.i].id))
611 self.i += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800612
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800613
Elliott Hughes40596aa2013-11-05 14:54:29 -0800614 def expectIdent(self):
615 self.skip_spaces()
616 if self.i >= self.n or self.tok[self.i].id != tokIDENT:
617 self.throw(BadExpectedToken, self.i,"### expecting identifier in expression, got '%s'" % (id, self.tok[self.i].id))
618 self.i += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800619
Elliott Hughes40596aa2013-11-05 14:54:29 -0800620
621 def is_decimal(self):
622 v = self.tok[self.i].value[:]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800623 while len(v) > 0 and v[-1] in "ULul":
624 v = v[:-1]
625 for digit in v:
626 if not digit.isdigit():
627 return None
628
Elliott Hughes40596aa2013-11-05 14:54:29 -0800629 self.i += 1
630 return ("int", string.atoi(v))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800631
Elliott Hughes40596aa2013-11-05 14:54:29 -0800632
633 def is_hexadecimal(self):
634 v = self.tok[self.i].value[:]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800635 while len(v) > 0 and v[-1] in "ULul":
636 v = v[:-1]
637 if len(v) > 2 and (v[0:2] == "0x" or v[0:2] == "0X"):
638 for digit in v[2:]:
639 if not digit in "0123456789abcdefABCDEF":
640 return None
641
Elliott Hughes40596aa2013-11-05 14:54:29 -0800642 # for a hex expression tuple, the argument
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800643 # is the value as an integer
Elliott Hughes40596aa2013-11-05 14:54:29 -0800644 self.i += 1
645 return ("hex", int(v[2:], 16))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800646
647 return None
648
Elliott Hughes40596aa2013-11-05 14:54:29 -0800649
650 def is_integer(self):
651 if self.tok[self.i].id != tokNUMBER:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800652 return None
653
Elliott Hughes40596aa2013-11-05 14:54:29 -0800654 c = self.is_decimal()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800655 if c: return c
656
Elliott Hughes40596aa2013-11-05 14:54:29 -0800657 c = self.is_hexadecimal()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800658 if c: return c
659
660 return None
661
Elliott Hughes40596aa2013-11-05 14:54:29 -0800662
663 def is_number(self):
664 t = self.tok[self.i]
665 if t.id == tokMINUS and self.i+1 < self.n:
666 self.i += 1
667 c = self.is_integer()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800668 if c:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800669 op, val = c
670 return (op, -val)
671 if t.id == tokPLUS and self.i+1 < self.n:
672 c = self.is_integer()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800673 if c: return c
674
Elliott Hughes40596aa2013-11-05 14:54:29 -0800675 return self.is_integer()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800676
677
Elliott Hughes40596aa2013-11-05 14:54:29 -0800678 def is_defined(self):
679 t = self.tok[self.i]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800680 if t.id != tokDEFINED:
681 return None
682
683 # we have the defined keyword, check the rest
Elliott Hughes40596aa2013-11-05 14:54:29 -0800684 self.i += 1
685 self.skip_spaces()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800686 use_parens = 0
Elliott Hughes40596aa2013-11-05 14:54:29 -0800687 if self.i < self.n and self.tok[self.i].id == tokLPAREN:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800688 use_parens = 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800689 self.i += 1
690 self.skip_spaces()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800691
Elliott Hughes40596aa2013-11-05 14:54:29 -0800692 if self.i >= self.n:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800693 self.throw(CppConstantExpected,i,"### 'defined' must be followed by macro name or left paren")
694
Elliott Hughes40596aa2013-11-05 14:54:29 -0800695 t = self.tok[self.i]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800696 if t.id != tokIDENT:
697 self.throw(CppConstantExpected,i,"### 'defined' must be followed by macro name")
698
Elliott Hughes40596aa2013-11-05 14:54:29 -0800699 self.i += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800700 if use_parens:
Elliott Hughes40596aa2013-11-05 14:54:29 -0800701 self.expectId(tokRPAREN)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800702
Elliott Hughes40596aa2013-11-05 14:54:29 -0800703 return ("defined", t.value)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800704
705
Elliott Hughes40596aa2013-11-05 14:54:29 -0800706 def is_call_or_ident(self):
707 self.skip_spaces()
708 if self.i >= self.n:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800709 return None
710
Elliott Hughes40596aa2013-11-05 14:54:29 -0800711 t = self.tok[self.i]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800712 if t.id != tokIDENT:
713 return None
714
715 name = t.value
716
Elliott Hughes40596aa2013-11-05 14:54:29 -0800717 self.i += 1
718 self.skip_spaces()
719 if self.i >= self.n or self.tok[self.i].id != tokLPAREN:
720 return ("ident", name)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800721
722 params = []
723 depth = 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800724 self.i += 1
725 j = self.i
726 while self.i < self.n:
727 id = self.tok[self.i].id
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800728 if id == tokLPAREN:
729 depth += 1
730 elif depth == 1 and (id == tokCOMMA or id == tokRPAREN):
Elliott Hughes40596aa2013-11-05 14:54:29 -0800731 while j < self.i and self.tok[j].id == tokSPACE:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800732 j += 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800733 k = self.i
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800734 while k > j and self.tok[k-1].id == tokSPACE:
735 k -= 1
736 param = self.tok[j:k]
Elliott Hughes40596aa2013-11-05 14:54:29 -0800737 params.append(param)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800738 if id == tokRPAREN:
739 break
Elliott Hughes40596aa2013-11-05 14:54:29 -0800740 j = self.i+1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800741 elif id == tokRPAREN:
742 depth -= 1
Elliott Hughes40596aa2013-11-05 14:54:29 -0800743 self.i += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800744
Elliott Hughes40596aa2013-11-05 14:54:29 -0800745 if self.i >= self.n:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800746 return None
747
Elliott Hughes40596aa2013-11-05 14:54:29 -0800748 self.i += 1
749 return ("call", (name, params))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800750
751
Elliott Hughes40596aa2013-11-05 14:54:29 -0800752 # Implements the "precedence climbing" algorithm from http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm.
753 # The "classic" algorithm would be fine if we were using a tool to generate the parser, but we're not.
754 # Dijkstra's "shunting yard" algorithm hasn't been necessary yet.
755 def parseExpression(self, minPrecedence):
756 self.skip_spaces()
757 if self.i >= self.n:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800758 return None
759
Elliott Hughes40596aa2013-11-05 14:54:29 -0800760 node = self.parsePrimary()
761 while self.token() != None and self.isBinary(self.token()) and self.precedence(self.token()) >= minPrecedence:
762 op = self.token()
763 self.nextToken()
764 rhs = self.parseExpression(self.precedence(op) + 1)
765 node = (op.id, node, rhs)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800766
Elliott Hughes40596aa2013-11-05 14:54:29 -0800767 return node
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800768
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800769
Elliott Hughes40596aa2013-11-05 14:54:29 -0800770 def parsePrimary(self):
771 op = self.token()
772 if self.isUnary(op):
773 self.nextToken()
774 return (op.id, self.parseExpression(self.precedence(op)))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800775
Elliott Hughes40596aa2013-11-05 14:54:29 -0800776 primary = None
777 if op.id == tokLPAREN:
778 self.nextToken()
779 primary = self.parseExpression(0)
780 self.expectId(tokRPAREN)
781 elif op.id == tokNUMBER:
782 primary = self.is_number()
783 elif op.id == tokIDENT:
784 primary = self.is_call_or_ident()
785 elif op.id == tokDEFINED:
786 primary = self.is_defined()
787 else:
788 self.throw(BadExpectedToken, "didn't expect to see a %s in factor" % (self.tok[self.i].id))
789
790 self.skip_spaces()
791
792 return primary;
793
794
795 def isBinary(self, token):
796 return token.id in self.binaries
797
798
799 def isUnary(self, token):
800 return token.id in self.unaries
801
802
803 def precedence(self, token):
804 return self.precedences.get(token.id)
805
806
807 def token(self):
808 if self.i >= self.n:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800809 return None
Elliott Hughes40596aa2013-11-05 14:54:29 -0800810 return self.tok[self.i]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800811
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800812
Elliott Hughes40596aa2013-11-05 14:54:29 -0800813 def nextToken(self):
814 self.i += 1
815 self.skip_spaces()
816 if self.i >= self.n:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800817 return None
Elliott Hughes40596aa2013-11-05 14:54:29 -0800818 return self.tok[self.i]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800819
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800820
Elliott Hughes40596aa2013-11-05 14:54:29 -0800821 def dump_node(self, e):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800822 op = e[0]
823 line = "(" + op
824 if op == "int":
825 line += " %d)" % e[1]
826 elif op == "hex":
827 line += " 0x%x)" % e[1]
828 elif op == "ident":
829 line += " %s)" % e[1]
830 elif op == "defined":
831 line += " %s)" % e[1]
832 elif op == "call":
833 arg = e[1]
834 line += " %s [" % arg[0]
835 prefix = ""
836 for param in arg[1]:
837 par = ""
838 for tok in param:
839 par += str(tok)
840 line += "%s%s" % (prefix, par)
841 prefix = ","
842 line += "])"
843 elif op in CppExpr.unaries:
844 line += " %s)" % self.dump_node(e[1])
845 elif op in CppExpr.binaries:
846 line += " %s %s)" % (self.dump_node(e[1]), self.dump_node(e[2]))
847 else:
848 line += " ?%s)" % repr(e[1])
849
850 return line
851
852 def __repr__(self):
853 return self.dump_node(self.expr)
854
Elliott Hughes40596aa2013-11-05 14:54:29 -0800855 def source_node(self, e):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800856 op = e[0]
857 if op == "int":
858 return "%d" % e[1]
859 if op == "hex":
860 return "0x%x" % e[1]
861 if op == "ident":
862 # XXX: should try to expand
863 return e[1]
864 if op == "defined":
865 return "defined(%s)" % e[1]
866
867 prec = CppExpr.precedences.get(op,1000)
868 arg = e[1]
869 if op in CppExpr.unaries:
870 arg_src = self.source_node(arg)
871 arg_op = arg[0]
872 arg_prec = CppExpr.precedences.get(arg[0],1000)
873 if arg_prec < prec:
874 return "!(" + arg_src + ")"
875 else:
876 return "!" + arg_src
877 if op in CppExpr.binaries:
878 arg2 = e[2]
879 arg1_op = arg[0]
880 arg2_op = arg2[0]
881 arg1_src = self.source_node(arg)
882 arg2_src = self.source_node(arg2)
883 if CppExpr.precedences.get(arg1_op,1000) < prec:
884 arg1_src = "(%s)" % arg1_src
885 if CppExpr.precedences.get(arg2_op,1000) < prec:
886 arg2_src = "(%s)" % arg2_src
887
888 return "%s %s %s" % (arg1_src, op, arg2_src)
889 return "???"
890
891 def __str__(self):
892 return self.source_node(self.expr)
893
894 def int_node(self,e):
895 if e[0] == "int":
896 return e[1]
897 elif e[1] == "hex":
898 return int(e[1],16)
899 else:
900 return None
901
902 def toInt(self):
903 return self.int_node(self.expr)
904
Elliott Hughes40596aa2013-11-05 14:54:29 -0800905 def optimize_node(self, e, macros={}):
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800906 op = e[0]
907 if op == "defined":
Elliott Hughes40596aa2013-11-05 14:54:29 -0800908 op, name = e
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800909 if macros.has_key(name):
910 if macros[name] == kCppUndefinedMacro:
911 return ("int", 0)
912 else:
Elliott Hughesd3e64a32013-09-30 17:41:08 -0700913 try:
914 value = int(macros[name])
915 return ("int", value)
916 except:
917 return ("defined", macros[name])
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800918
919 if kernel_remove_config_macros and name.startswith("CONFIG_"):
920 return ("int", 0)
921
Elliott Hughes40596aa2013-11-05 14:54:29 -0800922 return e
923
924 elif op == "ident":
925 op, name = e
926 if macros.has_key(name):
927 try:
928 value = int(macros[name])
929 expanded = ("int", value)
930 except:
931 expanded = ("ident", macros[name])
932 return self.optimize_node(expanded, macros)
933 return e
934
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800935 elif op == "!":
936 op, v = e
937 v = self.optimize_node(v, macros)
938 if v[0] == "int":
939 if v[1] == 0:
940 return ("int", 1)
941 else:
942 return ("int", 0)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800943 return ('!', v)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800944
945 elif op == "&&":
946 op, l, r = e
947 l = self.optimize_node(l, macros)
948 r = self.optimize_node(r, macros)
949 li = self.int_node(l)
950 ri = self.int_node(r)
951 if li != None:
952 if li == 0:
953 return ("int", 0)
954 else:
955 return r
Elliott Hughes40596aa2013-11-05 14:54:29 -0800956 elif ri != None:
957 if ri == 0:
958 return ("int", 0)
959 else:
960 return l
961 return (op, l, r)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800962
963 elif op == "||":
964 op, l, r = e
965 l = self.optimize_node(l, macros)
966 r = self.optimize_node(r, macros)
967 li = self.int_node(l)
968 ri = self.int_node(r)
969 if li != None:
970 if li == 0:
971 return r
972 else:
973 return ("int", 1)
974 elif ri != None:
975 if ri == 0:
976 return l
977 else:
978 return ("int", 1)
Elliott Hughes40596aa2013-11-05 14:54:29 -0800979 return (op, l, r)
980
981 else:
982 return e
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800983
984 def optimize(self,macros={}):
Elliott Hughes40596aa2013-11-05 14:54:29 -0800985 self.expr = self.optimize_node(self.expr, macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800986
987 def is_equal_node(self,e1,e2):
988 if e1[0] != e2[0] or len(e1) != len(e2):
989 return False
990
991 op = e1[0]
992 if op == "int" or op == "hex" or op == "!" or op == "defined":
993 return e1[0] == e2[0]
994
995 return self.is_equal_node(e1[1],e2[1]) and self.is_equal_node(e1[2],e2[2])
996
997 def is_equal(self,other):
998 return self.is_equal_node(self.expr,other.expr)
999
1000def test_cpp_expr(expr, expected):
1001 e = CppExpr( CppLineTokenizer( expr ).toTokenList() )
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001002 s1 = repr(e)
1003 if s1 != expected:
Elliott Hughes40596aa2013-11-05 14:54:29 -08001004 print "[FAIL]: expression '%s' generates '%s', should be '%s'" % (expr, s1, expected)
1005 global failure_count
1006 failure_count += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001007
1008def test_cpp_expr_optim(expr, expected, macros={}):
1009 e = CppExpr( CppLineTokenizer( expr ).toTokenList() )
1010 e.optimize(macros)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001011 s1 = repr(e)
1012 if s1 != expected:
Elliott Hughes40596aa2013-11-05 14:54:29 -08001013 print "[FAIL]: optimized expression '%s' generates '%s' with macros %s, should be '%s'" % (expr, s1, macros, expected)
1014 global failure_count
1015 failure_count += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001016
1017def test_cpp_expr_source(expr, expected):
1018 e = CppExpr( CppLineTokenizer( expr ).toTokenList() )
1019 s1 = str(e)
1020 if s1 != expected:
Elliott Hughes40596aa2013-11-05 14:54:29 -08001021 print "[FAIL]: source expression '%s' generates '%s', should be '%s'" % (expr, s1, expected)
1022 global failure_count
1023 failure_count += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001024
1025def test_CppExpr():
Elliott Hughes40596aa2013-11-05 14:54:29 -08001026 test_cpp_expr("0", "(int 0)")
1027 test_cpp_expr("1", "(int 1)")
1028 test_cpp_expr("(0)", "(int 0)")
1029 test_cpp_expr("1 && 1", "(&& (int 1) (int 1))")
1030 test_cpp_expr("1 && 0", "(&& (int 1) (int 0))")
1031 test_cpp_expr("EXAMPLE", "(ident EXAMPLE)")
1032 test_cpp_expr("EXAMPLE - 3", "(- (ident EXAMPLE) (int 3))")
1033 test_cpp_expr("defined(EXAMPLE)", "(defined EXAMPLE)")
1034 test_cpp_expr("defined ( EXAMPLE ) ", "(defined EXAMPLE)")
1035 test_cpp_expr("!defined(EXAMPLE)", "(! (defined EXAMPLE))")
1036 test_cpp_expr("defined(ABC) || defined(BINGO)", "(|| (defined ABC) (defined BINGO))")
1037 test_cpp_expr("FOO(BAR)", "(call FOO [BAR])")
1038 test_cpp_expr("A == 1 || defined(B)", "(|| (== (ident A) (int 1)) (defined B))")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001039
Elliott Hughes40596aa2013-11-05 14:54:29 -08001040 test_cpp_expr_optim("0", "(int 0)")
1041 test_cpp_expr_optim("1", "(int 1)")
1042 test_cpp_expr_optim("1 && 1", "(int 1)")
1043 test_cpp_expr_optim("1 && 0", "(int 0)")
1044 test_cpp_expr_optim("0 && 1", "(int 0)")
1045 test_cpp_expr_optim("0 && 0", "(int 0)")
1046 test_cpp_expr_optim("1 || 1", "(int 1)")
1047 test_cpp_expr_optim("1 || 0", "(int 1)")
1048 test_cpp_expr_optim("0 || 1", "(int 1)")
1049 test_cpp_expr_optim("0 || 0", "(int 0)")
1050 test_cpp_expr_optim("A", "(ident A)")
1051 test_cpp_expr_optim("A", "(int 1)", { "A": 1 })
1052 test_cpp_expr_optim("A || B", "(int 1)", { "A": 1 })
1053 test_cpp_expr_optim("A || B", "(int 1)", { "B": 1 })
1054 test_cpp_expr_optim("A && B", "(ident B)", { "A": 1 })
1055 test_cpp_expr_optim("A && B", "(ident A)", { "B": 1 })
1056 test_cpp_expr_optim("A && B", "(&& (ident A) (ident B))")
1057 test_cpp_expr_optim("EXAMPLE", "(ident EXAMPLE)")
1058 test_cpp_expr_optim("EXAMPLE - 3", "(- (ident EXAMPLE) (int 3))")
1059 test_cpp_expr_optim("defined(EXAMPLE)", "(defined EXAMPLE)")
1060 test_cpp_expr_optim("defined(EXAMPLE)", "(defined XOWOE)", { "EXAMPLE": "XOWOE" })
1061 test_cpp_expr_optim("defined(EXAMPLE)", "(int 0)", { "EXAMPLE": kCppUndefinedMacro})
1062 test_cpp_expr_optim("!defined(EXAMPLE)", "(! (defined EXAMPLE))")
1063 test_cpp_expr_optim("!defined(EXAMPLE)", "(! (defined XOWOE))", { "EXAMPLE" : "XOWOE" })
1064 test_cpp_expr_optim("!defined(EXAMPLE)", "(int 1)", { "EXAMPLE" : kCppUndefinedMacro })
1065 test_cpp_expr_optim("defined(A) || defined(B)", "(|| (defined A) (defined B))")
1066 test_cpp_expr_optim("defined(A) || defined(B)", "(int 1)", { "A" : "1" })
1067 test_cpp_expr_optim("defined(A) || defined(B)", "(int 1)", { "B" : "1" })
1068 test_cpp_expr_optim("defined(A) || defined(B)", "(defined A)", { "B" : kCppUndefinedMacro })
1069 test_cpp_expr_optim("defined(A) || defined(B)", "(int 0)", { "A" : kCppUndefinedMacro, "B" : kCppUndefinedMacro })
1070 test_cpp_expr_optim("defined(A) && defined(B)", "(&& (defined A) (defined B))")
1071 test_cpp_expr_optim("defined(A) && defined(B)", "(defined B)", { "A" : "1" })
1072 test_cpp_expr_optim("defined(A) && defined(B)", "(defined A)", { "B" : "1" })
1073 test_cpp_expr_optim("defined(A) && defined(B)", "(int 0)", { "B" : kCppUndefinedMacro })
1074 test_cpp_expr_optim("defined(A) && defined(B)", "(int 0)", { "A" : kCppUndefinedMacro })
1075 test_cpp_expr_optim("A == 1 || defined(B)", "(|| (== (ident A) (int 1)) (defined B))" )
1076 test_cpp_expr_optim("defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)", "(|| (! (defined __GLIBC__)) (< (ident __GLIBC__) (int 2)))", { "__KERNEL__": kCppUndefinedMacro })
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001077
Elliott Hughes40596aa2013-11-05 14:54:29 -08001078 test_cpp_expr_source("0", "0")
1079 test_cpp_expr_source("1", "1")
1080 test_cpp_expr_source("1 && 1", "1 && 1")
1081 test_cpp_expr_source("1 && 0", "1 && 0")
1082 test_cpp_expr_source("0 && 1", "0 && 1")
1083 test_cpp_expr_source("0 && 0", "0 && 0")
1084 test_cpp_expr_source("1 || 1", "1 || 1")
1085 test_cpp_expr_source("1 || 0", "1 || 0")
1086 test_cpp_expr_source("0 || 1", "0 || 1")
1087 test_cpp_expr_source("0 || 0", "0 || 0")
1088 test_cpp_expr_source("EXAMPLE", "EXAMPLE")
1089 test_cpp_expr_source("EXAMPLE - 3", "EXAMPLE - 3")
1090 test_cpp_expr_source("defined(EXAMPLE)", "defined(EXAMPLE)")
1091 test_cpp_expr_source("defined EXAMPLE", "defined(EXAMPLE)")
1092 test_cpp_expr_source("A == 1 || defined(B)", "A == 1 || defined(B)")
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001093
1094
1095#####################################################################################
1096#####################################################################################
1097##### #####
1098##### C P P B L O C K #####
1099##### #####
1100#####################################################################################
1101#####################################################################################
1102
1103class Block:
1104 """a class used to model a block of input source text. there are two block types:
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001105 - directive blocks: contain the tokens of a single pre-processor directive (e.g. #if)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001106 - text blocks, contain the tokens of non-directive blocks
1107
1108 the cpp parser class below will transform an input source file into a list of Block
1109 objects (grouped in a BlockList object for convenience)"""
1110
1111 def __init__(self,tokens,directive=None,lineno=0):
1112 """initialize a new block, if 'directive' is None, this is a text block
1113 NOTE: this automatically converts '#ifdef MACRO' into '#if defined(MACRO)'
1114 and '#ifndef MACRO' into '#if !defined(MACRO)'"""
1115 if directive == "ifdef":
1116 tok = Token()
1117 tok.set(tokDEFINED)
1118 tokens = [ tok ] + tokens
1119 directive = "if"
1120
1121 elif directive == "ifndef":
1122 tok1 = Token()
1123 tok2 = Token()
1124 tok1.set(tokNOT)
1125 tok2.set(tokDEFINED)
1126 tokens = [ tok1, tok2 ] + tokens
1127 directive = "if"
1128
1129 self.tokens = tokens
1130 self.directive = directive
1131 if lineno > 0:
1132 self.lineno = lineno
1133 else:
1134 self.lineno = self.tokens[0].lineno
1135
1136 if self.isIf():
1137 self.expr = CppExpr( self.tokens )
1138
1139 def isDirective(self):
1140 """returns True iff this is a directive block"""
1141 return self.directive != None
1142
1143 def isConditional(self):
1144 """returns True iff this is a conditional directive block"""
1145 return self.directive in ["if","ifdef","ifndef","else","elif","endif"]
1146
1147 def isDefine(self):
1148 """returns the macro name in a #define directive, or None otherwise"""
1149 if self.directive != "define":
1150 return None
1151
1152 return self.tokens[0].value
1153
1154 def isIf(self):
1155 """returns True iff this is an #if-like directive block"""
1156 return self.directive in ["if","ifdef","ifndef","elif"]
1157
1158 def isInclude(self):
1159 """checks wether this is a #include directive. if true, then returns the
1160 corresponding file name (with brackets or double-qoutes). None otherwise"""
1161 if self.directive != "include":
1162 return None
1163
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001164 if self.tokens[0].id == tokSTRING:
1165 # a double-quote include, that's easy
1166 return self.tokens[0].value
1167
1168 # we only want the bracket part, not any comments or junk after it
1169 if self.tokens[0].id == "<":
1170 i = 0
1171 tok = self.tokens
1172 n = len(tok)
1173 while i < n and tok[i].id != ">":
1174 i += 1
1175
1176 if i >= n:
1177 return None
1178
1179 return string.join([ str(x) for x in tok[:i+1] ],"")
1180
1181 else:
1182 return None
1183
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001184 def removeWhiteSpace(self):
1185 # Remove trailing whitespace and empty lines
1186 # All whitespace is also contracted to a single space
1187 if self.directive != None:
1188 return
1189
1190 tokens = []
1191 line = 0 # index of line start
1192 space = -1 # index of first space, or -1
1193 ii = 0
1194 nn = len(self.tokens)
1195 while ii < nn:
1196 tok = self.tokens[ii]
1197
1198 # If we find a space, record its position if this is the first
1199 # one the line start or the previous character. Don't append
1200 # anything to tokens array yet though.
1201 if tok.id == tokSPACE:
1202 if space < 0:
1203 space = ii
1204 ii += 1
1205 continue
1206
1207 # If this is a line space, ignore the spaces we found previously
1208 # on the line, and remove empty lines.
1209 if tok.id == tokLN:
1210 old_line = line
1211 old_space = space
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001212 ii += 1
1213 line = ii
1214 space = -1
1215 if old_space == old_line: # line only contains spaces
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001216 continue
1217 if ii-1 == old_line: # line is empty
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001218 continue
1219 tokens.append(tok)
1220 continue
1221
1222 # Other token, append any space range if any, converting each
1223 # one to a single space character, then append the token.
1224 if space >= 0:
1225 jj = space
1226 space = -1
1227 while jj < ii:
1228 tok2 = self.tokens[jj]
1229 tok2.value = " "
1230 tokens.append(tok2)
1231 jj += 1
1232
1233 tokens.append(tok)
1234 ii += 1
1235
1236 self.tokens = tokens
1237
1238 def writeWithWarning(self,out,warning,left_count,repeat_count):
1239 # removeWhiteSpace() will sometimes creates non-directive blocks
1240 # without any tokens. These come from blocks that only contained
1241 # empty lines and spaces. They should not be printed in the final
1242 # output, and then should not be counted for this operation.
1243 #
1244 if not self.directive and self.tokens == []:
1245 return left_count
1246
1247 if self.directive:
Elliott Hughesc95eb572013-01-29 18:15:55 -08001248 out.write(str(self).rstrip() + "\n")
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001249 left_count -= 1
1250 if left_count == 0:
1251 out.write(warning)
1252 left_count = repeat_count
1253
1254 else:
1255 for tok in self.tokens:
1256 out.write(str(tok))
1257 if tok.id == tokLN:
1258 left_count -= 1
1259 if left_count == 0:
1260 out.write(warning)
1261 left_count = repeat_count
1262
1263 return left_count
1264
1265
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001266 def __repr__(self):
1267 """generate the representation of a given block"""
1268 if self.directive:
1269 result = "#%s " % self.directive
1270 if self.isIf():
1271 result += repr(self.expr)
1272 else:
1273 for tok in self.tokens:
1274 result += repr(tok)
1275 else:
1276 result = ""
1277 for tok in self.tokens:
1278 result += repr(tok)
1279
1280 return result
1281
1282 def __str__(self):
1283 """generate the string representation of a given block"""
1284 if self.directive:
1285 if self.directive == "if":
1286 # small optimization to re-generate #ifdef and #ifndef
1287 e = self.expr.expr
1288 op = e[0]
1289 if op == "defined":
1290 result = "#ifdef %s" % e[1]
1291 elif op == "!" and e[1][0] == "defined":
1292 result = "#ifndef %s" % e[1][1]
1293 else:
1294 result = "#if " + str(self.expr)
1295 else:
1296 result = "#%s" % self.directive
1297 if len(self.tokens):
1298 result += " "
1299 for tok in self.tokens:
1300 result += str(tok)
1301 else:
1302 result = ""
1303 for tok in self.tokens:
1304 result += str(tok)
1305
1306 return result
1307
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001308class BlockList:
1309 """a convenience class used to hold and process a list of blocks returned by
1310 the cpp parser"""
1311 def __init__(self,blocks):
1312 self.blocks = blocks
1313
1314 def __len__(self):
1315 return len(self.blocks)
1316
1317 def __getitem__(self,n):
1318 return self.blocks[n]
1319
1320 def __repr__(self):
1321 return repr(self.blocks)
1322
1323 def __str__(self):
1324 result = ""
1325 for b in self.blocks:
1326 result += str(b)
1327 if b.isDirective():
Elliott Hughesc95eb572013-01-29 18:15:55 -08001328 result = result.rstrip() + '\n'
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001329 return result
1330
1331 def optimizeIf01(self):
1332 """remove the code between #if 0 .. #endif in a BlockList"""
1333 self.blocks = optimize_if01(self.blocks)
1334
1335 def optimizeMacros(self, macros):
1336 """remove known defined and undefined macros from a BlockList"""
1337 for b in self.blocks:
1338 if b.isIf():
1339 b.expr.optimize(macros)
1340
1341 def removeMacroDefines(self,macros):
1342 """remove known macro definitions from a BlockList"""
1343 self.blocks = remove_macro_defines(self.blocks,macros)
1344
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001345 def removeWhiteSpace(self):
1346 for b in self.blocks:
1347 b.removeWhiteSpace()
1348
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001349 def optimizeAll(self,macros):
1350 self.optimizeMacros(macros)
1351 self.optimizeIf01()
1352 return
1353
1354 def findIncludes(self):
1355 """return the list of included files in a BlockList"""
1356 result = []
1357 for b in self.blocks:
1358 i = b.isInclude()
1359 if i:
1360 result.append(i)
1361
1362 return result
1363
1364
1365 def write(self,out):
1366 out.write(str(self))
1367
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001368 def writeWithWarning(self,out,warning,repeat_count):
1369 left_count = repeat_count
1370 for b in self.blocks:
1371 left_count = b.writeWithWarning(out,warning,left_count,repeat_count)
1372
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001373 def removeComments(self):
1374 for b in self.blocks:
1375 for tok in b.tokens:
1376 if tok.id == tokSPACE:
1377 tok.value = " "
1378
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001379 def removeVarsAndFuncs(self,knownStatics=set()):
1380 """remove all extern and static declarations corresponding
1381 to variable and function declarations. we only accept typedefs
1382 and enum/structs/union declarations.
1383
1384 however, we keep the definitions corresponding to the set
1385 of known static inline functions in the set 'knownStatics',
1386 which is useful for optimized byteorder swap functions and
1387 stuff like that.
1388 """
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001389 # state = 0 => normal (i.e. LN + spaces)
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001390 # state = 1 => typedef/struct encountered, ends with ";"
1391 # state = 2 => var declaration encountered, ends with ";"
1392 # state = 3 => func declaration encountered, ends with "}"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001393 state = 0
1394 depth = 0
1395 blocks2 = []
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001396 skipTokens = False
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001397 for b in self.blocks:
1398 if b.isDirective():
1399 blocks2.append(b)
1400 else:
1401 n = len(b.tokens)
1402 i = 0
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001403 if skipTokens:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001404 first = n
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001405 else:
1406 first = 0
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001407 while i < n:
1408 tok = b.tokens[i]
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001409 tokid = tok.id
1410 # If we are not looking for the start of a new
1411 # type/var/func, then skip over tokens until
1412 # we find our terminator, managing the depth of
1413 # accolades as we go.
1414 if state > 0:
1415 terminator = False
1416 if tokid == '{':
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001417 depth += 1
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001418 elif tokid == '}':
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001419 if depth > 0:
1420 depth -= 1
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001421 if (depth == 0) and (state == 3):
1422 terminator = True
1423 elif tokid == ';' and depth == 0:
1424 terminator = True
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001425
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001426 if terminator:
1427 # we found the terminator
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001428 state = 0
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001429 if skipTokens:
1430 skipTokens = False
1431 first = i+1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001432
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001433 i = i+1
1434 continue
1435
1436 # We are looking for the start of a new type/func/var
1437 # ignore whitespace
1438 if tokid in [tokLN, tokSPACE]:
1439 i = i+1
1440 continue
1441
1442 # Is it a new type definition, then start recording it
1443 if tok.value in [ 'struct', 'typedef', 'enum', 'union', '__extension__' ]:
David 'Digit' Turnerfc269312010-10-11 22:11:06 +02001444 state = 1
1445 i = i+1
1446 continue
1447
1448 # Is it a variable or function definition. If so, first
1449 # try to determine which type it is, and also extract
1450 # its name.
1451 #
1452 # We're going to parse the next tokens of the same block
1453 # until we find a semi-column or a left parenthesis.
1454 #
1455 # The semi-column corresponds to a variable definition,
1456 # the left-parenthesis to a function definition.
1457 #
1458 # We also assume that the var/func name is the last
1459 # identifier before the terminator.
1460 #
1461 j = i+1
1462 ident = ""
1463 while j < n:
1464 tokid = b.tokens[j].id
1465 if tokid == '(': # a function declaration
1466 state = 3
1467 break
1468 elif tokid == ';': # a variable declaration
1469 state = 2
1470 break
1471 if tokid == tokIDENT:
1472 ident = b.tokens[j].value
1473 j += 1
1474
1475 if j >= n:
1476 # This can only happen when the declaration
1477 # does not end on the current block (e.g. with
1478 # a directive mixed inside it.
1479 #
1480 # We will treat it as malformed because
1481 # it's very hard to recover from this case
1482 # without making our parser much more
1483 # complex.
1484 #
1485 #print "### skip unterminated static '%s'" % ident
1486 break
1487
1488 if ident in knownStatics:
1489 #print "### keep var/func '%s': %s" % (ident,repr(b.tokens[i:j]))
1490 pass
1491 else:
1492 # We're going to skip the tokens for this declaration
1493 #print "### skip variable /func'%s': %s" % (ident,repr(b.tokens[i:j]))
1494 if i > first:
1495 blocks2.append( Block(b.tokens[first:i]))
1496 skipTokens = True
1497 first = n
1498
1499 i = i+1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001500
1501 if i > first:
1502 #print "### final '%s'" % repr(b.tokens[first:i])
1503 blocks2.append( Block(b.tokens[first:i]) )
1504
1505 self.blocks = blocks2
1506
1507 def insertDisclaimer(self,disclaimer="/* auto-generated file, DO NOT EDIT */"):
1508 """insert your standard issue disclaimer that this is an
1509 auto-generated file, etc.."""
1510 tokens = CppLineTokenizer( disclaimer ).toTokenList()
1511 tokens = tokens[:-1] # remove trailing tokLN
1512 self.blocks = [ Block(tokens) ] + self.blocks
1513
Martin Storsjod32c8052010-12-08 11:38:14 +01001514 def replaceTokens(self,replacements=dict()):
1515 """replace tokens according to the given dict
1516 """
1517 for b in self.blocks:
1518 if not b.isDirective():
1519 for tok in b.tokens:
1520 if tok.id == tokIDENT:
1521 if tok.value in replacements:
1522 tok.value = replacements[tok.value]
1523
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001524class BlockParser:
1525 """a class used to convert an input source file into a BlockList object"""
1526
1527 def __init__(self,tokzer=None):
1528 """initialize a block parser. the input source is provided through a Tokenizer
1529 object"""
1530 self.reset(tokzer)
1531
1532 def reset(self,tokzer):
1533 self.state = 1
1534 self.tokzer = tokzer
1535
1536 def getBlocks(self,tokzer=None):
1537 """tokenize and parse the input source, return a BlockList object
1538 NOTE: empty and line-numbering directives are ignored and removed
1539 from the result. as a consequence, it is possible to have
1540 two successive text blocks in the result"""
1541 # state 0 => in source code
1542 # state 1 => in source code, after a LN
1543 # state 2 => in source code, after LN then some space
1544 state = 1
1545 lastLN = 0
1546 current = []
1547 blocks = []
1548
1549 if tokzer == None:
1550 tokzer = self.tokzer
1551
1552 while 1:
1553 tok = tokzer.getToken()
1554 if tok.id == tokEOF:
1555 break
1556
1557 if tok.id == tokLN:
1558 state = 1
1559 current.append(tok)
1560 lastLN = len(current)
1561
1562 elif tok.id == tokSPACE:
1563 if state == 1:
1564 state = 2
1565 current.append(tok)
1566
1567 elif tok.id == "#":
1568 if state > 0:
1569 # this is the start of a directive
1570
1571 if lastLN > 0:
1572 # record previous tokens as text block
1573 block = Block(current[:lastLN])
1574 blocks.append(block)
1575 lastLN = 0
1576
1577 current = []
1578
1579 # skip spaces after the #
1580 while 1:
1581 tok = tokzer.getToken()
1582 if tok.id != tokSPACE:
1583 break
1584
1585 if tok.id != tokIDENT:
1586 # empty or line-numbering, ignore it
1587 if tok.id != tokLN and tok.id != tokEOF:
1588 while 1:
1589 tok = tokzer.getToken()
1590 if tok.id == tokLN or tok.id == tokEOF:
1591 break
1592 continue
1593
1594 directive = tok.value
1595 lineno = tok.lineno
1596
1597 # skip spaces
1598 tok = tokzer.getToken()
1599 while tok.id == tokSPACE:
1600 tok = tokzer.getToken()
1601
1602 # then record tokens until LN
1603 dirtokens = []
1604 while tok.id != tokLN and tok.id != tokEOF:
1605 dirtokens.append(tok)
1606 tok = tokzer.getToken()
1607
1608 block = Block(dirtokens,directive,lineno)
1609 blocks.append(block)
1610 state = 1
1611
1612 else:
1613 state = 0
1614 current.append(tok)
1615
1616 if len(current) > 0:
1617 block = Block(current)
1618 blocks.append(block)
1619
1620 return BlockList(blocks)
1621
1622 def parse(self,tokzer):
1623 return self.getBlocks( tokzer )
1624
1625 def parseLines(self,lines):
1626 """parse a list of text lines into a BlockList object"""
1627 return self.getBlocks( CppLinesTokenizer(lines) )
1628
1629 def parseFile(self,path):
1630 """parse a file into a BlockList object"""
1631 file = open(path, "rt")
1632 result = self.getBlocks( CppFileTokenizer(file) )
1633 file.close()
1634 return result
1635
1636
1637def test_block_parsing(lines,expected):
1638 blocks = BlockParser().parse( CppLinesTokenizer(lines) )
1639 if len(blocks) != len(expected):
1640 raise BadExpectedToken, "parser.buildBlocks returned '%s' expecting '%s'" \
1641 % (str(blocks), repr(expected))
1642 for n in range(len(blocks)):
1643 if str(blocks[n]) != expected[n]:
1644 raise BadExpectedToken, "parser.buildBlocks()[%d] is '%s', expecting '%s'" \
1645 % (n, str(blocks[n]), expected[n])
1646 #for block in blocks:
1647 # print block
1648
1649def test_BlockParser():
1650 test_block_parsing(["#error hello"],["#error hello"])
1651 test_block_parsing([ "foo", "", "bar" ], [ "foo\n\nbar\n" ])
1652 test_block_parsing([ "foo", " # ", "bar" ], [ "foo\n","bar\n" ])
1653 test_block_parsing(\
1654 [ "foo", " # ", " # /* ahah */ if defined(__KERNEL__) ", "bar", "#endif" ],
1655 [ "foo\n", "#ifdef __KERNEL__", "bar\n", "#endif" ] )
1656
1657
1658#####################################################################################
1659#####################################################################################
1660##### #####
1661##### B L O C K L I S T O P T I M I Z A T I O N #####
1662##### #####
1663#####################################################################################
1664#####################################################################################
1665
1666def remove_macro_defines( blocks, excludedMacros=set() ):
1667 """remove macro definitions like #define <macroName> ...."""
1668 result = []
1669 for b in blocks:
1670 macroName = b.isDefine()
1671 if macroName == None or not macroName in excludedMacros:
1672 result.append(b)
1673
1674 return result
1675
1676def find_matching_endif( blocks, i ):
1677 n = len(blocks)
1678 depth = 1
1679 while i < n:
1680 if blocks[i].isDirective():
1681 dir = blocks[i].directive
1682 if dir in [ "if", "ifndef", "ifdef" ]:
1683 depth += 1
1684 elif depth == 1 and dir in [ "else", "elif" ]:
1685 return i
1686 elif dir == "endif":
1687 depth -= 1
1688 if depth == 0:
1689 return i
1690 i += 1
1691 return i
1692
1693def optimize_if01( blocks ):
1694 """remove the code between #if 0 .. #endif in a list of CppBlocks"""
1695 i = 0
1696 n = len(blocks)
1697 result = []
1698 while i < n:
1699 j = i
1700 while j < n and not blocks[j].isIf():
1701 j += 1
1702 if j > i:
1703 D2("appending lines %d to %d" % (blocks[i].lineno, blocks[j-1].lineno))
1704 result += blocks[i:j]
1705 if j >= n:
1706 break
1707 expr = blocks[j].expr
1708 r = expr.toInt()
1709 if r == None:
1710 result.append(blocks[j])
1711 i = j + 1
1712 continue
1713
1714 if r == 0:
1715 # if 0 => skip everything until the corresponding #endif
1716 j = find_matching_endif( blocks, j+1 )
1717 if j >= n:
1718 # unterminated #if 0, finish here
1719 break
1720 dir = blocks[j].directive
1721 if dir == "endif":
1722 D2("remove 'if 0' .. 'endif' (lines %d to %d)" % (blocks[i].lineno, blocks[j].lineno))
1723 i = j + 1
1724 elif dir == "else":
1725 # convert 'else' into 'if 1'
1726 D2("convert 'if 0' .. 'else' into 'if 1' (lines %d to %d)" % (blocks[i].lineno, blocks[j-1].lineno))
1727 blocks[j].directive = "if"
1728 blocks[j].expr = CppExpr( CppLineTokenizer("1").toTokenList() )
1729 i = j
1730 elif dir == "elif":
1731 # convert 'elif' into 'if'
1732 D2("convert 'if 0' .. 'elif' into 'if'")
1733 blocks[j].directive = "if"
1734 i = j
1735 continue
1736
1737 # if 1 => find corresponding endif and remove/transform them
1738 k = find_matching_endif( blocks, j+1 )
1739 if k >= n:
1740 # unterminated #if 1, finish here
1741 D2("unterminated 'if 1'")
1742 result += blocks[j+1:k]
1743 break
1744
1745 dir = blocks[k].directive
1746 if dir == "endif":
1747 D2("convert 'if 1' .. 'endif' (lines %d to %d)" % (blocks[j].lineno, blocks[k].lineno))
1748 result += optimize_if01(blocks[j+1:k])
1749 i = k+1
1750 elif dir == "else":
1751 # convert 'else' into 'if 0'
1752 D2("convert 'if 1' .. 'else' (lines %d to %d)" % (blocks[j].lineno, blocks[k].lineno))
1753 result += optimize_if01(blocks[j+1:k])
1754 blocks[k].directive = "if"
1755 blocks[k].expr = CppExpr( CppLineTokenizer("0").toTokenList() )
1756 i = k
1757 elif dir == "elif":
1758 # convert 'elif' into 'if 0'
1759 D2("convert 'if 1' .. 'elif' (lines %d to %d)" % (blocks[j].lineno, blocks[k].lineno))
1760 result += optimize_if01(blocks[j+1:k])
1761 blocks[k].expr = CppExpr( CppLineTokenizer("0").toTokenList() )
1762 i = k
1763 return result
1764
1765def test_optimizeAll():
1766 text = """\
1767#if 1
1768#define GOOD_1
1769#endif
1770#if 0
1771#define BAD_2
1772#define BAD_3
1773#endif
1774
1775#if 1
1776#define GOOD_2
1777#else
1778#define BAD_4
1779#endif
1780
1781#if 0
1782#define BAD_5
1783#else
1784#define GOOD_3
1785#endif
1786
Elliott Hughes40596aa2013-11-05 14:54:29 -08001787#if defined(__KERNEL__)
1788#define BAD_KERNEL
1789#endif
1790
1791#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
1792#define X
1793#endif
1794
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001795#if 0
1796#if 1
1797#define BAD_6
1798#endif
1799#endif\
1800"""
1801
1802 expected = """\
1803#define GOOD_1
1804
1805#define GOOD_2
1806
1807#define GOOD_3
1808
Elliott Hughes40596aa2013-11-05 14:54:29 -08001809
1810#if !defined(__GLIBC__) || __GLIBC__ < 2
1811#define X
1812#endif
1813
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001814"""
1815
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001816 out = StringOutput()
1817 lines = string.split(text, '\n')
1818 list = BlockParser().parse( CppLinesTokenizer(lines) )
1819 #D_setlevel(2)
1820 list.optimizeAll( {"__KERNEL__":kCppUndefinedMacro} )
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001821 list.write(out)
1822 if out.get() != expected:
Elliott Hughes40596aa2013-11-05 14:54:29 -08001823 print "[FAIL]: macro optimization failed\n"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001824 print "<<<< expecting '",
1825 print expected,
1826 print "'\n>>>> result '"
1827 print out.get(),
1828 print "'\n----"
Elliott Hughes40596aa2013-11-05 14:54:29 -08001829 global failure_count
1830 failure_count += 1
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001831
1832
Elliott Hughes40596aa2013-11-05 14:54:29 -08001833# -- Always run the unit tests.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001834
1835def runUnitTests():
1836 """run all unit tests for this program"""
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001837 test_CppTokenizer()
1838 test_CppExpr()
1839 test_optimizeAll()
1840 test_BlockParser()
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001841
Elliott Hughes40596aa2013-11-05 14:54:29 -08001842failure_count = 0
1843runUnitTests()
1844if failure_count != 0:
1845 sys.exit(1)