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 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 warning_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 # Skip "#include", "#ifndef", "#undef", "template" 52 53 if self.it.is_skip_line(): 54 add_to_statistics("skip", self.it.current_line) 55 56 elif self.it.is_template(): 57 self.it.end, self.template = parse_template_prefix(self.it.data, self.it.start) 58 59 # Namespaces 60 elif self.it.is_namespace(): 61 self.it.end, self.parsed = parse_namespace(self.it.data, self.it.start) 62 self.res_update() 63 64 # Enum class 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 # Struct 70 elif self.it.is_struct(): 71 self.it.end, self.parsed = parse_struct(self.it.data, self.it.start) 72 self.res_append("structs") 73 74 # Using 75 elif self.it.is_using(): 76 self.it.end, self.parsed = parse_using(self.it.data, self.it.start) 77 self.res_append_in_modifier("usings") 78 79 # define macros (from class parser) 80 elif self.it.is_define_macro(): 81 self.it.end, self.parsed = parse_define_macros(self.it.data, self.it.start) 82 self.res_append("macros") 83 84 # Known macroses (from class parser) 85 elif self.it.is_known_macros(): 86 self.parsed = self.it.current_line 87 self.res_append("known_macroses") 88 89 # Private, public, protected modifier (from class parser) 90 elif self.it.is_access_modifier(): 91 self.update_access_modifier() 92 93 # Friend class 94 elif self.it.is_firend_class(): 95 self.it.end, self.parsed = parse_friend_class(self.it.data, self.it.start) 96 self.res_append("friends") 97 98 # Class forward declaration 99 elif self.it.is_class_forward_decl(): 100 self.parsed = self.it.current_line.replace("class", "").strip(" ;") 101 self.res_append("class_forward_declaration") 102 103 # Class definition 104 elif self.it.is_class_definition(): 105 self.it.end, self.parsed = parse_class( 106 self.it.data, self.it.start, self.namespace, self.parent_class_name 107 ) 108 self.res_append_class_definition() 109 110 # Function, method or constructor 111 elif self.it.is_method_or_constructor(): 112 self.it.end, self.parsed = parse_method_or_constructor(self.it.data, self.it.start) 113 self.res_append_method_or_constructor() 114 115 # Field 116 elif self.it.is_field(): 117 self.parsed = parse_argument(self.it.data[self.it.start : self.it.next_semicolon]) 118 self.it.end = self.it.next_semicolon 119 self.res_append_field() 120 121 else: 122 add_to_statistics("unreachable", self.it.current_line) 123 124 return self.res 125 126 def res_append(self, key: str) -> None: 127 if not self.parsed: 128 return 129 130 self.parsed_update_template() 131 if key not in self.res: 132 self.res[key] = [] 133 self.res[key].append(deep_copy(self.parsed)) 134 135 def res_append_in_modifier(self, key: str) -> None: 136 if not self.parsed: 137 return 138 139 self.parsed_update_template() 140 if self.current_modifier == "": 141 if key == "usings": 142 self.res_append("usings") 143 return 144 raise RuntimeError("Unreachable") 145 146 if key not in self.res[self.current_modifier]: 147 self.res[self.current_modifier][key] = [] 148 149 self.res[self.current_modifier][key].append(deep_copy(self.parsed)) 150 151 def res_update(self) -> None: 152 if self.parsed: 153 self.parsed_update_template() 154 self.res.update(self.parsed) 155 156 def res_append_namespace(self) -> None: 157 self.parsed["namespace"] = self.namespace 158 159 if self.parent_class_name != "": 160 self.parsed["parent_class_name"] = self.parent_class_name 161 162 if "flags" in self.parsed or "flag_unions" in self.parsed: 163 self.res_append("enums") 164 add_to_custom_yamls("allEnums", "enums", self.parsed) 165 166 def update_access_modifier(self) -> None: 167 if self.parent_class_name == "": 168 raise RuntimeError("Found modifier not in class") 169 self.current_modifier = self.it.current_line.strip(" :") 170 if self.current_modifier not in self.res: 171 self.res[self.current_modifier] = {} 172 173 def res_append_method_or_constructor(self) -> None: 174 # Constructor 175 if self.parsed["name"] == self.parent_class_name: 176 self.res_append_in_modifier("constructors") 177 178 # Destructor 179 elif self.parsed["name"] == "~" + self.parent_class_name: 180 self.res_append_in_modifier("destructors") 181 182 # Method 183 elif self.current_modifier != "": 184 self.res_append_in_modifier("methods") 185 186 # Function 187 else: 188 self.res_append("functions") 189 190 def res_append_field(self) -> None: 191 # Class field 192 if self.current_modifier != "": 193 self.res_append_in_modifier("fields") 194 195 # Top level variable 196 else: 197 self.res_append("vars") 198 199 def res_append_class_definition(self) -> None: 200 self.res_append("class_definitions") 201 202 def parsed_update_template(self) -> None: 203 if self.template and self.parsed: 204 if isinstance(self.parsed, dict): 205 self.parsed["template"] = self.template 206 self.template = None 207 else: 208 warning_log("Skipping template for '" + self.parsed + "'") 209