@ugo-studio/jspp 0.1.2 → 0.1.4
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/README.md +5 -3
- package/dist/analysis/scope.js +38 -15
- package/dist/analysis/typeAnalyzer.js +257 -23
- package/dist/ast/types.js +6 -0
- package/dist/cli.js +3 -4
- package/dist/core/codegen/class-handlers.js +127 -0
- package/dist/core/codegen/control-flow-handlers.js +464 -0
- package/dist/core/codegen/declaration-handlers.js +31 -14
- package/dist/core/codegen/expression-handlers.js +432 -116
- package/dist/core/codegen/function-handlers.js +110 -13
- package/dist/core/codegen/helpers.js +76 -8
- package/dist/core/codegen/index.js +18 -5
- package/dist/core/codegen/literal-handlers.js +3 -0
- package/dist/core/codegen/statement-handlers.js +152 -186
- package/dist/core/codegen/visitor.js +35 -3
- package/package.json +3 -3
- package/src/prelude/any_value.hpp +658 -734
- package/src/prelude/any_value_access.hpp +103 -0
- package/src/prelude/any_value_defines.hpp +151 -0
- package/src/prelude/any_value_helpers.hpp +246 -0
- package/src/prelude/exception.hpp +31 -0
- package/src/prelude/exception_helpers.hpp +49 -0
- package/src/prelude/index.hpp +35 -12
- package/src/prelude/library/console.hpp +20 -20
- package/src/prelude/library/error.hpp +111 -0
- package/src/prelude/library/global.hpp +15 -4
- package/src/prelude/library/performance.hpp +25 -0
- package/src/prelude/library/promise.hpp +121 -0
- package/src/prelude/library/symbol.hpp +60 -4
- package/src/prelude/library/timer.hpp +92 -0
- package/src/prelude/scheduler.hpp +145 -0
- package/src/prelude/types.hpp +33 -6
- package/src/prelude/utils/access.hpp +174 -0
- package/src/prelude/utils/log_any_value/array.hpp +245 -0
- package/src/prelude/utils/log_any_value/config.hpp +32 -0
- package/src/prelude/utils/log_any_value/function.hpp +37 -0
- package/src/prelude/utils/log_any_value/fwd.hpp +15 -0
- package/src/prelude/utils/log_any_value/helpers.hpp +62 -0
- package/src/prelude/utils/log_any_value/log_any_value.hpp +94 -0
- package/src/prelude/utils/log_any_value/object.hpp +119 -0
- package/src/prelude/utils/log_any_value/primitives.hpp +41 -0
- package/src/prelude/{operators.hpp → utils/operators.hpp} +31 -12
- package/src/prelude/utils/well_known_symbols.hpp +13 -0
- package/src/prelude/values/array.hpp +5 -2
- package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
- package/src/prelude/values/function.hpp +76 -19
- package/src/prelude/values/{operators → helpers}/array.hpp +30 -14
- package/src/prelude/values/helpers/function.hpp +125 -0
- package/src/prelude/values/helpers/iterator.hpp +107 -0
- package/src/prelude/values/helpers/object.hpp +64 -0
- package/src/prelude/values/helpers/promise.hpp +181 -0
- package/src/prelude/values/helpers/string.hpp +50 -0
- package/src/prelude/values/helpers/symbol.hpp +23 -0
- package/src/prelude/values/iterator.hpp +96 -0
- package/src/prelude/values/object.hpp +8 -3
- package/src/prelude/values/promise.hpp +73 -0
- package/src/prelude/values/prototypes/array.hpp +23 -16
- package/src/prelude/values/prototypes/function.hpp +26 -0
- package/src/prelude/values/prototypes/iterator.hpp +58 -0
- package/src/prelude/values/prototypes/object.hpp +26 -0
- package/src/prelude/values/prototypes/promise.hpp +124 -0
- package/src/prelude/values/prototypes/string.hpp +366 -357
- package/src/prelude/values/prototypes/symbol.hpp +41 -0
- package/src/prelude/values/string.hpp +25 -0
- package/src/prelude/values/symbol.hpp +102 -0
- package/src/prelude/access.hpp +0 -86
- package/src/prelude/error.hpp +0 -31
- package/src/prelude/error_helpers.hpp +0 -59
- package/src/prelude/log_string.hpp +0 -403
- package/src/prelude/values/operators/function.hpp +0 -34
- package/src/prelude/values/operators/object.hpp +0 -34
- package/src/prelude/well_known_symbols.hpp +0 -10
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { CodeGenerator } from "./";
|
|
3
|
+
export function visitForStatement(node, context) {
|
|
4
|
+
const forStmt = node;
|
|
5
|
+
let code = "";
|
|
6
|
+
if (context.currentLabel) {
|
|
7
|
+
code += `${this.indent()}${context.currentLabel}: {\n`;
|
|
8
|
+
this.indentationLevel++;
|
|
9
|
+
}
|
|
10
|
+
this.indentationLevel++; // Enter a new scope for the for loop
|
|
11
|
+
// Handle initializer
|
|
12
|
+
let initializerCode = "";
|
|
13
|
+
if (forStmt.initializer) {
|
|
14
|
+
if (ts.isVariableDeclarationList(forStmt.initializer)) {
|
|
15
|
+
const varDeclList = forStmt.initializer;
|
|
16
|
+
const isLetOrConst = (varDeclList.flags &
|
|
17
|
+
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
|
|
18
|
+
0;
|
|
19
|
+
if (isLetOrConst) {
|
|
20
|
+
const decl = varDeclList.declarations[0];
|
|
21
|
+
if (decl) {
|
|
22
|
+
const name = decl.name.getText();
|
|
23
|
+
const initValue = decl.initializer
|
|
24
|
+
? this.visit(decl.initializer, context)
|
|
25
|
+
: "jspp::AnyValue::make_undefined()";
|
|
26
|
+
const scope = this.getScopeForNode(decl);
|
|
27
|
+
const typeInfo = this.typeAnalyzer.scopeManager
|
|
28
|
+
.lookupFromScope(name, scope);
|
|
29
|
+
if (typeInfo.needsHeapAllocation) {
|
|
30
|
+
initializerCode =
|
|
31
|
+
`auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
initializerCode =
|
|
35
|
+
`jspp::AnyValue ${name} = ${initValue}`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
initializerCode = this.visit(forStmt.initializer, {
|
|
41
|
+
...context,
|
|
42
|
+
isAssignmentOnly: true,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
initializerCode = this.visit(forStmt.initializer, context);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
code += `${this.indent()}for (${initializerCode}; `;
|
|
51
|
+
if (forStmt.condition) {
|
|
52
|
+
code += `(${this.visit(forStmt.condition, context)}).is_truthy()`;
|
|
53
|
+
}
|
|
54
|
+
code += "; ";
|
|
55
|
+
if (forStmt.incrementor) {
|
|
56
|
+
code += this.visit(forStmt.incrementor, context);
|
|
57
|
+
}
|
|
58
|
+
code += ") ";
|
|
59
|
+
const statementCode = this.visit(forStmt.statement, {
|
|
60
|
+
...context,
|
|
61
|
+
currentLabel: undefined,
|
|
62
|
+
isFunctionBody: false,
|
|
63
|
+
});
|
|
64
|
+
if (ts.isBlock(node.statement)) {
|
|
65
|
+
let blockContent = statementCode.substring(1, statementCode.length - 2); // remove curly braces
|
|
66
|
+
if (context.currentLabel) {
|
|
67
|
+
blockContent +=
|
|
68
|
+
`${this.indent()}${context.currentLabel}_continue:;\n`;
|
|
69
|
+
}
|
|
70
|
+
code += `{\n${blockContent}}\n`;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
code += `{\n`;
|
|
74
|
+
code += statementCode;
|
|
75
|
+
if (context.currentLabel) {
|
|
76
|
+
code += `${this.indent()}${context.currentLabel}_continue:;\n`;
|
|
77
|
+
}
|
|
78
|
+
code += `${this.indent()}}\n`;
|
|
79
|
+
}
|
|
80
|
+
this.indentationLevel--; // Exit the scope for the for loop
|
|
81
|
+
if (context.currentLabel) {
|
|
82
|
+
this.indentationLevel--;
|
|
83
|
+
code += `${this.indent()}}\n`;
|
|
84
|
+
code +=
|
|
85
|
+
`${this.indent()}${context.currentLabel}_break:; // break target\n`;
|
|
86
|
+
}
|
|
87
|
+
return code;
|
|
88
|
+
}
|
|
89
|
+
export function visitForInStatement(node, context) {
|
|
90
|
+
const forIn = node;
|
|
91
|
+
let code = "";
|
|
92
|
+
if (context.currentLabel) {
|
|
93
|
+
code += `${this.indent()}${context.currentLabel}: {\n`;
|
|
94
|
+
this.indentationLevel++;
|
|
95
|
+
}
|
|
96
|
+
code += `${this.indent()}{\n`;
|
|
97
|
+
this.indentationLevel++; // Enter a new scope for the for-in loop
|
|
98
|
+
let varName = "";
|
|
99
|
+
let assignmentTarget = "";
|
|
100
|
+
if (ts.isVariableDeclarationList(forIn.initializer)) {
|
|
101
|
+
const decl = forIn.initializer.declarations[0];
|
|
102
|
+
if (decl) {
|
|
103
|
+
varName = decl.name.getText();
|
|
104
|
+
const scope = this.getScopeForNode(decl);
|
|
105
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(varName, scope);
|
|
106
|
+
if (typeInfo.needsHeapAllocation) {
|
|
107
|
+
code +=
|
|
108
|
+
`${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
|
|
109
|
+
assignmentTarget = `*${varName}`;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
code +=
|
|
113
|
+
`${this.indent()}jspp::AnyValue ${varName} = jspp::AnyValue::make_undefined();\n`;
|
|
114
|
+
assignmentTarget = varName;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else if (ts.isIdentifier(forIn.initializer)) {
|
|
119
|
+
varName = forIn.initializer.getText();
|
|
120
|
+
const scope = this.getScopeForNode(forIn.initializer);
|
|
121
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(varName, scope);
|
|
122
|
+
assignmentTarget = typeInfo.needsHeapAllocation
|
|
123
|
+
? `*${varName}`
|
|
124
|
+
: varName;
|
|
125
|
+
}
|
|
126
|
+
const expr = forIn.expression;
|
|
127
|
+
const exprText = this.visit(expr, context);
|
|
128
|
+
let derefExpr = exprText;
|
|
129
|
+
if (ts.isIdentifier(expr)) {
|
|
130
|
+
const scope = this.getScopeForNode(expr);
|
|
131
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.getText(), scope);
|
|
132
|
+
derefExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
|
|
133
|
+
}
|
|
134
|
+
const keysVar = this.generateUniqueName("__keys_", new Set([varName]));
|
|
135
|
+
code +=
|
|
136
|
+
`${this.indent()}std::vector<std::string> ${keysVar} = jspp::Access::get_object_keys(${derefExpr});\n`;
|
|
137
|
+
code += `${this.indent()}for (const auto& ${varName}_str : ${keysVar}) {\n`;
|
|
138
|
+
this.indentationLevel++;
|
|
139
|
+
code +=
|
|
140
|
+
`${this.indent()}${assignmentTarget} = jspp::AnyValue::make_string(${varName}_str);\n`;
|
|
141
|
+
code += this.visit(forIn.statement, {
|
|
142
|
+
...context,
|
|
143
|
+
currentLabel: undefined,
|
|
144
|
+
isFunctionBody: false,
|
|
145
|
+
});
|
|
146
|
+
this.indentationLevel--;
|
|
147
|
+
if (context.currentLabel) {
|
|
148
|
+
code += `${this.indent()}${context.currentLabel}_continue:;\n`;
|
|
149
|
+
}
|
|
150
|
+
code += `${this.indent()}}\n`;
|
|
151
|
+
this.indentationLevel--; // Exit the scope for the for-in loop
|
|
152
|
+
code += `${this.indent()}}\n`;
|
|
153
|
+
if (context.currentLabel) {
|
|
154
|
+
this.indentationLevel--;
|
|
155
|
+
code += `${this.indent()}}\n`;
|
|
156
|
+
code +=
|
|
157
|
+
`${this.indent()}${context.currentLabel}_break:; // break target\n`;
|
|
158
|
+
}
|
|
159
|
+
return code;
|
|
160
|
+
}
|
|
161
|
+
export function visitForOfStatement(node, context) {
|
|
162
|
+
const forOf = node;
|
|
163
|
+
let code = "";
|
|
164
|
+
if (context.currentLabel) {
|
|
165
|
+
code += `${this.indent()}${context.currentLabel}: {\n`;
|
|
166
|
+
this.indentationLevel++;
|
|
167
|
+
}
|
|
168
|
+
this.indentationLevel++; // Enter a new scope for the for-of loop
|
|
169
|
+
let elemName = "";
|
|
170
|
+
let assignmentTarget = "";
|
|
171
|
+
code += `${this.indent()}{\n`;
|
|
172
|
+
if (ts.isVariableDeclarationList(forOf.initializer)) {
|
|
173
|
+
const decl = forOf.initializer.declarations[0];
|
|
174
|
+
if (decl) {
|
|
175
|
+
elemName = decl.name.getText();
|
|
176
|
+
const scope = this.getScopeForNode(decl);
|
|
177
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemName, scope);
|
|
178
|
+
if (typeInfo.needsHeapAllocation) {
|
|
179
|
+
code +=
|
|
180
|
+
`${this.indent()}auto ${elemName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
|
|
181
|
+
assignmentTarget = `*${elemName}`;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
code +=
|
|
185
|
+
`${this.indent()}jspp::AnyValue ${elemName} = jspp::AnyValue::make_undefined();\n`;
|
|
186
|
+
assignmentTarget = elemName;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else if (ts.isIdentifier(forOf.initializer)) {
|
|
191
|
+
elemName = forOf.initializer.getText();
|
|
192
|
+
const scope = this.getScopeForNode(forOf.initializer);
|
|
193
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemName, scope);
|
|
194
|
+
assignmentTarget = typeInfo.needsHeapAllocation
|
|
195
|
+
? `*${elemName}`
|
|
196
|
+
: elemName;
|
|
197
|
+
}
|
|
198
|
+
const iterableExpr = this.visit(forOf.expression, context);
|
|
199
|
+
let derefIterable = iterableExpr;
|
|
200
|
+
if (ts.isIdentifier(forOf.expression)) {
|
|
201
|
+
const scope = this.getScopeForNode(forOf.expression);
|
|
202
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(forOf.expression.getText(), scope);
|
|
203
|
+
const varName = this.getJsVarName(forOf.expression);
|
|
204
|
+
derefIterable = this.getDerefCode(iterableExpr, varName, typeInfo);
|
|
205
|
+
}
|
|
206
|
+
const declaredSymbols = this.getDeclaredSymbols(forOf.statement);
|
|
207
|
+
const iterableRef = this.generateUniqueName("__iter_ref", declaredSymbols);
|
|
208
|
+
const iterator = this.generateUniqueName("__iter", declaredSymbols);
|
|
209
|
+
const nextFunc = this.generateUniqueName("__next_func", declaredSymbols);
|
|
210
|
+
const nextRes = this.generateUniqueName("__next_res", declaredSymbols);
|
|
211
|
+
const varName = this.getJsVarName(forOf.expression);
|
|
212
|
+
code += `${this.indent()}auto ${iterableRef} = ${derefIterable};\n`;
|
|
213
|
+
code +=
|
|
214
|
+
`${this.indent()}auto ${iterator} = jspp::Access::get_object_value_iterator(${iterableRef}, ${varName});\n`;
|
|
215
|
+
code +=
|
|
216
|
+
`${this.indent()}auto ${nextFunc} = ${iterator}.get_own_property("next").as_function();\n`;
|
|
217
|
+
code +=
|
|
218
|
+
`${this.indent()}auto ${nextRes} = ${nextFunc}->call(${iterator}, {});\n`;
|
|
219
|
+
code +=
|
|
220
|
+
`${this.indent()}while (!${nextRes}.get_own_property("done").is_truthy()) {\n`;
|
|
221
|
+
this.indentationLevel++;
|
|
222
|
+
code +=
|
|
223
|
+
`${this.indent()}${assignmentTarget} = ${nextRes}.get_own_property("value");\n`;
|
|
224
|
+
code += this.visit(forOf.statement, {
|
|
225
|
+
...context,
|
|
226
|
+
currentLabel: undefined,
|
|
227
|
+
isFunctionBody: false,
|
|
228
|
+
});
|
|
229
|
+
if (context.currentLabel) {
|
|
230
|
+
code += `${this.indent()}${context.currentLabel}_continue:;\n`;
|
|
231
|
+
}
|
|
232
|
+
code +=
|
|
233
|
+
`${this.indent()}${nextRes} = ${nextFunc}->call(${iterator}, {});\n`;
|
|
234
|
+
this.indentationLevel--;
|
|
235
|
+
code += `${this.indent()}}\n`;
|
|
236
|
+
this.indentationLevel--; // Exit the scope for the for-of loop
|
|
237
|
+
code += `${this.indent()}}\n`;
|
|
238
|
+
if (context.currentLabel) {
|
|
239
|
+
this.indentationLevel--;
|
|
240
|
+
code += `${this.indent()}}\n`;
|
|
241
|
+
code +=
|
|
242
|
+
`${this.indent()}${context.currentLabel}_break:; // break target\n`;
|
|
243
|
+
}
|
|
244
|
+
return code;
|
|
245
|
+
}
|
|
246
|
+
export function visitWhileStatement(node, context) {
|
|
247
|
+
const condition = node.expression;
|
|
248
|
+
const conditionText = condition.kind === ts.SyntaxKind.TrueKeyword ||
|
|
249
|
+
condition.kind === ts.SyntaxKind.FalseKeyword
|
|
250
|
+
? condition.getText()
|
|
251
|
+
: `(${this.visit(condition, context)}).is_truthy()`;
|
|
252
|
+
let code = "";
|
|
253
|
+
if (context.currentLabel) {
|
|
254
|
+
code += `${this.indent()}${context.currentLabel}: {\n`;
|
|
255
|
+
this.indentationLevel++;
|
|
256
|
+
}
|
|
257
|
+
code += `${this.indent()}while (${conditionText}) `;
|
|
258
|
+
const statementCode = this.visit(node.statement, {
|
|
259
|
+
...context,
|
|
260
|
+
currentLabel: undefined,
|
|
261
|
+
isFunctionBody: false,
|
|
262
|
+
});
|
|
263
|
+
if (ts.isBlock(node.statement)) {
|
|
264
|
+
let blockContent = statementCode.substring(1, statementCode.length - 2); // remove curly braces
|
|
265
|
+
if (context.currentLabel) {
|
|
266
|
+
blockContent +=
|
|
267
|
+
`${this.indent()}${context.currentLabel}_continue:;\n`;
|
|
268
|
+
}
|
|
269
|
+
code += `{\n${blockContent}}\n`;
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
code += `{\n`;
|
|
273
|
+
this.indentationLevel++;
|
|
274
|
+
code += statementCode;
|
|
275
|
+
if (context.currentLabel) {
|
|
276
|
+
code += `${this.indent()}${context.currentLabel}_continue:;\n`;
|
|
277
|
+
}
|
|
278
|
+
this.indentationLevel--;
|
|
279
|
+
code += `${this.indent()}}\n`;
|
|
280
|
+
}
|
|
281
|
+
if (context.currentLabel) {
|
|
282
|
+
this.indentationLevel--;
|
|
283
|
+
code += `${this.indent()}}\n`;
|
|
284
|
+
code +=
|
|
285
|
+
`${this.indent()}${context.currentLabel}_break:; // break target\n`;
|
|
286
|
+
}
|
|
287
|
+
return code;
|
|
288
|
+
}
|
|
289
|
+
export function visitDoStatement(node, context) {
|
|
290
|
+
const condition = node.expression;
|
|
291
|
+
const conditionText = `(${this.visit(condition, context)}).is_truthy()`;
|
|
292
|
+
let code = "";
|
|
293
|
+
if (context.currentLabel) {
|
|
294
|
+
code += `${this.indent()}${context.currentLabel}: {\n`;
|
|
295
|
+
this.indentationLevel++;
|
|
296
|
+
}
|
|
297
|
+
code += `${this.indent()}do `;
|
|
298
|
+
const statementCode = this.visit(node.statement, {
|
|
299
|
+
...context,
|
|
300
|
+
currentLabel: undefined,
|
|
301
|
+
isFunctionBody: false,
|
|
302
|
+
});
|
|
303
|
+
if (ts.isBlock(node.statement)) {
|
|
304
|
+
let blockContent = statementCode.substring(1, statementCode.length - 2); // remove curly braces
|
|
305
|
+
if (context.currentLabel) {
|
|
306
|
+
blockContent +=
|
|
307
|
+
`${this.indent()}${context.currentLabel}_continue:;\n`;
|
|
308
|
+
}
|
|
309
|
+
code += `{\n${blockContent}}`;
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
code += `{\n`;
|
|
313
|
+
this.indentationLevel++;
|
|
314
|
+
code += statementCode;
|
|
315
|
+
if (context.currentLabel) {
|
|
316
|
+
code += `${this.indent()}${context.currentLabel}_continue:;\n`;
|
|
317
|
+
}
|
|
318
|
+
this.indentationLevel--;
|
|
319
|
+
code += `${this.indent()}}`;
|
|
320
|
+
}
|
|
321
|
+
code += ` while (${conditionText});\n`;
|
|
322
|
+
if (context.currentLabel) {
|
|
323
|
+
this.indentationLevel--;
|
|
324
|
+
code += `${this.indent()}}\n`;
|
|
325
|
+
code +=
|
|
326
|
+
`${this.indent()}${context.currentLabel}_break:; // break target\n`;
|
|
327
|
+
}
|
|
328
|
+
return code;
|
|
329
|
+
}
|
|
330
|
+
export function visitSwitchStatement(node, context) {
|
|
331
|
+
const switchStmt = node;
|
|
332
|
+
let code = "";
|
|
333
|
+
const declaredSymbols = this.getDeclaredSymbols(switchStmt.caseBlock);
|
|
334
|
+
const switchBreakLabel = this.generateUniqueName("__switch_break_", declaredSymbols);
|
|
335
|
+
const fallthroughVar = this.generateUniqueName("__switch_fallthrough_", declaredSymbols);
|
|
336
|
+
if (context.currentLabel) {
|
|
337
|
+
code += `${this.indent()}${context.currentLabel}: {\n`;
|
|
338
|
+
this.indentationLevel++;
|
|
339
|
+
}
|
|
340
|
+
code += `${this.indent()}{\n`; // Wrap the entire switch logic in a block
|
|
341
|
+
this.indentationLevel++;
|
|
342
|
+
// Evaluate the switch expression once
|
|
343
|
+
const expressionCode = this.visit(switchStmt.expression, context);
|
|
344
|
+
const switchValueVar = this.generateUniqueName("__switch_value_", declaredSymbols);
|
|
345
|
+
code +=
|
|
346
|
+
`${this.indent()}const jspp::AnyValue ${switchValueVar} = ${expressionCode};\n`;
|
|
347
|
+
code += `${this.indent()}bool ${fallthroughVar} = false;\n`;
|
|
348
|
+
// Hoist variable declarations
|
|
349
|
+
const hoistedSymbols = new Map();
|
|
350
|
+
for (const clause of switchStmt.caseBlock.clauses) {
|
|
351
|
+
if (ts.isCaseClause(clause) || ts.isDefaultClause(clause)) {
|
|
352
|
+
for (const stmt of clause.statements) {
|
|
353
|
+
if (ts.isVariableStatement(stmt)) {
|
|
354
|
+
const varDecls = stmt.declarationList.declarations;
|
|
355
|
+
for (const decl of varDecls) {
|
|
356
|
+
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
else if (ts.isFunctionDeclaration(stmt)) {
|
|
360
|
+
code += this.hoistDeclaration(stmt, hoistedSymbols);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Prepare scope symbols for the switch block
|
|
366
|
+
const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.currentScopeSymbols);
|
|
367
|
+
let firstIf = true;
|
|
368
|
+
for (const clause of switchStmt.caseBlock.clauses) {
|
|
369
|
+
if (ts.isCaseClause(clause)) {
|
|
370
|
+
const caseExprCode = this.visit(clause.expression, {
|
|
371
|
+
...context,
|
|
372
|
+
currentLabel: undefined, // Clear currentLabel for nested visits
|
|
373
|
+
});
|
|
374
|
+
let condition = "";
|
|
375
|
+
if (firstIf) {
|
|
376
|
+
condition =
|
|
377
|
+
`(${fallthroughVar} || ${switchValueVar}.is_strictly_equal_to_primitive(${caseExprCode}))`;
|
|
378
|
+
code += `${this.indent()}if ${condition} {\n`;
|
|
379
|
+
firstIf = false;
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
condition =
|
|
383
|
+
`(${fallthroughVar} || ${switchValueVar}.is_strictly_equal_to_primitive(${caseExprCode}))`;
|
|
384
|
+
code += `${this.indent()}if ${condition} {\n`;
|
|
385
|
+
}
|
|
386
|
+
this.indentationLevel++;
|
|
387
|
+
code += `${this.indent()}${fallthroughVar} = true;\n`;
|
|
388
|
+
for (const stmt of clause.statements) {
|
|
389
|
+
if (ts.isFunctionDeclaration(stmt)) {
|
|
390
|
+
const funcName = stmt.name?.getText();
|
|
391
|
+
if (funcName) {
|
|
392
|
+
const contextForFunction = {
|
|
393
|
+
...context,
|
|
394
|
+
topLevelScopeSymbols,
|
|
395
|
+
currentScopeSymbols: hoistedSymbols,
|
|
396
|
+
};
|
|
397
|
+
const lambda = this.generateLambda(stmt, contextForFunction, { isAssignment: true });
|
|
398
|
+
code += `${this.indent()}*${funcName} = ${lambda};\n`;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
code += this.visit(stmt, {
|
|
403
|
+
...context,
|
|
404
|
+
switchBreakLabel,
|
|
405
|
+
currentLabel: undefined, // Clear currentLabel for nested visits
|
|
406
|
+
topLevelScopeSymbols,
|
|
407
|
+
currentScopeSymbols: hoistedSymbols,
|
|
408
|
+
derefBeforeAssignment: true,
|
|
409
|
+
isAssignmentOnly: ts.isVariableStatement(stmt),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
this.indentationLevel--;
|
|
414
|
+
code += `${this.indent()}}\n`;
|
|
415
|
+
}
|
|
416
|
+
else if (ts.isDefaultClause(clause)) {
|
|
417
|
+
// Default clause
|
|
418
|
+
code +=
|
|
419
|
+
`${this.indent()}if (!${fallthroughVar}) ${fallthroughVar} = true;\n`;
|
|
420
|
+
if (firstIf) {
|
|
421
|
+
code += `${this.indent()}if (true) {\n`; // Always execute if no prior cases match and it's the first clause
|
|
422
|
+
firstIf = false;
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
code += `${this.indent()}if (${fallthroughVar}) {\n`; // Only execute if no prior case (or default) has matched and caused fallthrough
|
|
426
|
+
}
|
|
427
|
+
this.indentationLevel++;
|
|
428
|
+
for (const stmt of clause.statements) {
|
|
429
|
+
code += this.visit(stmt, {
|
|
430
|
+
...context,
|
|
431
|
+
switchBreakLabel,
|
|
432
|
+
currentLabel: undefined, // Clear currentLabel for nested visits
|
|
433
|
+
topLevelScopeSymbols,
|
|
434
|
+
currentScopeSymbols: hoistedSymbols,
|
|
435
|
+
derefBeforeAssignment: true,
|
|
436
|
+
isAssignmentOnly: ts.isVariableStatement(stmt),
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
this.indentationLevel--;
|
|
440
|
+
code += `${this.indent()}}\n`;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
this.indentationLevel--;
|
|
444
|
+
code += `${this.indent()}}\n`; // End of the switch block
|
|
445
|
+
code +=
|
|
446
|
+
`${this.indent()}${switchBreakLabel}:; // break target for switch\n`;
|
|
447
|
+
if (context.currentLabel) {
|
|
448
|
+
this.indentationLevel--;
|
|
449
|
+
code += `${this.indent()}}\n`;
|
|
450
|
+
code +=
|
|
451
|
+
`${this.indent()}${context.currentLabel}_break:; // break target for labeled switch\n`;
|
|
452
|
+
}
|
|
453
|
+
return code;
|
|
454
|
+
}
|
|
455
|
+
export function visitCaseClause(node, context) {
|
|
456
|
+
// Case clauses are handled inline by visitSwitchStatement, not generated directly
|
|
457
|
+
// This function will likely not be called, or can return an empty string
|
|
458
|
+
return "";
|
|
459
|
+
}
|
|
460
|
+
export function visitDefaultClause(node, context) {
|
|
461
|
+
// Default clauses are handled inline by visitSwitchStatement, not generated directly
|
|
462
|
+
// This function will likely not be called, or can return an empty string
|
|
463
|
+
return "";
|
|
464
|
+
}
|
|
@@ -9,41 +9,58 @@ export function visitVariableDeclarationList(node, context) {
|
|
|
9
9
|
export function visitVariableDeclaration(node, context) {
|
|
10
10
|
const varDecl = node;
|
|
11
11
|
const name = varDecl.name.getText();
|
|
12
|
+
const scope = this.getScopeForNode(varDecl);
|
|
13
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
12
14
|
let initializer = "";
|
|
13
15
|
if (varDecl.initializer) {
|
|
14
16
|
const initExpr = varDecl.initializer;
|
|
15
|
-
|
|
17
|
+
const initContext = {
|
|
18
|
+
...context,
|
|
19
|
+
lambdaName: ts.isArrowFunction(initExpr) ? name : undefined, // Pass the variable name for arrow functions
|
|
20
|
+
};
|
|
21
|
+
let initText = this.visit(initExpr, initContext);
|
|
16
22
|
if (ts.isIdentifier(initExpr)) {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
23
|
+
const initScope = this.getScopeForNode(initExpr);
|
|
24
|
+
const initTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(initExpr.text, initScope);
|
|
19
25
|
const varName = this.getJsVarName(initExpr);
|
|
20
|
-
if (
|
|
21
|
-
|
|
26
|
+
if (initTypeInfo &&
|
|
27
|
+
!initTypeInfo.isParameter &&
|
|
28
|
+
!initTypeInfo.isBuiltin) {
|
|
29
|
+
initText = this.getDerefCode(initText, varName, initTypeInfo);
|
|
22
30
|
}
|
|
23
31
|
}
|
|
24
32
|
initializer = " = " + initText;
|
|
25
33
|
}
|
|
26
34
|
const isLetOrConst = (varDecl.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
|
|
35
|
+
const shouldDeref = context.derefBeforeAssignment &&
|
|
36
|
+
(!context.currentScopeSymbols.has(name));
|
|
37
|
+
const assignmentTarget = shouldDeref
|
|
38
|
+
? this.getDerefCode(name, name, typeInfo)
|
|
39
|
+
: (typeInfo.needsHeapAllocation ? `*${name}` : name);
|
|
27
40
|
if (isLetOrConst) {
|
|
28
41
|
// If there's no initializer, it should be assigned undefined.
|
|
29
|
-
if (!initializer)
|
|
30
|
-
return
|
|
31
|
-
|
|
42
|
+
if (!initializer) {
|
|
43
|
+
return `${assignmentTarget} = jspp::AnyValue::make_undefined()`;
|
|
44
|
+
}
|
|
45
|
+
return `${assignmentTarget}${initializer}`;
|
|
32
46
|
}
|
|
33
47
|
// For 'var', it's a bit more complex.
|
|
34
|
-
// If we are in a non-function-body block, 'var' is hoisted, so it's an assignment.
|
|
35
|
-
// If we are at the top level or in a function body, it's a declaration if not already hoisted.
|
|
36
|
-
// The current logic hoists at the function level, so we need to decide if this is the *hoisting* declaration or a later assignment.
|
|
37
|
-
// The `isAssignmentOnly` flag helps here.
|
|
38
48
|
if (context.isAssignmentOnly) {
|
|
39
49
|
if (!initializer)
|
|
40
50
|
return "";
|
|
41
|
-
return
|
|
51
|
+
return `${assignmentTarget}${initializer}`;
|
|
42
52
|
}
|
|
43
53
|
else {
|
|
54
|
+
// This case should not be hit with the new hoisting logic,
|
|
55
|
+
// but is kept for safety.
|
|
44
56
|
const initValue = initializer
|
|
45
57
|
? initializer.substring(3)
|
|
46
58
|
: "jspp::AnyValue::make_undefined()";
|
|
47
|
-
|
|
59
|
+
if (typeInfo.needsHeapAllocation) {
|
|
60
|
+
return `auto ${name} = std::make_shared<jspp::AnyValue>(${initValue})`;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
return `jspp::AnyValue ${name} = ${initValue}`;
|
|
64
|
+
}
|
|
48
65
|
}
|
|
49
66
|
}
|