OLD | NEW |
| (Empty) |
1 # Copyright 2012 the V8 project authors. All rights reserved. | |
2 # Redistribution and use in source and binary forms, with or without | |
3 # modification, are permitted provided that the following conditions are | |
4 # met: | |
5 # | |
6 # * Redistributions of source code must retain the above copyright | |
7 # notice, this list of conditions and the following disclaimer. | |
8 # * Redistributions in binary form must reproduce the above | |
9 # copyright notice, this list of conditions and the following | |
10 # disclaimer in the documentation and/or other materials provided | |
11 # with the distribution. | |
12 # * Neither the name of Google Inc. nor the names of its | |
13 # contributors may be used to endorse or promote products derived | |
14 # from this software without specific prior written permission. | |
15 # | |
16 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
17 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
18 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
19 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
20 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
23 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
24 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
27 | |
28 | |
29 import cStringIO | |
30 import re | |
31 | |
32 # These outcomes can occur in a TestCase's outcomes list: | |
33 SKIP = 'SKIP' | |
34 FAIL = 'FAIL' | |
35 PASS = 'PASS' | |
36 OKAY = 'OKAY' | |
37 TIMEOUT = 'TIMEOUT' | |
38 CRASH = 'CRASH' | |
39 SLOW = 'SLOW' | |
40 # These are just for the status files and are mapped below in DEFS: | |
41 FAIL_OK = 'FAIL_OK' | |
42 PASS_OR_FAIL = 'PASS_OR_FAIL' | |
43 | |
44 KEYWORDS = {SKIP: SKIP, | |
45 FAIL: FAIL, | |
46 PASS: PASS, | |
47 OKAY: OKAY, | |
48 TIMEOUT: TIMEOUT, | |
49 CRASH: CRASH, | |
50 SLOW: SLOW, | |
51 FAIL_OK: FAIL_OK, | |
52 PASS_OR_FAIL: PASS_OR_FAIL} | |
53 | |
54 class Expression(object): | |
55 pass | |
56 | |
57 | |
58 class Constant(Expression): | |
59 | |
60 def __init__(self, value): | |
61 self.value = value | |
62 | |
63 def Evaluate(self, env, defs): | |
64 return self.value | |
65 | |
66 | |
67 class Variable(Expression): | |
68 | |
69 def __init__(self, name): | |
70 self.name = name | |
71 | |
72 def GetOutcomes(self, env, defs): | |
73 if self.name in env: return set([env[self.name]]) | |
74 else: return set([]) | |
75 | |
76 def Evaluate(self, env, defs): | |
77 return env[self.name] | |
78 | |
79 def __str__(self): | |
80 return self.name | |
81 | |
82 def string(self, logical): | |
83 return self.__str__() | |
84 | |
85 | |
86 class Outcome(Expression): | |
87 | |
88 def __init__(self, name): | |
89 self.name = name | |
90 | |
91 def GetOutcomes(self, env, defs): | |
92 if self.name in defs: | |
93 return defs[self.name].GetOutcomes(env, defs) | |
94 else: | |
95 return set([self.name]) | |
96 | |
97 def __str__(self): | |
98 if self.name in KEYWORDS: | |
99 return "%s" % KEYWORDS[self.name] | |
100 return "'%s'" % self.name | |
101 | |
102 def string(self, logical): | |
103 if logical: | |
104 return "%s" % self.name | |
105 return self.__str__() | |
106 | |
107 | |
108 class Operation(Expression): | |
109 | |
110 def __init__(self, left, op, right): | |
111 self.left = left | |
112 self.op = op | |
113 self.right = right | |
114 | |
115 def Evaluate(self, env, defs): | |
116 if self.op == '||' or self.op == ',': | |
117 return self.left.Evaluate(env, defs) or self.right.Evaluate(env, defs) | |
118 elif self.op == 'if': | |
119 return False | |
120 elif self.op == '==': | |
121 return not self.left.GetOutcomes(env, defs).isdisjoint(self.right.GetOutco
mes(env, defs)) | |
122 elif self.op == '!=': | |
123 return self.left.GetOutcomes(env, defs).isdisjoint(self.right.GetOutcomes(
env, defs)) | |
124 else: | |
125 assert self.op == '&&' | |
126 return self.left.Evaluate(env, defs) and self.right.Evaluate(env, defs) | |
127 | |
128 def GetOutcomes(self, env, defs): | |
129 if self.op == '||' or self.op == ',': | |
130 return self.left.GetOutcomes(env, defs) | self.right.GetOutcomes(env, defs
) | |
131 elif self.op == 'if': | |
132 if self.right.Evaluate(env, defs): return self.left.GetOutcomes(env, defs) | |
133 else: return set([]) | |
134 else: | |
135 assert self.op == '&&' | |
136 return self.left.GetOutcomes(env, defs) & self.right.GetOutcomes(env, defs
) | |
137 | |
138 def __str__(self): | |
139 return self.string(False) | |
140 | |
141 def string(self, logical=False): | |
142 if self.op == 'if': | |
143 return "['%s', %s]" % (self.right.string(True), self.left.string(logical)) | |
144 elif self.op == "||" or self.op == ",": | |
145 if logical: | |
146 return "%s or %s" % (self.left.string(True), self.right.string(True)) | |
147 else: | |
148 return "%s, %s" % (self.left, self.right) | |
149 elif self.op == "&&": | |
150 return "%s and %s" % (self.left.string(True), self.right.string(True)) | |
151 return "%s %s %s" % (self.left.string(logical), self.op, | |
152 self.right.string(logical)) | |
153 | |
154 | |
155 def IsAlpha(string): | |
156 for char in string: | |
157 if not (char.isalpha() or char.isdigit() or char == '_'): | |
158 return False | |
159 return True | |
160 | |
161 | |
162 class Tokenizer(object): | |
163 """A simple string tokenizer that chops expressions into variables, | |
164 parens and operators""" | |
165 | |
166 def __init__(self, expr): | |
167 self.index = 0 | |
168 self.expr = expr | |
169 self.length = len(expr) | |
170 self.tokens = None | |
171 | |
172 def Current(self, length=1): | |
173 if not self.HasMore(length): return "" | |
174 return self.expr[self.index:self.index + length] | |
175 | |
176 def HasMore(self, length=1): | |
177 return self.index < self.length + (length - 1) | |
178 | |
179 def Advance(self, count=1): | |
180 self.index = self.index + count | |
181 | |
182 def AddToken(self, token): | |
183 self.tokens.append(token) | |
184 | |
185 def SkipSpaces(self): | |
186 while self.HasMore() and self.Current().isspace(): | |
187 self.Advance() | |
188 | |
189 def Tokenize(self): | |
190 self.tokens = [ ] | |
191 while self.HasMore(): | |
192 self.SkipSpaces() | |
193 if not self.HasMore(): | |
194 return None | |
195 if self.Current() == '(': | |
196 self.AddToken('(') | |
197 self.Advance() | |
198 elif self.Current() == ')': | |
199 self.AddToken(')') | |
200 self.Advance() | |
201 elif self.Current() == '$': | |
202 self.AddToken('$') | |
203 self.Advance() | |
204 elif self.Current() == ',': | |
205 self.AddToken(',') | |
206 self.Advance() | |
207 elif IsAlpha(self.Current()): | |
208 buf = "" | |
209 while self.HasMore() and IsAlpha(self.Current()): | |
210 buf += self.Current() | |
211 self.Advance() | |
212 self.AddToken(buf) | |
213 elif self.Current(2) == '&&': | |
214 self.AddToken('&&') | |
215 self.Advance(2) | |
216 elif self.Current(2) == '||': | |
217 self.AddToken('||') | |
218 self.Advance(2) | |
219 elif self.Current(2) == '==': | |
220 self.AddToken('==') | |
221 self.Advance(2) | |
222 elif self.Current(2) == '!=': | |
223 self.AddToken('!=') | |
224 self.Advance(2) | |
225 else: | |
226 return None | |
227 return self.tokens | |
228 | |
229 | |
230 class Scanner(object): | |
231 """A simple scanner that can serve out tokens from a given list""" | |
232 | |
233 def __init__(self, tokens): | |
234 self.tokens = tokens | |
235 self.length = len(tokens) | |
236 self.index = 0 | |
237 | |
238 def HasMore(self): | |
239 return self.index < self.length | |
240 | |
241 def Current(self): | |
242 return self.tokens[self.index] | |
243 | |
244 def Advance(self): | |
245 self.index = self.index + 1 | |
246 | |
247 | |
248 def ParseAtomicExpression(scan): | |
249 if scan.Current() == "true": | |
250 scan.Advance() | |
251 return Constant(True) | |
252 elif scan.Current() == "false": | |
253 scan.Advance() | |
254 return Constant(False) | |
255 elif IsAlpha(scan.Current()): | |
256 name = scan.Current() | |
257 scan.Advance() | |
258 return Outcome(name) | |
259 elif scan.Current() == '$': | |
260 scan.Advance() | |
261 if not IsAlpha(scan.Current()): | |
262 return None | |
263 name = scan.Current() | |
264 scan.Advance() | |
265 return Variable(name.lower()) | |
266 elif scan.Current() == '(': | |
267 scan.Advance() | |
268 result = ParseLogicalExpression(scan) | |
269 if (not result) or (scan.Current() != ')'): | |
270 return None | |
271 scan.Advance() | |
272 return result | |
273 else: | |
274 return None | |
275 | |
276 | |
277 BINARIES = ['==', '!='] | |
278 def ParseOperatorExpression(scan): | |
279 left = ParseAtomicExpression(scan) | |
280 if not left: return None | |
281 while scan.HasMore() and (scan.Current() in BINARIES): | |
282 op = scan.Current() | |
283 scan.Advance() | |
284 right = ParseOperatorExpression(scan) | |
285 if not right: | |
286 return None | |
287 left = Operation(left, op, right) | |
288 return left | |
289 | |
290 | |
291 def ParseConditionalExpression(scan): | |
292 left = ParseOperatorExpression(scan) | |
293 if not left: return None | |
294 while scan.HasMore() and (scan.Current() == 'if'): | |
295 scan.Advance() | |
296 right = ParseOperatorExpression(scan) | |
297 if not right: | |
298 return None | |
299 left = Operation(left, 'if', right) | |
300 return left | |
301 | |
302 | |
303 LOGICALS = ["&&", "||", ","] | |
304 def ParseLogicalExpression(scan): | |
305 left = ParseConditionalExpression(scan) | |
306 if not left: return None | |
307 while scan.HasMore() and (scan.Current() in LOGICALS): | |
308 op = scan.Current() | |
309 scan.Advance() | |
310 right = ParseConditionalExpression(scan) | |
311 if not right: | |
312 return None | |
313 left = Operation(left, op, right) | |
314 return left | |
315 | |
316 | |
317 def ParseCondition(expr): | |
318 """Parses a logical expression into an Expression object""" | |
319 tokens = Tokenizer(expr).Tokenize() | |
320 if not tokens: | |
321 print "Malformed expression: '%s'" % expr | |
322 return None | |
323 scan = Scanner(tokens) | |
324 ast = ParseLogicalExpression(scan) | |
325 if not ast: | |
326 print "Malformed expression: '%s'" % expr | |
327 return None | |
328 if scan.HasMore(): | |
329 print "Malformed expression: '%s'" % expr | |
330 return None | |
331 return ast | |
332 | |
333 | |
334 class Section(object): | |
335 """A section of the configuration file. Sections are enabled or | |
336 disabled prior to running the tests, based on their conditions""" | |
337 | |
338 def __init__(self, condition): | |
339 self.condition = condition | |
340 self.rules = [ ] | |
341 | |
342 def AddRule(self, rule): | |
343 self.rules.append(rule) | |
344 | |
345 | |
346 class Rule(object): | |
347 """A single rule that specifies the expected outcome for a single | |
348 test.""" | |
349 | |
350 def __init__(self, raw_path, path, value): | |
351 self.raw_path = raw_path | |
352 self.path = path | |
353 self.value = value | |
354 | |
355 def GetOutcomes(self, env, defs): | |
356 return self.value.GetOutcomes(env, defs) | |
357 | |
358 def Contains(self, path): | |
359 if len(self.path) > len(path): | |
360 return False | |
361 for i in xrange(len(self.path)): | |
362 if not self.path[i].match(path[i]): | |
363 return False | |
364 return True | |
365 | |
366 | |
367 HEADER_PATTERN = re.compile(r'\[([^]]+)\]') | |
368 RULE_PATTERN = re.compile(r'\s*([^: ]*)\s*:(.*)') | |
369 DEF_PATTERN = re.compile(r'^def\s*(\w+)\s*=(.*)$') | |
370 PREFIX_PATTERN = re.compile(r'^\s*prefix\s+([\w\_\.\-\/]+)$') | |
371 | |
372 | |
373 class ConvertNotation(object): | |
374 def __init__(self, path): | |
375 self.path = path | |
376 self.indent = "" | |
377 self.comment = [] | |
378 self.init = False | |
379 self.section = False | |
380 self.out = cStringIO.StringIO() | |
381 | |
382 def OpenGlobal(self): | |
383 if self.init: return | |
384 self.WriteComment() | |
385 print >> self.out, "[" | |
386 self.init = True | |
387 | |
388 def CloseGlobal(self): | |
389 if not self.init: return | |
390 print >> self.out, "]" | |
391 self.init = False | |
392 | |
393 def OpenSection(self, condition="ALWAYS"): | |
394 if self.section: return | |
395 self.OpenGlobal() | |
396 if type(condition) != str: | |
397 condition = "'%s'" % condition.string(True) | |
398 print >> self.out, "%s[%s, {" % (self.indent, condition) | |
399 self.indent += " " * 2 | |
400 self.section = condition | |
401 | |
402 def CloseSection(self): | |
403 if not self.section: return | |
404 self.indent = self.indent[:-2] | |
405 print >> self.out, "%s}], # %s" % (self.indent, self.section) | |
406 self.section = False | |
407 | |
408 def WriteComment(self): | |
409 if not self.comment: return | |
410 for c in self.comment: | |
411 if len(c.strip()) == 0: | |
412 print >> self.out, "" | |
413 else: | |
414 print >> self.out, "%s%s" % (self.indent, c), | |
415 self.comment = [] | |
416 | |
417 def GetOutput(self): | |
418 with open(self.path) as f: | |
419 for line in f: | |
420 if line[0] == '#': | |
421 self.comment += [line] | |
422 continue | |
423 if len(line.strip()) == 0: | |
424 self.comment += [line] | |
425 continue | |
426 header_match = HEADER_PATTERN.match(line) | |
427 if header_match: | |
428 condition = ParseCondition(header_match.group(1).strip()) | |
429 self.CloseSection() | |
430 self.WriteComment() | |
431 self.OpenSection(condition) | |
432 continue | |
433 rule_match = RULE_PATTERN.match(line) | |
434 if rule_match: | |
435 self.OpenSection() | |
436 self.WriteComment() | |
437 path = rule_match.group(1).strip() | |
438 value_str = rule_match.group(2).strip() | |
439 comment = "" | |
440 if '#' in value_str: | |
441 pos = value_str.find('#') | |
442 comment = " %s" % value_str[pos:].strip() | |
443 value_str = value_str[:pos].strip() | |
444 value = ParseCondition(value_str) | |
445 print >> self.out, ("%s'%s': [%s],%s" % | |
446 (self.indent, path, value, comment)) | |
447 continue | |
448 def_match = DEF_PATTERN.match(line) | |
449 if def_match: | |
450 # Custom definitions are deprecated. | |
451 continue | |
452 prefix_match = PREFIX_PATTERN.match(line) | |
453 if prefix_match: | |
454 continue | |
455 print "Malformed line: '%s'." % line | |
456 self.CloseSection() | |
457 self.CloseGlobal() | |
458 result = self.out.getvalue() | |
459 self.out.close() | |
460 return result | |
OLD | NEW |