# Copyright (C) 2014 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from common.logger import Logger from file_format.checker.struct import TestExpression, TestStatement # Required for eval. import os import re def head_and_tail(list): return list[0], list[1:] def split_at_separators(expressions): """ Splits a list of TestExpressions at separators. """ split_expressions = [] word_start = 0 for index, expression in enumerate(expressions): if expression.variant == TestExpression.Variant.SEPARATOR: split_expressions.append(expressions[word_start:index]) word_start = index + 1 split_expressions.append(expressions[word_start:]) return split_expressions def get_variable(name, variables, pos): if name in variables: return variables[name] else: Logger.test_failed('Missing definition of variable "{}"'.format(name), pos, variables) def set_variable(name, value, variables, pos): if name not in variables: return variables.copy_with(name, value) else: Logger.test_failed('Multiple definitions of variable "{}"'.format(name), pos, variables) def match_words(checker_word, string_word, variables, pos): """ Attempts to match a list of TestExpressions against a string. Returns updated variable dictionary if successful and None otherwise. """ for expression in checker_word: # If `expression` is a variable reference, replace it with the value. if expression.variant == TestExpression.Variant.VAR_REF: pattern = re.escape(get_variable(expression.name, variables, pos)) else: pattern = expression.text try: pattern = re.compile(pattern) except re.error as e: message = ('Invalid regex "{}" at {}:{},' ' compiling fails with error: {}'.format(pattern, pos.filename, pos.line_no, e)) raise RuntimeError(message) # Match the expression's regex pattern against the remainder of the word. # Note: re.match will succeed only if matched from the beginning. match = re.match(pattern, string_word) if not match: return None # If `expression` was a variable definition, set the variable's value. if expression.variant == TestExpression.Variant.VAR_DEF: variables = set_variable(expression.name, string_word[:match.end()], variables, pos) # Move cursor by deleting the matched characters. string_word = string_word[match.end():] # Make sure the entire word matched, i.e. `stringWord` is empty. if string_word: return None return variables def match_lines(checker_line, string_line, variables): """ Attempts to match a CHECK line against a string. Returns variable state after the match if successful and None otherwise. """ assert checker_line.variant != TestStatement.Variant.EVAL checker_words = split_at_separators(checker_line.expressions) string_words = string_line.split() while checker_words: # Get the next run of TestExpressions which must match one string word. checker_word, checker_words = head_and_tail(checker_words) # Keep reading words until a match is found. word_matched = False while string_words: string_word, string_words = head_and_tail(string_words) new_variables = match_words(checker_word, string_word, variables, checker_line) if new_variables is not None: word_matched = True variables = new_variables break if not word_matched: return None # All TestExpressions matched. Return new variable state. return variables def get_eval_text(expression, variables, pos): if expression.variant == TestExpression.Variant.PLAIN_TEXT: return expression.text else: assert expression.variant == TestExpression.Variant.VAR_REF return get_variable(expression.name, variables, pos) def evaluate_line(checker_line, variables): assert checker_line.is_eval_content_statement() # Required for eval. hasIsaFeature = lambda feature: variables["ISA_FEATURES"].get(feature, False) eval_string = "".join(get_eval_text(expr, variables, checker_line) for expr in checker_line.expressions) return eval(eval_string)