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"""Module provides custom text tools for parsing."""
19
20from typing import Tuple, Dict
21from log_tools import warning_log
22
23
24MAX_LEN = 10000000
25
26
27def find_first_not_restricted_character(restricted: str, data: str, pos: int = 0, pos_end: int = MAX_LEN) -> int:
28    for i in range(pos, min(len(data), pos_end)):
29        if data[i] not in restricted:
30            return i
31    return len(data)
32
33
34def rfind_first_not_restricted_character(restricted: str, data: str, pos: int, pos_end: int = 0) -> int:
35    """pos_end includes in searching"""
36    if pos > len(data):
37        pos = len(data) - 1
38    while pos >= max(0, pos_end):
39        if data[pos] not in restricted:
40            return pos
41        pos -= 1
42    return len(data)
43
44
45def find_first_of_characters(characters: str, data: str, pos: int = 0, pos_end: int = MAX_LEN) -> int:
46    for i in range(pos, min(len(data), pos_end)):
47        if data[i] in characters:
48            return i
49    return len(data)
50
51
52def rfind_first_of_characters(characters: str, data: str, pos: int, pos_end: int = 0) -> int:
53    """pos_end includes in searching"""
54    if pos > len(data):
55        pos = len(data) - 1
56    while pos >= max(0, pos_end):
57        if data[pos] in characters:
58            return pos
59        pos -= 1
60    return len(data)
61
62
63def find_scope_borders(data: str, start: int = 0, opening: str = "{") -> Tuple[int, int]:
64    """
65    Returns pos of opening and closing brackets in 'data'.
66    If it can't find proper scope -> raise error.
67    """
68    brackets_match: Dict[str, str] = {
69        "{": "}",
70        "(": ")",
71        "<": ">",
72        "[": "]",
73    }
74
75    if opening == "":
76        opening_pos = find_first_of_characters("({<[", data, start)
77        if opening_pos == len(data):
78            raise RuntimeError("Error while finding end of scope in ANY mode")
79        opening = data[opening_pos]
80
81    closing = brackets_match[opening]
82    start_of_scope = data.find(opening, start)
83
84    if start_of_scope == -1:
85        raise RuntimeError("No opening bracket found!")
86
87    end_of_scope = start_of_scope + 1
88
89    def check_opening_closing() -> bool:
90        openings = data[start_of_scope : end_of_scope + 1].count(opening)
91        closings = data[start_of_scope : end_of_scope + 1].count(closing)
92        return openings == closings
93
94    while not check_opening_closing():
95        end_of_scope = data.find(closing, end_of_scope + 1)
96
97        if end_of_scope == -1:
98            raise RuntimeError("Error while finding end of scope.")
99
100    return start_of_scope, end_of_scope
101
102
103def smart_split_by(data: str, delim: str = ",") -> list:
104    data = data.strip(" \n")
105
106    res = []
107    segment_start = 0
108
109    while segment_start < len(data):
110
111        next_delim = smart_find_first_of_characters(delim, data, segment_start)
112
113        segment = data[segment_start:next_delim].strip(" \n")
114        if segment != "":
115            res.append(segment)
116        else:
117            warning_log("Warning: empty segment in smart_split_by")
118
119        segment_start = find_first_not_restricted_character(f"{delim} \n", data, next_delim)
120
121    return res
122
123
124def smart_find_first_of_characters(characters: str, data: str, pos: int) -> int:
125    i = pos
126    while i < len(data):
127        if data[i] in characters:
128            return i
129
130        if data[i] in "<({[":
131            _, close_bracket = find_scope_borders(data, i, "")
132            i = close_bracket
133
134        elif data[i] == '"':
135            i = data.find('"', i + 1)
136            while i != -1 and data[i] == '"' and i != 0 and data[i - 1] == "\\":
137                i = data.find('"', i + 1)
138
139        elif data[i] == "'":
140            i = data.find("'", i + 1)
141
142        i += 1
143
144    return len(data)
145
146
147def check_cpp_name(data: str) -> bool:
148    data = data.lower()
149    forbidden_chars = " ~!@#$%^&*()-+=[]\\{}|;:'\",./<>?"
150    return find_first_of_characters(forbidden_chars, data) == len(data)
151