• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2* Copyright (c) 2022 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*/
15const re = require("../tools/re");
16const { removeEmptyLine, checkOutBody, addUniqFunc2List } = require("../tools/tool");
17const { analyzeFunction } = require("./function");
18const { analyzeInterface, parseNotes } = require("./interface");
19const { analyzeEnum } = require("./enum");
20const { NapiLog } = require("../tools/NapiLog");
21const { analyzeType, analyzeType2, analyzeType2Result } = require("./type");
22const { NumberIncrease, EnumValueType, getLogErrInfo } = require("../tools/common");
23
24function preProcessData(data) {
25    data = data.indexOf("//") < 0 ? data : parseNotes(data);
26    data = re.replaceAll(data, "\n{", "{");
27    return data;
28}
29
30function getDataByResult(result) {
31    let data = null
32    if (result !== null) {
33        data = result
34    }
35    return data
36}
37
38/**namespace解析 */
39function analyzeNamespace(data) {
40    let result = {
41        exports: [],
42        enum: [],
43        const: [],
44        type: [],
45        function: [],
46        interface: [],
47        class: [],
48        namespace: [],
49        callFunction: [],
50    }
51    while (data !== '\n') {
52        let oldData = data
53        data = removeEmptyLine(data)
54        let matchs = re.match(" *\n*", data)
55        data = preProcessData(data);
56        // 只剩下空格和回车时,解析完成
57        if (matchs && matchs.regs[0][1] === data.length) break
58        let parseEnumResult = parseEnum(matchs, data, result)
59        data = getDataByResult(parseEnumResult)
60
61        result = parseEnumType(result);
62
63        let parseInterResult = parseInterface(matchs, data, result)
64        data = getDataByResult(parseInterResult)
65
66        let parseFunctionResult = parseFunction(matchs, data, result)
67        data = getDataByResult(parseFunctionResult)
68
69        let parseTypeResult = parseType(matchs, data, result)
70        data = getDataByResult(parseTypeResult)
71
72        let parseClassResult = parseClass(matchs, data, result)
73        data = getDataByResult(parseClassResult)
74
75        let parseNamespaceResult = parseNamespace(matchs, data, result)
76        data = getDataByResult(parseNamespaceResult)
77
78        data = removeReg(matchs, data, result)
79        if (oldData === data) {
80            NapiLog.logError("解析Namespace失败");
81            NapiLog.logError("[", data.substring(0, data.length > 128 ? 128 : data.length), "]");
82            break;
83        }
84    }
85    return result
86}
87
88function parseEnumType(result) {
89    if (null === result) {
90        return null;
91    }
92
93    for (let i in result.enum) {
94        let enumm = result.enum[i]
95
96        // interface 匹配
97        for (let i in result.interface) {
98          let interf = result.interface[i]
99          if(!isValidValue(interf)) {
100            NapiLog.logError("parseEnumType interf is null!");
101            return null;
102          }
103
104          // function 匹配
105          for (let j in interf.body.function) {
106            let func = interf.body.function[j];
107            if(!isValidValue(func)) {
108                NapiLog.logError("parseEnumType func is null!");
109                return null;
110            }
111
112            // 参数匹配
113            for (let k in func.value) {
114                let v = func.value[k];
115                if(!isValidValue(v)) {
116                    NapiLog.logError("parseEnumType func.value is null!");
117                    return null;
118                }
119
120                if (v.type ===  enumm.name) {
121                    if (enumm.body.enumValueType === EnumValueType.ENUM_VALUE_TYPE_NUMBER) {
122                        v.type = "NUMBER_TYPE_" + NumberIncrease.getAndIncrease();
123                    } else if (enumm.body.enumValueType === EnumValueType.ENUM_VALUE_TYPE_STRING) {
124                        v.type = "string";
125                    } else {
126                        NapiLog.logError("parseEnumType for interface function value is not support this type %s."
127                            .format(enumm.body.enumValueType), getLogErrInfo());
128                        return null;
129                    }
130                    result.interface[i].body.function[j].value[k].type = v.type;
131                }
132            }
133          }
134        }
135    }
136    return result
137}
138
139function parseNamespace(matchs, data, result) {
140    matchs = re.match("(export )*namespace ([a-zA-Z0-9]+) ({)", data)
141    if (matchs) {
142        let namespaceName = re.getReg(data, matchs.regs[2])
143        let namespaceBody = checkOutBody(data, matchs.regs[3][0], null, true)
144        result.namespace.push({
145            name: namespaceName,
146            body: analyzeNamespace(namespaceBody)
147        })
148        data = data.substring(matchs.regs[3][0] + namespaceBody.length + 2, data.length)
149        if (matchs.regs[1][0] !== -1) {
150            result.exports.push(namespaceName)
151        }
152    }
153    return data
154}
155
156function parseClass(matchs, data, result) {
157    matchs = re.match(
158        "(export )*class ([A-Za-z_0-9]+)(<T>)* *(extends [a-zA-Z_0-9, ]+)* *(implements [a-zA-Z_0-9, ]+)* *({)"
159        , data)
160    if (matchs) {
161        // class类型也解析成interface结构,该结构在后面生成C++代码时会按napi_define_class处理成C++的class
162        return createInterfaceData(matchs, data, result)
163    }
164    return data
165}
166
167function parseEnum(matchs, data, result) {
168    matchs = re.match("(export )*enum *([A-Za-z_0-9]+) *({)", data)
169    if (matchs !== null) {
170        let enumName = re.getReg(data, matchs.regs[2]);
171        let enumBody = checkOutBody(data, matchs.regs[3][0], null, null)
172        result.enum.push({
173            name: enumName,
174            body: analyzeEnum(enumBody.substring(1, enumBody.length - 1))
175        })
176        data = data.substring(matchs.regs[3][0] + enumBody.length)
177        if (matchs.regs[1][0] !== -1) {
178            result.exports.push(enumName)
179        }
180    }
181    matchs = re.match("(export )*const ([A-Za-z_0-9]+) *[:=]{1} ([A-Za-z_0-9]+);", data)
182    if (matchs) {
183        let constName = re.getReg(data, matchs.regs[1])
184        result.const.push({
185            name: constName,
186            body: re.getReg(data, matchs.regs[2])
187        })
188        data = re.removeReg(data, matchs.regs[0])
189        if (matchs.regs[1][0] !== -1) {
190            result.exports.push(constName)
191        }
192    }
193    return data
194}
195
196function isValidValue(value) {
197    if (value === null || value === undefined) {
198      return false;
199    }
200    return true;
201}
202
203function getTypeInfo(result, typeName, typeType, isEnum) {
204    if (!isValidValue(result) || !isValidValue(result.type)) {
205        NapiLog.logError("getTypeInfo: result or result.type is invalid!");
206    }
207    result.type.push({
208        name: typeName,
209        body: typeType,
210        isEnum: isEnum
211    })
212}
213
214function parseType(matchs, data, result) {
215    matchs = re.match("(export )*type ([a-zA-Z]+) *= *([\\(\\):=a-zA-Z<> |]+);", data)
216    if (matchs) {
217        let typeName = re.getReg(data, matchs.regs[2]);
218        let typeType = re.getReg(data, matchs.regs[3]);
219        let index = typeType.indexOf("number")
220        if (index !== -1) {
221          typeType = typeType.replace("number", "NUMBER_TYPE_" + NumberIncrease.getAndIncrease())
222        }
223        getTypeInfo(result, typeName, typeType, false);
224        data = re.removeReg(data, matchs.regs[0])
225        if (matchs.regs[1][0] !== -1) {
226            result.exports.push(typeName)
227        }
228    }
229
230    matchs = re.match("(export )*type ([a-zA-Z]+) *= *([\\(\\):=a-zA-Z<> |\n']+);", data)
231    if (matchs) {
232        let typeName = re.getReg(data, matchs.regs[2]);
233        let typeBody = re.getReg(data, matchs.regs[3]);
234
235        getTypeInfo(result, typeName, analyzeType2(typeBody.substring(1, typeBody.length - 1)), true);
236        data = re.removeReg(data, matchs.regs[0])
237        if (matchs.regs[1][0] !== -1) {
238            result.exports.push(typeName)
239        }
240    }
241
242    matchs = re.match("(export )*type ([a-zA-Z]+) *= *({)", data)
243    if (matchs) {
244        let typeName = re.getReg(data, matchs.regs[2]);
245        let typeBody = checkOutBody(data, matchs.regs[3][0], null, true)
246        if (typeBody === null) {
247            NapiLog.logError("ParseType typeBody is null!");
248        }
249        let bodyObj = analyzeType(typeBody.substring(1, typeBody.length - 1), result.type)
250        getTypeInfo(result, typeName, bodyObj, false);
251        data = data.substring(matchs.regs[3][0] + typeBody.length + 2, data.length)
252        if (matchs.regs[1][0] !== -1) {
253            result.exports.push(typeName)
254        }
255    }
256    return data
257}
258
259function parseFunction(matchs, data, result) {
260    matchs = re.match("(export )*function (\\$*[A-Za-z0-9_]+) *(\\()", data)
261    if (null == matchs) {
262        matchs = re.match("(export )*function (static )*(\\$*[A-Za-z0-9_]+) *(\\()", data)
263    }
264    if (null == matchs) {
265        matchs = re.match("(export )*function (static )*(register\\$*[A-Za-z0-9_]+) *(\\()", data)
266    }
267    if (matchs) {
268        let funcName = re.getReg(data,
269            matchs.regs.length === 5 ? [matchs.regs[2][0], matchs.regs[3][1]] : matchs.regs[2])
270        let funcValue = checkOutBody(data,
271            matchs.regs.length === 5 ? matchs.regs[4][0] : matchs.regs[3][0], ["(", ")"], null)
272        let funcRet = checkOutBody(data.substring(matchs.regs.length === 5 ?
273            matchs.regs[4][0] : matchs.regs[3][0] + funcValue.length), 0, ["", "\n"], null)
274        data = data.substring(matchs.regs.length === 5 ?
275            matchs.regs[4][0] : matchs.regs[3][0] + funcValue.length + funcRet.length)
276        let matchFunc = re.match(" *: *([A-Za-z0-9_<>{}\\[\\]:;, .=]+);*", funcRet)
277        let matchFuncArray = re.match(" *: *([A-Za-z0-9]+)(\\[]);*", funcRet)
278        if (matchFuncArray) {
279            funcRet = re.getReg(funcRet, [matchFuncArray.regs[1][0], matchFuncArray.regs[2][1]])
280        }
281        else if (matchFunc) {
282            funcRet = re.getReg(funcRet, matchFunc.regs[1])
283        }
284        else {
285            funcRet = "void"
286        }
287        funcRet = re.replaceAll(re.replaceAll(funcRet, " ", ""), "\n", "")
288
289        if(funcRet[funcRet.length-1] === ";"){
290            funcRet = funcRet.substring(0, funcRet.length-1)
291        }
292        let funcDetail = analyzeFunction(
293            result, false, funcName, funcValue.substring(1, funcValue.length - 1), funcRet, result)
294        if (funcDetail !== null) {
295            // 完全一样的方法不重复添加 (如同名同参的AsyncCallback和Promise方法)
296            addUniqFunc2List(funcDetail, result.function)
297        }
298        if (matchs.regs[1][0] !== -1) {
299            result.exports.push(funcName)
300        }
301    }
302    return data
303}
304
305/**
306 * 提取当前类继承或实现的父类名称列表
307 * @param firstKey 继承/实现关键字 (extends或implements)
308 * @param secondKey 继承/实现关键字 (extends或implements)
309 * @param parentStr 正则匹配到的继承语句 (如 extends xx1, xx2 implements yy1, yy2)
310 * @returns 继承的名称列表 ([xx1, xx2, yy1, yy2])
311 */
312function getParentNameList(firstKey, secondKey, parentStr) {
313    if (parentStr === '') {
314        return []
315    }
316
317    let firstParents = ''
318    let secondParents = ''
319    if (parentStr.indexOf(secondKey) > 0) {
320        // 同时出现extends和implements关键字的情况 (如 extends xx1, xx2 implements yy1, yy2)
321        firstParents = parentStr.split(secondKey)[0].split(firstKey)[1]
322        secondParents = parentStr.split(secondKey)[1].trim()
323    } else {
324        // 只有extends或implements一种关键字的情况 (如 extends xx1, xx2 或者 implements yy1, yy2)
325        firstParents = parentStr.split(firstKey)[1]
326    }
327
328    let nameList = firstParents.split(",")
329    if (secondParents !== '') {
330        let secondList = secondParents.split(",")
331        nameList.push(...secondList)
332    }
333
334    return nameList
335}
336
337/**
338 * 创建interface数据结构
339 * @param matchs 正则匹配对象
340 * @param data 原始ts文件内容
341 * @param result 解析后的ts数据结构
342 * @returns data 原始ts文件内容中剩余未解析的部分
343 */
344function createInterfaceData (matchs, data, result) {
345    let interfaceName = re.getReg(data, matchs.regs[2])
346    let interfaceBody = checkOutBody(data, matchs.regs[6][0], null, null)
347    let bodyObj = analyzeInterface(interfaceBody.substring(1, interfaceBody.length - 1), result.interface,
348      result, interfaceName)
349    let extendsParent = re.getReg(data, matchs.regs[4])
350    let implementParent = re.getReg(data, matchs.regs[5])
351    bodyObj.parentNameList = []
352    if(extendsParent !== '') {
353        bodyObj.parentNameList = getParentNameList("extends", "implements", extendsParent)
354    }
355    if(implementParent !== '') {
356        bodyObj.parentNameList = getParentNameList("implements", "extends", implementParent)
357    }
358    for (let i in bodyObj.parentNameList) {
359        bodyObj.parentNameList[i] = bodyObj.parentNameList[i].trim()
360        if (bodyObj.parentNameList[i] === interfaceName) {
361            // 接口不能自己继承自己
362            NapiLog.logError("The interface [%s] can not extends with itself.".format(interfaceName))
363            return data
364        }
365    }
366
367    bodyObj.parentList = [] // 该接口继承的父类型列表
368    bodyObj.childList = [] // 继承自该接口的子类型列表
369
370    result.interface.push({
371        name: interfaceName,
372        body: bodyObj
373    })
374    let rr = matchs.regs[6][0]
375    rr = matchs.regs[6][0] + interfaceBody.length
376    let tmp = data[rr]
377    data = data.substring(matchs.regs[6][0] + interfaceBody.length, data.length)
378    if (matchs.regs[1][0] !== -1) {
379        result.exports.push(interfaceName)
380    }
381    return data
382}
383
384function parseInterface(matchs, data, result) {
385    matchs = re.match(
386        "(export )*interface ([A-Za-z_0-9]+)(<T>)* *(extends [a-zA-Z_0-9, ]+)* *(implements [a-zA-Z_0-9, ]+)* *({)"
387        , data)
388    if (matchs) {
389        return createInterfaceData (matchs, data, result)
390    }
391    return data
392}
393
394function removeReg(matchs, data, result) {
395    matchs = re.match("export { ([a-zA-Z]+) };", data)
396    if (matchs) {
397        let exportName = re.getReg(data, matchs.regs[1])
398        result.exports.push(exportName)
399        data = re.removeReg(data, matchs.regs[0])
400    }
401    matchs = re.match("export import [a-zA-Z]+ = [a-zA-Z\\.]+;", data)
402    if (matchs) {
403        data = re.removeReg(data, matchs.regs[0])
404    }
405    matchs = re.match("readonly [a-zA-Z]+: [a-z\\[\\]]+;*", data)
406    if (matchs) {
407        data = re.removeReg(data, matchs.regs[0])
408    }
409    return data
410}
411module.exports = {
412    analyzeNamespace,
413    parseNamespace,
414    parseEnum,
415    parseFunction,
416    parseInterface,
417    parseClass,
418    parseType
419}