xref: /aosp_15_r20/art/tools/checker/match/line.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker# Copyright (C) 2014 The Android Open Source Project
2*795d594fSAndroid Build Coastguard Worker#
3*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*795d594fSAndroid Build Coastguard Worker#
7*795d594fSAndroid Build Coastguard Worker#   http://www.apache.org/licenses/LICENSE-2.0
8*795d594fSAndroid Build Coastguard Worker#
9*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*795d594fSAndroid Build Coastguard Worker# limitations under the License.
14*795d594fSAndroid Build Coastguard Worker
15*795d594fSAndroid Build Coastguard Workerfrom common.logger import Logger
16*795d594fSAndroid Build Coastguard Workerfrom file_format.checker.struct import TestExpression, TestStatement
17*795d594fSAndroid Build Coastguard Worker
18*795d594fSAndroid Build Coastguard Worker# Required for eval.
19*795d594fSAndroid Build Coastguard Workerimport os
20*795d594fSAndroid Build Coastguard Workerimport re
21*795d594fSAndroid Build Coastguard Worker
22*795d594fSAndroid Build Coastguard Worker
23*795d594fSAndroid Build Coastguard Workerdef head_and_tail(list):
24*795d594fSAndroid Build Coastguard Worker  return list[0], list[1:]
25*795d594fSAndroid Build Coastguard Worker
26*795d594fSAndroid Build Coastguard Worker
27*795d594fSAndroid Build Coastguard Workerdef split_at_separators(expressions):
28*795d594fSAndroid Build Coastguard Worker  """ Splits a list of TestExpressions at separators. """
29*795d594fSAndroid Build Coastguard Worker  split_expressions = []
30*795d594fSAndroid Build Coastguard Worker  word_start = 0
31*795d594fSAndroid Build Coastguard Worker  for index, expression in enumerate(expressions):
32*795d594fSAndroid Build Coastguard Worker    if expression.variant == TestExpression.Variant.SEPARATOR:
33*795d594fSAndroid Build Coastguard Worker      split_expressions.append(expressions[word_start:index])
34*795d594fSAndroid Build Coastguard Worker      word_start = index + 1
35*795d594fSAndroid Build Coastguard Worker  split_expressions.append(expressions[word_start:])
36*795d594fSAndroid Build Coastguard Worker  return split_expressions
37*795d594fSAndroid Build Coastguard Worker
38*795d594fSAndroid Build Coastguard Worker
39*795d594fSAndroid Build Coastguard Workerdef get_variable(name, variables, pos):
40*795d594fSAndroid Build Coastguard Worker  if name in variables:
41*795d594fSAndroid Build Coastguard Worker    return variables[name]
42*795d594fSAndroid Build Coastguard Worker  else:
43*795d594fSAndroid Build Coastguard Worker    Logger.test_failed('Missing definition of variable "{}"'.format(name), pos, variables)
44*795d594fSAndroid Build Coastguard Worker
45*795d594fSAndroid Build Coastguard Worker
46*795d594fSAndroid Build Coastguard Workerdef set_variable(name, value, variables, pos):
47*795d594fSAndroid Build Coastguard Worker  if name not in variables:
48*795d594fSAndroid Build Coastguard Worker    return variables.copy_with(name, value)
49*795d594fSAndroid Build Coastguard Worker  else:
50*795d594fSAndroid Build Coastguard Worker    Logger.test_failed('Multiple definitions of variable "{}"'.format(name), pos, variables)
51*795d594fSAndroid Build Coastguard Worker
52*795d594fSAndroid Build Coastguard Worker
53*795d594fSAndroid Build Coastguard Workerdef match_words(checker_word, string_word, variables, pos):
54*795d594fSAndroid Build Coastguard Worker  """ Attempts to match a list of TestExpressions against a string.
55*795d594fSAndroid Build Coastguard Worker      Returns updated variable dictionary if successful and None otherwise.
56*795d594fSAndroid Build Coastguard Worker  """
57*795d594fSAndroid Build Coastguard Worker  for expression in checker_word:
58*795d594fSAndroid Build Coastguard Worker    # If `expression` is a variable reference, replace it with the value.
59*795d594fSAndroid Build Coastguard Worker    if expression.variant == TestExpression.Variant.VAR_REF:
60*795d594fSAndroid Build Coastguard Worker      pattern = re.escape(get_variable(expression.name, variables, pos))
61*795d594fSAndroid Build Coastguard Worker    else:
62*795d594fSAndroid Build Coastguard Worker      pattern = expression.text
63*795d594fSAndroid Build Coastguard Worker
64*795d594fSAndroid Build Coastguard Worker    try:
65*795d594fSAndroid Build Coastguard Worker      pattern = re.compile(pattern)
66*795d594fSAndroid Build Coastguard Worker    except re.error as e:
67*795d594fSAndroid Build Coastguard Worker      message = ('Invalid regex "{}" at {}:{},'
68*795d594fSAndroid Build Coastguard Worker                 ' compiling fails with error: {}'.format(pattern, pos.filename, pos.line_no, e))
69*795d594fSAndroid Build Coastguard Worker      raise RuntimeError(message)
70*795d594fSAndroid Build Coastguard Worker
71*795d594fSAndroid Build Coastguard Worker    # Match the expression's regex pattern against the remainder of the word.
72*795d594fSAndroid Build Coastguard Worker    # Note: re.match will succeed only if matched from the beginning.
73*795d594fSAndroid Build Coastguard Worker    match = re.match(pattern, string_word)
74*795d594fSAndroid Build Coastguard Worker    if not match:
75*795d594fSAndroid Build Coastguard Worker      return None
76*795d594fSAndroid Build Coastguard Worker
77*795d594fSAndroid Build Coastguard Worker    # If `expression` was a variable definition, set the variable's value.
78*795d594fSAndroid Build Coastguard Worker    if expression.variant == TestExpression.Variant.VAR_DEF:
79*795d594fSAndroid Build Coastguard Worker      variables = set_variable(expression.name, string_word[:match.end()], variables, pos)
80*795d594fSAndroid Build Coastguard Worker
81*795d594fSAndroid Build Coastguard Worker    # Move cursor by deleting the matched characters.
82*795d594fSAndroid Build Coastguard Worker    string_word = string_word[match.end():]
83*795d594fSAndroid Build Coastguard Worker
84*795d594fSAndroid Build Coastguard Worker  # Make sure the entire word matched, i.e. `stringWord` is empty.
85*795d594fSAndroid Build Coastguard Worker  if string_word:
86*795d594fSAndroid Build Coastguard Worker    return None
87*795d594fSAndroid Build Coastguard Worker
88*795d594fSAndroid Build Coastguard Worker  return variables
89*795d594fSAndroid Build Coastguard Worker
90*795d594fSAndroid Build Coastguard Worker
91*795d594fSAndroid Build Coastguard Workerdef match_lines(checker_line, string_line, variables):
92*795d594fSAndroid Build Coastguard Worker  """ Attempts to match a CHECK line against a string. Returns variable state
93*795d594fSAndroid Build Coastguard Worker      after the match if successful and None otherwise.
94*795d594fSAndroid Build Coastguard Worker  """
95*795d594fSAndroid Build Coastguard Worker  assert checker_line.variant != TestStatement.Variant.EVAL
96*795d594fSAndroid Build Coastguard Worker
97*795d594fSAndroid Build Coastguard Worker  checker_words = split_at_separators(checker_line.expressions)
98*795d594fSAndroid Build Coastguard Worker  string_words = string_line.split()
99*795d594fSAndroid Build Coastguard Worker
100*795d594fSAndroid Build Coastguard Worker  while checker_words:
101*795d594fSAndroid Build Coastguard Worker    # Get the next run of TestExpressions which must match one string word.
102*795d594fSAndroid Build Coastguard Worker    checker_word, checker_words = head_and_tail(checker_words)
103*795d594fSAndroid Build Coastguard Worker
104*795d594fSAndroid Build Coastguard Worker    # Keep reading words until a match is found.
105*795d594fSAndroid Build Coastguard Worker    word_matched = False
106*795d594fSAndroid Build Coastguard Worker    while string_words:
107*795d594fSAndroid Build Coastguard Worker      string_word, string_words = head_and_tail(string_words)
108*795d594fSAndroid Build Coastguard Worker      new_variables = match_words(checker_word, string_word, variables, checker_line)
109*795d594fSAndroid Build Coastguard Worker      if new_variables is not None:
110*795d594fSAndroid Build Coastguard Worker        word_matched = True
111*795d594fSAndroid Build Coastguard Worker        variables = new_variables
112*795d594fSAndroid Build Coastguard Worker        break
113*795d594fSAndroid Build Coastguard Worker    if not word_matched:
114*795d594fSAndroid Build Coastguard Worker      return None
115*795d594fSAndroid Build Coastguard Worker
116*795d594fSAndroid Build Coastguard Worker  # All TestExpressions matched. Return new variable state.
117*795d594fSAndroid Build Coastguard Worker  return variables
118*795d594fSAndroid Build Coastguard Worker
119*795d594fSAndroid Build Coastguard Worker
120*795d594fSAndroid Build Coastguard Workerdef get_eval_text(expression, variables, pos):
121*795d594fSAndroid Build Coastguard Worker  if expression.variant == TestExpression.Variant.PLAIN_TEXT:
122*795d594fSAndroid Build Coastguard Worker    return expression.text
123*795d594fSAndroid Build Coastguard Worker  else:
124*795d594fSAndroid Build Coastguard Worker    assert expression.variant == TestExpression.Variant.VAR_REF
125*795d594fSAndroid Build Coastguard Worker    return get_variable(expression.name, variables, pos)
126*795d594fSAndroid Build Coastguard Worker
127*795d594fSAndroid Build Coastguard Worker
128*795d594fSAndroid Build Coastguard Workerdef evaluate_line(checker_line, variables):
129*795d594fSAndroid Build Coastguard Worker  assert checker_line.is_eval_content_statement()
130*795d594fSAndroid Build Coastguard Worker  # Required for eval.
131*795d594fSAndroid Build Coastguard Worker  hasIsaFeature = lambda feature: variables["ISA_FEATURES"].get(feature, False)
132*795d594fSAndroid Build Coastguard Worker  readBarrierType = lambda barrier_type: variables["READ_BARRIER_TYPE"] == barrier_type
133*795d594fSAndroid Build Coastguard Worker  eval_string = "".join(get_eval_text(expr,
134*795d594fSAndroid Build Coastguard Worker                                      variables,
135*795d594fSAndroid Build Coastguard Worker                                      checker_line) for expr in checker_line.expressions)
136*795d594fSAndroid Build Coastguard Worker  return eval(eval_string)
137