1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# Copyright (c) 2024 Huawei Device Co., Ltd. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import os 17import sys 18from queue import Queue 19 20 21class TokenType(object): 22 UNKNOWN = 0 23 END_OF_FILE = 1 24 COMMENT = 2 # comment 25 INCLUDE = 3 # include 26 STRING = 4 # character string 27 28 29class Token(object): 30 def __init__(self, file_name, token_type, value): 31 self.token_type = token_type 32 self.value = value 33 self.row = 1 34 self.col = 1 35 self.file_name = file_name 36 37 def clean(self): 38 self.token_type = TokenType.UNKNOWN 39 self.value = "" 40 self.row = 1 41 self.col = 1 42 43 def dump(self): 44 return "<{}:{}:{}: {},'{}'>".format(self.file_name, self.row, self.col, 45 self.token_type, self.value) 46 47 def info(self): 48 return "{}:{}:{}".format(self.file_name, self.row, self.col) 49 50 51class Char(object): 52 def __init__(self, is_eof, char): 53 self.is_eof = is_eof 54 self.char = char 55 56 def dump(self): 57 return "{%s, %s}" % (self.is_eof, self.char) 58 59 60class Lexer(object): 61 def __init__(self, idl_file_path): 62 self.have_peek = False 63 with open(idl_file_path, 'r') as idl_file: 64 file_info = idl_file.read() 65 self.data = file_info 66 self.data_len = len(self.data) 67 self.read_index = 0 68 self.cur_token = Token(os.path.basename(idl_file_path), 69 TokenType.UNKNOWN, "") 70 self.cur_row = 1 71 self.cur_col = 1 72 73 def peek_char(self, peek_count=0): 74 index = self.read_index + peek_count 75 if index >= self.data_len: 76 return Char(True, '0') 77 return Char(False, self.data[index]) 78 79 def get_char(self): 80 if self.read_index >= self.data_len: 81 return Char(True, '0') 82 read_index = self.read_index 83 self.read_index += 1 84 if self.data[read_index] == '\n': 85 self.cur_row += 1 86 self.cur_col = 1 87 else: 88 self.cur_col += 1 89 return Char(False, self.data[read_index]) 90 91 def peek_token(self): 92 if not self.have_peek: 93 self.read_token() 94 self.have_peek = True 95 return self.cur_token 96 97 def get_token(self): 98 if not self.have_peek: 99 self.read_token() 100 self.have_peek = False 101 return self.cur_token 102 103 def read_token(self): 104 self.cur_token.clean() 105 while not self.peek_char().is_eof: 106 new_char = self.peek_char() 107 if new_char.char.isspace(): 108 self.get_char() 109 continue 110 self.cur_token.row = self.cur_row 111 self.cur_token.col = self.cur_col 112 if new_char.char == '#': 113 self.read_include() 114 return 115 if new_char.char == '"': 116 self.read_string() 117 return 118 if new_char.char == '/': 119 self.read_comment() 120 return 121 self.cur_token.value = new_char.char 122 self.cur_token.token_type = TokenType.UNKNOWN 123 self.get_char() 124 return 125 self.cur_token.token_type = TokenType.END_OF_FILE 126 127 def read_include(self): 128 token_value = [] 129 token_value.append(self.get_char().char) 130 while not self.peek_char().is_eof: 131 new_char = self.peek_char() 132 if new_char.char.isalpha(): 133 token_value.append(new_char.char) 134 self.get_char() 135 continue 136 break 137 key_str = "".join(token_value) 138 if key_str == "#include": 139 self.cur_token.token_type = TokenType.INCLUDE 140 else: 141 self.cur_token.token_type = TokenType.UNKNOWN 142 self.cur_token.value = key_str 143 144 def read_string(self): 145 token_value = [] 146 self.get_char() 147 while not self.peek_char().is_eof and self.peek_char().char != '"': 148 token_value.append(self.get_char().char) 149 150 if self.peek_char().char == '"': 151 self.cur_token.token_type = TokenType.STRING 152 self.get_char() 153 else: 154 self.cur_token.token_type = TokenType.UNKNOWN 155 self.cur_token.value = "".join(token_value) 156 157 def read_comment(self): 158 token_value = [] 159 token_value.append(self.get_char().char) 160 new_char = self.peek_char() 161 if not new_char.is_eof: 162 if new_char.char == '/': 163 self.read_line_comment(token_value) 164 return 165 elif new_char.char == '*': 166 self.read_block_comment(token_value) 167 return 168 self.cur_token.token_type = TokenType.UNKNOWN 169 self.cur_token.value = "".join(token_value) 170 171 def read_line_comment(self, token_value): 172 token_value.append(self.get_char().char) 173 while not self.peek_char().is_eof: 174 new_char = self.get_char() 175 if new_char.char == '\n': 176 break 177 token_value.append(new_char.char) 178 self.cur_token.token_type = TokenType.COMMENT 179 self.cur_token.value = "".join(token_value) 180 181 def read_block_comment(self, token_value): 182 # read * 183 token_value.append(self.get_char().char) 184 while not self.peek_char().is_eof: 185 new_char = self.get_char() 186 token_value.append(new_char.char) 187 if new_char.char == '*' and self.peek_char().char == '/': 188 token_value.append(self.get_char().char) 189 break 190 value = "".join(token_value) 191 if value.endswith("*/"): 192 self.cur_token.token_type = TokenType.COMMENT 193 else: 194 self.cur_token.token_type = TokenType.UNKNOWN 195 self.cur_token.value = value 196 197 198class HcsParser(object): 199 def __init__(self): 200 self.all_hcs_files = set() 201 self.src_queue = Queue() 202 203 # get all hcs files by root hcs file 204 def get_hcs_info(self): 205 result_str = "" 206 all_hcs_files_list = sorted(list(self.all_hcs_files)) 207 for file_path in all_hcs_files_list: 208 result_str += "{}\n".format(file_path) 209 return result_str 210 211 def parse(self, root_hcs_file): 212 if not os.path.exists(root_hcs_file): 213 return 214 self.src_queue.put(os.path.abspath(root_hcs_file)) 215 while not self.src_queue.empty(): 216 cur_hcs_file = self.src_queue.get() 217 self.all_hcs_files.add(cur_hcs_file) 218 self.parse_one(cur_hcs_file) 219 220 def parse_one(self, cur_hcs_file_path): 221 hcs_file_dir = os.path.dirname(cur_hcs_file_path) 222 lex = Lexer(cur_hcs_file_path) 223 while lex.peek_token().token_type != TokenType.END_OF_FILE: 224 cur_token_type = lex.peek_token().token_type 225 if cur_token_type == TokenType.INCLUDE: 226 self.parse_include(lex, hcs_file_dir) 227 else: 228 lex.get_token() 229 230 def parse_include(self, lex, hcs_file_dir): 231 lex.get_token() # include token 232 token = lex.peek_token() 233 if token.token_type == TokenType.STRING: 234 hcs_file_path = os.path.join(hcs_file_dir, token.value) 235 # do not parse the hcs file that does not exist 236 if not os.path.exists(hcs_file_path): 237 return 238 self.src_queue.put(os.path.abspath(hcs_file_path)) 239 240 241def check_python_version(): 242 if sys.version_info < (3, 0): 243 raise Exception("Please run with python version >= 3.0") 244 245 246if __name__ == "__main__": 247 check_python_version() 248 if len(sys.argv) < 2: 249 raise Exception("No hcs source files, please check input") 250 all_hcs_files = sys.argv[1:] 251 parser = HcsParser() 252 for hcs_file in all_hcs_files: 253 parser.parse(hcs_file) 254 255 sys.stdout.write(parser.get_hcs_info()) 256 sys.stdout.flush() 257