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 } = require("./interface"); 19const { analyzeClass } = require("./class"); 20const { analyzeEnum } = require("./enum"); 21const { NapiLog } = require("../tools/NapiLog"); 22 23/**namespace解析 */ 24function analyzeNamespace(data) { 25 let result = { 26 exports: [], 27 enum: [], 28 const: [], 29 type: [], 30 function: [], 31 interface: [], 32 class: [], 33 namespace: [], 34 } 35 while (data != '\n') { 36 let oldData = data 37 data = removeEmptyLine(data) 38 let matchs = re.match(" *\n*", data) 39 // 只剩下空格和回车时,解析完成 40 if (matchs && matchs.regs[0][1] == data.length) break 41 let parseEnumResult = parseEnum(matchs, data, result) 42 if (parseEnumResult != null) { 43 data = parseEnumResult 44 } 45 let parseInterResult = parseInterface(matchs, data, result) 46 if (parseInterResult != null) { 47 data = parseInterResult 48 } 49 let parseFunctionResult = parseFunction(matchs, data, result) 50 if (parseFunctionResult != null) { 51 data = parseFunctionResult 52 } 53 let parseTypeResult = parseType(matchs, data, result) 54 if (parseTypeResult != null) { 55 data = parseTypeResult 56 } 57 let parseClassResult = parseClass(matchs, data, result) 58 if (parseClassResult != null) { 59 data = parseClassResult 60 } 61 let parseNamespaceResult = parseNamespace(matchs, data, result) 62 if (parseNamespaceResult != null) { 63 data = parseNamespaceResult 64 } 65 data = removeReg(matchs, data, result) 66 if (oldData == data) { 67 NapiLog.logError("解析Namespace失败"); 68 NapiLog.logError("[", data.substring(0, data.length > 128 ? 128 : data.length), "]"); 69 break; 70 } 71 } 72 return result 73} 74 75function parseNamespace(matchs, data, result) { 76 matchs = re.match("(export )*namespace ([a-zA-Z0-9]+) ({)", data) 77 if (matchs) { 78 let namespaceName = re.getReg(data, matchs.regs[2]) 79 let namespaceBody = checkOutBody(data, matchs.regs[3][0], null, true) 80 result.namespace.push({ 81 name: namespaceName, 82 body: analyzeNamespace(namespaceBody) 83 }) 84 data = data.substring(matchs.regs[3][0] + namespaceBody.length + 2, data.length) 85 if (matchs.regs[1][0] != -1) { 86 result.exports.push(namespaceName) 87 } 88 } 89 return data 90} 91 92function parseClass(matchs, data, result) { 93 matchs = re.match( 94 "(export )*class ([A-Za-z_0-9]+)(<T>)* *(extends [a-zA-Z_0-9, ]+)* *(implements [a-zA-Z_0-9, ]+)* *({)" 95 , data) 96 if (matchs) { 97 // class类型也解析成interface结构,该结构在后面生成C++代码时会按napi_define_class处理成C++的class 98 return createInterfaceData(matchs, data, result) 99 } 100 return data 101} 102 103function parseEnum(matchs, data, result) { 104 matchs = re.match("(export )*enum *([A-Za-z_0-9]+) *({)", data) 105 if (matchs != null) { 106 let enumName = re.getReg(data, matchs.regs[2]); 107 let enumBody = checkOutBody(data, matchs.regs[3][0], null, null) 108 result.enum.push({ 109 name: enumName, 110 body: analyzeEnum(enumBody.substring(1, enumBody.length - 1)) 111 }) 112 data = data.substring(matchs.regs[3][0] + enumBody.length) 113 if (matchs.regs[1][0] != -1) { 114 result.exports.push(enumName) 115 } 116 } 117 matchs = re.match("(export )*const ([A-Za-z_0-9]+) *[:=]{1} ([A-Za-z_0-9]+);", data) 118 if (matchs) { 119 let constName = re.getReg(data, matchs.regs[1]) 120 result.const.push({ 121 name: constName, 122 body: re.getReg(data, matchs.regs[2]) 123 }) 124 data = re.removeReg(data, matchs.regs[0]) 125 if (matchs.regs[1][0] != -1) { 126 result.exports.push(constName) 127 } 128 } 129 return data 130} 131 132function parseType(matchs, data, result) { 133 matchs = re.match("(export )*type ([a-zA-Z]+) = *([\\(\\):=a-zA-Z<> |\n']+);", data) 134 if (matchs) { 135 let typeName = re.getReg(data, matchs.regs[2]); 136 result.type.push({ 137 name: typeName, 138 body: re.getReg(data, matchs.regs[3]) 139 }) 140 data = re.removeReg(data, matchs.regs[0]) 141 if (matchs.regs[1][0] != -1) { 142 result.exports.push(typeName) 143 } 144 } 145 matchs = re.match("(export )*type ([a-zA-Z]+) = ({)", data) 146 if (matchs) { 147 let typeName = re.getReg(data, matchs.regs[2]); 148 let typeBody = checkOutBody(data, matchs.regs[3][0], null, true) 149 result.type.push({ 150 name: typeName, 151 body: typeBody 152 }) 153 data = data.substring(matchs.regs[3][0] + typeBody.length + 2, data.length) 154 if (matchs.regs[1][0] != -1) { 155 result.exports.push(typeName) 156 } 157 } 158 return data 159} 160 161function parseFunction(matchs, data, result) { 162 matchs = re.match("(export )*function (\\$*[A-Za-z0-9_]+) *(\\()", data) 163 if (null == matchs) { 164 matchs = re.match("(export )*function (static )*(\\$*[A-Za-z0-9_]+) *(\\()", data) 165 } 166 if (matchs) { 167 let funcName = re.getReg(data, 168 matchs.regs.length == 5 ? [matchs.regs[2][0], matchs.regs[3][1]] : matchs.regs[2]) 169 let funcValue = checkOutBody(data, 170 matchs.regs.length == 5 ? matchs.regs[4][0] : matchs.regs[3][0], ["(", ")"], null) 171 let funcRet = checkOutBody(data.substring(matchs.regs.length == 5 ? 172 matchs.regs[4][0] : matchs.regs[3][0] + funcValue.length), 0, ["", "\n"], null) 173 data = data.substring(matchs.regs.length == 5 ? 174 matchs.regs[4][0] : matchs.regs[3][0] + funcValue.length + funcRet.length) 175 let matchFunc = re.match(" *: *([A-Za-z0-9_<>{}:;, .=]+);*", funcRet) 176 let matchFuncArray = re.match(" *: *([A-Za-z0-9]+)(\\[]);*", funcRet) 177 if (matchFuncArray) { 178 funcRet = re.getReg(funcRet, [matchFuncArray.regs[1][0], matchFuncArray.regs[2][1]]) 179 } 180 else if (matchFunc) { 181 funcRet = re.getReg(funcRet, matchFunc.regs[1]) 182 } 183 else { 184 funcRet = "void" 185 } 186 funcRet = re.replaceAll(re.replaceAll(funcRet, " ", ""), "\n", "") 187 188 if(funcRet[funcRet.length-1] == ";"){ 189 funcRet = funcRet.substring(0, funcRet.length-1) 190 } 191 let funcDetail = analyzeFunction( 192 result, false, funcName, funcValue.substring(1, funcValue.length - 1), funcRet) 193 if (funcDetail != null) { 194 // 完全一样的方法不重复添加 (如同名同参的AsyncCallback和Promise方法) 195 addUniqFunc2List(funcDetail, result.function) 196 } 197 if (matchs.regs[1][0] != -1) { 198 result.exports.push(funcName) 199 } 200 } 201 return data 202} 203 204/** 205 * 提取当前类继承或实现的父类名称列表 206 * @param firstKey 继承/实现关键字 (extends或implements) 207 * @param secondKey 继承/实现关键字 (extends或implements) 208 * @param parentStr 正则匹配到的继承语句 (如 extends xx1, xx2 implements yy1, yy2) 209 * @returns 继承的名称列表 ([xx1, xx2, yy1, yy2]) 210 */ 211function getParentNameList(firstKey, secondKey, parentStr) { 212 if (parentStr == '') { 213 return [] 214 } 215 216 let firstParents = '' 217 let secondParents = '' 218 if (parentStr.indexOf(secondKey) > 0) { 219 // 同时出现extends和implements关键字的情况 (如 extends xx1, xx2 implements yy1, yy2) 220 firstParents = parentStr.split(secondKey)[0].split(firstKey)[1] 221 secondParents = parentStr.split(secondKey)[1].trim() 222 } else { 223 // 只有extends或implements一种关键字的情况 (如 extends xx1, xx2 或者 implements yy1, yy2) 224 firstParents = parentStr.split(firstKey)[1] 225 } 226 227 let nameList = firstParents.split(",") 228 if (secondParents != '') { 229 let secondList = secondParents.split(",") 230 nameList.push(...secondList) 231 } 232 233 return nameList 234} 235 236/** 237 * 创建interface数据结构 238 * @param matchs 正则匹配对象 239 * @param data 原始ts文件内容 240 * @param result 解析后的ts数据结构 241 * @returns data 原始ts文件内容中剩余未解析的部分 242 */ 243function createInterfaceData (matchs, data, result) { 244 let interfaceName = re.getReg(data, matchs.regs[2]) 245 let interfaceBody = checkOutBody(data, matchs.regs[6][0], null, null) 246 let bodyObj = analyzeInterface(interfaceBody.substring(1, interfaceBody.length - 1), result.interface) 247 let extendsParent = re.getReg(data, matchs.regs[4]) 248 let implementParent = re.getReg(data, matchs.regs[5]) 249 bodyObj.parentNameList = [] 250 if(extendsParent != '') { 251 bodyObj.parentNameList = getParentNameList("extends", "implements", extendsParent) 252 } 253 if(implementParent != '') { 254 bodyObj.parentNameList = getParentNameList("implements", "extends", implementParent) 255 } 256 for (let i in bodyObj.parentNameList) { 257 bodyObj.parentNameList[i] = bodyObj.parentNameList[i].trim() 258 if (bodyObj.parentNameList[i] == interfaceName) { 259 // 接口不能自己继承自己 260 NapiLog.logError("The interface [%s] can not extends with itself.".format(interfaceName)) 261 return data 262 } 263 } 264 265 bodyObj.parentList = [] //该接口继承的父类型列表 266 bodyObj.childList = [] //继承自该接口的子类型列表 267 268 result.interface.push({ 269 name: interfaceName, 270 body: bodyObj 271 }) 272 let rr = matchs.regs[6][0] 273 rr = matchs.regs[6][0] + interfaceBody.length 274 let tmp = data[rr] 275 data = data.substring(matchs.regs[6][0] + interfaceBody.length, data.length) 276 if (matchs.regs[1][0] != -1) { 277 result.exports.push(interfaceName) 278 } 279 return data 280} 281 282function parseInterface(matchs, data, result) { 283 matchs = re.match( 284 "(export )*interface ([A-Za-z_0-9]+)(<T>)* *(extends [a-zA-Z_0-9, ]+)* *(implements [a-zA-Z_0-9, ]+)* *({)" 285 , data) 286 if (matchs) { 287 return createInterfaceData (matchs, data, result) 288 } 289 return data 290} 291 292function removeReg(matchs, data, result) { 293 matchs = re.match("export { ([a-zA-Z]+) };", data) 294 if (matchs) { 295 let exportName = re.getReg(data, matchs.regs[1]) 296 result.exports.push(exportName) 297 data = re.removeReg(data, matchs.regs[0]) 298 } 299 matchs = re.match("export import [a-zA-Z]+ = [a-zA-Z\\.]+;", data) 300 if (matchs) { 301 data = re.removeReg(data, matchs.regs[0]) 302 } 303 matchs = re.match("readonly [a-zA-Z]+: [a-z\\[\\]]+;*", data) 304 if (matchs) { 305 data = re.removeReg(data, matchs.regs[0]) 306 } 307 return data 308} 309module.exports = { 310 analyzeNamespace, 311 parseNamespace, 312 parseEnum, 313 parseFunction, 314 parseInterface 315}