| # 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) |