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