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