struct-parser.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. const TYPE_ALIASES = {
  2. bit: 'uint8_t',
  3. bool: 'uint8_t',
  4. char: 'int8_t',
  5. double: 'float',
  6. float: 'float',
  7. int: 'int16_t',
  8. int8: 'int8_t',
  9. int8_t: 'int8_t',
  10. int16: 'int16_t',
  11. int16_t: 'int16_t',
  12. int32: 'int32_t',
  13. int32_t: 'int32_t',
  14. long: 'int32_t',
  15. short: 'int16_t',
  16. 'signed char': 'int8_t',
  17. 'signed int': 'int16_t',
  18. 'signed long': 'int32_t',
  19. 'signed short': 'int16_t',
  20. uint8: 'uint8_t',
  21. uint8_t: 'uint8_t',
  22. uint16: 'uint16_t',
  23. uint16_t: 'uint16_t',
  24. uint32: 'uint32_t',
  25. uint32_t: 'uint32_t',
  26. 'unsigned char': 'uint8_t',
  27. 'unsigned int': 'uint16_t',
  28. 'unsigned long': 'uint32_t',
  29. 'unsigned short': 'uint16_t'
  30. }
  31. const TYPE_QUALIFIERS = {
  32. _I: true,
  33. _IO: true,
  34. _O: true,
  35. const: true,
  36. extern: true,
  37. register: true,
  38. static: true,
  39. volatile: true
  40. }
  41. const STRUCT_PATTERNS = [
  42. /typedef\s+struct(?:\s+[A-Za-z_]\w*)?\s*\{([\s\S]*?)\}\s*([A-Za-z_]\w*)\s*;/g,
  43. /struct\s+([A-Za-z_]\w*)\s*\{([\s\S]*?)\}\s*;/g
  44. ]
  45. function stripComments(source) {
  46. return String(source || '')
  47. .replace(/\/\*[\s\S]*?\*\//g, '')
  48. .replace(/\/\/.*$/gm, '')
  49. }
  50. function normalizeTypeText(typeText) {
  51. return String(typeText || '')
  52. .replace(/\*/g, ' ')
  53. .replace(/\s+/g, ' ')
  54. .trim()
  55. }
  56. function createAliasMap(source) {
  57. const aliases = {
  58. ...TYPE_ALIASES
  59. }
  60. const definePattern = /^\s*#\s*define\s+([A-Za-z_]\w*)\s+([A-Za-z_]\w*)\s*$/gm
  61. let defineMatch
  62. while ((defineMatch = definePattern.exec(source))) {
  63. const name = defineMatch[1]
  64. const value = defineMatch[2]
  65. if (aliases[value]) aliases[name] = aliases[value]
  66. }
  67. const typedefPattern = /typedef\s+(?!struct\b)([^;{}]+?)\s+([A-Za-z_]\w*)\s*;/g
  68. let typedefMatch
  69. while ((typedefMatch = typedefPattern.exec(source))) {
  70. const resolvedType = resolveType(typedefMatch[1], aliases)
  71. if (resolvedType) aliases[typedefMatch[2]] = resolvedType
  72. }
  73. return aliases
  74. }
  75. function resolveType(typeText, aliases) {
  76. const normalized = normalizeTypeText(typeText)
  77. if (!normalized) return ''
  78. const compact = normalized
  79. .split(/\s+/)
  80. .filter((token) => !TYPE_QUALIFIERS[token])
  81. .join(' ')
  82. .trim()
  83. if (!compact || /^struct\b/.test(compact) || /^enum\b/.test(compact) || compact.indexOf('*') >= 0) {
  84. return ''
  85. }
  86. if (aliases[compact]) return aliases[compact]
  87. const tokens = compact.split(/\s+/).filter(Boolean)
  88. for (const token of tokens) {
  89. if (aliases[token]) return aliases[token]
  90. }
  91. return ''
  92. }
  93. function findStruct(source) {
  94. for (const pattern of STRUCT_PATTERNS) {
  95. pattern.lastIndex = 0
  96. const match = pattern.exec(source)
  97. if (!match) continue
  98. if (pattern === STRUCT_PATTERNS[0]) {
  99. return {
  100. body: match[1],
  101. name: match[2]
  102. }
  103. }
  104. return {
  105. body: match[2],
  106. name: match[1]
  107. }
  108. }
  109. return null
  110. }
  111. function parseArrayDimensions(suffix) {
  112. const dimensions = []
  113. const pattern = /\[([^\]]*)\]/g
  114. let match
  115. while ((match = pattern.exec(suffix || ''))) {
  116. const text = String(match[1] || '').trim()
  117. const value = Number(text)
  118. if (!Number.isInteger(value) || value < 1) {
  119. throw new Error('数组长度需为正整数')
  120. }
  121. dimensions.push(value)
  122. }
  123. return dimensions
  124. }
  125. function splitDeclarations(body) {
  126. return String(body || '')
  127. .split(';')
  128. .map((item) => item.trim())
  129. .filter(Boolean)
  130. }
  131. function splitDeclarators(statement) {
  132. return String(statement || '')
  133. .split(',')
  134. .map((item) => item.trim())
  135. .filter(Boolean)
  136. }
  137. function parseFirstDeclarator(text) {
  138. const match = String(text || '').match(/^(.+?)\s+(\**\s*[A-Za-z_]\w*(?:\s*\[[^\]]*\])*(?:\s*:\s*\d+)?)$/)
  139. if (!match) return null
  140. return {
  141. declarator: match[2],
  142. typeText: match[1]
  143. }
  144. }
  145. function parseDeclarator(text) {
  146. const cleaned = String(text || '')
  147. .replace(/=.*/, '')
  148. .replace(/:\s*\d+\s*$/, '')
  149. .replace(/\*/g, '')
  150. .trim()
  151. const match = cleaned.match(/^([A-Za-z_]\w*)\s*((?:\[[^\]]*\])*)$/)
  152. if (!match) return null
  153. return {
  154. arrayDimensions: parseArrayDimensions(match[2]),
  155. name: match[1]
  156. }
  157. }
  158. function isAsciiArray(typeText, dataType, name, arrayLength) {
  159. if (!arrayLength || arrayLength < 2 || arrayLength > 32) return false
  160. const normalizedType = normalizeTypeText(typeText).toLowerCase()
  161. if (normalizedType === 'char' || normalizedType === 'signed char') return true
  162. return dataType === 'uint8_t' && /(^|_)(model|name|text|str|string|chip|version|ver|serial|sn)($|_)/i.test(name)
  163. }
  164. function createRegisterFromField(field, dataType, originalTypeText) {
  165. const arrayLength = field.arrayDimensions.reduce((total, value) => total * value, 1)
  166. const hasArray = field.arrayDimensions.length > 0
  167. if (hasArray && isAsciiArray(originalTypeText, dataType, field.name, arrayLength)) {
  168. return [{
  169. dataType: 'ascii',
  170. name: field.name,
  171. textByteLength: String(arrayLength)
  172. }]
  173. }
  174. if (!hasArray) {
  175. return [{
  176. dataType,
  177. name: field.name
  178. }]
  179. }
  180. const registers = []
  181. for (let index = 0; index < arrayLength; index += 1) {
  182. registers.push({
  183. dataType,
  184. name: `${field.name}[${index}]`
  185. })
  186. }
  187. return registers
  188. }
  189. function parseStructFields(body, aliases) {
  190. const registers = []
  191. const declarations = splitDeclarations(body)
  192. declarations.forEach((statement) => {
  193. if (!statement || statement.indexOf('(') >= 0) return
  194. const parts = splitDeclarators(statement)
  195. if (!parts.length) return
  196. const first = parseFirstDeclarator(parts[0])
  197. if (!first) return
  198. const dataType = resolveType(first.typeText, aliases)
  199. if (!dataType) return
  200. const declarators = [first.declarator].concat(parts.slice(1))
  201. declarators.forEach((declaratorText) => {
  202. const field = parseDeclarator(declaratorText)
  203. if (!field) return
  204. registers.push(...createRegisterFromField(field, dataType, first.typeText))
  205. })
  206. })
  207. return registers
  208. }
  209. function parseStructDefinition(sourceText) {
  210. const source = stripComments(sourceText)
  211. const aliases = createAliasMap(source)
  212. const structInfo = findStruct(source)
  213. if (!structInfo) {
  214. throw new Error('未找到结构体定义')
  215. }
  216. const registers = parseStructFields(structInfo.body, aliases)
  217. if (!registers.length) {
  218. throw new Error('结构体中没有可识别的变量定义')
  219. }
  220. return {
  221. name: structInfo.name || 'Struct',
  222. registers,
  223. structName: structInfo.name || 'Struct'
  224. }
  225. }
  226. module.exports = {
  227. parseStructDefinition,
  228. stripComments
  229. }