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