сделано автозаполнение при поиске переменых сделано правильное формирование структур, через . или -> пофиксены мелкие фиксы
293 lines
10 KiB
Python
293 lines
10 KiB
Python
import sys
|
||
import os
|
||
import re
|
||
import xml.etree.ElementTree as ET
|
||
from generateVars import map_type_to_pt, get_iq_define, type_map
|
||
from enum import IntEnum
|
||
from scanVars import *
|
||
from generateVars import *
|
||
|
||
|
||
def make_absolute_path(path, base_path):
|
||
if not os.path.isabs(path) and os.path.isdir(base_path):
|
||
try:
|
||
return os.path.abspath(os.path.join(base_path, path))
|
||
except Exception:
|
||
pass # На случай сбоя в os.path.join или abspath
|
||
elif os.path.isabs(path):
|
||
return os.path.abspath(path)
|
||
else:
|
||
return path
|
||
|
||
|
||
def make_relative_path(abs_path, base_path):
|
||
abs_path = os.path.abspath(abs_path)
|
||
base_path = os.path.abspath(base_path)
|
||
|
||
# Разбиваем на списки директорий
|
||
abs_parts = abs_path.split(os.sep)
|
||
base_parts = base_path.split(os.sep)
|
||
|
||
# Проверяем, является ли base_path настоящим префиксом пути (по папкам)
|
||
if abs_parts[:len(base_parts)] == base_parts:
|
||
rel_parts = abs_parts[len(base_parts):]
|
||
return "/".join(rel_parts)
|
||
|
||
# Иначе пробуем relpath
|
||
try:
|
||
return os.path.relpath(abs_path, base_path).replace("\\", "/")
|
||
except Exception:
|
||
return abs_path.replace("\\", "/")
|
||
|
||
|
||
def parse_vars(filename, typedef_map=None):
|
||
root, tree = safe_parse_xml(filename)
|
||
if root is None:
|
||
return []
|
||
|
||
if typedef_map is None:
|
||
typedef_map = {}
|
||
|
||
vars_list = []
|
||
variables_elem = root.find('variables')
|
||
if variables_elem is not None:
|
||
for var in variables_elem.findall('var'):
|
||
name = var.attrib.get('name', '')
|
||
var_type = var.findtext('type', 'unknown').strip()
|
||
|
||
# Вычисляем pt_type и iq_type
|
||
pt_type = var.findtext('pt_type')
|
||
if not pt_type:
|
||
pt_type = map_type_to_pt(var_type, name, typedef_map)
|
||
|
||
iq_type = var.findtext('iq_type')
|
||
if not iq_type:
|
||
iq_type = get_iq_define(var_type)
|
||
# Записываем iq_type в XML
|
||
iq_type_elem = var.find('iq_type')
|
||
if iq_type_elem is None:
|
||
iq_type_elem = ET.SubElement(var, 'iq_type')
|
||
iq_type_elem.text = iq_type
|
||
|
||
# Вычисляем pt_type и iq_type
|
||
pt_type = var.findtext('pt_type')
|
||
if not pt_type:
|
||
pt_type = map_type_to_pt(var_type, name, typedef_map)
|
||
# Записываем pt_type в XML
|
||
pt_type_elem = var.find('pt_type')
|
||
if pt_type_elem is None:
|
||
pt_type_elem = ET.SubElement(var, 'pt_type')
|
||
pt_type_elem.text = pt_type
|
||
|
||
vars_list.append({
|
||
'name': name,
|
||
'show_var': var.findtext('show_var', 'false'),
|
||
'enable': var.findtext('enable', 'false'),
|
||
'shortname': var.findtext('shortname', name),
|
||
'pt_type': pt_type,
|
||
'iq_type': iq_type,
|
||
'return_type': var.findtext('return_type', 'int'),
|
||
'type': var_type,
|
||
'file': var.findtext('file', ''),
|
||
'extern': var.findtext('extern', 'false') == 'true',
|
||
'static': var.findtext('static', 'false') == 'true',
|
||
})
|
||
|
||
ET.indent(tree, space=" ", level=0)
|
||
# Сохраняем изменения в XML-файл
|
||
tree.write(filename, encoding='utf-8', xml_declaration=True)
|
||
|
||
return vars_list
|
||
|
||
|
||
# 2. Парсим structSup.xml
|
||
def parse_structs(filename):
|
||
root, tree = safe_parse_xml(filename)
|
||
if root is None:
|
||
return {}, {}
|
||
|
||
structs = {}
|
||
typedef_map = {}
|
||
|
||
def parse_struct_element(elem):
|
||
fields = {}
|
||
|
||
for field in elem.findall("field"):
|
||
fname = field.attrib.get("name")
|
||
ftype = field.attrib.get("type", "")
|
||
|
||
# Проверка на вложенную структуру
|
||
nested_struct_elem = field.find("struct")
|
||
|
||
if nested_struct_elem is not None:
|
||
# Рекурсивно парсим вложенную структуру и вставляем её как подсловарь
|
||
nested_fields = parse_struct_element(nested_struct_elem)
|
||
|
||
# Оборачиваем в dict с ключом 'type' для хранения типа из XML
|
||
fields[fname] = {
|
||
'type': ftype, # здесь тип, например "BENDER_ERROR"
|
||
**nested_fields # развёрнутые поля вложенной структуры
|
||
}
|
||
else:
|
||
# Обычное поле
|
||
fields[fname] = ftype
|
||
|
||
return fields
|
||
|
||
structs_elem = root.find("structs")
|
||
if structs_elem is not None:
|
||
for struct in structs_elem.findall("struct"):
|
||
name = struct.attrib.get("name")
|
||
if name and name not in structs:
|
||
fields = parse_struct_element(struct)
|
||
structs[name] = fields
|
||
|
||
# typedefs без изменений
|
||
typedefs_elem = root.find("typedefs")
|
||
if typedefs_elem is not None:
|
||
for typedef in typedefs_elem.findall("typedef"):
|
||
name = typedef.attrib.get('name')
|
||
target_type = typedef.attrib.get('type')
|
||
if name and target_type:
|
||
typedef_map[name.strip()] = target_type.strip()
|
||
|
||
return structs, typedef_map
|
||
|
||
|
||
def safe_parse_xml(xml_path):
|
||
"""
|
||
Безопасно парсит XML-файл.
|
||
|
||
Возвращает кортеж (root, tree) или (None, None) при ошибках.
|
||
"""
|
||
if not xml_path or not os.path.isfile(xml_path):
|
||
#print(f"Файл '{xml_path}' не найден или путь пустой")
|
||
return None, None
|
||
|
||
try:
|
||
if os.path.getsize(xml_path) == 0:
|
||
return None, None
|
||
|
||
tree = ET.parse(xml_path)
|
||
root = tree.getroot()
|
||
return root, tree
|
||
|
||
except ET.ParseError as e:
|
||
print(f"Ошибка парсинга XML файла '{xml_path}': {e}")
|
||
return None, None
|
||
except Exception as e:
|
||
print(f"Неожиданная ошибка при чтении XML файла '{xml_path}': {e}")
|
||
return None, None
|
||
|
||
|
||
|
||
def expand_struct_recursively(prefix, type_str, structs, typedefs, var_attrs, depth=0):
|
||
if depth > 10:
|
||
return []
|
||
|
||
# Если type_str — словарь структуры
|
||
if isinstance(type_str, dict):
|
||
fields = type_str
|
||
else:
|
||
base_type = strip_ptr_and_array(type_str)
|
||
fields = structs.get(base_type)
|
||
if not isinstance(fields, dict):
|
||
return []
|
||
|
||
children = []
|
||
for field_name, field_value in fields.items():
|
||
# Пропускаем поле 'type', оно служит для хранения имени типа
|
||
if field_name == 'type':
|
||
continue
|
||
|
||
# Определяем разделитель между prefix и полем
|
||
if prefix.endswith('*'):
|
||
separator = '->'
|
||
# Для красоты можно убрать пробелы у указателя
|
||
# например, если prefix="ptr*" -> "ptr->field"
|
||
full_name = f"{prefix[:-1]}{separator}{field_name}"
|
||
else:
|
||
separator = '.'
|
||
full_name = f"{prefix}{separator}{field_name}"
|
||
|
||
if isinstance(field_value, dict):
|
||
# Если вложенная структура — берем её имя типа из поля 'type' или пустую строку
|
||
type_name = field_value.get('type', '')
|
||
child = {
|
||
'name': full_name,
|
||
'type': type_name,
|
||
'pt_type': '',
|
||
'iq_type': '',
|
||
'return_type': '',
|
||
'file': var_attrs.get('file'),
|
||
'extern': var_attrs.get('extern'),
|
||
'static': var_attrs.get('static'),
|
||
}
|
||
if '*' in type_name and not full_name.endswith('*'):
|
||
full_name += '*'
|
||
# Рекурсивно раскрываем вложенные поля
|
||
subchildren = expand_struct_recursively(full_name, field_value, structs, typedefs, var_attrs, depth + 1)
|
||
if subchildren:
|
||
child['children'] = subchildren
|
||
else:
|
||
# Простое поле — строка типа
|
||
# Пропускаем указатели на функции
|
||
if isinstance(field_value, str) and "(" in field_value and "*" in field_value and ")" in field_value:
|
||
continue
|
||
|
||
child = {
|
||
'name': full_name,
|
||
'type': field_value,
|
||
'pt_type': '',
|
||
'iq_type': '',
|
||
'return_type': '',
|
||
'file': var_attrs.get('file'),
|
||
'extern': var_attrs.get('extern'),
|
||
'static': var_attrs.get('static'),
|
||
}
|
||
|
||
children.append(child)
|
||
|
||
return children
|
||
|
||
|
||
|
||
def expand_vars(vars_list, structs, typedefs):
|
||
"""
|
||
Раскрывает структуры и массивы структур в деревья.
|
||
"""
|
||
expanded = []
|
||
|
||
for var in vars_list:
|
||
pt_type = var.get('pt_type', '')
|
||
raw_type = var.get('type', '')
|
||
base_type = strip_ptr_and_array(raw_type)
|
||
|
||
fields = structs.get(base_type)
|
||
|
||
if pt_type.startswith('pt_ptr_') and isinstance(fields, dict):
|
||
new_var = var.copy()
|
||
new_var['children'] = expand_struct_recursively(var['name']+'*', raw_type, structs, typedefs, var)
|
||
expanded.append(new_var)
|
||
|
||
if pt_type.startswith('pt_arr_') and isinstance(fields, dict):
|
||
new_var = var.copy()
|
||
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
|
||
expanded.append(new_var)
|
||
|
||
elif pt_type == 'pt_struct' and isinstance(fields, dict):
|
||
new_var = var.copy()
|
||
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
|
||
expanded.append(new_var)
|
||
|
||
elif pt_type == 'pt_union' and isinstance(fields, dict):
|
||
new_var = var.copy()
|
||
new_var['children'] = expand_struct_recursively(var['name'], raw_type, structs, typedefs, var)
|
||
expanded.append(new_var)
|
||
|
||
else:
|
||
expanded.append(var)
|
||
|
||
return expanded
|
||
|