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 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_using(): 62 self.it.end, self.parsed = parse_using(self.it.data, self.it.start) 63 self.res_append_in_modifier("usings") 64 65 elif self.it.is_namespace(): 66 self.it.end, self.parsed = parse_namespace(self.it.data, self.it.start) 67 self.res_update() 68 69 elif self.it.is_enum(): 70 self.it.end, self.parsed = parse_enum_class(self.it.data, self.it.start) 71 self.res_append_namespace() 72 73 elif self.it.is_struct(): 74 self.it.end, self.parsed = parse_struct(self.it.data, self.it.start) 75 self.res_append("structs") 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 if not self.parsed: 145 return 146 147 self.parsed["namespace"] = self.namespace 148 149 if self.parent_class_name != "": 150 self.parsed["parent_class_name"] = self.parent_class_name 151 152 if "flags" in self.parsed or "flag_unions" in self.parsed: 153 self.res_append("enums") 154 add_to_custom_yamls("allEnums", "enums", self.parsed) 155 156 def update_access_modifier(self) -> None: 157 if self.parent_class_name == "": 158 raise RuntimeError("Found modifier not in class") 159 self.current_modifier = self.it.current_line.strip(" :") 160 if self.current_modifier not in self.res: 161 self.res[self.current_modifier] = {} 162 163 def res_append_method_or_constructor(self) -> None: 164 if not self.parsed: 165 return 166 167 # Constructor 168 if self.parsed["name"] == self.parent_class_name: # CC-OFF(G.TYP.07) dict key exist 169 self.res_append_in_modifier("constructors") 170 171 # Destructor 172 elif self.parsed["name"] == "~" + self.parent_class_name: # CC-OFF(G.TYP.07) dict key exist 173 self.res_append_in_modifier("destructors") 174 175 # Method 176 elif self.current_modifier != "": 177 self.res_append_in_modifier("methods") 178 179 # Function 180 else: 181 self.res_append("functions") 182 183 def res_append_field(self) -> None: 184 # Class field 185 if self.current_modifier != "": 186 self.res_append_in_modifier("fields") 187 188 # Top level variable 189 else: 190 self.res_append("vars") 191 192 def res_append_class_definition(self) -> None: 193 self.res_append("class_definitions") 194 195 def parsed_update_template(self) -> None: 196 if self.template and self.parsed: 197 if isinstance(self.parsed, dict): 198 self.parsed["template"] = self.template 199 self.template = None 200 else: 201 debug_log(f"Skipping template for '{self.parsed}'") 202