1#! /usr/bin/env python3
2#
3# -----------------------------------------------------------------------------
4# Copyright (c) 2019-2021, Arm Limited. All rights reserved.
5#
6# SPDX-License-Identifier: BSD-3-Clause
7#
8# -----------------------------------------------------------------------------
9
10
11import re
12import os
13
14# Match (((x) + (y))) mode and ((x) + (y)) mode. x, y can be HEX or DEC value.
15expression_re = re.compile(r"([(]?[(]?[(]?(([(]?(((0x)[0-9a-fA-F]+)|([0-9]+))[)]?)\s*([\+\-]\s*([(]?(((0x)[0-9a-fA-F]+)|([0-9]+))[)]?)\s*)*)[)]?\s*([\+\-])\s*[(]?(([(]?(((0x)[0-9a-fA-F]+)|([0-9]+))[)]?)\s*([\+\-]\s*([(]?(((0x)[0-9a-fA-F]+)|([0-9]+))[)]?)\s*)*)[)]?[)]?[)]?)|([(]?[(]?[(]?(([(]?(((0x)[0-9a-fA-F]+)|([0-9]+))[)]?)\s*([\+\-]\s*([(]?(((0x)[0-9a-fA-F]+)|([0-9]+))[)]?)\s*)*)[)]?[)]?[)]?)")
16
17# Simple parser that takes a string and evaluates an expression from it.
18# The expression might contain additions and subtractions amongst numbers that
19# are written in decimal or hexadecimal form.
20# The parses can process expressions in which the parentheses does not change
21# the sign of the following number or numbers in an expression.
22# Thus the parser can process the following expression: (x + y)
23# However it will not calculate the correct sum for the expression below:
24# (x - (y + z))
25def parse_and_sum(text):
26    m = expression_re.match(text)
27    if m is None:
28        msg = "The script was probably invoked manually"
29        msg += " with having certain macros nested in flash_layouts.h.\n"
30        msg += "Please revisit the flash_layout.h file and hardcode values"
31        msg += " for the (NON-)SECURE_IMAGE_OFFSET and"
32        msg += " (NON-)SECURE_IMAGE_MAX_SIZE macros"
33        raise Exception(msg)
34
35    nums = re.findall(r'0x[A-Fa-f0-9]+|[\d]+', m.group(0))
36    for i in range(len(nums)):
37        nums[i] = int(nums[i], 0)
38    ops = re.findall(r'\+|\-', m.group(0))
39    sum = nums[0]
40    for i in range(len(ops)):
41        if ops[i] == '+':
42            sum += nums[i+1]
43        else:
44            sum -= nums[i+1]
45    return sum
46
47
48# Opens a file that contains the macro of interest, then finds the macro with
49# a regular expression, parses the expression that is defined for the given
50# macro. Lastly it evaluates the expression with the parse_and_sum function
51def evaluate_macro(file, regexp, matchGroupKey, matchGroupData, bracketless=False):
52    regexp_compiled = re.compile(regexp)
53
54    if os.path.isabs(file):
55        configFile = file
56    else:
57        scriptsDir = os.path.dirname(os.path.abspath(__file__))
58        configFile = os.path.join(scriptsDir, file)
59
60    macroValue = {}
61    with open(configFile, 'r') as macros_preprocessed_file:
62        for line in macros_preprocessed_file:
63            if bracketless:
64                line=line.replace("(","")
65                line=line.replace(")","")
66            m = regexp_compiled.match(line)
67            if m is not None:
68                macroValue[m.group(matchGroupKey)] = \
69                parse_and_sum(m.group(matchGroupData))
70
71    if (matchGroupKey == 0 and not macroValue):
72        macroValue["None"] = None
73
74    return list(macroValue.values())[0] if (matchGroupKey == 0) else macroValue
75