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