const { normalizeTypeText, parseDeclarator, parseFirstDeclarator, resolveType, splitDeclarations, splitDeclarators } = require('./struct-c-syntax.js') const FIELD_TYPE_QUALIFIERS = { _I: true, _IO: true, _O: true, code: true, const: true, data: true, extern: true, idata: true, pdata: true, register: true, static: true, volatile: true, xdata: true } 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 === 'int8_t' || dataType === 'uint8_t') return 1 if (dataType === 'int16_t' || dataType === 'uint16_t') return 2 if (dataType === 'float' || dataType === 'int32_t' || dataType === 'uint32_t') return 4 if (dataType === 'double' || dataType === 'int64_t' || dataType === 'uint64_t') return 8 if (dataType === 'int128_t' || dataType === 'uint128_t') return 16 if (dataType === 'int256_t' || dataType === 'uint256_t') return 32 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 cloneEnumOptions(enumInfo) { return (Array.isArray(enumInfo && enumInfo.options) ? enumInfo.options : []).map((option) => ({ label: option.label || option.name, name: option.name || option.label, value: Number(option.value) || 0 })) } function createEnumMeta(enumInfo) { if (!enumInfo || !Array.isArray(enumInfo.options) || !enumInfo.options.length) return {} return { enumName: enumInfo.name, enumOptions: cloneEnumOptions(enumInfo), sourceSymbolType: enumInfo.name } } function normalizeEnumLookupKey(typeText) { return normalizeTypeText(typeText) .split(/\s+/) .filter((token) => !FIELD_TYPE_QUALIFIERS[token]) .join(' ') .trim() .toLowerCase() } function resolveEnumInfo(typeText, enumTypes = {}) { const key = normalizeEnumLookupKey(typeText) if (!key) return null return enumTypes[key] || enumTypes[key.replace(/^enum\s+/, '')] || null } 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, enumInfo = null) { 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), ...createEnumMeta(enumInfo), isBitField: true, name, unit: 'bit' }] } function createRegisterFromField(field, dataType, originalTypeText, layoutState, enumInfo = null) { 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}]` : '', enumInfo )) } return registers } return createBitFieldRegister(field, bitFieldWidth, layoutState, field.name, enumInfo) } alignLayoutToByte(layoutState) if (hasArray && isAsciiArray(originalTypeText, dataType, field.name, arrayLength)) { const byteStart = getLayoutByteStart(layoutState) advanceLayoutBytes(layoutState, arrayLength) return [{ byteStart, dataType: 'text', ...createEnumMeta(enumInfo), name: field.name, textByteLength: String(arrayLength) }] } if (!hasArray) { const byteStart = getLayoutByteStart(layoutState) advanceLayoutBytes(layoutState, getDataTypeByteLength(dataType)) return [{ byteStart, dataType, ...createEnumMeta(enumInfo), 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, ...createEnumMeta(enumInfo), name: `${field.name}[${index}]` }) } return registers } function parseStructFields(body, aliases, enumTypes = {}) { 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 enumInfo = resolveEnumInfo(first.typeText, enumTypes) 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, enumInfo)) }) }) return registers.map((register) => ({ ...register, structByteLength: Math.ceil(layoutState.bitOffset / 8) })) } module.exports = { parseStructFields }