const TYPE_ALIASES = { bit: 'uint8_t', bool: 'uint8_t', char: 'int8_t', double: 'float', float: 'float', int: 'int16_t', int8: 'int8_t', int8_t: 'int8_t', int16: 'int16_t', int16_t: 'int16_t', int32: 'int32_t', int32_t: 'int32_t', long: 'int32_t', short: 'int16_t', 'signed char': 'int8_t', 'signed int': 'int16_t', 'signed long': 'int32_t', 'signed short': 'int16_t', uint8: 'uint8_t', uint8_t: 'uint8_t', uint16: 'uint16_t', uint16_t: 'uint16_t', uint32: 'uint32_t', uint32_t: 'uint32_t', 'unsigned char': 'uint8_t', 'unsigned int': 'uint16_t', 'unsigned long': 'uint32_t', 'unsigned short': 'uint16_t' } const TYPE_QUALIFIERS = { _I: true, _IO: true, _O: true, const: true, extern: true, register: true, static: true, volatile: true } const STRUCT_PATTERNS = [ /typedef\s+struct(?:\s+[A-Za-z_]\w*)?\s*\{([\s\S]*?)\}\s*([A-Za-z_]\w*)\s*;/g, /struct\s+([A-Za-z_]\w*)\s*\{([\s\S]*?)\}\s*;/g ] function stripComments(source) { return String(source || '') .replace(/\/\*[\s\S]*?\*\//g, '') .replace(/\/\/.*$/gm, '') } function normalizeTypeText(typeText) { return String(typeText || '') .replace(/\*/g, ' ') .replace(/\s+/g, ' ') .trim() } function createAliasMap(source) { const aliases = { ...TYPE_ALIASES } const definePattern = /^\s*#\s*define\s+([A-Za-z_]\w*)\s+([A-Za-z_]\w*)\s*$/gm let defineMatch while ((defineMatch = definePattern.exec(source))) { const name = defineMatch[1] const value = defineMatch[2] if (aliases[value]) aliases[name] = aliases[value] } const typedefPattern = /typedef\s+(?!struct\b)([^;{}]+?)\s+([A-Za-z_]\w*)\s*;/g let typedefMatch while ((typedefMatch = typedefPattern.exec(source))) { const resolvedType = resolveType(typedefMatch[1], aliases) if (resolvedType) aliases[typedefMatch[2]] = resolvedType } return aliases } function resolveType(typeText, aliases) { const normalized = normalizeTypeText(typeText) if (!normalized) return '' const compact = normalized .split(/\s+/) .filter((token) => !TYPE_QUALIFIERS[token]) .join(' ') .trim() if (!compact || /^struct\b/.test(compact) || /^enum\b/.test(compact) || compact.indexOf('*') >= 0) { return '' } if (aliases[compact]) return aliases[compact] const tokens = compact.split(/\s+/).filter(Boolean) for (const token of tokens) { if (aliases[token]) return aliases[token] } return '' } function findStruct(source) { for (const pattern of STRUCT_PATTERNS) { pattern.lastIndex = 0 const match = pattern.exec(source) if (!match) continue if (pattern === STRUCT_PATTERNS[0]) { return { body: match[1], name: match[2] } } return { body: match[2], name: match[1] } } return null } function parseArrayDimensions(suffix) { const dimensions = [] const pattern = /\[([^\]]*)\]/g let match while ((match = pattern.exec(suffix || ''))) { const text = String(match[1] || '').trim() const value = Number(text) if (!Number.isInteger(value) || value < 1) { throw new Error('数组长度需为正整数') } dimensions.push(value) } return dimensions } function splitDeclarations(body) { return String(body || '') .split(';') .map((item) => item.trim()) .filter(Boolean) } function splitDeclarators(statement) { return String(statement || '') .split(',') .map((item) => item.trim()) .filter(Boolean) } function parseFirstDeclarator(text) { const match = String(text || '').match(/^(.+?)\s+(\**\s*[A-Za-z_]\w*(?:\s*\[[^\]]*\])*(?:\s*:\s*\d+)?)$/) if (!match) return null return { declarator: match[2], typeText: match[1] } } function parseDeclarator(text) { const cleaned = String(text || '') .replace(/=.*/, '') .replace(/:\s*\d+\s*$/, '') .replace(/\*/g, '') .trim() const match = cleaned.match(/^([A-Za-z_]\w*)\s*((?:\[[^\]]*\])*)$/) if (!match) return null return { arrayDimensions: parseArrayDimensions(match[2]), name: match[1] } } function isAsciiArray(typeText, dataType, name, arrayLength) { if (!arrayLength || arrayLength < 2 || arrayLength > 32) return false const normalizedType = normalizeTypeText(typeText).toLowerCase() if (normalizedType === 'char' || normalizedType === 'signed char') return true return dataType === 'uint8_t' && /(^|_)(model|name|text|str|string|chip|version|ver|serial|sn)($|_)/i.test(name) } function createRegisterFromField(field, dataType, originalTypeText) { const arrayLength = field.arrayDimensions.reduce((total, value) => total * value, 1) const hasArray = field.arrayDimensions.length > 0 if (hasArray && isAsciiArray(originalTypeText, dataType, field.name, arrayLength)) { return [{ dataType: 'ascii', name: field.name, textByteLength: String(arrayLength) }] } if (!hasArray) { return [{ dataType, name: field.name }] } const registers = [] for (let index = 0; index < arrayLength; index += 1) { registers.push({ dataType, name: `${field.name}[${index}]` }) } return registers } function parseStructFields(body, aliases) { const registers = [] const declarations = splitDeclarations(body) declarations.forEach((statement) => { if (!statement || statement.indexOf('(') >= 0) return const parts = splitDeclarators(statement) if (!parts.length) return const first = parseFirstDeclarator(parts[0]) if (!first) return const dataType = resolveType(first.typeText, aliases) if (!dataType) return const declarators = [first.declarator].concat(parts.slice(1)) declarators.forEach((declaratorText) => { const field = parseDeclarator(declaratorText) if (!field) return registers.push(...createRegisterFromField(field, dataType, first.typeText)) }) }) return registers } function parseStructDefinition(sourceText) { const source = stripComments(sourceText) const aliases = createAliasMap(source) const structInfo = findStruct(source) if (!structInfo) { throw new Error('未找到结构体定义') } const registers = parseStructFields(structInfo.body, aliases) if (!registers.length) { throw new Error('结构体中没有可识别的变量定义') } return { name: structInfo.name || 'Struct', registers, structName: structInfo.name || 'Struct' } } module.exports = { parseStructDefinition, stripComments }