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 ] const STRUCT_VARIABLE_QUALIFIERS = { code: true, const: true, data: true, extern: true, idata: true, pdata: true, static: true, volatile: true, xdata: true } function normalizeLookupName(value) { return String(value || '') .replace(/^_+/, '') .replace(/[^A-Za-z0-9]/g, '') .toLowerCase() } 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 } const typedefStructPattern = /typedef\s+struct(?:\s+([A-Za-z_]\w*))?\s*\{[\s\S]*?\}\s*([A-Za-z_]\w*)\s*;/g let structTypedefMatch while ((structTypedefMatch = typedefStructPattern.exec(source))) { const tagName = structTypedefMatch[1] const typedefName = structTypedefMatch[2] if (tagName && typedefName) aliases[`struct ${tagName}`] = typedefName } 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 findStructs(source) { const structs = [] STRUCT_PATTERNS.forEach((pattern) => { pattern.lastIndex = 0 let match while ((match = pattern.exec(source))) { if (pattern === STRUCT_PATTERNS[0]) { structs.push({ body: match[1], name: match[2] }) } else { structs.push({ body: match[2], name: match[1], tagName: match[1] }) } } }) const seen = {} return structs.filter((item) => { const key = item.name if (!key || seen[key]) return false seen[key] = true return true }) } 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 rawText = String(text || '') const bitWidthMatch = rawText.match(/:\s*(\d+)\s*$/) const cleaned = rawText .replace(/=.*/, '') .replace(/:\s*\d+\s*$/, '') .replace(/\*/g, '') .trim() if (!cleaned && bitWidthMatch) { return { arrayDimensions: [], bitWidth: Number(bitWidthMatch[1]), name: '' } } const match = cleaned.match(/^([A-Za-z_]\w*)\s*((?:\[[^\]]*\])*)$/) if (!match) return null return { arrayDimensions: parseArrayDimensions(match[2]), bitWidth: bitWidthMatch ? Number(bitWidthMatch[1]) : null, 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 getDataTypeByteLength(dataType) { if (dataType === 'float' || dataType === 'int32_t' || dataType === 'uint32_t') return 4 if (dataType === 'int8_t' || dataType === 'uint8_t') return 1 return 2 } function getBitFieldDataType(bitWidth) { const width = Math.max(1, Math.round(Number(bitWidth) || 1)) if (width <= 8) return 'uint8_t' if (width <= 16) return 'uint16_t' return 'uint32_t' } function isBitType(typeText) { return normalizeTypeText(typeText).toLowerCase() === 'bit' } function alignLayoutToByte(layoutState) { if (layoutState.bitOffset % 8 !== 0) { layoutState.bitOffset += 8 - (layoutState.bitOffset % 8) } } function getLayoutByteStart(layoutState) { return Math.floor(layoutState.bitOffset / 8) } function advanceLayoutBytes(layoutState, byteLength) { layoutState.bitOffset += Math.max(1, Number(byteLength) || 1) * 8 } function createBitFieldRegister(field, bitWidth, layoutState, name) { const width = Math.max(0, Math.round(Number(bitWidth) || 0)) if (width === 0) { alignLayoutToByte(layoutState) return [] } const byteStart = getLayoutByteStart(layoutState) const bitOffset = layoutState.bitOffset % 8 layoutState.bitOffset += width if (!name) return [] return [{ bitOffset, bitWidth: width, byteStart, dataType: getBitFieldDataType(width), isBitField: true, name, unit: 'bit' }] } function createRegisterFromField(field, dataType, originalTypeText, layoutState) { const arrayLength = field.arrayDimensions.reduce((total, value) => total * value, 1) const hasArray = field.arrayDimensions.length > 0 const bitFieldWidth = field.bitWidth !== null && field.bitWidth !== undefined ? field.bitWidth : (isBitType(originalTypeText) ? 1 : null) if (bitFieldWidth !== null && bitFieldWidth !== undefined) { if (hasArray) { const registers = [] for (let index = 0; index < arrayLength; index += 1) { registers.push(...createBitFieldRegister( field, bitFieldWidth, layoutState, field.name ? `${field.name}[${index}]` : '' )) } return registers } return createBitFieldRegister(field, bitFieldWidth, layoutState, field.name) } alignLayoutToByte(layoutState) if (hasArray && isAsciiArray(originalTypeText, dataType, field.name, arrayLength)) { const byteStart = getLayoutByteStart(layoutState) advanceLayoutBytes(layoutState, arrayLength) return [{ byteStart, dataType: 'ascii', name: field.name, textByteLength: String(arrayLength) }] } if (!hasArray) { const byteStart = getLayoutByteStart(layoutState) advanceLayoutBytes(layoutState, getDataTypeByteLength(dataType)) return [{ byteStart, dataType, name: field.name }] } const registers = [] for (let index = 0; index < arrayLength; index += 1) { const byteStart = getLayoutByteStart(layoutState) advanceLayoutBytes(layoutState, getDataTypeByteLength(dataType)) registers.push({ byteStart, dataType, name: `${field.name}[${index}]` }) } return registers } function parseStructFields(body, aliases) { const registers = [] const layoutState = { bitOffset: 0 } 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, layoutState)) }) }) return registers.map((register) => ({ ...register, structByteLength: Math.ceil(layoutState.bitOffset / 8) })) } 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' } } function getStructNameAliases(structInfo, aliases) { const names = [structInfo.name] if (structInfo.tagName) names.push(`struct ${structInfo.tagName}`) Object.keys(aliases || {}).forEach((aliasName) => { if (aliases[aliasName] === structInfo.name) names.push(aliasName) }) return names.filter(Boolean) } function normalizeVariableTypeText(typeText, aliases) { const normalized = normalizeTypeText(typeText) if (!normalized) return '' const tokens = normalized .split(/\s+/) .filter((token) => !STRUCT_VARIABLE_QUALIFIERS[token]) const compact = tokens.join(' ') if (aliases && aliases[compact]) return aliases[compact] return compact } function parseStructVariables(source, structs, aliases) { const variablesByName = {} structs.forEach((structInfo) => { const structNames = getStructNameAliases(structInfo, aliases) structNames.forEach((structName) => { const escaped = structName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') const variablePattern = new RegExp(`(^|[;\\n{}])\\s*([A-Za-z_][\\w\\s]*?\\s+)?${escaped}\\s+([^;{}()]+);`, 'g') let match while ((match = variablePattern.exec(source))) { const prefix = normalizeVariableTypeText(match[2] || '', aliases) if (prefix) continue splitDeclarators(match[3]).forEach((declaratorText) => { const field = parseDeclarator(declaratorText) if (!field) return const variableInfo = { arrayDimensions: field.arrayDimensions, name: field.name, registers: structInfo.registers, structName: structInfo.name } variablesByName[field.name] = variableInfo variablesByName[field.name.replace(/^_+/, '').toLowerCase()] = variableInfo variablesByName[normalizeLookupName(field.name)] = variableInfo }) } }) }) return variablesByName } function parseStructCatalog(sourceText) { const source = stripComments(sourceText) const aliases = createAliasMap(source) const structs = findStructs(source).map((structInfo) => ({ ...structInfo, registers: parseStructFields(structInfo.body, aliases) })).filter((structInfo) => structInfo.registers.length) if (!structs.length) { throw new Error('未找到可识别的结构体定义') } return { structs, variablesByName: parseStructVariables(source, structs, aliases) } } module.exports = { parseStructCatalog, parseStructDefinition, stripComments }