| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- 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
- }
|