1#!/usr/bin/env python3
2# coding=utf-8
3#
4# Copyright (c) 2024 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
18"""
19Provides:
20- parse_type
21- parse_argument
22- parse_arguments
23"""
24
25from typing import Tuple, List, Any, Dict
26from log_tools import warning_log
27from cpp_keywords import modifiers_list
28from text_tools import (
29    find_first_of_characters,
30    find_first_not_restricted_character,
31    find_scope_borders,
32    smart_find_first_of_characters,
33    rfind_first_not_restricted_character,
34    smart_split_by,
35    rfind_first_of_characters,
36)
37
38
39def parse_type(data: str) -> dict:
40    data = data.strip(" \n")
41    if len(data) > 100:
42        warning_log("Parsing big type!\n---\n" + data + "\n---\n")
43
44    if data == "":
45        return {}
46
47    res: Dict[str, Any] = {}
48    current_pos = extract_type_name(data, res)
49
50    for modifier in modifiers_list:
51        if data.find(modifier, current_pos) != -1:
52            if "other_modifiers" not in res:
53                res["other_modifiers"] = ""
54
55            res["other_modifiers"] = f"{res['other_modifiers']} {modifier}".strip(" ")
56
57    # Weakness (<>)
58    start_of_parenthes = data.find("(")
59    if start_of_parenthes != -1:
60        start_of_parenthes, end_of_parenthes = find_scope_borders(data, start_of_parenthes, "(")
61        res["cast_from"] = data[start_of_parenthes + 1 : end_of_parenthes]
62        current_pos = end_of_parenthes + 1
63
64    # Template in arg
65    template_open_pos = data.find("<", current_pos)
66    if template_open_pos != -1:
67        template_open_pos, template_close_pos = find_scope_borders(data, 0, "<")
68
69        offset, res["template_args"] = parse_arguments(data[template_open_pos + 1 : template_close_pos], 0, "types")
70        current_pos = template_open_pos + 1 + offset + 1
71
72    # Ptr
73    ptr_start = data.find("*", current_pos)
74    if ptr_start != -1:
75
76        ptr_end = find_first_not_restricted_character("*", data, ptr_start)
77
78        res["ptr_depth"] = ptr_end - ptr_start
79        current_pos = ptr_end
80
81    # Ref
82    ref_start = data.find("&", current_pos)
83    if ref_start != -1:
84
85        ref_end = find_first_not_restricted_character("&", data, ref_start)
86
87        res["ref_depth"] = ref_end - ref_start
88        current_pos = ref_end
89
90    postfix = data[current_pos:].strip(" \n")
91    if postfix != "":
92        res["postfix"] = postfix
93
94    return res
95
96
97def extract_type_name(data: str, res: Dict[str, Any]) -> int:
98    prefix_modifiers = ""
99    type_name_start = 0
100    type_name_end = find_first_of_characters(" <([{&*", data, type_name_start)
101    type_name = data[type_name_start:type_name_end].strip(" \n")
102
103    # Extract type name
104    while type_name in modifiers_list or type_name == "":
105        prefix_modifiers += f" {type_name}"
106
107        type_name_start = find_first_not_restricted_character(" <*", data, type_name_end)
108        type_name_end = find_first_of_characters(" <(*", data, type_name_start)
109
110        if type_name_start == len(data):
111            type_name = ""
112            break
113
114        type_name = data[type_name_start:type_name_end].strip(" \n")
115
116    # 'varbinder::LocalVariable' -> 'LocalVariable'
117    if type_name.find("::") != -1:
118        namespace = type_name[: type_name.rfind("::")]
119        type_name = type_name[type_name.rfind("::") + 2 :]
120        res["namespace"] = namespace
121
122    if type_name != "":
123        res["name"] = type_name
124
125    prefix_modifiers = prefix_modifiers.strip(" ")
126    if prefix_modifiers != "":
127        res["prefix"] = prefix_modifiers
128
129    return type_name_end
130
131
132def parse_argument(arg: str, mode: str = "args") -> Dict:
133    """
134    modes:
135        - args: <type> <var_name>
136        - types: <only_type>
137    """
138
139    res: Dict[str, Any] = {}
140    arg = arg.strip(" \n")
141
142    if arg == "":
143        return {}
144
145    # Default value
146    equally_pos = smart_find_first_of_characters("=", arg, 0)
147    if equally_pos != len(arg):
148        default_value_start = find_first_not_restricted_character(" ", arg, equally_pos + 1)
149        default_value_end = rfind_first_not_restricted_character("\n; ", arg, len(arg) - 1)
150        res["default_value"] = arg[default_value_start : default_value_end + 1]
151
152        arg = arg[:equally_pos].strip(" \n")
153
154    # Default constructor
155    if smart_find_first_of_characters("{", arg, 0) != len(arg):
156        start_of_constr, end_of_constr = find_scope_borders(arg)
157        res["default_constructor"] = arg[start_of_constr + 1 : end_of_constr]
158        arg = arg[:start_of_constr].strip(" \n")
159
160    # Name
161    if mode == "args":
162        name_start = rfind_first_of_characters(" *&>)}", arg, len(arg) - 1)
163        if name_start != len(arg) and name_start != len(arg) - 1:
164            name = arg[name_start + 1 :].strip(" \n")
165            if name != "":
166                res["name"] = name
167            else:
168                raise RuntimeError("Error! Argument without name!")
169
170        res["type"] = parse_type(arg[: name_start + 1])
171    else:
172        res["type"] = parse_type(arg)
173
174    return res
175
176
177def parse_arguments(data: str, start: int = 0, mode: str = "args") -> Tuple[int, List]:
178    """
179    data:
180        - (some arguments, ...) ... some code ...
181        - arg1, arg2, ..., arg_k
182    mode:
183        - args: TYPE ARG_NAME, ...
184        - types: TYPE, ...
185    """
186    res = []
187
188    start_of_args = 0
189    end_of_args = len(data)
190
191    if data[start] == "(":
192        start_of_args, end_of_args = find_scope_borders(data, start, "(")
193        start_of_args += 1
194
195    args = data[start_of_args:end_of_args]
196    args_list = smart_split_by(args, ",")
197
198    for arg in args_list:
199        parsed_arg = parse_argument(arg, mode)
200
201        if parsed_arg:
202            res.append(parsed_arg)
203
204    return end_of_args, res
205