• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2* Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7* http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15import fs = require('fs');
16import { DtscppRootInfo, FuncObj, InterfaceBody, ParamObj, FuncInfo, GenInfo, InterfaceList, TypeList } from './datatype';
17import { cpp2DtsKey } from '../template/dtscpp/dts2cpp_key';
18import path = require('path');
19import { Logger } from '../common/log';
20
21import { generateRandomInteger, removeComments, removeTab, replaceAll } from '../common/tool';
22import util = require('util');
23import re = require('../common/re');
24import { dtsFuncTemplate } from '../template/func_template';
25const dtsFileExt = '.d.ts';
26
27export function genTsFunction(func: FuncInfo, rawFileName: string) {
28  let funcParams = '';
29  for (let i = 0; i < func.params.length; ++i) {
30      funcParams += i > 0 ? ', ' : '';
31      funcParams += func.params[i].name + ': ' + func.params[i].type;
32  }
33  let funcContent = replaceAll(dtsFuncTemplate, '[file_introduce_replace]', rawFileName);
34  funcContent = replaceAll(funcContent, '[func_introduce_replace]', func.name);
35  funcContent = replaceAll(funcContent, '[input_introduce_replace]', funcParams === '' ? 'void' : funcParams);
36  funcContent = replaceAll(funcContent, '[func_name_replace]', func.name);
37  funcContent = replaceAll(funcContent, '[func_param_replace]', funcParams);
38  funcContent = replaceAll(funcContent, '[func_return_replace]', func.retType);
39
40  return funcContent;
41}
42
43
44export function getInterFuncRetType(str: string) {
45  let strArr = str.split(' ');
46  // let retType = getJsTypeFromC(replaceAll(strArr[0], '*', ''));
47  return replaceAll(strArr[0], '*', '');
48}
49
50export function getInterFuncName(str: string) {
51  let strArr = str.split(' ');
52  return replaceAll(strArr[1], '*', '');
53}
54
55
56export function getInterFuncParams(str: string, paramObj: ParamObj[]) {
57  let paramsStr = '';
58  let paramObject: ParamObj = {
59    name: '',
60    type: '',
61    arraySize: 0,
62    arraySizeList: []
63  }
64  let paramArr = replaceAll(str, '*', '').split(',');
65  for (let i = 0; i < paramArr.length; i++) {
66    let param = removeTab(paramArr[i]).split(' ');
67    const paramType = replaceAll(param[0], ' ', '');
68    const paramVal = replaceAll(param[1], ' ', '');
69    paramObject.name = paramVal;
70    paramObject.type = paramType;
71    paramObj.push(paramObject);
72    let rawType = transTskey2Ckey(paramType);
73    paramsStr += paramVal + ': ' + rawType;
74    if (i !== paramArr.length - 1) {
75      paramsStr += ', ';
76    }
77  }
78  return paramsStr;
79}
80
81export function isJsBasicType(type: string) {
82  if (type === 'number' || type === 'string' || type === 'boolean') {
83    return true;
84  } else {
85    return false;
86  }
87}
88
89export function removeMarco(type: string) {
90  // 去掉宏定义
91  if (type) {
92    let leftCraftIndex = type.indexOf('(');
93    let rightCraftIndex = type.indexOf(')');
94    if (leftCraftIndex >= 0 && rightCraftIndex > 0) {
95      type = removeTab(type.substring(leftCraftIndex + 1, rightCraftIndex));
96    }
97  }
98
99  return type;
100}
101
102export function createParam(parseParamInfo: ParamObj) {
103  let tsParam: ParamObj = {
104      name: '',
105      type: '',
106      arraySize: 0,
107      arraySizeList: []
108  };
109
110  let cppParam: ParamObj = {
111    name: '',
112    type: '',
113    arraySize: 0,
114    arraySizeList: []
115  };
116  tsParam.name = replaceAll(parseParamInfo.name, '*', '');
117  cppParam.name = tsParam.name;
118  cppParam.type = removeMarco(parseParamInfo.type);
119  let rawType = transTskey2Ckey(parseParamInfo.type);
120  tsParam.type = removeMarco(rawType);
121  return [tsParam, cppParam];
122}
123
124export function createFuncInfo(parseFuncInfo: FuncObj) {
125  let funcInfo: FuncInfo = {
126      name: '',
127      params: [],
128      retType: '',
129  };
130
131  let cppFuncInfo: FuncInfo = {
132    name: '',
133    params: [],
134    retType: '',
135  }
136  funcInfo.name = parseFuncInfo.name;
137  cppFuncInfo.name = parseFuncInfo.name;
138  let parseParams = parseFuncInfo.parameters;
139  for (let i = 0; i < parseParams.length; ++i) {
140      let paramsRes = createParam(parseParams[i]);
141      let tsParam = paramsRes[0]
142      let cppParam = paramsRes[1];
143      if (tsParam.type !== '') {
144        funcInfo.params.push(tsParam);
145        cppFuncInfo.params.push(cppParam);
146      }
147  }
148
149  let retType = parseFuncInfo.returns === '' ? 'void' : parseFuncInfo.returns;
150  retType = removeMarco(retType);
151  cppFuncInfo.retType = retType;
152  funcInfo.retType = transTskey2Ckey(retType);
153  return [funcInfo, cppFuncInfo];
154}
155
156export function analyzeRootFunction(funcInfo: FuncInfo[], cppFuncInfo: FuncInfo[], parseFunctions: FuncObj[]) {
157  for (let i = 0; i < parseFunctions.length; ++i) {
158      let result = createFuncInfo(parseFunctions[i]);
159      funcInfo[i] = result[0];
160      cppFuncInfo[i] = result[1];
161  }
162}
163
164export function genDtsInterface(path: string, typeList: TypeList[], interfaceList: InterfaceList[]) {
165  // 解析typedef: 使用正则表达式提取typedef struct定义
166  const typedefsRegex1 = /typedef\s+struct\s+\w+\s*{\s*[\s\S]*?}\s*\w+;/g;
167  // 正则表达式匹配 typedef 后跟基本数据类型和自定义类型名称
168  const typedefsRegex2 = /typedef\s+\w+\s+\w+\s*;/g;
169  // 正则表达式匹配 class xxx {};
170  const classRegex = /class\s+(\w+)\s+([a-zA-Z0-9_]+)?\s*(\{[^}]*\};)/g;
171  let rawContent = removeComments(fs.readFileSync(path).toString());
172  let structMatch = rawContent.match(typedefsRegex1);
173  if (!structMatch) {
174    structMatch = rawContent.match(classRegex);
175  }
176  let basicTypeMatch = rawContent.match(typedefsRegex2);
177  let interfaceListDef: string = '';
178
179  // 使用正则表达式的 exec 方法来获取匹配项
180  if (structMatch) {
181  for (let index = 0; index < structMatch.length; index++) {
182    let matchs = removeComments(structMatch[index]);
183      let structIndex = matchs.indexOf('struct');
184      let classIndex = matchs.indexOf('class')
185      let leftIndex = matchs.indexOf('{');
186      let rightIndex = matchs.indexOf('}');
187      let interfaceName = '';
188      if (structIndex >= 0) {
189        interfaceName = matchs.substring(structIndex + 6, leftIndex).trim();
190      } else if (classIndex >= 0) {
191        interfaceName = matchs.substring(classIndex + 5, leftIndex).trim();
192      }
193      let params = matchs.substring(leftIndex + 1, rightIndex).split(';');
194      let interDefine = 'interface ' + interfaceName + ' {\n';
195      let paramsContent: ParamObj[] = [];
196      let interFuncsContent: FuncObj[] = [];
197      let interfaceBody: InterfaceBody = {
198        params: paramsContent,
199        funcs: interFuncsContent
200      }
201      let interfaceContent: InterfaceList = {
202        interfaceName: interfaceName,
203        interfaceBody: interfaceBody
204      }
205      for (let i = 0; i < params.length; i++) {
206        // 去除空格和换行符
207        let paramStr = removeTab(params[i]);
208        if (paramStr === '') {
209          continue;
210        }
211        // 成员函数的处理
212        const funcRegex = /\w+\s+\*?\(([^\)]+)\)\s*\(([^\)]*)\)\s*/;
213        const funcRegex2 = /(\w+)\s+(::\w+|[\w:]+)\s*\(([^)]*)\)\s*/;
214        let match = paramStr.match(funcRegex);
215        let match2 = paramStr.match(funcRegex2);
216        if (match) {
217          // 处理成员函数  仅仅限于成员函数是函数指针的情况
218          let interFuncParams: ParamObj[] = []
219          let returnType = getInterFuncRetType(match[0]);
220          let funcName = getInterFuncName(match[1]);
221          let params = getInterFuncParams(match[2], interFuncParams);
222          interDefine += util.format('  %s:(%s) => %s;\n',funcName, params, returnType);
223          let funcObj: FuncObj = {
224            type: '',
225            name: funcName,
226            returns: returnType,
227            parameters: interFuncParams
228          }
229          interFuncsContent.push(funcObj);
230        } else if (match2) {
231          let interFuncParams: ParamObj[] = []
232          let returnType = getInterFuncRetType(match2[1]);
233          let funcName = match2[2];
234          let params = getInterFuncParams(match2[3], interFuncParams);
235          interDefine += util.format('  %s:(%s) => %s;\n',funcName, params, returnType);
236          let funcObj: FuncObj = {
237            type: '',
238            name: funcName,
239            returns: returnType,
240            parameters: interFuncParams
241          }
242          interFuncsContent.push(funcObj);
243        } else {
244          let lastTabIndex = paramStr.lastIndexOf(' ');
245          const variableName = paramStr.substring(lastTabIndex + 1, paramStr.length).replace('*', '')
246          const variabletype = paramStr.substring(0, lastTabIndex);
247          let rawType = transTskey2Ckey(variabletype);
248          if (!isJsBasicType(rawType)) {
249            rawType += ' | null';
250          }
251          let variableDefine = '  ' + variableName + ': ' + rawType + ';\n'
252          interDefine += variableDefine;
253          let paramObj: ParamObj = {
254            name: variableName,
255            type: replaceAll(variabletype, 'struct', '').trim(),
256            arraySize: 0,
257            arraySizeList: [],
258
259          }
260          paramsContent.push(paramObj);
261        }
262        }
263        interfaceBody.funcs = interFuncsContent;
264        interfaceBody.params = paramsContent;
265        interfaceContent.interfaceBody = interfaceBody;
266        interfaceList.push(interfaceContent);
267
268        interDefine += '}\n';
269        interfaceListDef += interDefine;
270      }
271    }
272
273    if (basicTypeMatch) {
274      for (let index = 0; index < basicTypeMatch.length; index++) {
275        // 输出匹配的基本类型定义
276        Logger.getInstance().debug('Basic type typedef match:' + basicTypeMatch[0]);
277        let matchs = basicTypeMatch[index].split(' ');
278        let rawType = transTskey2Ckey(matchs[1].trim());
279        let defineType = matchs[2].split(';')
280        let typedefine = 'type ' + defineType[0] + ' = ' + rawType + ';\n';
281        interfaceListDef += typedefine;
282        let typeListContent: TypeList = {
283          typeName: defineType[0],
284          typeBody: matchs[1].trim()
285        }
286        typeList.push(typeListContent);
287      }
288    }
289
290    return interfaceListDef;
291}
292
293export function getTypeBody(testType: string, typeList: TypeList[]) {
294  for (let i = 0; i < typeList.length; i++)
295  {
296    if (typeList[i].typeName === testType) {
297      return typeList[i].typeBody;
298    }
299    return '';
300  }
301}
302
303export function getInterfaceBody(testType: string, interfaceList: InterfaceList[]) {
304  for (let i = 0; i < interfaceList.length; i++)
305  {
306    if (interfaceList[i].interfaceName === testType) {
307      return interfaceList[i].interfaceBody;
308    }
309  }
310}
311
312//----------------------------
313
314// h2dts
315export function transTskey2Ckey(key: string): string {
316  // 判断是否是std::function, 转换为箭头函数 如:std::function<void(int, int)> 转换为 (a: number, b: number)=>void
317  const regexFunction = /\b(std::)?function<([\w\s\:\*]+)\s*\(([\w\:\<\>\,\s*]*)\)>/;
318  const matchFunction = key.match(regexFunction);
319  if (matchFunction) {
320    const returnType = matchFunction[2].trim(); // 返回类型
321    let paramstr = matchFunction[3] ? matchFunction[3].trim() : ''
322    let paramreg = /([\w\s\:\*]+<[^>]*>|[\*\w\s\:]+)/g;
323    let pmatch;
324    let paramList = [];
325    while ((pmatch = paramreg.exec(paramstr)) !== null) {
326      paramList.push(pmatch[0]);
327    }
328    let str = '';
329    for (let i = 0; i < paramList.length; ++i) {
330      str += paramList[i].trim() === ''? '': `param${i}: ${transTskey2Ckey(paramList[i])}`;
331      if (i != paramList.length - 1) {
332        str += ', ';
333      }
334    }
335    return `(${str})=>${transTskey2Ckey(returnType)}`;
336  }
337
338  // 智能指针,例如: std::unique_ptr<int> -> number
339  const regexSmartPtr = /\b((std::)?(?:unique_ptr|shared_ptr|weak_ptr))\s*<([\w\:\<\>\,\s*]+)>/;
340  const matchSmartPtr = key.match(regexSmartPtr);
341  if (matchSmartPtr) {
342    return transTskey2Ckey(matchSmartPtr[3].trim());
343  }
344
345  // 判断迭代器: 如std::vector<int>::iterator  ->  IterableIterator<Array<number>>
346  const regexIterator = /(std::(string|(\w+<[^>]+>)))::iterator/;
347  const matchIterator = key.match(regexIterator);
348  if (matchIterator) {
349    return 'IterableIterator<' + transTskey2Ckey(matchIterator[1].trim()) + '>';
350  }
351
352  // 转换为Array<xxx>类型
353  const regexArray = /\b((std::)?(?:vector|array|deque|list|forward_list|stack|queue|valarray|priority_queue))\s*<([^>]*)>/;
354  const matchArray = key.match(regexArray);
355  if (matchArray) {
356    return `Array<${transTskey2Ckey(matchArray[3])}>`;
357  }
358
359  // 转换为Map<xxx, xxx>类型
360  const regexMap = /\b((std::)?(?:map|unordered_map|multimap|unordered_multimap))\s*<([^>]*)>/;
361  const matchMap = key.match(regexMap);
362  if (matchMap) {
363    const arr = matchMap[3].split(',');
364    if (arr.length == 2) {
365      return `Map<${transTskey2Ckey(arr[0])}, ${transTskey2Ckey(arr[1])}>`;
366    }
367  }
368
369  // 转换为Set<xxx>
370  const regexSet = /\b((std::)?(?:set|unordered_set|multiset|unordered_multiset))\s*<([^>]*)>/;
371  const matchSet = key.match(regexSet);
372  if (matchSet) {
373    return `Set<${transTskey2Ckey(matchSet[3])}>`;
374  }
375
376  // 转换为元组
377  const regexTuple = /\b((std::)?(?:tuple|pair))\s*<([^>]*)>/;
378  const matchTuple = key.match(regexTuple);
379  if (matchTuple) {
380    const arr = matchTuple[3].split(',');
381    let str = '';
382    for (let i = 0; i < arr.length; ++i) {
383      str += transTskey2Ckey(arr[i]);
384      if (i != arr.length - 1) {
385        str += ', ';
386      }
387    }
388    return `[${str}]`;
389  }
390
391  // 判断是否是std::complex , 将复数类型转换为{real: number, imag: number}类型
392  const regexComplex = /\b((std::)?(?:complex))\s*<([^<>]*)>/;
393  const matchComplex = key.match(regexComplex);
394  if (matchComplex) {
395    const type = transTskey2Ckey(matchComplex[3].trim()); // 返回类型
396    return `{real: ${type}, imag: ${type}}`;
397  }
398
399  // 判断日期类型: std::time_t /std::clock_t /std::tm 转换为ts的Date类型
400  const regexDate = /\b((std::)?(?:time_t|clock_t|tm|(?:chrono::(time_point|duration|system_clock|steady_clock|high_resolution_clock|hours|minutes|seconds|milliseconds|microseconds|nanoseconds))))\b/;
401  const matchDate = key.match(regexDate);
402  if (matchDate) {
403    return 'Date';
404  }
405  for(const keyItem of cpp2DtsKey) {
406    for(const str of keyItem.keys) {
407      if (key.includes(str)) {
408        return keyItem.value;
409      }
410    }
411  }
412  let replaceKeyList = ['enum', 'struct', 'union'];
413  for(const rkey of replaceKeyList) {
414    key = key.replace(rkey, '').trim();
415  }
416  // 其他类型转换为 any 类型,如typeDef定义的类型
417  return 'any'
418}
419
420export function getDtsEnum(rootInfo: GenInfo) {
421  let enumList = rootInfo.parseObj.enums;
422  let out = '';
423  for(const enumItem of enumList) {
424    let enumHead = `export enum ${enumItem.name} {\n`
425    let enumBody = ''
426    try {
427      enumItem.members.forEach(element => {
428        enumBody += `\t${element},\n`
429      });
430    } catch (e) {
431      let errmsg = 'generate dts file error: ' + JSON.stringify(e);
432      Logger.getInstance().error(errmsg);
433    }
434    out += enumHead + enumBody + '};\n\n'
435    if (enumItem.name && enumItem.alias && enumItem.name !== enumItem.alias) {
436      out += `export type ${enumItem.alias} = ${enumItem.name};\n\n`
437    }
438  }
439  return out;
440}
441
442export function getDtsFunction(rootInfo: GenInfo) {
443  let funcList = rootInfo.parseObj.funcs;
444  let out = '';
445  for(const funcItem of funcList) {
446    let funcHead = '';
447    let funcTail = '';
448    let enumBody = ''
449    let returnType = transTskey2Ckey(funcItem.returns);
450    try {
451      if (funcItem.type === 'typedef') {
452        funcHead = `export interface ${funcItem.name} {\n`;
453        funcTail = '};\n\n';
454        funcItem.parameters.forEach(element => {
455          if (element.name && element.type) {
456            enumBody += `${element.name}: ${transTskey2Ckey(element.type)}, `
457          }
458        });
459        enumBody = `\t(${enumBody.slice(0, -2)}): ${returnType};\n`
460        out += funcHead + `${enumBody}` + funcTail;
461      } else {
462        funcHead = `export function ${funcItem.name}(`
463        funcTail = `): ${returnType};\n\n`;
464        funcItem.parameters.forEach(element => {
465          if (element.name && element.type) {
466            enumBody += `${element.name}: ${transTskey2Ckey(element.type)}, `
467          }
468        });
469        out += funcHead + enumBody.slice(0, -2) + funcTail;
470      }
471    } catch (e) {
472      let errmsg = 'generate dts file error: ' + JSON.stringify(e);
473      Logger.getInstance().error(errmsg);
474    }
475  }
476  return out;
477}
478
479export function getDtsClasses(rootInfo: GenInfo) {
480  let classList = rootInfo.parseObj.classes;
481  let out = '';
482  for(const classItem of classList) {
483    let classHead = `export class ${classItem.name} {\n`
484    let classBody = ''
485    try {
486      if (classItem.variableList.length > 0) {
487        for (const attribute of classItem.variableList) {
488          classBody += `\t${attribute.name}: ${transTskey2Ckey(attribute.type)};\n`
489        };
490      }
491    } catch (e) {
492      let errmsg = 'generate dts file error: ' + JSON.stringify(e);
493      Logger.getInstance().error(errmsg);
494    }
495    try {
496      if (classItem.functionList.length > 0) {
497        for (const method of classItem.functionList) {
498          let methodContent = '';
499          for (const param of method.parameters) {
500            methodContent += `${param.name}: ${transTskey2Ckey(param.type)}, `;
501          }
502          classBody += `\t${method.name}(${methodContent.slice(0, -2)}): ${transTskey2Ckey(method.returns)};\n`
503        };
504      }
505    } catch (e) {
506      let errmsg = 'generate dts file error: ' + JSON.stringify(e);
507      Logger.getInstance().error(errmsg);
508    }
509    out += classHead + classBody + '};\n\n'
510    if (classItem.name && classItem.alias) {
511      out += `export type ${classItem.alias} = ${classItem.name};\n\n`
512    }
513  }
514  return out;
515}
516
517export function getDtsStructs(rootInfo: GenInfo) {
518  let structList = rootInfo.parseObj.structs;
519  let out = '';
520  for(const structItem of structList) {
521    let structHead = `export type ${structItem.name} = {\n`
522    let structBody = ''
523    try {
524      if (structItem.members.length > 0) {
525        for (const attribute of structItem.members) {
526          structBody += `\t${attribute.name}: ${transTskey2Ckey(attribute.type)};\n`
527        };
528      }
529    } catch (e) {
530      let errmsg = 'generate dts file error: ' + JSON.stringify(e);
531      Logger.getInstance().error(errmsg);
532    }
533    try {
534      if (structItem.functions.length > 0) {
535        for (const method of structItem.functions) {
536          let methodContent = '';
537          for (const param of method.parameters) {
538            if (param.name && param.type) {
539              methodContent += `${param.name}: ${transTskey2Ckey(param.type)}, `;
540            }
541          }
542          structBody += `\t${method.name}(${methodContent.slice(0, -2)}): ${transTskey2Ckey(method.returns)};\n`
543        };
544      }
545    } catch (e) {
546      let errmsg = 'generate dts file error: ' + JSON.stringify(e);
547      Logger.getInstance().error(errmsg);
548    }
549    out += structHead + structBody + '};\n\n'
550    if (structItem.name && structItem.alias && structItem.name !== structItem.alias) {
551      out += `export type ${structItem.alias} = ${structItem.name};\n\n`
552    }
553  }
554  return out;
555}
556
557export function getDtsUnions(rootInfo: GenInfo) {
558  let unionList = rootInfo.parseObj.unions;
559  let out = '';
560  for(const unionItem of unionList) {
561    let unionHead = `export type ${unionItem.name} = `
562    let unionBody = ''
563    try {
564      for (const element of unionItem.members) {
565        unionBody += `${transTskey2Ckey(element.type)} | `
566      };
567    } catch (e) {
568      let errmsg = 'generate dts file error: ' + JSON.stringify(e);
569      Logger.getInstance().error(errmsg);
570    }
571    out += unionHead + unionBody.slice(0, -2) + ';\n\n'
572    if (unionItem.name && unionItem.alias && unionItem.name !== unionItem.alias) {
573      out += `export type ${unionItem.alias} = ${unionItem.name};\n\n`
574    }
575  }
576  return out;
577}
578
579export function genDtsFile(rootInfo: GenInfo, out: string) {
580  // gen enums
581  let fileContent = getDtsEnum(rootInfo);
582  // gen functions
583  fileContent += getDtsFunction(rootInfo);
584  // gen classes
585  fileContent += getDtsClasses(rootInfo);
586  // gen struct
587  fileContent += getDtsStructs(rootInfo);
588  // gen union
589  fileContent += getDtsUnions(rootInfo);
590
591  let dtsFileName = rootInfo.fileName + dtsFileExt;
592  let outPath = ''
593  if (out === undefined || out === null || out.trim() === '') {
594    let dirPath = path.dirname(rootInfo.rawFilePath);
595    outPath = path.join(dirPath, dtsFileName);
596  } else {
597    outPath = path.join(out, dtsFileName);
598  }
599  fs.writeFileSync(outPath, fileContent);
600  Logger.getInstance().info('generate success!')
601  return outPath;
602}