const TYPE_ALIASES = { bit: 'uint8_t', bool: 'uint8_t', char: 'int8_t', double: 'double', 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', int64: 'int64_t', int64_t: 'int64_t', int128: 'int128_t', int128_t: 'int128_t', int256: 'int256_t', int256_t: 'int256_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', uint64: 'uint64_t', uint64_t: 'uint64_t', uint128: 'uint128_t', uint128_t: 'uint128_t', uint256: 'uint256_t', uint256_t: 'uint256_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 ENUM_PATTERNS = [ /typedef\s+enum(?:\s+([A-Za-z_]\w*))?\s*\{([\s\S]*?)\}\s*([A-Za-z_]\w*)\s*;/g, /enum\s+([A-Za-z_]\w*)\s*\{([\s\S]*?)\}\s*;/g ] 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 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) || 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 getEnumTypeNames(enumInfo = {}) { return [ enumInfo.name, enumInfo.typedefName, enumInfo.tagName ? `enum ${enumInfo.tagName}` : '', enumInfo.tagName ] .filter(Boolean) .filter((name, index, list) => list.indexOf(name) === index) } function normalizeEnumTypeKey(value) { return normalizeTypeText(value).toLowerCase() } function createEnumTypeMap(enums = []) { return enums.reduce((map, enumInfo) => { getEnumTypeNames(enumInfo).forEach((typeName) => { map[normalizeTypeText(typeName)] = enumInfo map[normalizeEnumTypeKey(typeName)] = enumInfo }) return map }, {}) } function createAliasMap(source, enums = findEnums(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 } enums.forEach((enumInfo) => { getEnumTypeNames(enumInfo).forEach((typeName) => { aliases[typeName] = enumInfo.dataType || 'uint16_t' }) }) return aliases } function tokenizeEnumExpression(expression, symbols = {}) { const source = String(expression || '').trim() const tokens = [] let index = 0 while (index < source.length) { const char = source[index] if (/\s/.test(char)) { index += 1 continue } const numberMatch = source.slice(index).match(/^(?:0x[0-9a-f]+|\d+)(?:u|U|l|L|ul|UL|uL|Ul|lu|LU|lU|Lu)?/i) if (numberMatch) { const raw = numberMatch[0].replace(/(?:u|U|l|L|ul|UL|uL|Ul|lu|LU|lU|Lu)+$/i, '') tokens.push({ type: 'number', value: raw.toLowerCase().startsWith('0x') ? parseInt(raw, 16) : Number(raw) }) index += numberMatch[0].length continue } const identifierMatch = source.slice(index).match(/^[A-Za-z_]\w*/) if (identifierMatch) { const name = identifierMatch[0] if (!Object.prototype.hasOwnProperty.call(symbols, name)) return null tokens.push({ type: 'number', value: Number(symbols[name]) }) index += name.length continue } const twoCharOperator = source.slice(index, index + 2) if (twoCharOperator === '<<' || twoCharOperator === '>>') { tokens.push({ type: 'operator', value: twoCharOperator }) index += 2 continue } if ('+-*/%&|^~()'.indexOf(char) >= 0) { tokens.push({ type: char === '(' || char === ')' ? 'paren' : 'operator', value: char }) index += 1 continue } return null } return tokens } function createEnumExpressionParser(tokens) { let index = 0 const precedence = { '|': 1, '^': 2, '&': 3, '<<': 4, '>>': 4, '+': 5, '-': 5, '*': 6, '/': 6, '%': 6 } function peek() { return tokens[index] || null } function consume() { const token = tokens[index] || null index += 1 return token } function applyBinaryOperator(operator, left, right) { if (operator === '+') return left + right if (operator === '-') return left - right if (operator === '*') return left * right if (operator === '/') return right === 0 ? NaN : Math.trunc(left / right) if (operator === '%') return right === 0 ? NaN : left % right if (operator === '<<') return left << right if (operator === '>>') return left >> right if (operator === '&') return left & right if (operator === '^') return left ^ right if (operator === '|') return left | right return NaN } function parsePrimary() { const token = consume() if (!token) return NaN if (token.type === 'number') return Number(token.value) if (token.value === '(') { const value = parseExpression(1) const endToken = consume() return endToken && endToken.value === ')' ? value : NaN } return NaN } function parseUnary() { const token = peek() if (token && token.type === 'operator' && ['+', '-', '~'].indexOf(token.value) >= 0) { consume() const value = parseUnary() if (token.value === '+') return value if (token.value === '-') return -value return ~value } return parsePrimary() } function parseExpression(minPrecedence) { let left = parseUnary() while (Number.isFinite(left)) { const token = peek() const tokenPrecedence = token && token.type === 'operator' ? precedence[token.value] : 0 if (!tokenPrecedence || tokenPrecedence < minPrecedence) break consume() const right = parseExpression(tokenPrecedence + 1) left = applyBinaryOperator(token.value, left, right) } return left } return { parse() { const value = parseExpression(1) return index === tokens.length && Number.isFinite(value) ? Math.trunc(value) : null } } } function evaluateEnumExpression(expression, symbols = {}) { const tokens = tokenizeEnumExpression(expression, symbols) if (!tokens || !tokens.length) return null return createEnumExpressionParser(tokens).parse() } function inferEnumDataType(options = []) { const values = options.map((option) => Number(option.value)).filter((value) => Number.isFinite(value)) const minValue = values.length ? Math.min.apply(null, values) : 0 const maxValue = values.length ? Math.max.apply(null, values) : 0 if (minValue < 0) { if (minValue >= -0x8000 && maxValue <= 0x7FFF) return 'int16_t' return 'int32_t' } if (maxValue <= 0xFFFF) return 'uint16_t' return 'uint32_t' } function parseEnumOptions(body, globalSymbols = {}) { const options = [] const symbols = { ...globalSymbols } let currentValue = -1 String(body || '').split(',').forEach((item) => { const text = item.trim() if (!text) return const match = text.match(/^([A-Za-z_]\w*)\s*(?:=\s*(.+))?$/) if (!match) return const name = match[1] const explicitValue = match[2] ? evaluateEnumExpression(match[2], symbols) : null currentValue = explicitValue === null ? currentValue + 1 : explicitValue symbols[name] = currentValue options.push({ label: name, name, value: currentValue }) }) return { options, symbols } } function findEnums(source) { const enums = [] const globalSymbols = {} ENUM_PATTERNS.forEach((pattern) => { pattern.lastIndex = 0 let match while ((match = pattern.exec(source))) { const isTypedef = pattern === ENUM_PATTERNS[0] const tagName = isTypedef ? match[1] : match[1] const body = isTypedef ? match[2] : match[2] const typedefName = isTypedef ? match[3] : '' const parsed = parseEnumOptions(body, globalSymbols) if (!parsed.options.length) continue Object.assign(globalSymbols, parsed.symbols) const name = typedefName || tagName || 'enum' const enumInfo = { dataType: inferEnumDataType(parsed.options), name, options: parsed.options, tagName, typedefName, typeNames: [] } enumInfo.typeNames = getEnumTypeNames(enumInfo) enums.push(enumInfo) } }) const seen = {} return enums.filter((enumInfo) => { const key = enumInfo.typeNames.map(normalizeEnumTypeKey).join('|') if (!key || seen[key]) return false seen[key] = true return true }) } 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] } } module.exports = { createAliasMap, createEnumTypeMap, findStruct, findStructs, findEnums, getEnumTypeNames, normalizeLookupName, normalizeTypeText, parseDeclarator, parseFirstDeclarator, resolveType, splitDeclarations, splitDeclarators, stripComments }