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 15from common.logger import Logger 16from file_format.checker.struct import TestExpression, TestStatement 17 18# Required for eval. 19import os 20import re 21 22 23def head_and_tail(list): 24 return list[0], list[1:] 25 26 27def split_at_separators(expressions): 28 """ Splits a list of TestExpressions at separators. """ 29 split_expressions = [] 30 word_start = 0 31 for index, expression in enumerate(expressions): 32 if expression.variant == TestExpression.Variant.SEPARATOR: 33 split_expressions.append(expressions[word_start:index]) 34 word_start = index + 1 35 split_expressions.append(expressions[word_start:]) 36 return split_expressions 37 38 39def get_variable(name, variables, pos): 40 if name in variables: 41 return variables[name] 42 else: 43 Logger.test_failed('Missing definition of variable "{}"'.format(name), pos, variables) 44 45 46def set_variable(name, value, variables, pos): 47 if name not in variables: 48 return variables.copy_with(name, value) 49 else: 50 Logger.test_failed('Multiple definitions of variable "{}"'.format(name), pos, variables) 51 52 53def match_words(checker_word, string_word, variables, pos): 54 """ Attempts to match a list of TestExpressions against a string. 55 Returns updated variable dictionary if successful and None otherwise. 56 """ 57 for expression in checker_word: 58 # If `expression` is a variable reference, replace it with the value. 59 if expression.variant == TestExpression.Variant.VAR_REF: 60 pattern = re.escape(get_variable(expression.name, variables, pos)) 61 else: 62 pattern = expression.text 63 64 try: 65 pattern = re.compile(pattern) 66 except re.error as e: 67 message = ('Invalid regex "{}" at {}:{},' 68 ' compiling fails with error: {}'.format(pattern, pos.filename, pos.line_no, e)) 69 raise RuntimeError(message) 70 71 # Match the expression's regex pattern against the remainder of the word. 72 # Note: re.match will succeed only if matched from the beginning. 73 match = re.match(pattern, string_word) 74 if not match: 75 return None 76 77 # If `expression` was a variable definition, set the variable's value. 78 if expression.variant == TestExpression.Variant.VAR_DEF: 79 variables = set_variable(expression.name, string_word[:match.end()], variables, pos) 80 81 # Move cursor by deleting the matched characters. 82 string_word = string_word[match.end():] 83 84 # Make sure the entire word matched, i.e. `stringWord` is empty. 85 if string_word: 86 return None 87 88 return variables 89 90 91def match_lines(checker_line, string_line, variables): 92 """ Attempts to match a CHECK line against a string. Returns variable state 93 after the match if successful and None otherwise. 94 """ 95 assert checker_line.variant != TestStatement.Variant.EVAL 96 97 checker_words = split_at_separators(checker_line.expressions) 98 string_words = string_line.split() 99 100 while checker_words: 101 # Get the next run of TestExpressions which must match one string word. 102 checker_word, checker_words = head_and_tail(checker_words) 103 104 # Keep reading words until a match is found. 105 word_matched = False 106 while string_words: 107 string_word, string_words = head_and_tail(string_words) 108 new_variables = match_words(checker_word, string_word, variables, checker_line) 109 if new_variables is not None: 110 word_matched = True 111 variables = new_variables 112 break 113 if not word_matched: 114 return None 115 116 # All TestExpressions matched. Return new variable state. 117 return variables 118 119 120def get_eval_text(expression, variables, pos): 121 if expression.variant == TestExpression.Variant.PLAIN_TEXT: 122 return expression.text 123 else: 124 assert expression.variant == TestExpression.Variant.VAR_REF 125 return get_variable(expression.name, variables, pos) 126 127 128def evaluate_line(checker_line, variables): 129 assert checker_line.is_eval_content_statement() 130 # Required for eval. 131 hasIsaFeature = lambda feature: variables["ISA_FEATURES"].get(feature, False) 132 eval_string = "".join(get_eval_text(expr, 133 variables, 134 checker_line) for expr in checker_line.expressions) 135 return eval(eval_string) 136