• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
17from typing import Any, Dict, Union
18from line_iterator import LineIterator
19from runtime_collections import add_to_statistics, add_to_custom_yamls
20from parse_namespace import parse_namespace
21from parse_enum import parse_enum_class
22from parse_struct import parse_struct
23from parse_using import parse_using
24from parse_define import parse_define_macros, is_known_macros
25from parse_class import parse_class, parse_friend_class, parse_template_prefix
26from parse_method import parse_method_or_constructor
27from parse_arguments import parse_argument
28from log_tools import debug_log
29
30
31def deep_copy(data: Any) -> Any:
32    if isinstance(data, (dict, list)):
33        return data.copy()
34    return data
35
36
37class CppParser:
38    def __init__(self, data: str, namespace: str = "", parent_class_name: str = ""):
39        self.it = LineIterator(data)
40        self.parsed: Any = None
41        self.res: Dict[str, Any] = {}
42        self.template: Union[str, None] = None
43
44        self.parent_class_name = parent_class_name
45        self.namespace = namespace
46        self.current_modifier = ""
47
48    def parse(self) -> Dict[str, Any]:  # pylint: disable=R0912
49
50        while self.it.next_line():
51
52            if self.it.is_access_modifier():
53                self.update_access_modifier()
54
55            elif self.it.is_skip_line() or self.current_modifier in ["private", "protected"]:
56                add_to_statistics("skip", self.it.current_line)
57
58            elif self.it.is_template():
59                self.it.end, self.template = parse_template_prefix(self.it.data, self.it.start)
60
61            elif self.it.is_namespace():
62                self.it.end, self.parsed = parse_namespace(self.it.data, self.it.start)
63                self.res_update()
64
65            elif self.it.is_enum():
66                self.it.end, self.parsed = parse_enum_class(self.it.data, self.it.start)
67                self.res_append_namespace()
68
69            elif self.it.is_struct():
70                self.it.end, self.parsed = parse_struct(self.it.data, self.it.start)
71                self.res_append("structs")
72
73            elif self.it.is_using():
74                self.it.end, self.parsed = parse_using(self.it.data, self.it.start)
75                self.res_append_in_modifier("usings")
76
77            elif self.it.is_define_macro():
78                self.it.end, self.parsed = parse_define_macros(self.it.data, self.it.start)
79                self.res_append("macros")
80
81            elif is_known_macros(self.it.current_line):
82                self.parsed = self.it.current_line
83                self.res_append("known_macroses")
84
85            elif self.it.is_firend_class():
86                self.it.end, self.parsed = parse_friend_class(self.it.data, self.it.start)
87                self.res_append("friends")
88
89            elif self.it.is_class_forward_decl():
90                self.parsed = self.it.current_line.replace("class", "").strip(" ;")
91                self.res_append("class_forward_declaration")
92
93            elif self.it.is_class_definition():
94                self.it.end, self.parsed = parse_class(
95                    self.it.data, self.it.start, self.namespace, self.parent_class_name
96                )
97                self.res_append_class_definition()
98
99            elif self.it.is_method_or_constructor():
100                self.it.end, self.parsed = parse_method_or_constructor(self.it.data, self.it.start)
101                self.res_append_method_or_constructor()
102
103            elif self.it.is_field():
104                self.parsed = parse_argument(self.it.data[self.it.start : self.it.next_semicolon])
105                self.it.end = self.it.next_semicolon
106                self.res_append_field()
107
108            else:
109                add_to_statistics("unreachable", self.it.current_line)
110
111        return self.res
112
113    def res_append(self, key: str) -> None:
114        if not self.parsed:
115            return
116
117        self.parsed_update_template()
118        if key not in self.res:
119            self.res[key] = []
120        self.res[key].append(deep_copy(self.parsed))
121
122    def res_append_in_modifier(self, key: str) -> None:
123        if not self.parsed:
124            return
125
126        self.parsed_update_template()
127        if self.current_modifier == "":
128            if key == "usings":
129                self.res_append("usings")
130                return
131            raise RuntimeError("Unreachable")
132
133        if key not in self.res[self.current_modifier]: # CC-OFF(G.TYP.07) dict key exist
134            self.res[self.current_modifier][key] = [] # CC-OFF(G.TYP.07) dict key exist
135
136        self.res[self.current_modifier][key].append(deep_copy(self.parsed)) # CC-OFF(G.TYP.07) dict key exist
137
138    def res_update(self) -> None:
139        if self.parsed:
140            self.parsed_update_template()
141            self.res.update(self.parsed)
142
143    def res_append_namespace(self) -> None:
144        self.parsed["namespace"] = self.namespace
145
146        if self.parent_class_name != "":
147            self.parsed["parent_class_name"] = self.parent_class_name
148
149        if "flags" in self.parsed or "flag_unions" in self.parsed:
150            self.res_append("enums")
151            add_to_custom_yamls("allEnums", "enums", self.parsed)
152
153    def update_access_modifier(self) -> None:
154        if self.parent_class_name == "":
155            raise RuntimeError("Found modifier not in class")
156        self.current_modifier = self.it.current_line.strip(" :")
157        if self.current_modifier not in self.res:
158            self.res[self.current_modifier] = {}
159
160    def res_append_method_or_constructor(self) -> None:
161        # Constructor
162        if self.parsed["name"] == self.parent_class_name: # CC-OFF(G.TYP.07) dict key exist
163            self.res_append_in_modifier("constructors")
164
165        # Destructor
166        elif self.parsed["name"] == "~" + self.parent_class_name: # CC-OFF(G.TYP.07) dict key exist
167            self.res_append_in_modifier("destructors")
168
169        # Method
170        elif self.current_modifier != "":
171            self.res_append_in_modifier("methods")
172
173        # Function
174        else:
175            self.res_append("functions")
176
177    def res_append_field(self) -> None:
178        # Class field
179        if self.current_modifier != "":
180            self.res_append_in_modifier("fields")
181
182        # Top level variable
183        else:
184            self.res_append("vars")
185
186    def res_append_class_definition(self) -> None:
187        self.res_append("class_definitions")
188
189    def parsed_update_template(self) -> None:
190        if self.template and self.parsed:
191            if isinstance(self.parsed, dict):
192                self.parsed["template"] = self.template
193                self.template = None
194            else:
195                debug_log(f"Skipping template for '{self.parsed}'")
196