1 # Copyright (C) 2014 The Android Open Source Project 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 from common.logger import Logger 16 from file_format.checker.struct import TestExpression, TestAssertion 17 18 import re 19 20 def headAndTail(list): 21 return list[0], list[1:] 22 23 def splitAtSeparators(expressions): 24 """ Splits a list of TestExpressions at separators. """ 25 splitExpressions = [] 26 wordStart = 0 27 for index, expression in enumerate(expressions): 28 if expression.variant == TestExpression.Variant.Separator: 29 splitExpressions.append(expressions[wordStart:index]) 30 wordStart = index + 1 31 splitExpressions.append(expressions[wordStart:]) 32 return splitExpressions 33 34 def getVariable(name, variables, pos): 35 if name in variables: 36 return variables[name] 37 else: 38 Logger.testFailed("Missing definition of variable \"{}\"".format(name), pos, variables) 39 40 def setVariable(name, value, variables, pos): 41 if name not in variables: 42 return variables.copyWith(name, value) 43 else: 44 Logger.testFailed("Multiple definitions of variable \"{}\"".format(name), pos, variables) 45 46 def matchWords(checkerWord, stringWord, variables, pos): 47 """ Attempts to match a list of TestExpressions against a string. 48 Returns updated variable dictionary if successful and None otherwise. 49 """ 50 for expression in checkerWord: 51 # If `expression` is a variable reference, replace it with the value. 52 if expression.variant == TestExpression.Variant.VarRef: 53 pattern = re.escape(getVariable(expression.name, variables, pos)) 54 else: 55 pattern = expression.text 56 57 # Match the expression's regex pattern against the remainder of the word. 58 # Note: re.match will succeed only if matched from the beginning. 59 match = re.match(pattern, stringWord) 60 if not match: 61 return None 62 63 # If `expression` was a variable definition, set the variable's value. 64 if expression.variant == TestExpression.Variant.VarDef: 65 variables = setVariable(expression.name, stringWord[:match.end()], variables, pos) 66 67 # Move cursor by deleting the matched characters. 68 stringWord = stringWord[match.end():] 69 70 # Make sure the entire word matched, i.e. `stringWord` is empty. 71 if stringWord: 72 return None 73 74 return variables 75 76 def MatchLines(checkerLine, stringLine, variables): 77 """ Attempts to match a CHECK line against a string. Returns variable state 78 after the match if successful and None otherwise. 79 """ 80 assert checkerLine.variant != TestAssertion.Variant.Eval 81 82 checkerWords = splitAtSeparators(checkerLine.expressions) 83 stringWords = stringLine.split() 84 85 while checkerWords: 86 # Get the next run of TestExpressions which must match one string word. 87 checkerWord, checkerWords = headAndTail(checkerWords) 88 89 # Keep reading words until a match is found. 90 wordMatched = False 91 while stringWords: 92 stringWord, stringWords = headAndTail(stringWords) 93 newVariables = matchWords(checkerWord, stringWord, variables, checkerLine) 94 if newVariables is not None: 95 wordMatched = True 96 variables = newVariables 97 break 98 if not wordMatched: 99 return None 100 101 # All TestExpressions matched. Return new variable state. 102 return variables 103 104 def getEvalText(expression, variables, pos): 105 if expression.variant == TestExpression.Variant.PlainText: 106 return expression.text 107 else: 108 assert expression.variant == TestExpression.Variant.VarRef 109 return getVariable(expression.name, variables, pos) 110 111 def EvaluateLine(checkerLine, variables): 112 assert checkerLine.variant == TestAssertion.Variant.Eval 113 eval_string = "".join(map(lambda expr: getEvalText(expr, variables, checkerLine), 114 checkerLine.expressions)) 115 return eval(eval_string) 116