typenative 0.0.13 → 0.0.15

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/bin/transpiler.js CHANGED
@@ -1,14 +1,25 @@
1
- import ts from 'typescript';
2
- let TypeCheker;
3
- const importedPackages = new Set();
4
- let outsideNodes = [];
5
- export function transpileToNative(code) {
6
- const sourceFile = ts.createSourceFile('main.ts', code, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS);
7
- TypeCheker = ts.createProgram(['main.ts'], {}).getTypeChecker();
8
- importedPackages.clear();
9
- outsideNodes = [];
10
- const transpiledCode = visit(sourceFile, { addFunctionOutside: true });
11
- const transpiledCodeOutside = outsideNodes.map((n) => visit(n, { isOutside: true })).join('\n');
1
+ import ts from 'typescript';
2
+ import { customAlphabet } from 'nanoid';
3
+ const goSafeId = customAlphabet('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 8);
4
+ let TypeCheker;
5
+ const importedPackages = new Set();
6
+ let outsideNodes = [];
7
+ const classNames = new Set();
8
+ let promiseResolveName = '';
9
+ const dangerousNames = new Set(['main']);
10
+ const renamedFunctions = new Map();
11
+ const variableTypes = new Map();
12
+ export function transpileToNative(code) {
13
+ const sourceFile = ts.createSourceFile('main.ts', code, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS);
14
+ TypeCheker = ts.createProgram(['main.ts'], {}).getTypeChecker();
15
+ importedPackages.clear();
16
+ outsideNodes = [];
17
+ classNames.clear();
18
+ promiseResolveName = '';
19
+ renamedFunctions.clear();
20
+ variableTypes.clear();
21
+ const transpiledCode = visit(sourceFile, { addFunctionOutside: true });
22
+ const transpiledCodeOutside = outsideNodes.map((n) => visit(n, { isOutside: true })).join('\n');
12
23
  return `package main
13
24
 
14
25
  ${[...importedPackages].map((pkg) => `import "${pkg}"`).join('\n')}
@@ -17,237 +28,746 @@ func main() {
17
28
  ${transpiledCode.trim()}
18
29
  }
19
30
 
20
- ${transpiledCodeOutside.trim()}`;
21
- }
22
- export function visit(node, options = {}) {
23
- let code = '';
24
- if (ts.isSourceFile(node)) {
25
- return node.statements
26
- .map((n) => visit(n, { addFunctionOutside: true }))
27
- .filter((n) => !!n)
28
- .join(options.inline ? '' : '\n\t');
29
- }
30
- else if (ts.isIdentifier(node)) {
31
- return node.text;
32
- }
33
- else if (ts.isStringLiteral(node)) {
34
- return `"${node.text}"`;
35
- }
36
- else if (ts.isNumericLiteral(node)) {
37
- return `float64(${node.text})`;
38
- }
39
- else if (ts.isToken(node) && node.kind === ts.SyntaxKind.TrueKeyword) {
40
- return `true`;
41
- }
42
- else if (ts.isToken(node) && node.kind === ts.SyntaxKind.FalseKeyword) {
43
- return `false`;
44
- }
45
- else if (ts.isArrayLiteralExpression(node)) {
46
- const type = ts.isVariableDeclaration(node.parent) ? getType(node.parent.type, true) : '';
47
- return `[]${type} {${node.elements.map((e) => visit(e)).join(', ')}}`;
48
- }
49
- else if (ts.isBlock(node)) {
50
- return `{\n\t\t${node.statements.map((n) => visit(n)).join('\t')}${options.extraBlockContent ?? ''}}${options.inline ? '' : '\n\t'}`;
51
- }
52
- else if (ts.isElementAccessExpression(node)) {
53
- return `${visit(node.expression)}[int(${visit(node.argumentExpression)})]`;
54
- }
55
- else if (ts.isPropertyAccessExpression(node)) {
56
- const leftSide = visit(node.expression);
57
- const rightSide = visit(node.name);
58
- return getAcessString(leftSide, rightSide);
59
- }
60
- else if (ts.isVariableDeclaration(node)) {
61
- const type = getType(node.type);
62
- const initializer = node.initializer ? `= ${visit(node.initializer)}` : '';
63
- return `${type === ':' ? '' : 'var '}${visit(node.name)} ${type}${type === ':' ? '' : ' '}${initializer}`;
64
- }
65
- else if (ts.isCallExpression(node)) {
66
- const caller = visit(node.expression);
67
- const args = node.arguments.map((a) => visit(a));
68
- return getCallString(caller, args);
69
- }
70
- else if (ts.isPrefixUnaryExpression(node)) {
71
- return `${getOperatorText(node.operator)}${visit(node.operand)}`;
72
- }
73
- else if (ts.isPostfixUnaryExpression(node)) {
74
- return `${visit(node.operand, { inline: true })}${getOperatorText(node.operator)}`;
75
- }
76
- else if (ts.isBinaryExpression(node)) {
77
- return `${visit(node.left)} ${node.operatorToken.getText()} ${visit(node.right)}`;
78
- }
79
- else if (ts.isParenthesizedExpression(node)) {
80
- return `(${visit(node.expression)})`;
81
- }
82
- else if (ts.isVariableDeclarationList(node)) {
83
- return (node.declarations.map((n) => visit(n)).join(options.inline ? ';' : ';\n\t') +
84
- (options.inline ? '' : ';\n\t'));
85
- }
86
- else if (ts.isExpressionStatement(node)) {
87
- return visit(node.expression) + (options.inline ? '' : ';\n\t');
88
- }
89
- else if (ts.isForStatement(node)) {
90
- return `for ${visit(node.initializer, { inline: true })}; ${visit(node.condition, {
91
- inline: true
92
- })}; ${visit(node.incrementor, { inline: true })}${visit(node.statement)}`;
93
- }
94
- else if (ts.isForOfStatement(node)) {
95
- return `for _,${visit(node.initializer, { inline: true })}= range ${visit(node.expression, {
96
- inline: true
97
- })}${visit(node.statement)}`;
98
- }
99
- else if (ts.isWhileStatement(node)) {
100
- return `for ${visit(node.expression, { inline: true })}${visit(node.statement)}`;
101
- }
102
- else if (ts.isDoStatement(node)) {
103
- const condition = `\tif !(${visit(node.expression, {
104
- inline: true
105
- })}) {\n\t\t\tbreak \n\t\t}\n\t`;
106
- return `for ${visit(node.statement, { inline: true, extraBlockContent: condition })}`;
107
- }
108
- else if (ts.isIfStatement(node)) {
109
- const condition = `if ${visit(node.expression, { inline: true })} ${visit(node.thenStatement, {
110
- inline: !!node.elseStatement
111
- })}`;
112
- if (node.elseStatement) {
113
- return `${condition} else ${visit(node.elseStatement)}`;
114
- }
115
- return condition;
116
- }
117
- else if (ts.isSwitchStatement(node)) {
118
- return `switch ${visit(node.expression)} ${visit(node.caseBlock)}`;
119
- }
120
- else if (ts.isCaseBlock(node)) {
121
- return `{\n\t\t${node.clauses.map((c) => visit(c)).join('\n\t\t')}\n\t}`;
122
- }
123
- else if (ts.isCaseClause(node)) {
124
- const isFallThrough = !node.statements.some((c) => ts.isBreakStatement(c));
125
- return `case ${visit(node.expression, { inline: true })}: \n\t\t\t${node.statements
126
- .filter((n) => !ts.isBreakStatement(n))
127
- .map((s) => visit(s))
128
- .join('')}${isFallThrough ? 'fallthrough\n\t' : ''}`;
129
- }
130
- else if (ts.isDefaultClause(node)) {
131
- return `default: \n\t\t\t${node.statements
132
- .filter((n) => !ts.isBreakStatement(n))
133
- .map((s) => visit(s))
134
- .join('')}`;
135
- }
136
- else if (ts.isBreakStatement(node)) {
137
- return 'break';
138
- }
139
- else if (ts.isReturnStatement(node)) {
140
- return (`return ${node.expression ? visit(node.expression) : ''}` + (options.inline ? '' : ';\n\t'));
141
- }
142
- else if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
143
- if (options.addFunctionOutside) {
144
- outsideNodes.push(node);
145
- return '';
146
- }
147
- const name = visit(node.name, { inline: true });
148
- const parameters = node.parameters
149
- .map((p) => `${visit(p.name)} ${getType(p.type)}`)
150
- .join(', ');
151
- const returnType = node.type ? ` ${getType(node.type)}` : '';
152
- if (options.isOutside) {
153
- return `func ${name}(${parameters})${returnType} ${visit(node.body)}`;
154
- }
155
- return `${name} := func(${parameters})${returnType} ${visit(node.body)}`;
156
- }
157
- else if (ts.isArrowFunction(node)) {
158
- const parameters = node.parameters
159
- .map((p) => `${visit(p.name)} ${getType(p.type)}`)
160
- .join(', ');
161
- const returnType = node.type ? ` ${getType(node.type)}` : '';
162
- return `func(${parameters})${returnType} ${visit(node.body)}`;
163
- }
164
- const syntaxKind = ts.SyntaxKind[node.kind];
165
- if (!['FirstStatement', 'EndOfFileToken'].includes(syntaxKind)) {
166
- console.log(ts.SyntaxKind[node.kind], node.getText());
167
- }
168
- ts.forEachChild(node, (subNode) => {
169
- code += visit(subNode);
170
- });
171
- return code;
172
- }
173
- function getType(typeNode, getArrayType = false) {
174
- const type = TypeCheker.getTypeFromTypeNode(typeNode);
175
- let typeName = TypeCheker.typeToString(type);
176
- const isArray = typeName.includes('[]');
177
- if (isArray) {
178
- if (getArrayType) {
179
- typeName = typeName.replace('[]', '');
180
- }
181
- else {
182
- return ':';
183
- }
184
- }
185
- switch (typeName) {
186
- case 'number':
187
- return 'float64';
188
- case 'boolean':
189
- return 'bool';
190
- case 'any':
191
- return ':';
192
- case 'void':
193
- return '';
194
- default:
195
- return typeName;
196
- }
197
- }
198
- function getAcessString(leftSide, rightSide) {
199
- if (rightSide === 'length') {
200
- return 'float64(len(arr))';
201
- }
202
- return `${leftSide}.${rightSide}`;
203
- }
204
- function getCallString(caller, args) {
205
- if (caller === 'console.log') {
206
- importedPackages.add('fmt');
207
- return `fmt.Println(${args.join(', ')})`;
208
- }
209
- else if (caller === 'console.time') {
210
- importedPackages.add('time');
211
- return `${getTimerName(args[0])} := time.Now()`;
212
- }
213
- else if (caller === 'console.timeEnd') {
214
- importedPackages.add('time');
215
- importedPackages.add('fmt');
216
- return `fmt.Println("Elapsed time:", time.Since(${getTimerName(args[0])}))`;
217
- }
218
- else if (caller === 'Math.random') {
219
- importedPackages.add('math/rand');
220
- return 'rand.Float64()';
221
- }
222
- else if (caller === 'Math.floor') {
223
- importedPackages.add('math');
224
- return `math.Floor(${args.join(', ')})`;
225
- }
226
- else if (caller.endsWith('.push')) {
227
- const arrayName = caller.substring(0, caller.length - '.push'.length);
228
- return `${arrayName} = append(${arrayName},${args.join(', ')})`;
229
- }
230
- return `${caller}(${args.join(', ')})`;
231
- }
232
- function getOperatorText(operator) {
233
- switch (operator) {
234
- case ts.SyntaxKind.PlusToken:
235
- return '+';
236
- case ts.SyntaxKind.MinusToken:
237
- return '-';
238
- case ts.SyntaxKind.TildeToken:
239
- return '~';
240
- case ts.SyntaxKind.ExclamationToken:
241
- return '!';
242
- case ts.SyntaxKind.PlusPlusToken:
243
- return '++';
244
- case ts.SyntaxKind.MinusMinusToken:
245
- return '--';
246
- default:
247
- console.error('Did not find operator', operator);
248
- return '';
249
- }
250
- }
251
- function getTimerName(name) {
252
- return `__timer_${name.replaceAll(' ', '_').replaceAll('"', '')}__`;
253
- }
31
+ ${transpiledCodeOutside.trim()}`;
32
+ }
33
+ export function visit(node, options = {}) {
34
+ let code = '';
35
+ if (ts.isSourceFile(node)) {
36
+ return node.statements
37
+ .map((n) => visit(n, { addFunctionOutside: true }))
38
+ .filter((n) => !!n)
39
+ .join(options.inline ? '' : '\n\t');
40
+ }
41
+ else if (ts.isIdentifier(node)) {
42
+ if (node.text === 'undefined')
43
+ return 'nil';
44
+ return getSafeName(node.text);
45
+ }
46
+ else if (ts.isStringLiteral(node)) {
47
+ return `"${node.text}"`;
48
+ }
49
+ else if (ts.isNumericLiteral(node)) {
50
+ return `float64(${node.text})`;
51
+ }
52
+ else if (ts.isToken(node) && node.kind === ts.SyntaxKind.TrueKeyword) {
53
+ return `true`;
54
+ }
55
+ else if (ts.isToken(node) && node.kind === ts.SyntaxKind.FalseKeyword) {
56
+ return `false`;
57
+ }
58
+ else if (ts.isToken(node) && node.kind === ts.SyntaxKind.NullKeyword) {
59
+ return `nil`;
60
+ }
61
+ else if (ts.isArrayLiteralExpression(node)) {
62
+ const type = ts.isVariableDeclaration(node.parent) ? getType(node.parent.type, true) : '';
63
+ return `[]${type} {${node.elements.map((e) => visit(e)).join(', ')}}`;
64
+ }
65
+ else if (ts.isBlock(node)) {
66
+ return `{\n\t\t${node.statements.map((n) => visit(n)).join('\t')}${options.extraBlockContent ?? ''}}${options.inline ? '' : '\n\t'}`;
67
+ }
68
+ else if (ts.isElementAccessExpression(node)) {
69
+ return `${visit(node.expression)}[int(${visit(node.argumentExpression)})]`;
70
+ }
71
+ else if (ts.isPropertyAccessExpression(node)) {
72
+ const leftSide = visit(node.expression);
73
+ const rightSide = visit(node.name);
74
+ const objectType = resolveExpressionType(node.expression);
75
+ return getAcessString(leftSide, rightSide, objectType);
76
+ }
77
+ else if (ts.isVariableDeclaration(node)) {
78
+ const type = getType(node.type);
79
+ // Track variable type for type-aware method dispatch
80
+ if (ts.isIdentifier(node.name)) {
81
+ const cat = node.type ? getTypeCategory(node.type) : undefined;
82
+ if (cat) {
83
+ variableTypes.set(node.name.text, cat);
84
+ }
85
+ else if (node.initializer &&
86
+ ts.isNewExpression(node.initializer) &&
87
+ ts.isIdentifier(node.initializer.expression)) {
88
+ if (classNames.has(node.initializer.expression.text)) {
89
+ variableTypes.set(node.name.text, 'class');
90
+ }
91
+ }
92
+ }
93
+ let initializer = node.initializer ? `= ${visit(node.initializer)}` : '';
94
+ // Wrap non-nil values assigned to nullable primitive pointer types
95
+ // Only wrap for primitive pointers (*string, *float64, *bool), not class pointers
96
+ if (node.initializer &&
97
+ type.startsWith('*') &&
98
+ !isNilLiteral(node.initializer) &&
99
+ ['*string', '*float64', '*bool'].includes(type)) {
100
+ const value = visit(node.initializer);
101
+ initializer = `= func() ${type} { v := ${value}; return &v }()`;
102
+ }
103
+ return `${type === ':' ? '' : 'var '}${visit(node.name)} ${type}${type === ':' ? '' : ' '}${initializer}`;
104
+ }
105
+ else if (ts.isCallExpression(node)) {
106
+ // Handle setTimeout specially to get raw delay value
107
+ if (ts.isIdentifier(node.expression) && node.expression.text === 'setTimeout') {
108
+ importedPackages.add('time');
109
+ const callback = visit(node.arguments[0]);
110
+ const delayNode = node.arguments[1];
111
+ const delay = ts.isNumericLiteral(delayNode) ? delayNode.text : visit(delayNode);
112
+ return `time.AfterFunc(${delay} * time.Millisecond, ${callback.trimEnd()})`;
113
+ }
114
+ const caller = visit(node.expression);
115
+ const safeCaller = getSafeName(caller);
116
+ const typeArgs = getTypeArguments(node.typeArguments);
117
+ const args = node.arguments.map((a) => visit(a));
118
+ // Resolve object type for type-aware method dispatch
119
+ let objectType;
120
+ if (ts.isPropertyAccessExpression(node.expression)) {
121
+ objectType = resolveExpressionType(node.expression.expression);
122
+ }
123
+ return getCallString(safeCaller, args, typeArgs, objectType);
124
+ }
125
+ else if (ts.isPrefixUnaryExpression(node)) {
126
+ return `${getOperatorText(node.operator)}${visit(node.operand)}`;
127
+ }
128
+ else if (ts.isPostfixUnaryExpression(node)) {
129
+ return `${visit(node.operand, { inline: true })}${getOperatorText(node.operator)}`;
130
+ }
131
+ else if (ts.isBinaryExpression(node)) {
132
+ let op = node.operatorToken.getText();
133
+ if (op === '===')
134
+ op = '==';
135
+ if (op === '!==')
136
+ op = '!=';
137
+ return `${visit(node.left)} ${op} ${visit(node.right)}`;
138
+ }
139
+ else if (ts.isParenthesizedExpression(node)) {
140
+ return `(${visit(node.expression)})`;
141
+ }
142
+ else if (ts.isAwaitExpression(node)) {
143
+ return `<-${visit(node.expression)}`;
144
+ }
145
+ else if (ts.isVariableDeclarationList(node)) {
146
+ return (node.declarations.map((n) => visit(n)).join(options.inline ? ';' : ';\n\t') +
147
+ (options.inline ? '' : ';\n\t'));
148
+ }
149
+ else if (ts.isExpressionStatement(node)) {
150
+ return visit(node.expression) + (options.inline ? '' : ';\n\t');
151
+ }
152
+ else if (ts.isForStatement(node)) {
153
+ return `for ${visit(node.initializer, { inline: true })}; ${visit(node.condition, {
154
+ inline: true
155
+ })}; ${visit(node.incrementor, { inline: true })}${visit(node.statement)}`;
156
+ }
157
+ else if (ts.isForOfStatement(node)) {
158
+ return `for _,${visit(node.initializer, { inline: true })}= range ${visit(node.expression, {
159
+ inline: true
160
+ })}${visit(node.statement)}`;
161
+ }
162
+ else if (ts.isWhileStatement(node)) {
163
+ return `for ${visit(node.expression, { inline: true })}${visit(node.statement)}`;
164
+ }
165
+ else if (ts.isDoStatement(node)) {
166
+ const condition = `\tif !(${visit(node.expression, {
167
+ inline: true
168
+ })}) {\n\t\t\tbreak \n\t\t}\n\t`;
169
+ return `for ${visit(node.statement, { inline: true, extraBlockContent: condition })}`;
170
+ }
171
+ else if (ts.isIfStatement(node)) {
172
+ const condition = `if ${visit(node.expression, { inline: true })} ${visit(node.thenStatement, {
173
+ inline: !!node.elseStatement
174
+ })}`;
175
+ if (node.elseStatement) {
176
+ return `${condition} else ${visit(node.elseStatement)}`;
177
+ }
178
+ return condition;
179
+ }
180
+ else if (ts.isSwitchStatement(node)) {
181
+ return `switch ${visit(node.expression)} ${visit(node.caseBlock)}`;
182
+ }
183
+ else if (ts.isCaseBlock(node)) {
184
+ return `{\n\t\t${node.clauses.map((c) => visit(c)).join('\n\t\t')}\n\t}`;
185
+ }
186
+ else if (ts.isCaseClause(node)) {
187
+ const isFallThrough = !node.statements.some((c) => ts.isBreakStatement(c));
188
+ return `case ${visit(node.expression, { inline: true })}: \n\t\t\t${node.statements
189
+ .filter((n) => !ts.isBreakStatement(n))
190
+ .map((s) => visit(s))
191
+ .join('')}${isFallThrough ? 'fallthrough\n\t' : ''}`;
192
+ }
193
+ else if (ts.isDefaultClause(node)) {
194
+ return `default: \n\t\t\t${node.statements
195
+ .filter((n) => !ts.isBreakStatement(n))
196
+ .map((s) => visit(s))
197
+ .join('')}`;
198
+ }
199
+ else if (ts.isBreakStatement(node)) {
200
+ return 'break';
201
+ }
202
+ else if (ts.isReturnStatement(node)) {
203
+ // Handle return new Promise(...)
204
+ if (node.expression &&
205
+ ts.isNewExpression(node.expression) &&
206
+ ts.isIdentifier(node.expression.expression) &&
207
+ node.expression.expression.text === 'Promise') {
208
+ return visitPromiseReturn(node.expression, options);
209
+ }
210
+ return (`return ${node.expression ? visit(node.expression) : ''}` + (options.inline ? '' : ';\n\t'));
211
+ }
212
+ else if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
213
+ if (options.addFunctionOutside) {
214
+ outsideNodes.push(node);
215
+ return '';
216
+ }
217
+ const name = visit(node.name, { inline: true });
218
+ const safeName = getSafeName(name);
219
+ const typeParams = getTypeParameters(node.typeParameters);
220
+ const parameters = node.parameters
221
+ .map((p) => `${visit(p.name)} ${getType(p.type)}`)
222
+ .join(', ');
223
+ const returnType = node.type ? ` ${getType(node.type)}` : '';
224
+ if (options.isOutside) {
225
+ return `func ${safeName}${typeParams}(${parameters})${returnType} ${visit(node.body)}`;
226
+ }
227
+ return `${safeName} := func${typeParams}(${parameters})${returnType} ${visit(node.body)}`;
228
+ }
229
+ else if (ts.isArrowFunction(node)) {
230
+ const parameters = node.parameters
231
+ .map((p) => `${visit(p.name)} ${getType(p.type)}`)
232
+ .join(', ');
233
+ const returnType = node.type ? ` ${getType(node.type)}` : '';
234
+ return `func(${parameters})${returnType} ${visit(node.body)}`;
235
+ }
236
+ else if (node.kind === ts.SyntaxKind.ThisKeyword) {
237
+ return 'self';
238
+ }
239
+ else if (ts.isInterfaceDeclaration(node)) {
240
+ if (options.addFunctionOutside) {
241
+ outsideNodes.push(node);
242
+ return '';
243
+ }
244
+ const name = visit(node.name);
245
+ const typeParams = getTypeParameters(node.typeParameters);
246
+ const extendedInterfaces = [];
247
+ if (node.heritageClauses) {
248
+ for (const clause of node.heritageClauses) {
249
+ if (clause.token === ts.SyntaxKind.ExtendsKeyword) {
250
+ for (const type of clause.types) {
251
+ extendedInterfaces.push(visit(type.expression));
252
+ }
253
+ }
254
+ }
255
+ }
256
+ const methods = [];
257
+ for (const member of node.members) {
258
+ if (ts.isMethodSignature(member)) {
259
+ const methodName = visit(member.name);
260
+ const params = member.parameters
261
+ .map((p) => `${visit(p.name)} ${getType(p.type)}`)
262
+ .join(', ');
263
+ const returnType = member.type ? ` ${getType(member.type)}` : '';
264
+ methods.push(`\t${methodName}(${params})${returnType}`);
265
+ }
266
+ }
267
+ const members = [...extendedInterfaces.map((e) => `\t${e}`), ...methods];
268
+ return `type ${name}${typeParams} interface {\n${members.join('\n')}\n}`;
269
+ }
270
+ else if (ts.isClassDeclaration(node)) {
271
+ if (options.addFunctionOutside) {
272
+ outsideNodes.push(node);
273
+ classNames.add(visit(node.name));
274
+ return '';
275
+ }
276
+ const name = visit(node.name);
277
+ const typeParams = getTypeParameters(node.typeParameters);
278
+ const typeParamNames = getTypeParameterNames(node.typeParameters);
279
+ let parentClass = null;
280
+ if (node.heritageClauses) {
281
+ for (const clause of node.heritageClauses) {
282
+ if (clause.token === ts.SyntaxKind.ExtendsKeyword) {
283
+ parentClass = visit(clause.types[0].expression);
284
+ }
285
+ }
286
+ }
287
+ const fields = [];
288
+ if (parentClass) {
289
+ fields.push(`\t${parentClass}`);
290
+ }
291
+ for (const member of node.members) {
292
+ if (ts.isPropertyDeclaration(member)) {
293
+ const fieldName = visit(member.name);
294
+ let fieldType;
295
+ if (member.type && ts.isArrayTypeNode(member.type)) {
296
+ fieldType = `[]${getType(member.type, true)}`;
297
+ }
298
+ else {
299
+ fieldType = member.type ? getType(member.type) : 'interface{}';
300
+ }
301
+ fields.push(`\t${fieldName} ${fieldType}`);
302
+ }
303
+ }
304
+ let result = `type ${name}${typeParams} struct {\n${fields.join('\n')}\n}\n\n`;
305
+ const ctor = node.members.find((m) => ts.isConstructorDeclaration(m));
306
+ if (ctor) {
307
+ const params = ctor.parameters.map((p) => `${visit(p.name)} ${getType(p.type)}`).join(', ');
308
+ const bodyStatements = ctor.body?.statements
309
+ .filter((s) => {
310
+ if (ts.isExpressionStatement(s) && ts.isCallExpression(s.expression)) {
311
+ return s.expression.expression.kind !== ts.SyntaxKind.SuperKeyword;
312
+ }
313
+ return true;
314
+ })
315
+ .map((s) => visit(s))
316
+ .join('\t') ?? '';
317
+ result += `func New${name}${typeParams}(${params}) *${name}${typeParamNames} {\n\t\tself := &${name}${typeParamNames}{}\n\t\t${bodyStatements}return self;\n\t}\n\n`;
318
+ }
319
+ else {
320
+ result += `func New${name}${typeParams}() *${name}${typeParamNames} {\n\t\treturn &${name}${typeParamNames}{}\n\t}\n\n`;
321
+ }
322
+ for (const member of node.members) {
323
+ if (ts.isMethodDeclaration(member)) {
324
+ const methodName = visit(member.name);
325
+ const params = member.parameters
326
+ .map((p) => `${visit(p.name)} ${getType(p.type)}`)
327
+ .join(', ');
328
+ const returnType = member.type ? ` ${getType(member.type)}` : '';
329
+ result += `func (self *${name}${typeParamNames}) ${methodName}(${params})${returnType} ${visit(member.body)}\n\n`;
330
+ }
331
+ }
332
+ return result.trim();
333
+ }
334
+ else if (ts.isNewExpression(node)) {
335
+ const className = visit(node.expression);
336
+ if (className === 'Promise') {
337
+ return visitNewPromise(node);
338
+ }
339
+ const typeArgs = getTypeArguments(node.typeArguments);
340
+ const args = node.arguments ? node.arguments.map((a) => visit(a)) : [];
341
+ return `New${className}${typeArgs}(${args.join(', ')})`;
342
+ }
343
+ else if (ts.isObjectLiteralExpression(node)) {
344
+ let typeName = '';
345
+ if (ts.isVariableDeclaration(node.parent) && node.parent.type) {
346
+ typeName = getTypeText(node.parent.type);
347
+ }
348
+ const properties = node.properties
349
+ .map((p) => {
350
+ if (ts.isPropertyAssignment(p)) {
351
+ return `${visit(p.name)}: ${visit(p.initializer)}`;
352
+ }
353
+ return '';
354
+ })
355
+ .filter((p) => p)
356
+ .join(', ');
357
+ return `${typeName}{${properties}}`;
358
+ }
359
+ else if (ts.isPropertyAssignment(node)) {
360
+ return `${visit(node.name)}: ${visit(node.initializer)}`;
361
+ }
362
+ else if (ts.isNonNullExpression(node)) {
363
+ return visit(node.expression);
364
+ }
365
+ const syntaxKind = ts.SyntaxKind[node.kind];
366
+ if (!['FirstStatement', 'EndOfFileToken'].includes(syntaxKind)) {
367
+ console.log(ts.SyntaxKind[node.kind], node.getText());
368
+ }
369
+ ts.forEachChild(node, (subNode) => {
370
+ code += visit(subNode);
371
+ });
372
+ return code;
373
+ }
374
+ function getTypeText(typeNode) {
375
+ if (!typeNode)
376
+ return ':';
377
+ if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
378
+ return typeNode.typeName.text;
379
+ }
380
+ return getType(typeNode);
381
+ }
382
+ function getType(typeNode, getArrayType = false) {
383
+ if (!typeNode)
384
+ return ':';
385
+ // Handle union types (e.g. string | null, number | undefined)
386
+ if (ts.isUnionTypeNode(typeNode)) {
387
+ const nonNullTypes = typeNode.types.filter((t) => t.kind !== ts.SyntaxKind.NullKeyword &&
388
+ t.kind !== ts.SyntaxKind.UndefinedKeyword &&
389
+ !(ts.isLiteralTypeNode(t) && t.literal.kind === ts.SyntaxKind.NullKeyword));
390
+ if (nonNullTypes.length === 1 && nonNullTypes.length < typeNode.types.length) {
391
+ // This is a nullable type T | null or T | undefined
392
+ const innerType = getType(nonNullTypes[0]);
393
+ if (['float64', 'string', 'bool'].includes(innerType)) {
394
+ return `*${innerType}`;
395
+ }
396
+ // Pointer/interface types already support nil
397
+ return innerType;
398
+ }
399
+ // Non-nullable union or multi-type union → interface{}
400
+ return 'interface{}';
401
+ }
402
+ if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
403
+ const name = typeNode.typeName.text;
404
+ if (name === 'Promise' && typeNode.typeArguments && typeNode.typeArguments.length > 0) {
405
+ return `chan ${getType(typeNode.typeArguments[0])}`;
406
+ }
407
+ const typeArgs = getTypeArguments(typeNode.typeArguments);
408
+ if (classNames.has(name)) {
409
+ return `*${name}${typeArgs}`;
410
+ }
411
+ return `${name}${typeArgs}`;
412
+ }
413
+ const type = TypeCheker.getTypeFromTypeNode(typeNode);
414
+ let typeName = TypeCheker.typeToString(type);
415
+ const isArray = typeName.includes('[]');
416
+ if (isArray) {
417
+ if (getArrayType) {
418
+ typeName = typeName.replace('[]', '');
419
+ }
420
+ else {
421
+ return ':';
422
+ }
423
+ }
424
+ switch (typeName) {
425
+ case 'number':
426
+ return 'float64';
427
+ case 'boolean':
428
+ return 'bool';
429
+ case 'any':
430
+ return ':';
431
+ case 'void':
432
+ return '';
433
+ default:
434
+ return typeName;
435
+ }
436
+ }
437
+ function getTypeCategory(typeNode) {
438
+ if (!typeNode)
439
+ return undefined;
440
+ if (ts.isArrayTypeNode(typeNode))
441
+ return 'array';
442
+ if (typeNode.kind === ts.SyntaxKind.StringKeyword)
443
+ return 'string';
444
+ if (typeNode.kind === ts.SyntaxKind.NumberKeyword)
445
+ return 'number';
446
+ if (typeNode.kind === ts.SyntaxKind.BooleanKeyword)
447
+ return 'boolean';
448
+ if (ts.isUnionTypeNode(typeNode)) {
449
+ const nonNullTypes = typeNode.types.filter((t) => t.kind !== ts.SyntaxKind.NullKeyword &&
450
+ t.kind !== ts.SyntaxKind.UndefinedKeyword &&
451
+ !(ts.isLiteralTypeNode(t) && t.literal.kind === ts.SyntaxKind.NullKeyword));
452
+ if (nonNullTypes.length === 1)
453
+ return getTypeCategory(nonNullTypes[0]);
454
+ }
455
+ if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
456
+ const name = typeNode.typeName.text;
457
+ if (classNames.has(name))
458
+ return 'class';
459
+ return name;
460
+ }
461
+ return undefined;
462
+ }
463
+ function resolveExpressionType(expr) {
464
+ if (ts.isIdentifier(expr)) {
465
+ return variableTypes.get(expr.text);
466
+ }
467
+ if (expr.kind === ts.SyntaxKind.ThisKeyword) {
468
+ return 'class';
469
+ }
470
+ return undefined;
471
+ }
472
+ function isNilLiteral(node) {
473
+ if (node.kind === ts.SyntaxKind.NullKeyword)
474
+ return true;
475
+ if (ts.isIdentifier(node) && node.text === 'undefined')
476
+ return true;
477
+ return false;
478
+ }
479
+ function getAcessString(leftSide, rightSide, objectType) {
480
+ if (rightSide === 'length' && objectType !== 'class') {
481
+ return `float64(len(${leftSide}))`;
482
+ }
483
+ return `${leftSide}.${rightSide}`;
484
+ }
485
+ const callHandlers = {
486
+ assert: (_caller, args) => {
487
+ const message = args.length > 1 ? args[1] : '"Assertion failed"';
488
+ return `if !(${args[0]}) {\n\t\tpanic(${message})\n\t}`;
489
+ },
490
+ 'console.log': (_caller, args) => {
491
+ importedPackages.add('fmt');
492
+ return `fmt.Println(${args.join(', ')})`;
493
+ },
494
+ 'console.time': (_caller, args) => {
495
+ importedPackages.add('time');
496
+ return `${getTimerName(args[0])} := time.Now()`;
497
+ },
498
+ 'console.timeEnd': (_caller, args) => {
499
+ importedPackages.add('time');
500
+ importedPackages.add('fmt');
501
+ return `fmt.Println("Elapsed time:", time.Since(${getTimerName(args[0])}))`;
502
+ },
503
+ 'Math.random': () => {
504
+ importedPackages.add('math/rand');
505
+ return 'rand.Float64()';
506
+ },
507
+ 'Math.floor': (_caller, args) => {
508
+ importedPackages.add('math');
509
+ return `math.Floor(${args[0]})`;
510
+ },
511
+ 'Math.ceil': (_caller, args) => {
512
+ importedPackages.add('math');
513
+ return `math.Ceil(${args[0]})`;
514
+ },
515
+ 'Math.round': (_caller, args) => {
516
+ importedPackages.add('math');
517
+ return `math.Round(${args[0]})`;
518
+ },
519
+ 'Math.abs': (_caller, args) => {
520
+ importedPackages.add('math');
521
+ return `math.Abs(${args[0]})`;
522
+ },
523
+ 'Math.max': (_caller, args) => {
524
+ importedPackages.add('math');
525
+ return `math.Max(${args[0]}, ${args[1]})`;
526
+ },
527
+ 'Math.min': (_caller, args) => {
528
+ importedPackages.add('math');
529
+ return `math.Min(${args[0]}, ${args[1]})`;
530
+ },
531
+ 'Math.sqrt': (_caller, args) => {
532
+ importedPackages.add('math');
533
+ return `math.Sqrt(${args[0]})`;
534
+ },
535
+ 'Math.pow': (_caller, args) => {
536
+ importedPackages.add('math');
537
+ return `math.Pow(${args[0]}, ${args[1]})`;
538
+ },
539
+ parseInt: (_caller, args) => {
540
+ importedPackages.add('strconv');
541
+ return `func() float64 { v, _ := strconv.Atoi(${args[0]}); return float64(v) }()`;
542
+ },
543
+ parseFloat: (_caller, args) => {
544
+ importedPackages.add('strconv');
545
+ return `func() float64 { v, _ := strconv.ParseFloat(${args[0]}, 64); return v }()`;
546
+ }
547
+ };
548
+ const stringMethodHandlers = {
549
+ split: (obj, args) => {
550
+ importedPackages.add('strings');
551
+ return `strings.Split(${obj}, ${args[0]})`;
552
+ },
553
+ trim: (obj) => {
554
+ importedPackages.add('strings');
555
+ return `strings.TrimSpace(${obj})`;
556
+ },
557
+ trimStart: (obj) => {
558
+ importedPackages.add('strings');
559
+ return `strings.TrimLeft(${obj}, " \\t\\n\\r")`;
560
+ },
561
+ trimEnd: (obj) => {
562
+ importedPackages.add('strings');
563
+ return `strings.TrimRight(${obj}, " \\t\\n\\r")`;
564
+ },
565
+ toUpperCase: (obj) => {
566
+ importedPackages.add('strings');
567
+ return `strings.ToUpper(${obj})`;
568
+ },
569
+ toLowerCase: (obj) => {
570
+ importedPackages.add('strings');
571
+ return `strings.ToLower(${obj})`;
572
+ },
573
+ indexOf: (obj, args) => {
574
+ importedPackages.add('strings');
575
+ return `float64(strings.Index(${obj}, ${args[0]}))`;
576
+ },
577
+ includes: (obj, args) => {
578
+ importedPackages.add('strings');
579
+ return `strings.Contains(${obj}, ${args[0]})`;
580
+ },
581
+ startsWith: (obj, args) => {
582
+ importedPackages.add('strings');
583
+ return `strings.HasPrefix(${obj}, ${args[0]})`;
584
+ },
585
+ endsWith: (obj, args) => {
586
+ importedPackages.add('strings');
587
+ return `strings.HasSuffix(${obj}, ${args[0]})`;
588
+ },
589
+ replace: (obj, args) => {
590
+ importedPackages.add('strings');
591
+ return `strings.Replace(${obj}, ${args[0]}, ${args[1]}, 1)`;
592
+ },
593
+ replaceAll: (obj, args) => {
594
+ importedPackages.add('strings');
595
+ return `strings.ReplaceAll(${obj}, ${args[0]}, ${args[1]})`;
596
+ },
597
+ repeat: (obj, args) => {
598
+ importedPackages.add('strings');
599
+ return `strings.Repeat(${obj}, int(${args[0]}))`;
600
+ },
601
+ charAt: (obj, args) => `string(${obj}[int(${args[0]})])`,
602
+ substring: (obj, args) => {
603
+ if (args.length >= 2)
604
+ return `${obj}[int(${args[0]}):int(${args[1]})]`;
605
+ return `${obj}[int(${args[0]}):]`;
606
+ },
607
+ slice: (obj, args) => {
608
+ if (args.length >= 2)
609
+ return `${obj}[int(${args[0]}):int(${args[1]})]`;
610
+ return `${obj}[int(${args[0]}):]`;
611
+ },
612
+ concat: (obj, args) => `${obj} + ${args.join(' + ')}`,
613
+ toString: (obj) => {
614
+ importedPackages.add('fmt');
615
+ return `fmt.Sprintf("%v", ${obj})`;
616
+ }
617
+ };
618
+ const arrayMethodHandlers = {
619
+ push: (obj, args) => `${obj} = append(${obj}, ${args.join(', ')})`,
620
+ join: (obj, args) => {
621
+ importedPackages.add('strings');
622
+ return `strings.Join(${obj}, ${args[0] ?? '""'})`;
623
+ },
624
+ slice: (obj, args) => {
625
+ if (args.length >= 2)
626
+ return `${obj}[int(${args[0]}):int(${args[1]})]`;
627
+ return `${obj}[int(${args[0]}):]`;
628
+ },
629
+ toString: (obj) => {
630
+ importedPackages.add('fmt');
631
+ return `fmt.Sprintf("%v", ${obj})`;
632
+ }
633
+ };
634
+ function getDynamicCallHandler(caller, objectType) {
635
+ if (promiseResolveName && caller === promiseResolveName) {
636
+ return (_caller, args) => `ch <- ${args[0]}`;
637
+ }
638
+ const dotIndex = caller.lastIndexOf('.');
639
+ if (dotIndex !== -1) {
640
+ const methodName = caller.substring(dotIndex + 1);
641
+ // Class instances use their own methods — never intercept
642
+ if (objectType === 'class')
643
+ return null;
644
+ let handler;
645
+ if (objectType === 'string') {
646
+ handler = stringMethodHandlers[methodName];
647
+ }
648
+ else if (objectType === 'array') {
649
+ handler = arrayMethodHandlers[methodName];
650
+ }
651
+ else {
652
+ // Unknown type: try both maps for backward compatibility
653
+ handler = stringMethodHandlers[methodName] ?? arrayMethodHandlers[methodName];
654
+ }
655
+ if (handler) {
656
+ return (c, args) => {
657
+ const obj = c.substring(0, dotIndex);
658
+ return handler(obj, args);
659
+ };
660
+ }
661
+ }
662
+ return null;
663
+ }
664
+ function getCallString(caller, args, typeArgs = '', objectType) {
665
+ const handler = callHandlers[caller] ?? getDynamicCallHandler(caller, objectType);
666
+ if (handler) {
667
+ return handler(caller, args, typeArgs);
668
+ }
669
+ return `${caller}${typeArgs}(${args.join(', ')})`;
670
+ }
671
+ function getOperatorText(operator) {
672
+ switch (operator) {
673
+ case ts.SyntaxKind.PlusToken:
674
+ return '+';
675
+ case ts.SyntaxKind.MinusToken:
676
+ return '-';
677
+ case ts.SyntaxKind.TildeToken:
678
+ return '~';
679
+ case ts.SyntaxKind.ExclamationToken:
680
+ return '!';
681
+ case ts.SyntaxKind.PlusPlusToken:
682
+ return '++';
683
+ case ts.SyntaxKind.MinusMinusToken:
684
+ return '--';
685
+ default:
686
+ console.error('Did not find operator', operator);
687
+ return '';
688
+ }
689
+ }
690
+ function getTimerName(name) {
691
+ return `__timer_${name.replaceAll(' ', '_').replaceAll('"', '')}__`;
692
+ }
693
+ function getTypeParameters(typeParameters) {
694
+ if (!typeParameters || typeParameters.length === 0)
695
+ return '';
696
+ const params = typeParameters.map((tp) => {
697
+ const name = visit(tp.name);
698
+ const constraint = tp.constraint ? getType(tp.constraint) : 'any';
699
+ return `${name} ${constraint}`;
700
+ });
701
+ return `[${params.join(', ')}]`;
702
+ }
703
+ function getTypeParameterNames(typeParameters) {
704
+ if (!typeParameters || typeParameters.length === 0)
705
+ return '';
706
+ const names = typeParameters.map((tp) => visit(tp.name));
707
+ return `[${names.join(', ')}]`;
708
+ }
709
+ function getTypeArguments(typeArguments) {
710
+ if (!typeArguments || typeArguments.length === 0)
711
+ return '';
712
+ const args = typeArguments.map((ta) => getType(ta));
713
+ return `[${args.join(', ')}]`;
714
+ }
715
+ function getSafeName(name) {
716
+ if (!dangerousNames.has(name)) {
717
+ return name;
718
+ }
719
+ if (!renamedFunctions.has(name)) {
720
+ renamedFunctions.set(name, `${name}_${goSafeId()}`);
721
+ }
722
+ return renamedFunctions.get(name);
723
+ }
724
+ function getPromiseChannelType(node) {
725
+ let parent = node.parent;
726
+ while (parent) {
727
+ if (ts.isFunctionDeclaration(parent) ||
728
+ ts.isMethodDeclaration(parent) ||
729
+ ts.isFunctionExpression(parent)) {
730
+ if (parent.type &&
731
+ ts.isTypeReferenceNode(parent.type) &&
732
+ ts.isIdentifier(parent.type.typeName)) {
733
+ if (parent.type.typeName.text === 'Promise' &&
734
+ parent.type.typeArguments &&
735
+ parent.type.typeArguments.length > 0) {
736
+ return getType(parent.type.typeArguments[0]);
737
+ }
738
+ }
739
+ break;
740
+ }
741
+ parent = parent.parent;
742
+ }
743
+ return 'interface{}';
744
+ }
745
+ function visitPromiseReturn(node, options) {
746
+ const callback = node.arguments?.[0];
747
+ if (!callback || (!ts.isArrowFunction(callback) && !ts.isFunctionExpression(callback))) {
748
+ return `return ${visit(node)}` + (options.inline ? '' : ';\n\t');
749
+ }
750
+ const channelType = getPromiseChannelType(node);
751
+ const resolveParam = callback.parameters[0];
752
+ const resolveName = resolveParam ? visit(resolveParam.name) : '';
753
+ const prevResolveName = promiseResolveName;
754
+ promiseResolveName = resolveName;
755
+ const body = ts.isBlock(callback.body) ? visit(callback.body) : `{ ${visit(callback.body)} }`;
756
+ promiseResolveName = prevResolveName;
757
+ return (`ch := make(chan ${channelType})\n\t\tgo func() ${body.trimEnd()}()\n\t\treturn ch` +
758
+ (options.inline ? '' : ';\n\t'));
759
+ }
760
+ function visitNewPromise(node) {
761
+ const callback = node.arguments?.[0];
762
+ if (!callback || (!ts.isArrowFunction(callback) && !ts.isFunctionExpression(callback))) {
763
+ return 'NewPromise()';
764
+ }
765
+ const channelType = getPromiseChannelType(node);
766
+ const resolveParam = callback.parameters[0];
767
+ const resolveName = resolveParam ? visit(resolveParam.name) : '';
768
+ const prevResolveName = promiseResolveName;
769
+ promiseResolveName = resolveName;
770
+ const body = ts.isBlock(callback.body) ? visit(callback.body) : `{ ${visit(callback.body)} }`;
771
+ promiseResolveName = prevResolveName;
772
+ return `func() chan ${channelType} {\n\t\tch := make(chan ${channelType})\n\t\tgo func() ${body.trimEnd()}()\n\t\treturn ch;\n\t}()`;
773
+ }