• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# coding=utf-8
3#
4# Copyright (c) 2024-2025 Huawei Device Co., Ltd.
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17
18from typing import Tuple, Dict, List, Any
19from text_tools import (
20    find_first_of_characters,
21    rfind_first_of_characters,
22    smart_find_first_of_characters,
23    find_scope_borders,
24    find_first_not_restricted_character,
25)
26from parse_arguments import parse_arguments, parse_type
27
28
29def parse_method_or_constructor(data: str, start: int = 0) -> Tuple[int, Dict]:
30    """
31    Returns end-pos, dict-parsed method or constructor
32    Note: 'function' in names of variables is alias for 'method or constructor'
33    """
34    res: Dict[str, Any] = {}
35
36    # Defines is it declaration or definition:
37    next_semicolon = smart_find_first_of_characters(";", data, start)  # <---  for declaration
38    start_of_body = smart_find_first_of_characters("{", data, start)  # <---  for definition
39
40    if next_semicolon <= start_of_body:  # <---  declaration case
41        end_of_function_declaration = next_semicolon
42        end_of_function = next_semicolon
43
44    elif start_of_body != len(data):  # <---  definition case
45        start_of_body, end_of_body = find_scope_borders(data, start_of_body)
46        end_of_function_declaration = start_of_body
47        end_of_function = end_of_body
48
49    else:
50        raise RuntimeError("Error! End of function declaration not found\n")
51
52    # Skip operator overloading
53    if data[start: find_first_of_characters("(", data, start)].find("operator") != -1:
54        return end_of_function + 1, {}
55
56    end_of_args = parse_declaration_without_postfix(data, start, res)
57
58    # Defines is it constructor or method
59    colon_pos = find_first_of_characters(":", data, end_of_args, end_of_function_declaration)
60
61    # Function postfix
62    if colon_pos == len(data):
63        # Postfix if method
64        function_declaration_postfix = data[end_of_args + 1 : end_of_function_declaration].strip(" \n")
65        res["raw_declaration"] = data[start:end_of_function_declaration].strip(" \n")
66    else:
67        # Postfix if constructor
68        function_declaration_postfix = data[end_of_args + 1 : colon_pos].strip(" \n")
69        res["raw_declaration"] = data[start:colon_pos].strip(" \n")
70
71        end_of_initializers, initializers = parse_initializers(data, colon_pos + 1)
72        # CC-OFFNXT(G.TYP.07) dict key exist
73        updated_args, other_initializers = extract_init_args(res["args"], initializers)
74
75        if updated_args != []:
76            res["args"] = updated_args
77        if other_initializers != []:
78            res["other_initializers"] = other_initializers
79
80        if data[end_of_initializers] == '{':
81            start_of_body, end_of_body = find_scope_borders(data, end_of_initializers, "{")
82            end_of_function = end_of_body
83
84    if len(function_declaration_postfix):
85        res["postfix"] = function_declaration_postfix
86
87    if end_of_function < len(data) and data[end_of_function] != ';':
88        first_body_token_pos = find_first_not_restricted_character(' \n\t', data, start_of_body + 1)
89        if data[first_body_token_pos: first_body_token_pos + len('return')] == 'return':
90            res["additional_attributes"] = "get"
91
92    return end_of_function + 1, res
93
94
95def parse_declaration_without_postfix(data: str, start: int, res: Dict[str, Any]) -> int:
96    # Arguments
97    start_of_args = smart_find_first_of_characters("(", data, start)
98
99    if start_of_args >= len("operator") and data[start_of_args - len("operator") : start_of_args] == "operator":
100        start_of_args = find_first_of_characters("(", data, start_of_args + 1)
101
102    if (
103        start_of_args > find_first_of_characters(";", data, start)
104        and data[start : data.find("\n", start)].find("operator==") == -1
105    ):
106        raise RuntimeError("Not method or constructor!")
107
108    end_of_args, res["args"] = parse_arguments(data, start_of_args)
109
110    # Name
111    start_of_function_name = rfind_first_of_characters(" *&\n", data, start_of_args - 1) + 1
112    if start_of_function_name > len(data):
113        start_of_function_name = 0
114    res["name"] = data[start_of_function_name:start_of_args]
115
116    if res["name"].isupper():
117        raise RuntimeError("New macros found: '" + res["name"] + "'. Please add it to list.")
118
119    # Prefix
120    res["return_type"] = parse_type(data[start:start_of_function_name].strip(" \n"))
121    if not res["return_type"]:
122        del res["return_type"]
123
124    return end_of_args
125
126
127def parse_initializer(init: str) -> dict:
128
129    """
130    Note ' left (left init) ' ---> 'class field': 'left', 'init value': 'left init'
131    """
132    init = init.strip(" \n")
133    res = {}
134
135    parenthese_open = find_first_of_characters("{(", init, 0)
136
137    if parenthese_open != -1:
138        parenthese_open, parenthese_close = find_scope_borders(init, parenthese_open, "")
139    else:
140        raise RuntimeError("Error! Can't find '(' or '{' in parse_initializer: '" + init + "'")
141
142    res["class_field"] = init[:parenthese_open].strip(" \n")
143    res["init_value"] = init[parenthese_open + 1 : parenthese_close]
144
145    return res
146
147
148def parse_initializers(data: str, start: int = 0) -> Tuple[int, List]:  # pylint: disable=C0116
149    res = []
150    current_pos = find_first_not_restricted_character(" \n", data, start)
151    parethese_open = find_first_of_characters("{(", data, current_pos)
152
153    while data[current_pos] != "{" and data[current_pos] != ';' and parethese_open != len(data):
154
155        parethese_open, parenthese_close = find_scope_borders(data, parethese_open, "")
156        res.append(parse_initializer(data[current_pos : parenthese_close + 1]))
157
158        current_pos = find_first_not_restricted_character(", \n", data, parenthese_close + 1)
159        parethese_open = find_first_of_characters("{(", data, current_pos)
160
161    return current_pos, res
162
163
164def extract_init_args(args: list, initializers: list) -> Tuple[List, List]:
165    """
166    If some argument is init value for class field, it adds property for this argument
167    and remove it from initializer list:
168
169    Constructor(char *a, ...) : class_field_(a), ... {}
170
171    Args list:
172    Before: {'name': 'a', 'type': 'char *'}
173    After:  {'name': 'a', 'type': 'char *', 'initializer_for': 'class_field_'}
174
175    Initializers list:
176    Before: {'class_field': 'class_field_', 'init_value': 'a'}
177    After:  {}
178    """
179
180    i = 0
181    j = 0
182
183    while i < len(initializers):
184        while j < len(args) and i < len(initializers):
185
186            if args[j]["name"] == initializers[i]["init_value"]:
187                args[j]["initializer_for"] = initializers[i]["class_field"]
188                initializers.pop(i)
189                j = 0
190                continue
191
192            j += 1
193
194        i += 1
195        j = 0
196
197    return args, initializers
198