typenative 0.0.16 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish-github-packages.yml +73 -36
- package/CHANGELOG.md +109 -80
- package/LICENSE +21 -21
- package/README.md +154 -109
- package/bin/index.js +133 -126
- package/bin/transpiler.js +950 -35
- package/package.json +63 -53
- package/types/typenative.d.ts +247 -177
package/bin/transpiler.js
CHANGED
|
@@ -9,6 +9,14 @@ let promiseResolveName = '';
|
|
|
9
9
|
const dangerousNames = new Set(['main']);
|
|
10
10
|
const renamedFunctions = new Map();
|
|
11
11
|
const variableTypes = new Map();
|
|
12
|
+
const variableGoTypes = new Map();
|
|
13
|
+
const variableClassNames = new Map();
|
|
14
|
+
const classPropertyTypes = new Map();
|
|
15
|
+
const classMethodReturnTypes = new Map();
|
|
16
|
+
const interfacePropertyTypes = new Map();
|
|
17
|
+
const typeAliases = new Map();
|
|
18
|
+
const enumNames = new Set();
|
|
19
|
+
const enumBaseTypes = new Map();
|
|
12
20
|
export function transpileToNative(code) {
|
|
13
21
|
const sourceFile = ts.createSourceFile('main.ts', code, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS);
|
|
14
22
|
TypeCheker = ts.createProgram(['main.ts'], {}).getTypeChecker();
|
|
@@ -18,16 +26,24 @@ export function transpileToNative(code) {
|
|
|
18
26
|
promiseResolveName = '';
|
|
19
27
|
renamedFunctions.clear();
|
|
20
28
|
variableTypes.clear();
|
|
29
|
+
variableGoTypes.clear();
|
|
30
|
+
variableClassNames.clear();
|
|
31
|
+
classPropertyTypes.clear();
|
|
32
|
+
classMethodReturnTypes.clear();
|
|
33
|
+
interfacePropertyTypes.clear();
|
|
34
|
+
typeAliases.clear();
|
|
35
|
+
enumNames.clear();
|
|
36
|
+
enumBaseTypes.clear();
|
|
21
37
|
const transpiledCode = visit(sourceFile, { addFunctionOutside: true });
|
|
22
38
|
const transpiledCodeOutside = outsideNodes.map((n) => visit(n, { isOutside: true })).join('\n');
|
|
23
|
-
return `package main
|
|
24
|
-
|
|
25
|
-
${[...importedPackages].map((pkg) => `import "${pkg}"`).join('\n')}
|
|
26
|
-
|
|
27
|
-
func main() {
|
|
28
|
-
${transpiledCode.trim()}
|
|
29
|
-
}
|
|
30
|
-
|
|
39
|
+
return `package main
|
|
40
|
+
|
|
41
|
+
${[...importedPackages].map((pkg) => `import "${pkg}"`).join('\n')}
|
|
42
|
+
|
|
43
|
+
func main() {
|
|
44
|
+
${transpiledCode.trim()}
|
|
45
|
+
}
|
|
46
|
+
|
|
31
47
|
${transpiledCodeOutside.trim()}`;
|
|
32
48
|
}
|
|
33
49
|
export function visit(node, options = {}) {
|
|
@@ -43,8 +59,17 @@ export function visit(node, options = {}) {
|
|
|
43
59
|
return 'nil';
|
|
44
60
|
return getSafeName(node.text);
|
|
45
61
|
}
|
|
46
|
-
else if (ts.isStringLiteral(node)) {
|
|
47
|
-
return
|
|
62
|
+
else if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
63
|
+
return toGoStringLiteral(node.text);
|
|
64
|
+
}
|
|
65
|
+
else if (ts.isAsExpression(node)) {
|
|
66
|
+
return visit(node.expression);
|
|
67
|
+
}
|
|
68
|
+
else if (ts.isTypeAssertionExpression(node)) {
|
|
69
|
+
return visit(node.expression);
|
|
70
|
+
}
|
|
71
|
+
else if (ts.isTemplateExpression(node)) {
|
|
72
|
+
return visitTemplateExpression(node);
|
|
48
73
|
}
|
|
49
74
|
else if (ts.isNumericLiteral(node)) {
|
|
50
75
|
return `float64(${node.text})`;
|
|
@@ -73,12 +98,23 @@ export function visit(node, options = {}) {
|
|
|
73
98
|
return `[]${type} {${node.elements.map((e) => visit(e)).join(', ')}}`;
|
|
74
99
|
}
|
|
75
100
|
else if (ts.isBlock(node)) {
|
|
76
|
-
return `{\n\t\t${
|
|
101
|
+
return `{\n\t\t${options.prefixBlockContent ?? ''}${node.statements
|
|
102
|
+
.map((n) => visit(n))
|
|
103
|
+
.join('\t')}${options.extraBlockContent ?? ''}}${options.inline ? '' : '\n\t'}`;
|
|
77
104
|
}
|
|
78
105
|
else if (ts.isElementAccessExpression(node)) {
|
|
106
|
+
if (hasQuestionDot(node)) {
|
|
107
|
+
return visitOptionalElementAccess(node);
|
|
108
|
+
}
|
|
79
109
|
return `${visit(node.expression)}[int(${visit(node.argumentExpression)})]`;
|
|
80
110
|
}
|
|
81
111
|
else if (ts.isPropertyAccessExpression(node)) {
|
|
112
|
+
if (hasQuestionDot(node)) {
|
|
113
|
+
return visitOptionalPropertyAccess(node);
|
|
114
|
+
}
|
|
115
|
+
if (ts.isIdentifier(node.expression) && enumNames.has(node.expression.text)) {
|
|
116
|
+
return `${getSafeName(node.expression.text)}_${getEnumMemberName(node.name)}`;
|
|
117
|
+
}
|
|
82
118
|
const leftSide = visit(node.expression);
|
|
83
119
|
const rightSide = visit(node.name);
|
|
84
120
|
const objectType = resolveExpressionType(node.expression);
|
|
@@ -88,9 +124,18 @@ export function visit(node, options = {}) {
|
|
|
88
124
|
const type = getType(node.type);
|
|
89
125
|
// Track variable type for type-aware method dispatch
|
|
90
126
|
if (ts.isIdentifier(node.name)) {
|
|
127
|
+
if (node.type) {
|
|
128
|
+
variableGoTypes.set(node.name.text, getType(node.type));
|
|
129
|
+
}
|
|
91
130
|
const cat = node.type ? getTypeCategory(node.type) : undefined;
|
|
92
131
|
if (cat) {
|
|
93
132
|
variableTypes.set(node.name.text, cat);
|
|
133
|
+
if (cat === 'class' && node.type) {
|
|
134
|
+
const className = getClassNameFromTypeNode(node.type);
|
|
135
|
+
if (className) {
|
|
136
|
+
variableClassNames.set(node.name.text, className);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
94
139
|
}
|
|
95
140
|
else if (node.initializer &&
|
|
96
141
|
ts.isNewExpression(node.initializer) &&
|
|
@@ -100,11 +145,18 @@ export function visit(node, options = {}) {
|
|
|
100
145
|
}
|
|
101
146
|
else if (classNames.has(node.initializer.expression.text)) {
|
|
102
147
|
variableTypes.set(node.name.text, 'class');
|
|
148
|
+
variableClassNames.set(node.name.text, node.initializer.expression.text);
|
|
103
149
|
}
|
|
104
150
|
}
|
|
105
151
|
else if (node.initializer && ts.isRegularExpressionLiteral(node.initializer)) {
|
|
106
152
|
variableTypes.set(node.name.text, 'RegExp');
|
|
107
153
|
}
|
|
154
|
+
if (!variableGoTypes.has(node.name.text) && node.initializer) {
|
|
155
|
+
const inferredType = inferExpressionType(node.initializer);
|
|
156
|
+
if (inferredType) {
|
|
157
|
+
variableGoTypes.set(node.name.text, inferredType);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
108
160
|
}
|
|
109
161
|
let initializer = node.initializer ? `= ${visit(node.initializer)}` : '';
|
|
110
162
|
// Wrap non-nil values assigned to nullable primitive pointer types
|
|
@@ -119,6 +171,10 @@ export function visit(node, options = {}) {
|
|
|
119
171
|
return `${type === ':' ? '' : 'var '}${visit(node.name)} ${type}${type === ':' ? '' : ' '}${initializer}`;
|
|
120
172
|
}
|
|
121
173
|
else if (ts.isCallExpression(node)) {
|
|
174
|
+
if (hasQuestionDot(node) ||
|
|
175
|
+
(ts.isPropertyAccessExpression(node.expression) && hasQuestionDot(node.expression))) {
|
|
176
|
+
return visitOptionalCall(node);
|
|
177
|
+
}
|
|
122
178
|
// Handle setTimeout specially to get raw delay value
|
|
123
179
|
if (ts.isIdentifier(node.expression) && node.expression.text === 'setTimeout') {
|
|
124
180
|
importedPackages.add('time');
|
|
@@ -127,6 +183,10 @@ export function visit(node, options = {}) {
|
|
|
127
183
|
const delay = ts.isNumericLiteral(delayNode) ? delayNode.text : visit(delayNode);
|
|
128
184
|
return `time.AfterFunc(${delay} * time.Millisecond, ${callback.trimEnd()})`;
|
|
129
185
|
}
|
|
186
|
+
const arrayHigherOrderCall = visitArrayHigherOrderCall(node);
|
|
187
|
+
if (arrayHigherOrderCall) {
|
|
188
|
+
return arrayHigherOrderCall;
|
|
189
|
+
}
|
|
130
190
|
const caller = visit(node.expression);
|
|
131
191
|
const safeCaller = getSafeName(caller);
|
|
132
192
|
const typeArgs = getTypeArguments(node.typeArguments);
|
|
@@ -144,7 +204,13 @@ export function visit(node, options = {}) {
|
|
|
144
204
|
else if (ts.isPostfixUnaryExpression(node)) {
|
|
145
205
|
return `${visit(node.operand, { inline: true })}${getOperatorText(node.operator)}`;
|
|
146
206
|
}
|
|
207
|
+
else if (ts.isConditionalExpression(node)) {
|
|
208
|
+
return visitConditionalExpression(node);
|
|
209
|
+
}
|
|
147
210
|
else if (ts.isBinaryExpression(node)) {
|
|
211
|
+
if (node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
212
|
+
return visitNullishCoalescingExpression(node);
|
|
213
|
+
}
|
|
148
214
|
let op = node.operatorToken.getText();
|
|
149
215
|
if (op === '===')
|
|
150
216
|
op = '==';
|
|
@@ -171,9 +237,23 @@ export function visit(node, options = {}) {
|
|
|
171
237
|
})}; ${visit(node.incrementor, { inline: true })}${visit(node.statement)}`;
|
|
172
238
|
}
|
|
173
239
|
else if (ts.isForOfStatement(node)) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
240
|
+
const iterExpr = visit(node.expression, { inline: true });
|
|
241
|
+
const iterType = inferExpressionType(node.expression);
|
|
242
|
+
if (iterType && iterType.startsWith('map[')) {
|
|
243
|
+
const valueType = extractMapValueType(iterType);
|
|
244
|
+
const isSet = valueType === 'struct{}';
|
|
245
|
+
const varInfo = getForOfVarNames(node.initializer);
|
|
246
|
+
if (isSet) {
|
|
247
|
+
return `for ${varInfo[0]} := range ${iterExpr}${visit(node.statement)}`;
|
|
248
|
+
}
|
|
249
|
+
else if (varInfo.length >= 2) {
|
|
250
|
+
return `for ${varInfo[0]}, ${varInfo[1]} := range ${iterExpr}${visit(node.statement)}`;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
return `for ${varInfo[0]} := range ${iterExpr}${visit(node.statement)}`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return `for _,${visit(node.initializer, { inline: true })}= range ${iterExpr}${visit(node.statement)}`;
|
|
177
257
|
}
|
|
178
258
|
else if (ts.isWhileStatement(node)) {
|
|
179
259
|
return `for ${visit(node.expression, { inline: true })}${visit(node.statement)}`;
|
|
@@ -215,6 +295,20 @@ export function visit(node, options = {}) {
|
|
|
215
295
|
else if (ts.isBreakStatement(node)) {
|
|
216
296
|
return 'break';
|
|
217
297
|
}
|
|
298
|
+
else if (ts.isThrowStatement(node)) {
|
|
299
|
+
const expr = node.expression;
|
|
300
|
+
if (ts.isNewExpression(expr) &&
|
|
301
|
+
ts.isIdentifier(expr.expression) &&
|
|
302
|
+
expr.expression.text === 'Error') {
|
|
303
|
+
const args = expr.arguments ?? [];
|
|
304
|
+
const msg = args.length > 0 ? visit(args[0]) : '""';
|
|
305
|
+
return `panic(${msg})` + (options.inline ? '' : ';\n\t');
|
|
306
|
+
}
|
|
307
|
+
return `panic(${visit(expr)})` + (options.inline ? '' : ';\n\t');
|
|
308
|
+
}
|
|
309
|
+
else if (ts.isTryStatement(node)) {
|
|
310
|
+
return visitTryStatement(node, options);
|
|
311
|
+
}
|
|
218
312
|
else if (ts.isReturnStatement(node)) {
|
|
219
313
|
// Handle return new Promise(...)
|
|
220
314
|
if (node.expression &&
|
|
@@ -230,31 +324,74 @@ export function visit(node, options = {}) {
|
|
|
230
324
|
outsideNodes.push(node);
|
|
231
325
|
return '';
|
|
232
326
|
}
|
|
233
|
-
const name = visit(node.name, { inline: true });
|
|
234
|
-
const safeName = getSafeName(name);
|
|
235
327
|
const typeParams = getTypeParameters(node.typeParameters);
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
.
|
|
239
|
-
|
|
328
|
+
const parameterInfo = getFunctionParametersInfo(node.parameters);
|
|
329
|
+
if (node.body && ts.isBlock(node.body)) {
|
|
330
|
+
prescanVariableDeclarations(node.body);
|
|
331
|
+
}
|
|
332
|
+
const inferredRetType = inferFunctionBodyReturnType(node);
|
|
333
|
+
const returnType = inferredRetType ? ` ${inferredRetType}` : '';
|
|
240
334
|
if (options.isOutside) {
|
|
241
|
-
|
|
335
|
+
const name = node.name ? visit(node.name, { inline: true }) : '';
|
|
336
|
+
const safeName = getSafeName(name);
|
|
337
|
+
return `func ${safeName}${typeParams}(${parameterInfo.signature})${returnType} ${visit(node.body, {
|
|
338
|
+
prefixBlockContent: parameterInfo.prefixBlockContent
|
|
339
|
+
})}`;
|
|
242
340
|
}
|
|
243
|
-
|
|
341
|
+
if (!node.name) {
|
|
342
|
+
return `func${typeParams}(${parameterInfo.signature})${returnType} ${visit(node.body, {
|
|
343
|
+
prefixBlockContent: parameterInfo.prefixBlockContent
|
|
344
|
+
})}`;
|
|
345
|
+
}
|
|
346
|
+
const name = visit(node.name, { inline: true });
|
|
347
|
+
const safeName = getSafeName(name);
|
|
348
|
+
return `${safeName} := func${typeParams}(${parameterInfo.signature})${returnType} ${visit(node.body, {
|
|
349
|
+
prefixBlockContent: parameterInfo.prefixBlockContent
|
|
350
|
+
})}`;
|
|
244
351
|
}
|
|
245
352
|
else if (ts.isArrowFunction(node)) {
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
353
|
+
const parameterInfo = getFunctionParametersInfo(node.parameters);
|
|
354
|
+
const inferredRetType = inferFunctionBodyReturnType(node);
|
|
355
|
+
const returnType = inferredRetType ? ` ${inferredRetType}` : '';
|
|
356
|
+
if (parameterInfo.prefixBlockContent && !ts.isBlock(node.body)) {
|
|
357
|
+
return `func(${parameterInfo.signature})${returnType} {\n\t\t${parameterInfo.prefixBlockContent}return ${visit(node.body)};\n\t}`;
|
|
358
|
+
}
|
|
359
|
+
if (!ts.isBlock(node.body)) {
|
|
360
|
+
return `func(${parameterInfo.signature})${returnType} { return ${visit(node.body)}; }`;
|
|
361
|
+
}
|
|
362
|
+
return `func(${parameterInfo.signature})${returnType} ${visit(node.body, {
|
|
363
|
+
prefixBlockContent: parameterInfo.prefixBlockContent
|
|
364
|
+
})}`;
|
|
251
365
|
}
|
|
252
366
|
else if (node.kind === ts.SyntaxKind.ThisKeyword) {
|
|
253
367
|
return 'self';
|
|
254
368
|
}
|
|
369
|
+
else if (ts.isEnumDeclaration(node)) {
|
|
370
|
+
const enumName = node.name.text;
|
|
371
|
+
enumNames.add(enumName);
|
|
372
|
+
enumBaseTypes.set(enumName, getEnumBaseType(node));
|
|
373
|
+
if (options.addFunctionOutside) {
|
|
374
|
+
outsideNodes.push(node);
|
|
375
|
+
return '';
|
|
376
|
+
}
|
|
377
|
+
return visitEnumDeclaration(node);
|
|
378
|
+
}
|
|
379
|
+
else if (ts.isTypeAliasDeclaration(node)) {
|
|
380
|
+
typeAliases.set(node.name.text, node.type);
|
|
381
|
+
return '';
|
|
382
|
+
}
|
|
255
383
|
else if (ts.isInterfaceDeclaration(node)) {
|
|
256
384
|
if (options.addFunctionOutside) {
|
|
257
385
|
outsideNodes.push(node);
|
|
386
|
+
const properties = new Map();
|
|
387
|
+
for (const member of node.members) {
|
|
388
|
+
if (ts.isPropertySignature(member) && ts.isIdentifier(member.name)) {
|
|
389
|
+
properties.set(member.name.text, getOptionalNodeType(member.type, !!member.questionToken));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (properties.size > 0) {
|
|
393
|
+
interfacePropertyTypes.set(visit(node.name), properties);
|
|
394
|
+
}
|
|
258
395
|
return '';
|
|
259
396
|
}
|
|
260
397
|
const name = visit(node.name);
|
|
@@ -270,6 +407,7 @@ export function visit(node, options = {}) {
|
|
|
270
407
|
}
|
|
271
408
|
}
|
|
272
409
|
const methods = [];
|
|
410
|
+
const properties = [];
|
|
273
411
|
for (const member of node.members) {
|
|
274
412
|
if (ts.isMethodSignature(member)) {
|
|
275
413
|
const methodName = visit(member.name);
|
|
@@ -279,6 +417,13 @@ export function visit(node, options = {}) {
|
|
|
279
417
|
const returnType = member.type ? ` ${getType(member.type)}` : '';
|
|
280
418
|
methods.push(`\t${methodName}(${params})${returnType}`);
|
|
281
419
|
}
|
|
420
|
+
else if (ts.isPropertySignature(member) && ts.isIdentifier(member.name)) {
|
|
421
|
+
properties.push(`\t${member.name.text} ${getOptionalNodeType(member.type, !!member.questionToken)}`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (properties.length > 0 && methods.length === 0) {
|
|
425
|
+
const fields = [...extendedInterfaces.map((e) => `\t${e}`), ...properties];
|
|
426
|
+
return `type ${name}${typeParams} struct {\n${fields.join('\n')}\n}`;
|
|
282
427
|
}
|
|
283
428
|
const members = [...extendedInterfaces.map((e) => `\t${e}`), ...methods];
|
|
284
429
|
return `type ${name}${typeParams} interface {\n${members.join('\n')}\n}`;
|
|
@@ -286,7 +431,20 @@ export function visit(node, options = {}) {
|
|
|
286
431
|
else if (ts.isClassDeclaration(node)) {
|
|
287
432
|
if (options.addFunctionOutside) {
|
|
288
433
|
outsideNodes.push(node);
|
|
289
|
-
|
|
434
|
+
const className = visit(node.name);
|
|
435
|
+
classNames.add(className);
|
|
436
|
+
const properties = new Map();
|
|
437
|
+
const methods = new Map();
|
|
438
|
+
for (const member of node.members) {
|
|
439
|
+
if (ts.isPropertyDeclaration(member) && ts.isIdentifier(member.name)) {
|
|
440
|
+
properties.set(member.name.text, getOptionalNodeType(member.type, !!member.questionToken));
|
|
441
|
+
}
|
|
442
|
+
if (ts.isMethodDeclaration(member) && ts.isIdentifier(member.name)) {
|
|
443
|
+
methods.set(member.name.text, member.type ? getType(member.type) : 'interface{}');
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
classPropertyTypes.set(className, properties);
|
|
447
|
+
classMethodReturnTypes.set(className, methods);
|
|
290
448
|
return '';
|
|
291
449
|
}
|
|
292
450
|
const name = visit(node.name);
|
|
@@ -312,7 +470,7 @@ export function visit(node, options = {}) {
|
|
|
312
470
|
fieldType = `[]${getType(member.type, true)}`;
|
|
313
471
|
}
|
|
314
472
|
else {
|
|
315
|
-
fieldType = member.type
|
|
473
|
+
fieldType = getOptionalNodeType(member.type, !!member.questionToken);
|
|
316
474
|
}
|
|
317
475
|
fields.push(`\t${fieldName} ${fieldType}`);
|
|
318
476
|
}
|
|
@@ -320,7 +478,7 @@ export function visit(node, options = {}) {
|
|
|
320
478
|
let result = `type ${name}${typeParams} struct {\n${fields.join('\n')}\n}\n\n`;
|
|
321
479
|
const ctor = node.members.find((m) => ts.isConstructorDeclaration(m));
|
|
322
480
|
if (ctor) {
|
|
323
|
-
const
|
|
481
|
+
const ctorParameterInfo = getFunctionParametersInfo(ctor.parameters);
|
|
324
482
|
const bodyStatements = ctor.body?.statements
|
|
325
483
|
.filter((s) => {
|
|
326
484
|
if (ts.isExpressionStatement(s) && ts.isCallExpression(s.expression)) {
|
|
@@ -330,7 +488,7 @@ export function visit(node, options = {}) {
|
|
|
330
488
|
})
|
|
331
489
|
.map((s) => visit(s))
|
|
332
490
|
.join('\t') ?? '';
|
|
333
|
-
result += `func New${name}${typeParams}(${
|
|
491
|
+
result += `func New${name}${typeParams}(${ctorParameterInfo.signature}) *${name}${typeParamNames} {\n\t\tself := &${name}${typeParamNames}{}\n\t\t${ctorParameterInfo.prefixBlockContent}${bodyStatements}return self;\n\t}\n\n`;
|
|
334
492
|
}
|
|
335
493
|
else {
|
|
336
494
|
result += `func New${name}${typeParams}() *${name}${typeParamNames} {\n\t\treturn &${name}${typeParamNames}{}\n\t}\n\n`;
|
|
@@ -338,11 +496,11 @@ export function visit(node, options = {}) {
|
|
|
338
496
|
for (const member of node.members) {
|
|
339
497
|
if (ts.isMethodDeclaration(member)) {
|
|
340
498
|
const methodName = visit(member.name);
|
|
341
|
-
const
|
|
342
|
-
.map((p) => `${visit(p.name)} ${getType(p.type)}`)
|
|
343
|
-
.join(', ');
|
|
499
|
+
const methodParameterInfo = getFunctionParametersInfo(member.parameters);
|
|
344
500
|
const returnType = member.type ? ` ${getType(member.type)}` : '';
|
|
345
|
-
result += `func (self *${name}${typeParamNames}) ${methodName}(${
|
|
501
|
+
result += `func (self *${name}${typeParamNames}) ${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, {
|
|
502
|
+
prefixBlockContent: methodParameterInfo.prefixBlockContent
|
|
503
|
+
})}\n\n`;
|
|
346
504
|
}
|
|
347
505
|
}
|
|
348
506
|
return result.trim();
|
|
@@ -365,6 +523,12 @@ export function visit(node, options = {}) {
|
|
|
365
523
|
}
|
|
366
524
|
return `regexp.MustCompile("")`;
|
|
367
525
|
}
|
|
526
|
+
if (className === 'Map') {
|
|
527
|
+
return visitNewMap(node);
|
|
528
|
+
}
|
|
529
|
+
if (className === 'Set') {
|
|
530
|
+
return visitNewSet(node);
|
|
531
|
+
}
|
|
368
532
|
const typeArgs = getTypeArguments(node.typeArguments);
|
|
369
533
|
const args = node.arguments ? node.arguments.map((a) => visit(a)) : [];
|
|
370
534
|
return `New${className}${typeArgs}(${args.join(', ')})`;
|
|
@@ -408,9 +572,552 @@ function getTypeText(typeNode) {
|
|
|
408
572
|
}
|
|
409
573
|
return getType(typeNode);
|
|
410
574
|
}
|
|
575
|
+
function toGoStringLiteral(value) {
|
|
576
|
+
return JSON.stringify(value);
|
|
577
|
+
}
|
|
578
|
+
function visitTemplateExpression(node) {
|
|
579
|
+
const parts = [];
|
|
580
|
+
if (node.head.text.length > 0) {
|
|
581
|
+
parts.push(toGoStringLiteral(node.head.text));
|
|
582
|
+
}
|
|
583
|
+
for (const span of node.templateSpans) {
|
|
584
|
+
importedPackages.add('fmt');
|
|
585
|
+
parts.push(`fmt.Sprintf("%v", ${visit(span.expression)})`);
|
|
586
|
+
if (span.literal.text.length > 0) {
|
|
587
|
+
parts.push(toGoStringLiteral(span.literal.text));
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (parts.length === 0) {
|
|
591
|
+
return '""';
|
|
592
|
+
}
|
|
593
|
+
return parts.join(' + ');
|
|
594
|
+
}
|
|
595
|
+
function hasQuestionDot(node) {
|
|
596
|
+
return ('questionDotToken' in node &&
|
|
597
|
+
!!node.questionDotToken);
|
|
598
|
+
}
|
|
599
|
+
function getTempName(prefix) {
|
|
600
|
+
return `__${prefix}_${goSafeId()}__`;
|
|
601
|
+
}
|
|
602
|
+
function inferExpectedTypeFromContext(node) {
|
|
603
|
+
const parent = node.parent;
|
|
604
|
+
if (ts.isVariableDeclaration(parent) && parent.initializer === node && parent.type) {
|
|
605
|
+
return getType(parent.type);
|
|
606
|
+
}
|
|
607
|
+
if (ts.isReturnStatement(parent)) {
|
|
608
|
+
let scope = parent.parent;
|
|
609
|
+
while (scope) {
|
|
610
|
+
if (ts.isFunctionDeclaration(scope) ||
|
|
611
|
+
ts.isMethodDeclaration(scope) ||
|
|
612
|
+
ts.isFunctionExpression(scope) ||
|
|
613
|
+
ts.isArrowFunction(scope)) {
|
|
614
|
+
if (scope.type)
|
|
615
|
+
return getType(scope.type);
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
scope = scope.parent;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return undefined;
|
|
622
|
+
}
|
|
623
|
+
function prescanVariableDeclarations(block) {
|
|
624
|
+
for (const stmt of block.statements) {
|
|
625
|
+
if (ts.isVariableStatement(stmt)) {
|
|
626
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
627
|
+
if (ts.isIdentifier(decl.name) && !variableGoTypes.has(decl.name.text)) {
|
|
628
|
+
if (decl.type) {
|
|
629
|
+
variableGoTypes.set(decl.name.text, getType(decl.type));
|
|
630
|
+
}
|
|
631
|
+
else if (decl.initializer) {
|
|
632
|
+
const inferredType = inferExpressionType(decl.initializer);
|
|
633
|
+
if (inferredType)
|
|
634
|
+
variableGoTypes.set(decl.name.text, inferredType);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
function inferFunctionBodyReturnType(node) {
|
|
642
|
+
if (node.type)
|
|
643
|
+
return getType(node.type);
|
|
644
|
+
if (ts.isArrowFunction(node) && !ts.isBlock(node.body)) {
|
|
645
|
+
return inferExpressionType(node.body);
|
|
646
|
+
}
|
|
647
|
+
if (node.body && ts.isBlock(node.body)) {
|
|
648
|
+
for (const stmt of node.body.statements) {
|
|
649
|
+
if (ts.isReturnStatement(stmt) && stmt.expression) {
|
|
650
|
+
const exprType = inferExpressionType(stmt.expression);
|
|
651
|
+
if (exprType)
|
|
652
|
+
return exprType;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return undefined;
|
|
657
|
+
}
|
|
658
|
+
function inferArrowFunctionGoType(node) {
|
|
659
|
+
const params = node.parameters
|
|
660
|
+
.map((p) => (p.type ? getType(p.type) : 'interface{}'))
|
|
661
|
+
.join(', ');
|
|
662
|
+
const retType = inferFunctionBodyReturnType(node);
|
|
663
|
+
return `func(${params})${retType ? ` ${retType}` : ''}`;
|
|
664
|
+
}
|
|
665
|
+
function inferExpressionType(expr) {
|
|
666
|
+
if (ts.isArrowFunction(expr) || ts.isFunctionExpression(expr)) {
|
|
667
|
+
return inferArrowFunctionGoType(expr);
|
|
668
|
+
}
|
|
669
|
+
if (ts.isParenthesizedExpression(expr))
|
|
670
|
+
return inferExpressionType(expr.expression);
|
|
671
|
+
if (ts.isNonNullExpression(expr))
|
|
672
|
+
return inferExpressionType(expr.expression);
|
|
673
|
+
if (ts.isAsExpression(expr))
|
|
674
|
+
return getType(expr.type);
|
|
675
|
+
if (ts.isTypeAssertionExpression(expr))
|
|
676
|
+
return getType(expr.type);
|
|
677
|
+
if (ts.isStringLiteral(expr) ||
|
|
678
|
+
ts.isNoSubstitutionTemplateLiteral(expr) ||
|
|
679
|
+
ts.isTemplateExpression(expr)) {
|
|
680
|
+
return 'string';
|
|
681
|
+
}
|
|
682
|
+
if (ts.isNumericLiteral(expr))
|
|
683
|
+
return 'float64';
|
|
684
|
+
if (expr.kind === ts.SyntaxKind.TrueKeyword || expr.kind === ts.SyntaxKind.FalseKeyword)
|
|
685
|
+
return 'bool';
|
|
686
|
+
if (expr.kind === ts.SyntaxKind.NullKeyword)
|
|
687
|
+
return 'nil';
|
|
688
|
+
if (ts.isIdentifier(expr))
|
|
689
|
+
return variableGoTypes.get(expr.text);
|
|
690
|
+
if (ts.isArrayLiteralExpression(expr)) {
|
|
691
|
+
if (expr.elements.length === 0)
|
|
692
|
+
return '[]interface{}';
|
|
693
|
+
const firstElementType = inferExpressionType(expr.elements[0]) ?? 'interface{}';
|
|
694
|
+
return `[]${firstElementType}`;
|
|
695
|
+
}
|
|
696
|
+
if (ts.isNewExpression(expr) && ts.isIdentifier(expr.expression)) {
|
|
697
|
+
const ctorName = expr.expression.text;
|
|
698
|
+
if (ctorName === 'Map' && expr.typeArguments && expr.typeArguments.length === 2) {
|
|
699
|
+
return `map[${getType(expr.typeArguments[0])}]${getType(expr.typeArguments[1])}`;
|
|
700
|
+
}
|
|
701
|
+
if (ctorName === 'Set' && expr.typeArguments && expr.typeArguments.length === 1) {
|
|
702
|
+
return `map[${getType(expr.typeArguments[0])}]struct{}`;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (ts.isPropertyAccessExpression(expr)) {
|
|
706
|
+
if (ts.isIdentifier(expr.expression) && enumNames.has(expr.expression.text)) {
|
|
707
|
+
const enumType = getSafeName(expr.expression.text);
|
|
708
|
+
return enumType;
|
|
709
|
+
}
|
|
710
|
+
const leftType = inferExpressionType(expr.expression);
|
|
711
|
+
if (expr.name.text === 'length')
|
|
712
|
+
return 'float64';
|
|
713
|
+
const resolvedLeftType = leftType?.replace(/^\*/, '').replace(/\[.*\]$/, '');
|
|
714
|
+
const resolvedPropertyType = resolvedLeftType
|
|
715
|
+
? (classPropertyTypes.get(resolvedLeftType)?.get(expr.name.text) ??
|
|
716
|
+
interfacePropertyTypes.get(resolvedLeftType)?.get(expr.name.text))
|
|
717
|
+
: undefined;
|
|
718
|
+
if (hasQuestionDot(expr)) {
|
|
719
|
+
if (leftType && leftType.startsWith('*')) {
|
|
720
|
+
const memberType = resolvedPropertyType ?? 'interface{}';
|
|
721
|
+
return makeNullableType(memberType);
|
|
722
|
+
}
|
|
723
|
+
return 'interface{}';
|
|
724
|
+
}
|
|
725
|
+
if (resolvedPropertyType) {
|
|
726
|
+
return resolvedPropertyType;
|
|
727
|
+
}
|
|
728
|
+
if (ts.isIdentifier(expr.expression)) {
|
|
729
|
+
const className = variableClassNames.get(expr.expression.text);
|
|
730
|
+
const memberType = className
|
|
731
|
+
? classPropertyTypes.get(className)?.get(expr.name.text)
|
|
732
|
+
: undefined;
|
|
733
|
+
if (memberType)
|
|
734
|
+
return memberType;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (ts.isCallExpression(expr) && ts.isPropertyAccessExpression(expr.expression)) {
|
|
738
|
+
const methodName = expr.expression.name.text;
|
|
739
|
+
const ownerType = inferExpressionType(expr.expression.expression);
|
|
740
|
+
if (ownerType && ownerType.startsWith('map[')) {
|
|
741
|
+
if (methodName === 'has')
|
|
742
|
+
return 'bool';
|
|
743
|
+
if (methodName === 'get')
|
|
744
|
+
return extractMapValueType(ownerType);
|
|
745
|
+
}
|
|
746
|
+
if (isArrayLikeGoType(ownerType)) {
|
|
747
|
+
const elementType = getArrayElementTypeFromGoType(ownerType);
|
|
748
|
+
if (methodName === 'map') {
|
|
749
|
+
const callback = expr.arguments[0];
|
|
750
|
+
const mappedType = callback
|
|
751
|
+
? inferArrayCallbackReturnType(callback, elementType, elementType)
|
|
752
|
+
: elementType;
|
|
753
|
+
return `[]${mappedType}`;
|
|
754
|
+
}
|
|
755
|
+
if (methodName === 'filter')
|
|
756
|
+
return `[]${elementType}`;
|
|
757
|
+
if (methodName === 'some')
|
|
758
|
+
return 'bool';
|
|
759
|
+
if (methodName === 'find')
|
|
760
|
+
return elementType;
|
|
761
|
+
if (methodName === 'join')
|
|
762
|
+
return 'string';
|
|
763
|
+
}
|
|
764
|
+
if (ownerType && ownerType.startsWith('*')) {
|
|
765
|
+
const className = ownerType.replace(/^\*/, '').replace(/\[.*\]$/, '');
|
|
766
|
+
const returnType = classMethodReturnTypes.get(className)?.get(methodName);
|
|
767
|
+
if (returnType) {
|
|
768
|
+
if (hasQuestionDot(expr) || hasQuestionDot(expr.expression)) {
|
|
769
|
+
return makeNullableType(returnType);
|
|
770
|
+
}
|
|
771
|
+
return returnType;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
if (ts.isIdentifier(expr.expression.expression)) {
|
|
775
|
+
const className = variableClassNames.get(expr.expression.expression.text);
|
|
776
|
+
const returnType = className
|
|
777
|
+
? classMethodReturnTypes.get(className)?.get(methodName)
|
|
778
|
+
: undefined;
|
|
779
|
+
if (returnType) {
|
|
780
|
+
if (hasQuestionDot(expr) || hasQuestionDot(expr.expression)) {
|
|
781
|
+
return makeNullableType(returnType);
|
|
782
|
+
}
|
|
783
|
+
return returnType;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (ts.isConditionalExpression(expr)) {
|
|
788
|
+
const whenTrueType = inferExpressionType(expr.whenTrue);
|
|
789
|
+
const whenFalseType = inferExpressionType(expr.whenFalse);
|
|
790
|
+
if (whenTrueType && whenTrueType === whenFalseType)
|
|
791
|
+
return whenTrueType;
|
|
792
|
+
return whenTrueType ?? whenFalseType;
|
|
793
|
+
}
|
|
794
|
+
if (ts.isBinaryExpression(expr) &&
|
|
795
|
+
expr.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
796
|
+
const leftType = inferExpressionType(expr.left);
|
|
797
|
+
const rightType = inferExpressionType(expr.right);
|
|
798
|
+
if (leftType && leftType.startsWith('*') && rightType === leftType.slice(1)) {
|
|
799
|
+
return rightType;
|
|
800
|
+
}
|
|
801
|
+
return rightType ?? leftType;
|
|
802
|
+
}
|
|
803
|
+
return undefined;
|
|
804
|
+
}
|
|
805
|
+
function makeNullableType(typeName) {
|
|
806
|
+
if (!typeName || typeName === 'interface{}' || typeName.startsWith('*'))
|
|
807
|
+
return typeName || 'interface{}';
|
|
808
|
+
if (['string', 'float64', 'bool'].includes(typeName))
|
|
809
|
+
return `*${typeName}`;
|
|
810
|
+
return typeName;
|
|
811
|
+
}
|
|
812
|
+
function visitConditionalExpression(node) {
|
|
813
|
+
const whenTrue = visit(node.whenTrue);
|
|
814
|
+
const whenFalse = visit(node.whenFalse);
|
|
815
|
+
const resultType = inferExpectedTypeFromContext(node) ||
|
|
816
|
+
(() => {
|
|
817
|
+
const whenTrueType = inferExpressionType(node.whenTrue);
|
|
818
|
+
const whenFalseType = inferExpressionType(node.whenFalse);
|
|
819
|
+
if (whenTrueType && whenTrueType === whenFalseType)
|
|
820
|
+
return whenTrueType;
|
|
821
|
+
return whenTrueType ?? whenFalseType ?? 'interface{}';
|
|
822
|
+
})();
|
|
823
|
+
return `func() ${resultType} { if ${visit(node.condition)} { return ${whenTrue} }; return ${whenFalse} }()`;
|
|
824
|
+
}
|
|
825
|
+
function visitNullishCoalescingExpression(node) {
|
|
826
|
+
const leftType = inferExpressionType(node.left);
|
|
827
|
+
const rightType = inferExpressionType(node.right);
|
|
828
|
+
if (leftType && leftType.startsWith('*')) {
|
|
829
|
+
const leftValueType = leftType.slice(1);
|
|
830
|
+
const expectedType = inferExpectedTypeFromContext(node);
|
|
831
|
+
const resultType = expectedType || (rightType === leftValueType ? leftValueType : (rightType ?? leftType));
|
|
832
|
+
const tmp = getTempName('nullish');
|
|
833
|
+
const leftExpr = visit(node.left);
|
|
834
|
+
const rightExpr = visit(node.right);
|
|
835
|
+
const returnLeft = resultType === leftValueType ? `*${tmp}` : tmp;
|
|
836
|
+
return `func() ${resultType} { ${tmp} := ${leftExpr}; if ${tmp} == nil { return ${rightExpr} }; return ${returnLeft} }()`;
|
|
837
|
+
}
|
|
838
|
+
return visit(node.left);
|
|
839
|
+
}
|
|
840
|
+
function visitOptionalPropertyAccess(node) {
|
|
841
|
+
const baseExpr = visit(node.expression);
|
|
842
|
+
const baseType = inferExpressionType(node.expression);
|
|
843
|
+
if (!baseType || !baseType.startsWith('*')) {
|
|
844
|
+
const objectType = resolveExpressionType(node.expression);
|
|
845
|
+
return getAcessString(baseExpr, visit(node.name), objectType);
|
|
846
|
+
}
|
|
847
|
+
const className = baseType.replace(/^\*/, '').replace(/\[.*\]$/, '');
|
|
848
|
+
const propertyType = classPropertyTypes.get(className)?.get(node.name.text) ?? 'interface{}';
|
|
849
|
+
const nullableType = makeNullableType(propertyType);
|
|
850
|
+
const tmp = getTempName('opt');
|
|
851
|
+
const propertyAccess = `${tmp}.${visit(node.name)}`;
|
|
852
|
+
if (nullableType.startsWith('*') && nullableType.slice(1) === propertyType) {
|
|
853
|
+
const valueTemp = getTempName('optv');
|
|
854
|
+
return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; ${valueTemp} := ${propertyAccess}; return &${valueTemp} }()`;
|
|
855
|
+
}
|
|
856
|
+
return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; return ${propertyAccess} }()`;
|
|
857
|
+
}
|
|
858
|
+
function visitOptionalElementAccess(node) {
|
|
859
|
+
const baseExpr = visit(node.expression);
|
|
860
|
+
const baseType = inferExpressionType(node.expression);
|
|
861
|
+
if (!baseType || !baseType.startsWith('*')) {
|
|
862
|
+
return `${baseExpr}[int(${visit(node.argumentExpression)})]`;
|
|
863
|
+
}
|
|
864
|
+
const valueType = inferExpectedTypeFromContext(node) ?? 'interface{}';
|
|
865
|
+
const nullableType = makeNullableType(valueType);
|
|
866
|
+
const tmp = getTempName('opte');
|
|
867
|
+
const elementExpr = `${tmp}[int(${visit(node.argumentExpression)})]`;
|
|
868
|
+
if (nullableType.startsWith('*') && nullableType.slice(1) === valueType) {
|
|
869
|
+
const valueTemp = getTempName('optev');
|
|
870
|
+
return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; ${valueTemp} := ${elementExpr}; return &${valueTemp} }()`;
|
|
871
|
+
}
|
|
872
|
+
return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; return ${elementExpr} }()`;
|
|
873
|
+
}
|
|
874
|
+
function visitOptionalCall(node) {
|
|
875
|
+
if (!ts.isPropertyAccessExpression(node.expression)) {
|
|
876
|
+
return `${visit(node.expression)}(${node.arguments.map((a) => visit(a)).join(', ')})`;
|
|
877
|
+
}
|
|
878
|
+
const baseNode = node.expression.expression;
|
|
879
|
+
const methodName = node.expression.name.text;
|
|
880
|
+
const baseExpr = visit(baseNode);
|
|
881
|
+
const baseType = inferExpressionType(baseNode);
|
|
882
|
+
const args = node.arguments.map((a) => visit(a)).join(', ');
|
|
883
|
+
if (!baseType || !baseType.startsWith('*')) {
|
|
884
|
+
return `${baseExpr}.${methodName}(${args})`;
|
|
885
|
+
}
|
|
886
|
+
const className = baseType.replace(/^\*/, '').replace(/\[.*\]$/, '');
|
|
887
|
+
const returnType = classMethodReturnTypes.get(className)?.get(methodName) ?? 'interface{}';
|
|
888
|
+
const nullableType = makeNullableType(returnType);
|
|
889
|
+
const tmp = getTempName('optc');
|
|
890
|
+
const callExpr = `${tmp}.${methodName}(${args})`;
|
|
891
|
+
if (nullableType.startsWith('*') && nullableType.slice(1) === returnType) {
|
|
892
|
+
const valueTemp = getTempName('optcv');
|
|
893
|
+
return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; ${valueTemp} := ${callExpr}; return &${valueTemp} }()`;
|
|
894
|
+
}
|
|
895
|
+
return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; return ${callExpr} }()`;
|
|
896
|
+
}
|
|
897
|
+
function isArrayLikeGoType(goType) {
|
|
898
|
+
return !!goType && goType.startsWith('[]');
|
|
899
|
+
}
|
|
900
|
+
function getArrayElementTypeFromGoType(goType) {
|
|
901
|
+
if (!goType.startsWith('[]'))
|
|
902
|
+
return 'interface{}';
|
|
903
|
+
const elementType = goType.slice(2);
|
|
904
|
+
return elementType || 'interface{}';
|
|
905
|
+
}
|
|
906
|
+
function inferArrayCallbackReturnType(callback, elementType, fallbackType) {
|
|
907
|
+
if (ts.isArrowFunction(callback) || ts.isFunctionExpression(callback)) {
|
|
908
|
+
if (callback.type) {
|
|
909
|
+
const explicitType = getType(callback.type);
|
|
910
|
+
return explicitType || fallbackType;
|
|
911
|
+
}
|
|
912
|
+
if (ts.isBlock(callback.body)) {
|
|
913
|
+
return fallbackType;
|
|
914
|
+
}
|
|
915
|
+
const inferred = inferExpressionType(callback.body);
|
|
916
|
+
return inferred ?? fallbackType;
|
|
917
|
+
}
|
|
918
|
+
if (ts.isIdentifier(callback)) {
|
|
919
|
+
const knownType = variableGoTypes.get(callback.text);
|
|
920
|
+
if (knownType)
|
|
921
|
+
return knownType;
|
|
922
|
+
}
|
|
923
|
+
return fallbackType;
|
|
924
|
+
}
|
|
925
|
+
function buildArrayCallbackInfo(callback, elementType, forcedReturnType) {
|
|
926
|
+
if (ts.isArrowFunction(callback) || ts.isFunctionExpression(callback)) {
|
|
927
|
+
const paramCount = callback.parameters.length;
|
|
928
|
+
const callbackReturnType = forcedReturnType ?? inferArrayCallbackReturnType(callback, elementType, 'interface{}');
|
|
929
|
+
const params = [];
|
|
930
|
+
if (paramCount > 0) {
|
|
931
|
+
params.push(`${visit(callback.parameters[0].name)} ${elementType}`);
|
|
932
|
+
}
|
|
933
|
+
if (paramCount > 1) {
|
|
934
|
+
params.push(`${visit(callback.parameters[1].name)} float64`);
|
|
935
|
+
}
|
|
936
|
+
if (paramCount > 2) {
|
|
937
|
+
params.push(`${visit(callback.parameters[2].name)} []${elementType}`);
|
|
938
|
+
}
|
|
939
|
+
const body = ts.isBlock(callback.body)
|
|
940
|
+
? visit(callback.body)
|
|
941
|
+
: `{\n\t\treturn ${visit(callback.body)};\n\t}`;
|
|
942
|
+
return {
|
|
943
|
+
fnExpr: `func(${params.join(', ')}) ${callbackReturnType} ${body}`,
|
|
944
|
+
paramCount,
|
|
945
|
+
returnType: callbackReturnType
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
return {
|
|
949
|
+
fnExpr: visit(callback),
|
|
950
|
+
paramCount: 1,
|
|
951
|
+
returnType: forcedReturnType ?? 'interface{}'
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
function buildArrayCallbackInvocation(callbackInfo, itemVar, indexVar, arrayVar) {
|
|
955
|
+
const args = [];
|
|
956
|
+
if (callbackInfo.paramCount > 0)
|
|
957
|
+
args.push(itemVar);
|
|
958
|
+
if (callbackInfo.paramCount > 1)
|
|
959
|
+
args.push(`float64(${indexVar})`);
|
|
960
|
+
if (callbackInfo.paramCount > 2)
|
|
961
|
+
args.push(arrayVar);
|
|
962
|
+
return `(${callbackInfo.fnExpr})(${args.join(', ')})`;
|
|
963
|
+
}
|
|
964
|
+
function visitArrayHigherOrderCall(node) {
|
|
965
|
+
if (!ts.isPropertyAccessExpression(node.expression))
|
|
966
|
+
return undefined;
|
|
967
|
+
const methodName = node.expression.name.text;
|
|
968
|
+
if (!['map', 'filter', 'some', 'find', 'join'].includes(methodName)) {
|
|
969
|
+
return undefined;
|
|
970
|
+
}
|
|
971
|
+
const arrayExprNode = node.expression.expression;
|
|
972
|
+
const arrayExpr = visit(arrayExprNode);
|
|
973
|
+
const ownerType = inferExpressionType(arrayExprNode);
|
|
974
|
+
const elementType = isArrayLikeGoType(ownerType)
|
|
975
|
+
? getArrayElementTypeFromGoType(ownerType)
|
|
976
|
+
: 'interface{}';
|
|
977
|
+
if (methodName === 'join') {
|
|
978
|
+
importedPackages.add('strings');
|
|
979
|
+
importedPackages.add('fmt');
|
|
980
|
+
const separator = node.arguments[0] ? visit(node.arguments[0]) : '""';
|
|
981
|
+
const arrVar = getTempName('arrjoin');
|
|
982
|
+
const partsVar = getTempName('parts');
|
|
983
|
+
return `func() string { ${arrVar} := ${arrayExpr}; ${partsVar} := make([]string, len(${arrVar})); for i, v := range ${arrVar} { ${partsVar}[i] = fmt.Sprintf("%v", v) }; return strings.Join(${partsVar}, ${separator}) }()`;
|
|
984
|
+
}
|
|
985
|
+
const callback = node.arguments[0];
|
|
986
|
+
if (!callback) {
|
|
987
|
+
return undefined;
|
|
988
|
+
}
|
|
989
|
+
const arrVar = getTempName('arrhof');
|
|
990
|
+
const idxVar = getTempName('i');
|
|
991
|
+
const itemVar = getTempName('item');
|
|
992
|
+
if (methodName === 'map') {
|
|
993
|
+
const callbackInfo = buildArrayCallbackInfo(callback, elementType, elementType);
|
|
994
|
+
const mappedType = callbackInfo.returnType || 'interface{}';
|
|
995
|
+
const resultVar = getTempName('mapres');
|
|
996
|
+
const rangeIndexVar = callbackInfo.paramCount > 1 ? idxVar : '_';
|
|
997
|
+
const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
|
|
998
|
+
return `func() []${mappedType} { ${arrVar} := ${arrayExpr}; ${resultVar} := make([]${mappedType}, 0, len(${arrVar})); for ${rangeIndexVar}, ${itemVar} := range ${arrVar} { ${resultVar} = append(${resultVar}, ${callbackCall}) }; return ${resultVar} }()`;
|
|
999
|
+
}
|
|
1000
|
+
if (methodName === 'filter') {
|
|
1001
|
+
const callbackInfo = buildArrayCallbackInfo(callback, elementType, 'bool');
|
|
1002
|
+
const resultVar = getTempName('filterres');
|
|
1003
|
+
const rangeIndexVar = callbackInfo.paramCount > 1 ? idxVar : '_';
|
|
1004
|
+
const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
|
|
1005
|
+
return `func() []${elementType} { ${arrVar} := ${arrayExpr}; ${resultVar} := make([]${elementType}, 0, len(${arrVar})); for ${rangeIndexVar}, ${itemVar} := range ${arrVar} { if ${callbackCall} { ${resultVar} = append(${resultVar}, ${itemVar}) } }; return ${resultVar} }()`;
|
|
1006
|
+
}
|
|
1007
|
+
if (methodName === 'some') {
|
|
1008
|
+
const callbackInfo = buildArrayCallbackInfo(callback, elementType, 'bool');
|
|
1009
|
+
const rangeIndexVar = callbackInfo.paramCount > 1 ? idxVar : '_';
|
|
1010
|
+
const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
|
|
1011
|
+
return `func() bool { ${arrVar} := ${arrayExpr}; for ${rangeIndexVar}, ${itemVar} := range ${arrVar} { if ${callbackCall} { return true } }; return false }()`;
|
|
1012
|
+
}
|
|
1013
|
+
const callbackInfo = buildArrayCallbackInfo(callback, elementType, 'bool');
|
|
1014
|
+
const rangeIndexVar = callbackInfo.paramCount > 1 ? idxVar : '_';
|
|
1015
|
+
const callbackCall = buildArrayCallbackInvocation(callbackInfo, itemVar, idxVar, arrVar);
|
|
1016
|
+
return `func() ${elementType} { ${arrVar} := ${arrayExpr}; for ${rangeIndexVar}, ${itemVar} := range ${arrVar} { if ${callbackCall} { return ${itemVar} } }; var __zero ${elementType}; return __zero }()`;
|
|
1017
|
+
}
|
|
1018
|
+
function getAliasType(name, seen = new Set()) {
|
|
1019
|
+
const aliasType = typeAliases.get(name);
|
|
1020
|
+
if (!aliasType)
|
|
1021
|
+
return undefined;
|
|
1022
|
+
if (seen.has(name))
|
|
1023
|
+
return undefined;
|
|
1024
|
+
if (ts.isTypeReferenceNode(aliasType) && ts.isIdentifier(aliasType.typeName)) {
|
|
1025
|
+
const nestedName = aliasType.typeName.text;
|
|
1026
|
+
if (typeAliases.has(nestedName)) {
|
|
1027
|
+
seen.add(name);
|
|
1028
|
+
return getAliasType(nestedName, seen) ?? aliasType;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
return aliasType;
|
|
1032
|
+
}
|
|
1033
|
+
function getOptionalNodeType(typeNode, isOptional) {
|
|
1034
|
+
const baseType = typeNode ? getType(typeNode) : 'interface{}';
|
|
1035
|
+
if (!isOptional)
|
|
1036
|
+
return baseType;
|
|
1037
|
+
if (baseType === 'interface{}' || baseType.startsWith('*'))
|
|
1038
|
+
return baseType;
|
|
1039
|
+
if (['string', 'float64', 'bool'].includes(baseType))
|
|
1040
|
+
return `*${baseType}`;
|
|
1041
|
+
return baseType;
|
|
1042
|
+
}
|
|
1043
|
+
function getEnumMemberName(name) {
|
|
1044
|
+
if (ts.isIdentifier(name)) {
|
|
1045
|
+
return getSafeName(name.text);
|
|
1046
|
+
}
|
|
1047
|
+
if (ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
|
|
1048
|
+
const sanitized = name.text.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
1049
|
+
return sanitized.length > 0 ? sanitized : 'Member';
|
|
1050
|
+
}
|
|
1051
|
+
return 'Member';
|
|
1052
|
+
}
|
|
1053
|
+
function getEnumBaseType(node) {
|
|
1054
|
+
for (const member of node.members) {
|
|
1055
|
+
const initializer = member.initializer;
|
|
1056
|
+
if (!initializer)
|
|
1057
|
+
continue;
|
|
1058
|
+
if (ts.isStringLiteral(initializer) || ts.isNoSubstitutionTemplateLiteral(initializer)) {
|
|
1059
|
+
return 'string';
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
return 'float64';
|
|
1063
|
+
}
|
|
1064
|
+
function readNumericEnumInitializer(initializer) {
|
|
1065
|
+
if (ts.isNumericLiteral(initializer)) {
|
|
1066
|
+
return Number(initializer.text);
|
|
1067
|
+
}
|
|
1068
|
+
if (ts.isPrefixUnaryExpression(initializer) &&
|
|
1069
|
+
initializer.operator === ts.SyntaxKind.MinusToken &&
|
|
1070
|
+
ts.isNumericLiteral(initializer.operand)) {
|
|
1071
|
+
return -Number(initializer.operand.text);
|
|
1072
|
+
}
|
|
1073
|
+
return undefined;
|
|
1074
|
+
}
|
|
1075
|
+
function visitEnumDeclaration(node) {
|
|
1076
|
+
const enumName = getSafeName(node.name.text);
|
|
1077
|
+
const baseType = enumBaseTypes.get(node.name.text) ?? getEnumBaseType(node);
|
|
1078
|
+
let nextNumericValue = 0;
|
|
1079
|
+
let canAutoIncrement = true;
|
|
1080
|
+
const members = [];
|
|
1081
|
+
for (const member of node.members) {
|
|
1082
|
+
const memberName = getEnumMemberName(member.name);
|
|
1083
|
+
const symbolName = `${enumName}_${memberName}`;
|
|
1084
|
+
let valueExpr;
|
|
1085
|
+
if (member.initializer) {
|
|
1086
|
+
if (baseType === 'float64') {
|
|
1087
|
+
const numericValue = readNumericEnumInitializer(member.initializer);
|
|
1088
|
+
if (numericValue !== undefined) {
|
|
1089
|
+
valueExpr = `${numericValue}`;
|
|
1090
|
+
nextNumericValue = numericValue + 1;
|
|
1091
|
+
canAutoIncrement = true;
|
|
1092
|
+
}
|
|
1093
|
+
else {
|
|
1094
|
+
valueExpr = `float64(${visit(member.initializer)})`;
|
|
1095
|
+
canAutoIncrement = false;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
else {
|
|
1099
|
+
valueExpr = visit(member.initializer);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
else if (baseType === 'float64') {
|
|
1103
|
+
const currentValue = canAutoIncrement ? nextNumericValue : 0;
|
|
1104
|
+
valueExpr = `${currentValue}`;
|
|
1105
|
+
nextNumericValue = currentValue + 1;
|
|
1106
|
+
}
|
|
1107
|
+
else {
|
|
1108
|
+
valueExpr = toGoStringLiteral(memberName);
|
|
1109
|
+
}
|
|
1110
|
+
members.push(`\t${symbolName} ${enumName} = ${enumName}(${valueExpr})`);
|
|
1111
|
+
}
|
|
1112
|
+
return `type ${enumName} ${baseType}\n\nvar (\n${members.join('\n')}\n)`;
|
|
1113
|
+
}
|
|
411
1114
|
function getType(typeNode, getArrayType = false) {
|
|
412
1115
|
if (!typeNode)
|
|
413
1116
|
return ':';
|
|
1117
|
+
if (ts.isArrayTypeNode(typeNode)) {
|
|
1118
|
+
const elementType = getType(typeNode.elementType);
|
|
1119
|
+
return getArrayType ? elementType : `[]${elementType}`;
|
|
1120
|
+
}
|
|
414
1121
|
// Handle union types (e.g. string | null, number | undefined)
|
|
415
1122
|
if (ts.isUnionTypeNode(typeNode)) {
|
|
416
1123
|
const nonNullTypes = typeNode.types.filter((t) => t.kind !== ts.SyntaxKind.NullKeyword &&
|
|
@@ -428,14 +1135,37 @@ function getType(typeNode, getArrayType = false) {
|
|
|
428
1135
|
// Non-nullable union or multi-type union → interface{}
|
|
429
1136
|
return 'interface{}';
|
|
430
1137
|
}
|
|
1138
|
+
if (ts.isFunctionTypeNode(typeNode)) {
|
|
1139
|
+
const params = typeNode.parameters
|
|
1140
|
+
.map((p) => (p.type ? getType(p.type) : 'interface{}'))
|
|
1141
|
+
.join(', ');
|
|
1142
|
+
const ret = typeNode.type ? ` ${getType(typeNode.type)}` : '';
|
|
1143
|
+
return `func(${params})${ret}`;
|
|
1144
|
+
}
|
|
431
1145
|
if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
|
|
432
1146
|
const name = typeNode.typeName.text;
|
|
1147
|
+
if (enumNames.has(name)) {
|
|
1148
|
+
return getSafeName(name);
|
|
1149
|
+
}
|
|
1150
|
+
const aliasType = getAliasType(name);
|
|
1151
|
+
if (aliasType) {
|
|
1152
|
+
return getType(aliasType, getArrayType);
|
|
1153
|
+
}
|
|
433
1154
|
if (name === 'Promise' && typeNode.typeArguments && typeNode.typeArguments.length > 0) {
|
|
434
1155
|
return `chan ${getType(typeNode.typeArguments[0])}`;
|
|
435
1156
|
}
|
|
436
1157
|
if (name === 'RegExp') {
|
|
437
1158
|
return '*regexp.Regexp';
|
|
438
1159
|
}
|
|
1160
|
+
if (name === 'Map' && typeNode.typeArguments && typeNode.typeArguments.length === 2) {
|
|
1161
|
+
const keyType = getType(typeNode.typeArguments[0]);
|
|
1162
|
+
const valueType = getType(typeNode.typeArguments[1]);
|
|
1163
|
+
return `map[${keyType}]${valueType}`;
|
|
1164
|
+
}
|
|
1165
|
+
if (name === 'Set' && typeNode.typeArguments && typeNode.typeArguments.length === 1) {
|
|
1166
|
+
const elementType = getType(typeNode.typeArguments[0]);
|
|
1167
|
+
return `map[${elementType}]struct{}`;
|
|
1168
|
+
}
|
|
439
1169
|
const typeArgs = getTypeArguments(typeNode.typeArguments);
|
|
440
1170
|
if (classNames.has(name)) {
|
|
441
1171
|
return `*${name}${typeArgs}`;
|
|
@@ -486,12 +1216,30 @@ function getTypeCategory(typeNode) {
|
|
|
486
1216
|
}
|
|
487
1217
|
if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
|
|
488
1218
|
const name = typeNode.typeName.text;
|
|
1219
|
+
if (name === 'Map')
|
|
1220
|
+
return 'Map';
|
|
1221
|
+
if (name === 'Set')
|
|
1222
|
+
return 'Set';
|
|
489
1223
|
if (classNames.has(name))
|
|
490
1224
|
return 'class';
|
|
491
1225
|
return name;
|
|
492
1226
|
}
|
|
493
1227
|
return undefined;
|
|
494
1228
|
}
|
|
1229
|
+
function getClassNameFromTypeNode(typeNode) {
|
|
1230
|
+
if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
|
|
1231
|
+
return classNames.has(typeNode.typeName.text) ? typeNode.typeName.text : undefined;
|
|
1232
|
+
}
|
|
1233
|
+
if (ts.isUnionTypeNode(typeNode)) {
|
|
1234
|
+
const nonNullTypes = typeNode.types.filter((t) => t.kind !== ts.SyntaxKind.NullKeyword &&
|
|
1235
|
+
t.kind !== ts.SyntaxKind.UndefinedKeyword &&
|
|
1236
|
+
!(ts.isLiteralTypeNode(t) && t.literal.kind === ts.SyntaxKind.NullKeyword));
|
|
1237
|
+
if (nonNullTypes.length === 1) {
|
|
1238
|
+
return getClassNameFromTypeNode(nonNullTypes[0]);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
return undefined;
|
|
1242
|
+
}
|
|
495
1243
|
function resolveExpressionType(expr) {
|
|
496
1244
|
if (ts.isIdentifier(expr)) {
|
|
497
1245
|
return variableTypes.get(expr.text);
|
|
@@ -512,6 +1260,9 @@ function getAcessString(leftSide, rightSide, objectType) {
|
|
|
512
1260
|
if (rightSide === 'length' && objectType !== 'class') {
|
|
513
1261
|
return `float64(len(${leftSide}))`;
|
|
514
1262
|
}
|
|
1263
|
+
if (rightSide === 'size' && (objectType === 'Map' || objectType === 'Set')) {
|
|
1264
|
+
return `float64(len(${leftSide}))`;
|
|
1265
|
+
}
|
|
515
1266
|
return `${leftSide}.${rightSide}`;
|
|
516
1267
|
}
|
|
517
1268
|
const callHandlers = {
|
|
@@ -667,6 +1418,25 @@ const arrayMethodHandlers = {
|
|
|
667
1418
|
return `fmt.Sprintf("%v", ${obj})`;
|
|
668
1419
|
}
|
|
669
1420
|
};
|
|
1421
|
+
const mapMethodHandlers = {
|
|
1422
|
+
set: (obj, args) => `${obj}[${args[0]}] = ${args[1]}`,
|
|
1423
|
+
get: (obj, args) => `${obj}[${args[0]}]`,
|
|
1424
|
+
has: (obj, args) => {
|
|
1425
|
+
const tmp = getTempName('ok');
|
|
1426
|
+
return `func() bool { _, ${tmp} := ${obj}[${args[0]}]; return ${tmp} }()`;
|
|
1427
|
+
},
|
|
1428
|
+
delete: (obj, args) => `delete(${obj}, ${args[0]})`,
|
|
1429
|
+
clear: (obj) => `clear(${obj})`
|
|
1430
|
+
};
|
|
1431
|
+
const setMethodHandlers = {
|
|
1432
|
+
add: (obj, args) => `${obj}[${args[0]}] = struct{}{}`,
|
|
1433
|
+
has: (obj, args) => {
|
|
1434
|
+
const tmp = getTempName('ok');
|
|
1435
|
+
return `func() bool { _, ${tmp} := ${obj}[${args[0]}]; return ${tmp} }()`;
|
|
1436
|
+
},
|
|
1437
|
+
delete: (obj, args) => `delete(${obj}, ${args[0]})`,
|
|
1438
|
+
clear: (obj) => `clear(${obj})`
|
|
1439
|
+
};
|
|
670
1440
|
function getDynamicCallHandler(caller, objectType) {
|
|
671
1441
|
if (promiseResolveName && caller === promiseResolveName) {
|
|
672
1442
|
return (_caller, args) => `ch <- ${args[0]}`;
|
|
@@ -695,6 +1465,12 @@ function getDynamicCallHandler(caller, objectType) {
|
|
|
695
1465
|
else if (objectType === 'RegExp') {
|
|
696
1466
|
handler = regexpMethodHandlers[methodName];
|
|
697
1467
|
}
|
|
1468
|
+
else if (objectType === 'Map') {
|
|
1469
|
+
handler = mapMethodHandlers[methodName];
|
|
1470
|
+
}
|
|
1471
|
+
else if (objectType === 'Set') {
|
|
1472
|
+
handler = setMethodHandlers[methodName];
|
|
1473
|
+
}
|
|
698
1474
|
else {
|
|
699
1475
|
// Unknown type: try both maps for backward compatibility
|
|
700
1476
|
handler =
|
|
@@ -772,6 +1548,59 @@ function getTypeArguments(typeArguments) {
|
|
|
772
1548
|
const args = typeArguments.map((ta) => getType(ta));
|
|
773
1549
|
return `[${args.join(', ')}]`;
|
|
774
1550
|
}
|
|
1551
|
+
function getParameterGoType(param) {
|
|
1552
|
+
if (param.type) {
|
|
1553
|
+
const explicitType = getType(param.type);
|
|
1554
|
+
return explicitType === ':' ? 'interface{}' : explicitType;
|
|
1555
|
+
}
|
|
1556
|
+
if (param.initializer) {
|
|
1557
|
+
const inferredType = inferExpressionType(param.initializer);
|
|
1558
|
+
if (inferredType && inferredType !== 'nil' && inferredType !== ':') {
|
|
1559
|
+
return inferredType;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
return 'interface{}';
|
|
1563
|
+
}
|
|
1564
|
+
function getFunctionParametersInfo(parameters) {
|
|
1565
|
+
if (parameters.length === 0) {
|
|
1566
|
+
return { signature: '', prefixBlockContent: '' };
|
|
1567
|
+
}
|
|
1568
|
+
const firstDefaultIndex = parameters.findIndex((p) => !!p.initializer);
|
|
1569
|
+
if (firstDefaultIndex === -1) {
|
|
1570
|
+
return {
|
|
1571
|
+
signature: parameters.map((p) => `${visit(p.name)} ${getParameterGoType(p)}`).join(', '),
|
|
1572
|
+
prefixBlockContent: ''
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
const hasRequiredAfterDefault = parameters
|
|
1576
|
+
.slice(firstDefaultIndex)
|
|
1577
|
+
.some((p) => !p.initializer);
|
|
1578
|
+
if (hasRequiredAfterDefault) {
|
|
1579
|
+
return {
|
|
1580
|
+
signature: parameters.map((p) => `${visit(p.name)} ${getParameterGoType(p)}`).join(', '),
|
|
1581
|
+
prefixBlockContent: ''
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
const requiredParams = parameters.slice(0, firstDefaultIndex);
|
|
1585
|
+
const defaultedParams = parameters.slice(firstDefaultIndex);
|
|
1586
|
+
const signatureParts = requiredParams.map((p) => `${visit(p.name)} ${getParameterGoType(p)}`);
|
|
1587
|
+
signatureParts.push('__defaultArgs ...interface{}');
|
|
1588
|
+
const prefixBlockContent = defaultedParams
|
|
1589
|
+
.map((param, index) => {
|
|
1590
|
+
const paramName = visit(param.name);
|
|
1591
|
+
const paramType = getParameterGoType(param);
|
|
1592
|
+
const defaultValue = visit(param.initializer);
|
|
1593
|
+
if (paramType === 'interface{}') {
|
|
1594
|
+
return `var ${paramName} interface{}\n\t\tif len(__defaultArgs) > ${index} {\n\t\t\t${paramName} = __defaultArgs[${index}]\n\t\t} else {\n\t\t\t${paramName} = ${defaultValue}\n\t\t}\n\t\t`;
|
|
1595
|
+
}
|
|
1596
|
+
return `var ${paramName} ${paramType}\n\t\tif len(__defaultArgs) > ${index} {\n\t\t\t${paramName} = __defaultArgs[${index}].(${paramType})\n\t\t} else {\n\t\t\t${paramName} = ${defaultValue}\n\t\t}\n\t\t`;
|
|
1597
|
+
})
|
|
1598
|
+
.join('');
|
|
1599
|
+
return {
|
|
1600
|
+
signature: signatureParts.join(', '),
|
|
1601
|
+
prefixBlockContent
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
775
1604
|
function getSafeName(name) {
|
|
776
1605
|
if (!dangerousNames.has(name)) {
|
|
777
1606
|
return name;
|
|
@@ -831,3 +1660,89 @@ function visitNewPromise(node) {
|
|
|
831
1660
|
promiseResolveName = prevResolveName;
|
|
832
1661
|
return `func() chan ${channelType} {\n\t\tch := make(chan ${channelType})\n\t\tgo func() ${body.trimEnd()}()\n\t\treturn ch;\n\t}()`;
|
|
833
1662
|
}
|
|
1663
|
+
function extractMapValueType(mapType) {
|
|
1664
|
+
// mapType is "map[K]V" — find the closing bracket of K accounting for nesting
|
|
1665
|
+
if (!mapType.startsWith('map['))
|
|
1666
|
+
return 'interface{}';
|
|
1667
|
+
let depth = 0;
|
|
1668
|
+
for (let i = 4; i < mapType.length; i++) {
|
|
1669
|
+
if (mapType[i] === '[')
|
|
1670
|
+
depth++;
|
|
1671
|
+
else if (mapType[i] === ']') {
|
|
1672
|
+
if (depth === 0)
|
|
1673
|
+
return mapType.substring(i + 1);
|
|
1674
|
+
depth--;
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
return 'interface{}';
|
|
1678
|
+
}
|
|
1679
|
+
function visitNewMap(node) {
|
|
1680
|
+
let keyType = 'interface{}';
|
|
1681
|
+
let valueType = 'interface{}';
|
|
1682
|
+
if (node.typeArguments && node.typeArguments.length === 2) {
|
|
1683
|
+
keyType = getType(node.typeArguments[0]);
|
|
1684
|
+
valueType = getType(node.typeArguments[1]);
|
|
1685
|
+
}
|
|
1686
|
+
const mapType = `map[${keyType}]${valueType}`;
|
|
1687
|
+
const args = node.arguments;
|
|
1688
|
+
if (!args || args.length === 0 || !ts.isArrayLiteralExpression(args[0])) {
|
|
1689
|
+
return `make(${mapType})`;
|
|
1690
|
+
}
|
|
1691
|
+
const initArg = args[0];
|
|
1692
|
+
const tmp = getTempName('map');
|
|
1693
|
+
const entries = initArg.elements
|
|
1694
|
+
.filter((el) => ts.isArrayLiteralExpression(el) && el.elements.length >= 2)
|
|
1695
|
+
.map((el) => {
|
|
1696
|
+
const pair = el;
|
|
1697
|
+
return `${tmp}[${visit(pair.elements[0])}] = ${visit(pair.elements[1])}`;
|
|
1698
|
+
})
|
|
1699
|
+
.join('; ');
|
|
1700
|
+
return `func() ${mapType} { ${tmp} := make(${mapType}); ${entries}; return ${tmp} }()`;
|
|
1701
|
+
}
|
|
1702
|
+
function visitNewSet(node) {
|
|
1703
|
+
let elementType = 'interface{}';
|
|
1704
|
+
if (node.typeArguments && node.typeArguments.length === 1) {
|
|
1705
|
+
elementType = getType(node.typeArguments[0]);
|
|
1706
|
+
}
|
|
1707
|
+
const setType = `map[${elementType}]struct{}`;
|
|
1708
|
+
const args = node.arguments;
|
|
1709
|
+
if (!args || args.length === 0 || !ts.isArrayLiteralExpression(args[0])) {
|
|
1710
|
+
return `make(${setType})`;
|
|
1711
|
+
}
|
|
1712
|
+
const initArg = args[0];
|
|
1713
|
+
const tmp = getTempName('set');
|
|
1714
|
+
const values = initArg.elements.map((el) => `${tmp}[${visit(el)}] = struct{}{}`).join('; ');
|
|
1715
|
+
return `func() ${setType} { ${tmp} := make(${setType}); ${values}; return ${tmp} }()`;
|
|
1716
|
+
}
|
|
1717
|
+
function getForOfVarNames(initializer) {
|
|
1718
|
+
if (!ts.isVariableDeclarationList(initializer) || initializer.declarations.length === 0) {
|
|
1719
|
+
return ['_'];
|
|
1720
|
+
}
|
|
1721
|
+
const decl = initializer.declarations[0];
|
|
1722
|
+
if (ts.isArrayBindingPattern(decl.name)) {
|
|
1723
|
+
return decl.name.elements.map((el) => {
|
|
1724
|
+
if (ts.isOmittedExpression(el))
|
|
1725
|
+
return '_';
|
|
1726
|
+
return visit(el.name);
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
return [visit(decl.name)];
|
|
1730
|
+
}
|
|
1731
|
+
function visitTryStatement(node, options) {
|
|
1732
|
+
const deferreds = [];
|
|
1733
|
+
// Register finally first (LIFO: runs last after catch)
|
|
1734
|
+
if (node.finallyBlock) {
|
|
1735
|
+
const finallyBody = node.finallyBlock.statements.map((s) => visit(s)).join('\t');
|
|
1736
|
+
deferreds.push(`defer func() {\n\t\t\t${finallyBody}\t\t\t}()`);
|
|
1737
|
+
}
|
|
1738
|
+
// Register catch second (LIFO: runs first, handles panic via recover)
|
|
1739
|
+
if (node.catchClause) {
|
|
1740
|
+
const varDecl = node.catchClause.variableDeclaration;
|
|
1741
|
+
const catchVar = varDecl ? visit(varDecl.name) : '_r';
|
|
1742
|
+
const catchBody = node.catchClause.block.statements.map((s) => visit(s)).join('\t');
|
|
1743
|
+
deferreds.push(`defer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\t${catchVar} := r\n\t\t\t\t_ = ${catchVar}\n\t\t\t\t${catchBody}\t\t\t}\n\t\t\t}()`);
|
|
1744
|
+
}
|
|
1745
|
+
const tryBody = node.tryBlock.statements.map((s) => visit(s)).join('\t');
|
|
1746
|
+
const body = [...deferreds, tryBody].join('\n\t\t\t');
|
|
1747
|
+
return `func() {\n\t\t\t${body}\n\t\t\t}()` + (options.inline ? '' : ';\n\t');
|
|
1748
|
+
}
|