const { normalizeTypeText, parseDeclarator, parseFirstDeclarator, resolveType, splitDeclarations, splitDeclarators } = require('./struct-c-syntax.js') 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) })) } module.exports = { parseStructFields }