@ugo-studio/jspp 0.1.3 → 0.1.5
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 +2 -2
- package/dist/analysis/scope.js +33 -4
- package/dist/analysis/typeAnalyzer.js +260 -21
- package/dist/ast/symbols.js +29 -0
- package/dist/cli-utils/args.js +57 -0
- package/dist/cli-utils/colors.js +9 -0
- package/dist/cli-utils/file-utils.js +20 -0
- package/dist/cli-utils/spinner.js +55 -0
- package/dist/cli.js +105 -31
- package/dist/core/codegen/class-handlers.js +131 -0
- package/dist/core/codegen/control-flow-handlers.js +474 -0
- package/dist/core/codegen/declaration-handlers.js +36 -15
- package/dist/core/codegen/expression-handlers.js +579 -125
- package/dist/core/codegen/function-handlers.js +222 -37
- package/dist/core/codegen/helpers.js +158 -4
- package/dist/core/codegen/index.js +20 -8
- package/dist/core/codegen/literal-handlers.js +18 -6
- package/dist/core/codegen/statement-handlers.js +171 -228
- package/dist/core/codegen/visitor.js +31 -3
- package/package.json +3 -3
- package/src/prelude/any_value.hpp +510 -633
- package/src/prelude/any_value_access.hpp +151 -0
- package/src/prelude/any_value_defines.hpp +190 -0
- package/src/prelude/any_value_helpers.hpp +139 -225
- package/src/prelude/exception.hpp +32 -0
- package/src/prelude/exception_helpers.hpp +49 -0
- package/src/prelude/index.hpp +25 -9
- package/src/prelude/library/array.hpp +190 -0
- package/src/prelude/library/console.hpp +14 -13
- package/src/prelude/library/error.hpp +113 -0
- package/src/prelude/library/function.hpp +10 -0
- package/src/prelude/library/global.hpp +35 -4
- package/src/prelude/library/math.hpp +308 -0
- package/src/prelude/library/object.hpp +288 -0
- package/src/prelude/library/performance.hpp +2 -2
- package/src/prelude/library/process.hpp +39 -0
- package/src/prelude/library/promise.hpp +131 -0
- package/src/prelude/library/symbol.hpp +46 -59
- package/src/prelude/library/timer.hpp +92 -0
- package/src/prelude/scheduler.hpp +145 -0
- package/src/prelude/types.hpp +58 -1
- package/src/prelude/utils/access.hpp +345 -0
- package/src/prelude/utils/assignment_operators.hpp +99 -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 +39 -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 +136 -0
- package/src/prelude/utils/log_any_value/primitives.hpp +43 -0
- package/src/prelude/utils/operators.hpp +751 -0
- package/src/prelude/utils/well_known_symbols.hpp +25 -0
- package/src/prelude/values/array.hpp +10 -7
- package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
- package/src/prelude/values/function.hpp +85 -51
- package/src/prelude/values/helpers/array.hpp +80 -35
- package/src/prelude/values/helpers/function.hpp +110 -77
- package/src/prelude/values/helpers/iterator.hpp +16 -10
- package/src/prelude/values/helpers/object.hpp +85 -10
- package/src/prelude/values/helpers/promise.hpp +181 -0
- package/src/prelude/values/helpers/string.hpp +3 -3
- package/src/prelude/values/helpers/symbol.hpp +2 -2
- package/src/prelude/values/iterator.hpp +14 -6
- package/src/prelude/values/object.hpp +14 -3
- package/src/prelude/values/promise.hpp +73 -0
- package/src/prelude/values/prototypes/array.hpp +855 -16
- package/src/prelude/values/prototypes/function.hpp +4 -4
- package/src/prelude/values/prototypes/iterator.hpp +11 -10
- package/src/prelude/values/prototypes/number.hpp +153 -0
- package/src/prelude/values/prototypes/object.hpp +26 -0
- package/src/prelude/values/prototypes/promise.hpp +134 -0
- package/src/prelude/values/prototypes/string.hpp +29 -29
- package/src/prelude/values/prototypes/symbol.hpp +22 -3
- package/src/prelude/values/shape.hpp +52 -0
- package/src/prelude/values/string.hpp +1 -1
- package/src/prelude/values/symbol.hpp +1 -1
- package/src/prelude/access.hpp +0 -91
- package/src/prelude/error.hpp +0 -31
- package/src/prelude/error_helpers.hpp +0 -59
- package/src/prelude/log_string.hpp +0 -407
- package/src/prelude/operators.hpp +0 -256
- package/src/prelude/well_known_symbols.hpp +0 -14
|
@@ -1,42 +1,48 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
+
import { DeclaredSymbols } from "../../ast/symbols";
|
|
2
3
|
import { CodeGenerator } from "./";
|
|
4
|
+
import { collectBlockScopedDeclarations, collectFunctionScopedDeclarations, } from "./helpers";
|
|
3
5
|
export function visitSourceFile(node, context) {
|
|
4
6
|
const sourceFile = node;
|
|
5
7
|
let code = "";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
// 1. Collect all var declarations (recursively) + top-level let/const
|
|
9
|
+
const varDecls = collectFunctionScopedDeclarations(sourceFile);
|
|
10
|
+
const topLevelLetConst = collectBlockScopedDeclarations(sourceFile.statements);
|
|
9
11
|
const funcDecls = sourceFile.statements.filter(ts.isFunctionDeclaration);
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
+
const classDecls = sourceFile.statements.filter(ts.isClassDeclaration);
|
|
13
|
+
const hoistedSymbols = new DeclaredSymbols();
|
|
14
|
+
// Hoist function declarations
|
|
12
15
|
funcDecls.forEach((func) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
16
|
+
code += this.hoistDeclaration(func, hoistedSymbols);
|
|
17
|
+
});
|
|
18
|
+
// Hoist class declarations
|
|
19
|
+
classDecls.forEach((cls) => {
|
|
20
|
+
code += this.hoistDeclaration(cls, hoistedSymbols);
|
|
19
21
|
});
|
|
20
|
-
// Hoist variable declarations
|
|
22
|
+
// Hoist variable declarations (var)
|
|
21
23
|
varDecls.forEach((decl) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const isLetOrConst = (decl.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
|
|
28
|
-
0;
|
|
29
|
-
const initializer = isLetOrConst
|
|
30
|
-
? "jspp::AnyValue::make_uninitialized()"
|
|
31
|
-
: "jspp::AnyValue::make_undefined()";
|
|
32
|
-
code +=
|
|
33
|
-
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initializer});\n`;
|
|
24
|
+
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
25
|
+
});
|
|
26
|
+
// Hoist top-level let/const
|
|
27
|
+
topLevelLetConst.forEach((decl) => {
|
|
28
|
+
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
34
29
|
});
|
|
30
|
+
// Compile symbols for other statements (excluding function)
|
|
31
|
+
const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
|
|
32
|
+
const localScopeSymbols = new DeclaredSymbols(hoistedSymbols); // hoistedSymbols becomes new local
|
|
35
33
|
// 2. Assign all hoisted functions first
|
|
34
|
+
const contextForFunctions = {
|
|
35
|
+
...context,
|
|
36
|
+
localScopeSymbols: new DeclaredSymbols(context.localScopeSymbols, hoistedSymbols),
|
|
37
|
+
};
|
|
36
38
|
funcDecls.forEach((stmt) => {
|
|
37
39
|
const funcName = stmt.name?.getText();
|
|
38
40
|
if (funcName) {
|
|
39
|
-
|
|
41
|
+
this.markSymbolAsChecked(funcName, contextForFunctions.topLevelScopeSymbols, contextForFunctions.localScopeSymbols);
|
|
42
|
+
this.markSymbolAsChecked(funcName, topLevelScopeSymbols, localScopeSymbols);
|
|
43
|
+
const lambda = this.generateLambda(stmt, contextForFunctions, {
|
|
44
|
+
isAssignment: true,
|
|
45
|
+
});
|
|
40
46
|
code += `${this.indent()}*${funcName} = ${lambda};\n`;
|
|
41
47
|
}
|
|
42
48
|
});
|
|
@@ -51,6 +57,8 @@ export function visitSourceFile(node, context) {
|
|
|
51
57
|
0;
|
|
52
58
|
const contextForVisit = {
|
|
53
59
|
...context,
|
|
60
|
+
topLevelScopeSymbols,
|
|
61
|
+
localScopeSymbols,
|
|
54
62
|
isAssignmentOnly: !isLetOrConst,
|
|
55
63
|
};
|
|
56
64
|
const assignments = this.visit(stmt.declarationList, contextForVisit);
|
|
@@ -59,7 +67,12 @@ export function visitSourceFile(node, context) {
|
|
|
59
67
|
}
|
|
60
68
|
}
|
|
61
69
|
else {
|
|
62
|
-
code += this.visit(stmt,
|
|
70
|
+
code += this.visit(stmt, {
|
|
71
|
+
...context,
|
|
72
|
+
isFunctionBody: false,
|
|
73
|
+
topLevelScopeSymbols,
|
|
74
|
+
localScopeSymbols,
|
|
75
|
+
});
|
|
63
76
|
}
|
|
64
77
|
});
|
|
65
78
|
return code;
|
|
@@ -68,40 +81,39 @@ export function visitBlock(node, context) {
|
|
|
68
81
|
let code = "{\n";
|
|
69
82
|
this.indentationLevel++;
|
|
70
83
|
const block = node;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
.flatMap((stmt) => stmt.declarationList.declarations);
|
|
84
|
+
// Collect ONLY block-scoped declarations (let/const)
|
|
85
|
+
const blockScopedDecls = collectBlockScopedDeclarations(block.statements);
|
|
74
86
|
const funcDecls = block.statements.filter(ts.isFunctionDeclaration);
|
|
75
|
-
const
|
|
87
|
+
const classDecls = block.statements.filter(ts.isClassDeclaration);
|
|
88
|
+
const hoistedSymbols = new DeclaredSymbols();
|
|
76
89
|
// 1. Hoist all function declarations
|
|
77
90
|
funcDecls.forEach((func) => {
|
|
78
|
-
|
|
79
|
-
if (funcName && !hoistedSymbols.has(funcName)) {
|
|
80
|
-
hoistedSymbols.add(funcName);
|
|
81
|
-
code +=
|
|
82
|
-
`${this.indent()}auto ${funcName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
|
|
83
|
-
}
|
|
91
|
+
code += this.hoistDeclaration(func, hoistedSymbols);
|
|
84
92
|
});
|
|
85
|
-
// Hoist
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (hoistedSymbols.has(name)) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
hoistedSymbols.add(name);
|
|
92
|
-
const isLetOrConst = (decl.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
|
|
93
|
-
0;
|
|
94
|
-
const initializer = isLetOrConst
|
|
95
|
-
? "jspp::AnyValue::make_uninitialized()"
|
|
96
|
-
: "jspp::AnyValue::make_undefined()";
|
|
97
|
-
code +=
|
|
98
|
-
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initializer});\n`;
|
|
93
|
+
// Hoist class declarations
|
|
94
|
+
classDecls.forEach((cls) => {
|
|
95
|
+
code += this.hoistDeclaration(cls, hoistedSymbols);
|
|
99
96
|
});
|
|
97
|
+
// Hoist variable declarations (let/const only)
|
|
98
|
+
blockScopedDecls.forEach((decl) => {
|
|
99
|
+
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
100
|
+
});
|
|
101
|
+
// Compile symbols for other statements (excluding function)
|
|
102
|
+
const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
|
|
103
|
+
const localScopeSymbols = new DeclaredSymbols(hoistedSymbols); // hoistedSymbols becomes new local
|
|
100
104
|
// 2. Assign all hoisted functions first
|
|
105
|
+
const contextForFunctions = {
|
|
106
|
+
...context,
|
|
107
|
+
localScopeSymbols: new DeclaredSymbols(context.localScopeSymbols, hoistedSymbols),
|
|
108
|
+
};
|
|
101
109
|
funcDecls.forEach((stmt) => {
|
|
102
110
|
const funcName = stmt.name?.getText();
|
|
103
111
|
if (funcName) {
|
|
104
|
-
|
|
112
|
+
this.markSymbolAsChecked(funcName, contextForFunctions.topLevelScopeSymbols, contextForFunctions.localScopeSymbols);
|
|
113
|
+
this.markSymbolAsChecked(funcName, topLevelScopeSymbols, localScopeSymbols);
|
|
114
|
+
const lambda = this.generateLambda(stmt, contextForFunctions, {
|
|
115
|
+
isAssignment: true,
|
|
116
|
+
});
|
|
105
117
|
code += `${this.indent()}*${funcName} = ${lambda};\n`;
|
|
106
118
|
}
|
|
107
119
|
});
|
|
@@ -116,6 +128,8 @@ export function visitBlock(node, context) {
|
|
|
116
128
|
0;
|
|
117
129
|
const contextForVisit = {
|
|
118
130
|
...context,
|
|
131
|
+
topLevelScopeSymbols,
|
|
132
|
+
localScopeSymbols,
|
|
119
133
|
isAssignmentOnly: !isLetOrConst,
|
|
120
134
|
};
|
|
121
135
|
const assignments = this.visit(stmt.declarationList, contextForVisit);
|
|
@@ -124,13 +138,18 @@ export function visitBlock(node, context) {
|
|
|
124
138
|
}
|
|
125
139
|
}
|
|
126
140
|
else {
|
|
127
|
-
code += this.visit(stmt,
|
|
141
|
+
code += this.visit(stmt, {
|
|
142
|
+
...context,
|
|
143
|
+
isFunctionBody: false,
|
|
144
|
+
topLevelScopeSymbols,
|
|
145
|
+
localScopeSymbols,
|
|
146
|
+
});
|
|
128
147
|
}
|
|
129
148
|
});
|
|
130
149
|
if (context.isFunctionBody) {
|
|
131
150
|
const lastStatement = block.statements[block.statements.length - 1];
|
|
132
151
|
if (!lastStatement || !ts.isReturnStatement(lastStatement)) {
|
|
133
|
-
code += `${this.indent()}${this.
|
|
152
|
+
code += `${this.indent()}${this.getReturnCommand(context)} jspp::Constants::UNDEFINED;\n`;
|
|
134
153
|
}
|
|
135
154
|
}
|
|
136
155
|
this.indentationLevel--;
|
|
@@ -142,156 +161,47 @@ export function visitVariableStatement(node, context) {
|
|
|
142
161
|
this.visit(node.declarationList, context) +
|
|
143
162
|
";\n");
|
|
144
163
|
}
|
|
145
|
-
export function
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
this.indentationLevel++; // Enter a new scope for the for loop
|
|
149
|
-
// Handle initializer
|
|
150
|
-
let initializerCode = "";
|
|
151
|
-
if (forStmt.initializer) {
|
|
152
|
-
if (ts.isVariableDeclarationList(forStmt.initializer)) {
|
|
153
|
-
const varDeclList = forStmt.initializer;
|
|
154
|
-
const isLetOrConst = (varDeclList.flags &
|
|
155
|
-
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
|
|
156
|
-
0;
|
|
157
|
-
if (isLetOrConst) {
|
|
158
|
-
// For `let` or `const` in for loop, they are block-scoped to the loop.
|
|
159
|
-
// Declare the variable within the loop's scope.
|
|
160
|
-
// The C++ for loop initializer can contain a declaration.
|
|
161
|
-
const decl = varDeclList.declarations[0]; // Assuming single declaration for simplicity
|
|
162
|
-
if (decl) {
|
|
163
|
-
const name = decl.name.getText();
|
|
164
|
-
const initValue = decl.initializer
|
|
165
|
-
? this.visit(decl.initializer, context)
|
|
166
|
-
: "jspp::AnyValue::make_undefined()";
|
|
167
|
-
initializerCode =
|
|
168
|
-
`auto ${name} = std::make_unique<jspp::AnyValue>(${initValue})`;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
// For 'var', it's already hoisted, so this is an assignment.
|
|
173
|
-
initializerCode = this.visit(forStmt.initializer, {
|
|
174
|
-
...context,
|
|
175
|
-
isAssignmentOnly: true,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
// If it's an expression (e.g., `i = 0`)
|
|
181
|
-
initializerCode = this.visit(forStmt.initializer, context);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
code += `${this.indent()}for (${initializerCode}; `;
|
|
185
|
-
if (forStmt.condition) {
|
|
186
|
-
code += `(${this.visit(forStmt.condition, context)}).is_truthy()`;
|
|
164
|
+
export function visitBreakStatement(node, context) {
|
|
165
|
+
if (node.label) {
|
|
166
|
+
return `${this.indent()}goto ${node.label.text}_break;\n`;
|
|
187
167
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
code += this.visit(forStmt.incrementor, context);
|
|
168
|
+
if (context.switchBreakLabel) {
|
|
169
|
+
return `${this.indent()}goto ${context.switchBreakLabel};\n`;
|
|
191
170
|
}
|
|
192
|
-
|
|
193
|
-
code += this.visit(forStmt.statement, {
|
|
194
|
-
...context,
|
|
195
|
-
isFunctionBody: false,
|
|
196
|
-
});
|
|
197
|
-
this.indentationLevel--; // Exit the scope for the for loop
|
|
198
|
-
return code;
|
|
171
|
+
return `${this.indent()}break;\n`;
|
|
199
172
|
}
|
|
200
|
-
export function
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
this.indentationLevel++; // Enter a new scope for the for-in loop
|
|
204
|
-
let varName = "";
|
|
205
|
-
if (ts.isVariableDeclarationList(forIn.initializer)) {
|
|
206
|
-
const decl = forIn.initializer.declarations[0];
|
|
207
|
-
if (decl) {
|
|
208
|
-
varName = decl.name.getText();
|
|
209
|
-
// Declare the shared_ptr before the loop
|
|
210
|
-
code +=
|
|
211
|
-
`${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
else if (ts.isIdentifier(forIn.initializer)) {
|
|
215
|
-
varName = forIn.initializer.getText();
|
|
216
|
-
// Assume it's already declared in an outer scope, just assign to it.
|
|
217
|
-
// No explicit declaration here.
|
|
218
|
-
}
|
|
219
|
-
const expr = forIn.expression;
|
|
220
|
-
const exprText = this.visit(expr, context);
|
|
221
|
-
let derefExpr = exprText;
|
|
222
|
-
if (ts.isIdentifier(expr)) {
|
|
223
|
-
derefExpr = `jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)})`;
|
|
173
|
+
export function visitContinueStatement(node, context) {
|
|
174
|
+
if (node.label) {
|
|
175
|
+
return `${this.indent()}goto ${node.label.text}_continue;\n`;
|
|
224
176
|
}
|
|
225
|
-
|
|
226
|
-
code +=
|
|
227
|
-
`${this.indent()}std::vector<std::string> ${keysVar} = jspp::Access::get_object_keys(${derefExpr});\n`;
|
|
228
|
-
code += `${this.indent()}for (const auto& ${varName}_str : ${keysVar}) {\n`;
|
|
229
|
-
this.indentationLevel++;
|
|
230
|
-
code +=
|
|
231
|
-
`${this.indent()}*${varName} = jspp::AnyValue::make_string(${varName}_str);\n`;
|
|
232
|
-
code += this.visit(forIn.statement, {
|
|
233
|
-
...context,
|
|
234
|
-
isFunctionBody: false,
|
|
235
|
-
});
|
|
236
|
-
this.indentationLevel--;
|
|
237
|
-
code += `${this.indent()}}}\n`;
|
|
238
|
-
this.indentationLevel--; // Exit the scope for the for-in loop
|
|
239
|
-
return code;
|
|
177
|
+
return `${this.indent()}continue;\n`;
|
|
240
178
|
}
|
|
241
|
-
export function
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
`${this.indent()}{ auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
|
|
253
|
-
}
|
|
179
|
+
export function visitLabeledStatement(node, context) {
|
|
180
|
+
const label = node.label.text;
|
|
181
|
+
const statement = node.statement;
|
|
182
|
+
const isLoop = ts.isForStatement(statement) ||
|
|
183
|
+
ts.isForInStatement(statement) ||
|
|
184
|
+
ts.isForOfStatement(statement) ||
|
|
185
|
+
ts.isWhileStatement(statement) ||
|
|
186
|
+
ts.isDoStatement(statement);
|
|
187
|
+
const statementContext = { ...context, currentLabel: label };
|
|
188
|
+
if (ts.isSwitchStatement(statement)) {
|
|
189
|
+
return this.visit(statement, statementContext);
|
|
254
190
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// No explicit declaration here.
|
|
259
|
-
code += `${this.indent()}{\n`;
|
|
191
|
+
const statementCode = this.visit(statement, statementContext);
|
|
192
|
+
if (isLoop) {
|
|
193
|
+
return statementCode;
|
|
260
194
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
: iterableExpr;
|
|
265
|
-
const iteratorFn = this.generateUniqueName("__iter_fn_", new Set());
|
|
266
|
-
const iteratorPtr = this.generateUniqueName("__iter_ptr_", new Set());
|
|
267
|
-
const nextRes = this.generateUniqueName("__res__", new Set());
|
|
268
|
-
code +=
|
|
269
|
-
`${this.indent()}auto ${iteratorFn} = ${derefIterable}.get_own_property(Symbol.get_own_property("iterator"));\n`;
|
|
270
|
-
code +=
|
|
271
|
-
`${this.indent()}if (!${iteratorFn}.is_generator()) { throw jspp::RuntimeError::make_error("${iterableExpr} is not iterable", "TypeError"); }\n`;
|
|
272
|
-
code +=
|
|
273
|
-
`${this.indent()}auto ${iteratorPtr} = ${iteratorFn}.as_function()->call({}).as_iterator();\n`;
|
|
274
|
-
code += `${this.indent()}auto ${nextRes} = ${iteratorPtr}->next();\n`;
|
|
275
|
-
code += `${this.indent()}while (!${nextRes}.done) {\n`;
|
|
195
|
+
// A non-loop statement can only be broken from.
|
|
196
|
+
// We wrap it in a labeled block.
|
|
197
|
+
let code = `${this.indent()}${label}: {\n`;
|
|
276
198
|
this.indentationLevel++;
|
|
277
|
-
code +=
|
|
278
|
-
`${this.indent()}*${varName} = ${nextRes}.value.value_or(jspp::AnyValue::make_undefined());\n`;
|
|
279
|
-
code += this.visit(forOf.statement, {
|
|
280
|
-
...context,
|
|
281
|
-
isFunctionBody: false,
|
|
282
|
-
});
|
|
283
|
-
code += `${this.indent()}${nextRes} = ${iteratorPtr}->next();\n`;
|
|
199
|
+
code += statementCode;
|
|
284
200
|
this.indentationLevel--;
|
|
285
|
-
code += `${this.indent()}}
|
|
286
|
-
this.
|
|
201
|
+
code += `${this.indent()}}\n`;
|
|
202
|
+
code += `${this.indent()}${label}_break:; // break target for ${label}\n`;
|
|
287
203
|
return code;
|
|
288
204
|
}
|
|
289
|
-
export function visitBreakStatement(node, context) {
|
|
290
|
-
return "break;";
|
|
291
|
-
}
|
|
292
|
-
export function visitContinueStatement(node, context) {
|
|
293
|
-
return "continue;";
|
|
294
|
-
}
|
|
295
205
|
export function visitIfStatement(node, context) {
|
|
296
206
|
const ifStmt = node;
|
|
297
207
|
const condition = this.visit(ifStmt.expression, context);
|
|
@@ -307,7 +217,7 @@ export function visitIfStatement(node, context) {
|
|
|
307
217
|
isFunctionBody: false,
|
|
308
218
|
});
|
|
309
219
|
}
|
|
310
|
-
return `${this.indent()}if ((${condition})
|
|
220
|
+
return `${this.indent()}if (is_truthy(${condition})) ${thenStmt}${elseStmt}`;
|
|
311
221
|
}
|
|
312
222
|
export function visitExpressionStatement(node, context) {
|
|
313
223
|
return (this.indent() +
|
|
@@ -317,7 +227,7 @@ export function visitExpressionStatement(node, context) {
|
|
|
317
227
|
export function visitThrowStatement(node, context) {
|
|
318
228
|
const throwStmt = node;
|
|
319
229
|
const expr = this.visit(throwStmt.expression, context);
|
|
320
|
-
return `${this.indent()}throw jspp::
|
|
230
|
+
return `${this.indent()}throw jspp::Exception(${expr});
|
|
321
231
|
`;
|
|
322
232
|
}
|
|
323
233
|
export function visitTryStatement(node, context) {
|
|
@@ -334,18 +244,23 @@ export function visitTryStatement(node, context) {
|
|
|
334
244
|
const hasReturnedFlagName = this.generateUniqueName("__try_has_returned_", declaredSymbols);
|
|
335
245
|
let code = `${this.indent()}{\n`;
|
|
336
246
|
this.indentationLevel++;
|
|
337
|
-
code += `${this.indent()}jspp::AnyValue ${resultVarName}
|
|
338
|
-
|
|
247
|
+
code += `${this.indent()}jspp::AnyValue ${resultVarName};
|
|
248
|
+
`;
|
|
249
|
+
code += `${this.indent()}bool ${hasReturnedFlagName} = false;
|
|
250
|
+
`;
|
|
339
251
|
const finallyBlockCode = this.visit(tryStmt.finallyBlock, {
|
|
340
252
|
...context,
|
|
341
253
|
isFunctionBody: false,
|
|
342
254
|
});
|
|
343
255
|
code +=
|
|
344
|
-
`${this.indent()}auto ${finallyLambdaName} = [=]() ${finallyBlockCode.trim()}
|
|
345
|
-
|
|
256
|
+
`${this.indent()}auto ${finallyLambdaName} = [=]() ${finallyBlockCode.trim()};
|
|
257
|
+
`;
|
|
258
|
+
code += `${this.indent()}try {
|
|
259
|
+
`;
|
|
346
260
|
this.indentationLevel++;
|
|
347
261
|
code +=
|
|
348
|
-
`${this.indent()}${resultVarName} = ([=, &${hasReturnedFlagName}]() -> jspp::AnyValue {
|
|
262
|
+
`${this.indent()}${resultVarName} = ([=, &${hasReturnedFlagName}]() -> jspp::AnyValue {
|
|
263
|
+
`;
|
|
349
264
|
this.indentationLevel++;
|
|
350
265
|
const innerContext = {
|
|
351
266
|
...context,
|
|
@@ -353,7 +268,8 @@ export function visitTryStatement(node, context) {
|
|
|
353
268
|
isInsideTryCatchLambda: true,
|
|
354
269
|
hasReturnedFlag: hasReturnedFlagName,
|
|
355
270
|
};
|
|
356
|
-
code += `${this.indent()}try {
|
|
271
|
+
code += `${this.indent()}try {
|
|
272
|
+
`;
|
|
357
273
|
this.indentationLevel++;
|
|
358
274
|
code += this.visit(tryStmt.tryBlock, innerContext);
|
|
359
275
|
this.indentationLevel--;
|
|
@@ -362,7 +278,8 @@ export function visitTryStatement(node, context) {
|
|
|
362
278
|
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText());
|
|
363
279
|
const catchContext = { ...innerContext, exceptionName };
|
|
364
280
|
code +=
|
|
365
|
-
`${this.indent()}catch (const std::exception& ${exceptionName}) {
|
|
281
|
+
`${this.indent()}catch (const std::exception& ${exceptionName}) {
|
|
282
|
+
`;
|
|
366
283
|
this.indentationLevel++;
|
|
367
284
|
code += this.visit(tryStmt.catchClause.block, catchContext);
|
|
368
285
|
this.indentationLevel--;
|
|
@@ -371,18 +288,20 @@ export function visitTryStatement(node, context) {
|
|
|
371
288
|
else {
|
|
372
289
|
code += `${this.indent()}catch (...) { throw; }\n`;
|
|
373
290
|
}
|
|
374
|
-
code += `${this.indent()}${this.
|
|
291
|
+
code += `${this.indent()}${this.getReturnCommand(context)} jspp::Constants::UNDEFINED;\n`;
|
|
375
292
|
this.indentationLevel--;
|
|
376
293
|
code += `${this.indent()}})();\n`;
|
|
377
294
|
this.indentationLevel--;
|
|
378
|
-
code += `${this.indent()}} catch (...) {
|
|
295
|
+
code += `${this.indent()}} catch (...) {
|
|
296
|
+
`;
|
|
379
297
|
this.indentationLevel++;
|
|
380
298
|
code += `${this.indent()}${finallyLambdaName}();\n`;
|
|
381
299
|
code += `${this.indent()}throw;\n`;
|
|
382
300
|
this.indentationLevel--;
|
|
383
301
|
code += `${this.indent()}}\n`;
|
|
384
302
|
code += `${this.indent()}${finallyLambdaName}();\n`;
|
|
385
|
-
code += `${this.indent()}if (${hasReturnedFlagName}) {
|
|
303
|
+
code += `${this.indent()}if (${hasReturnedFlagName}) {
|
|
304
|
+
`;
|
|
386
305
|
this.indentationLevel++;
|
|
387
306
|
code += `${this.indent()}return ${resultVarName};\n`;
|
|
388
307
|
this.indentationLevel--;
|
|
@@ -420,13 +339,13 @@ export function visitCatchClause(node, context) {
|
|
|
420
339
|
this.indentationLevel++;
|
|
421
340
|
code += `${this.indent()}{\n`;
|
|
422
341
|
this.indentationLevel++;
|
|
423
|
-
//
|
|
342
|
+
// The JS exception variable is always local to the catch block
|
|
424
343
|
code +=
|
|
425
|
-
`${this.indent()}
|
|
344
|
+
`${this.indent()}jspp::AnyValue ${varName} = jspp::Exception::exception_to_any_value(${exceptionName});\n`;
|
|
426
345
|
// Shadow the C++ exception variable *only if* the names don't clash.
|
|
427
346
|
if (varName !== exceptionName) {
|
|
428
347
|
code +=
|
|
429
|
-
`${this.indent()}auto ${exceptionName} = std::make_shared<jspp::AnyValue>(jspp::
|
|
348
|
+
`${this.indent()}auto ${exceptionName} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
|
|
430
349
|
}
|
|
431
350
|
code += this.visit(catchClause.block, context);
|
|
432
351
|
this.indentationLevel--;
|
|
@@ -446,77 +365,101 @@ export function visitCatchClause(node, context) {
|
|
|
446
365
|
export function visitYieldExpression(node, context) {
|
|
447
366
|
if (node.expression) {
|
|
448
367
|
const expr = node.expression;
|
|
449
|
-
|
|
368
|
+
let exprText = this.visit(expr, context);
|
|
450
369
|
if (ts.isIdentifier(expr)) {
|
|
451
370
|
const scope = this.getScopeForNode(expr);
|
|
452
371
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
|
|
453
372
|
if (!typeInfo) {
|
|
454
|
-
return `${this.indent()}jspp::
|
|
373
|
+
return `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})\n`; // THROWS, not returns
|
|
455
374
|
}
|
|
456
375
|
if (typeInfo &&
|
|
457
376
|
!typeInfo.isParameter &&
|
|
458
377
|
!typeInfo.isBuiltin) {
|
|
459
|
-
|
|
378
|
+
exprText = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
|
|
460
379
|
}
|
|
461
380
|
}
|
|
381
|
+
// Handle `yield*` expression
|
|
382
|
+
if (!!node.asteriskToken) {
|
|
383
|
+
let code = `${this.indent()}{\n`;
|
|
384
|
+
this.indentationLevel++;
|
|
385
|
+
const declaredSymbols = this.getDeclaredSymbols(expr);
|
|
386
|
+
const iterableRef = this.generateUniqueName("__iter_ref", declaredSymbols);
|
|
387
|
+
const iterator = this.generateUniqueName("__iter", declaredSymbols);
|
|
388
|
+
const nextFunc = this.generateUniqueName("__next_func", declaredSymbols);
|
|
389
|
+
const nextRes = this.generateUniqueName("__next_res", declaredSymbols);
|
|
390
|
+
const varName = this.getJsVarName(expr);
|
|
391
|
+
code += `${this.indent()}auto ${iterableRef} = ${exprText};\n`;
|
|
392
|
+
code +=
|
|
393
|
+
`${this.indent()}auto ${iterator} = jspp::Access::get_object_value_iterator(${iterableRef}, ${varName});\n`;
|
|
394
|
+
code +=
|
|
395
|
+
`${this.indent()}auto ${nextFunc} = ${iterator}.get_own_property("next");\n`;
|
|
396
|
+
code +=
|
|
397
|
+
`${this.indent()}auto ${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
|
|
398
|
+
code +=
|
|
399
|
+
`${this.indent()}while (!is_truthy(${nextRes}.get_own_property("done"))) {\n`;
|
|
400
|
+
this.indentationLevel++;
|
|
401
|
+
code +=
|
|
402
|
+
`${this.indent()}co_yield ${nextRes}.get_own_property("value");\n`;
|
|
403
|
+
code +=
|
|
404
|
+
`${this.indent()}${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
|
|
405
|
+
this.indentationLevel--;
|
|
406
|
+
code += `${this.indent()}}\n`;
|
|
407
|
+
return code;
|
|
408
|
+
}
|
|
462
409
|
return `${this.indent()}co_yield ${exprText}`;
|
|
463
410
|
}
|
|
464
|
-
return `${this.indent()}co_yield jspp::
|
|
411
|
+
return `${this.indent()}co_yield jspp::Constants::UNDEFINED`;
|
|
465
412
|
}
|
|
466
413
|
export function visitReturnStatement(node, context) {
|
|
467
414
|
if (context.isMainContext) {
|
|
468
|
-
return `${this.indent()}jspp::
|
|
415
|
+
return `${this.indent()}jspp::Exception::throw_invalid_return_statement();\n`;
|
|
469
416
|
}
|
|
470
417
|
const returnStmt = node;
|
|
471
|
-
const returnCmd = this.
|
|
418
|
+
const returnCmd = this.getReturnCommand(context);
|
|
472
419
|
if (context.isInsideTryCatchLambda && context.hasReturnedFlag) {
|
|
473
420
|
let returnCode = `${this.indent()}${context.hasReturnedFlag} = true;\n`;
|
|
474
421
|
if (returnStmt.expression) {
|
|
475
422
|
const expr = returnStmt.expression;
|
|
476
423
|
const exprText = this.visit(expr, context);
|
|
424
|
+
let finalExpr = exprText;
|
|
477
425
|
if (ts.isIdentifier(expr)) {
|
|
478
426
|
const scope = this.getScopeForNode(expr);
|
|
479
427
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
|
|
480
428
|
if (!typeInfo) {
|
|
481
429
|
returnCode +=
|
|
482
|
-
`${this.indent()}jspp::
|
|
430
|
+
`${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)});\n`; // THROWS, not returns
|
|
483
431
|
}
|
|
484
|
-
if (typeInfo &&
|
|
432
|
+
else if (typeInfo &&
|
|
485
433
|
!typeInfo.isParameter &&
|
|
486
434
|
!typeInfo.isBuiltin) {
|
|
487
|
-
|
|
488
|
-
`${this.indent()}${returnCmd} jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)});\n`;
|
|
435
|
+
finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
|
|
489
436
|
}
|
|
490
|
-
else {
|
|
491
|
-
returnCode += `${this.indent()}${returnCmd} ${exprText};\n`;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
else {
|
|
495
|
-
returnCode += `${this.indent()}${returnCmd} ${exprText};\n`;
|
|
496
437
|
}
|
|
438
|
+
returnCode += `${this.indent()}${returnCmd} ${finalExpr};\n`;
|
|
497
439
|
}
|
|
498
440
|
else {
|
|
499
441
|
returnCode +=
|
|
500
|
-
`${this.indent()}${returnCmd} jspp::
|
|
442
|
+
`${this.indent()}${returnCmd} jspp::Constants::UNDEFINED;\n`;
|
|
501
443
|
}
|
|
502
444
|
return returnCode;
|
|
503
445
|
}
|
|
504
446
|
if (returnStmt.expression) {
|
|
505
447
|
const expr = returnStmt.expression;
|
|
506
448
|
const exprText = this.visit(expr, context);
|
|
449
|
+
let finalExpr = exprText;
|
|
507
450
|
if (ts.isIdentifier(expr)) {
|
|
508
451
|
const scope = this.getScopeForNode(expr);
|
|
509
452
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
|
|
510
453
|
if (!typeInfo) {
|
|
511
|
-
return `${this.indent()}jspp::
|
|
454
|
+
return `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)});\n`; // THROWS, not returns
|
|
512
455
|
}
|
|
513
456
|
if (typeInfo &&
|
|
514
457
|
!typeInfo.isParameter &&
|
|
515
458
|
!typeInfo.isBuiltin) {
|
|
516
|
-
|
|
459
|
+
finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
|
|
517
460
|
}
|
|
518
461
|
}
|
|
519
|
-
return `${this.indent()}${returnCmd} ${
|
|
462
|
+
return `${this.indent()}${returnCmd} ${finalExpr};\n`;
|
|
520
463
|
}
|
|
521
|
-
return `${this.indent()}${returnCmd} jspp::
|
|
464
|
+
return `${this.indent()}${returnCmd} jspp::Constants::UNDEFINED;\n`;
|
|
522
465
|
}
|