@ugo-studio/jspp 0.1.3 → 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 +2 -2
- package/dist/analysis/scope.js +16 -4
- package/dist/analysis/typeAnalyzer.js +253 -20
- package/dist/ast/types.js +6 -0
- package/dist/cli.js +1 -2
- 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 +429 -117
- package/dist/core/codegen/function-handlers.js +91 -15
- package/dist/core/codegen/helpers.js +66 -2
- package/dist/core/codegen/index.js +17 -6
- package/dist/core/codegen/literal-handlers.js +3 -0
- package/dist/core/codegen/statement-handlers.js +133 -204
- package/dist/core/codegen/visitor.js +29 -3
- package/package.json +3 -3
- package/src/prelude/any_value.hpp +658 -634
- 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 -225
- package/src/prelude/exception.hpp +31 -0
- package/src/prelude/exception_helpers.hpp +49 -0
- package/src/prelude/index.hpp +18 -9
- package/src/prelude/library/console.hpp +13 -13
- package/src/prelude/library/error.hpp +111 -0
- package/src/prelude/library/global.hpp +15 -4
- package/src/prelude/library/performance.hpp +2 -2
- package/src/prelude/library/promise.hpp +121 -0
- package/src/prelude/library/symbol.hpp +3 -4
- package/src/prelude/library/timer.hpp +92 -0
- package/src/prelude/scheduler.hpp +145 -0
- package/src/prelude/types.hpp +10 -1
- 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} +29 -10
- package/src/prelude/{well_known_symbols.hpp → utils/well_known_symbols.hpp} +0 -1
- package/src/prelude/values/array.hpp +3 -2
- package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
- package/src/prelude/values/function.hpp +76 -51
- package/src/prelude/values/helpers/array.hpp +20 -11
- package/src/prelude/values/helpers/function.hpp +125 -77
- package/src/prelude/values/helpers/iterator.hpp +13 -7
- package/src/prelude/values/helpers/object.hpp +36 -6
- 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 +13 -5
- package/src/prelude/values/object.hpp +6 -2
- package/src/prelude/values/promise.hpp +73 -0
- package/src/prelude/values/prototypes/array.hpp +16 -16
- package/src/prelude/values/prototypes/function.hpp +4 -4
- package/src/prelude/values/prototypes/iterator.hpp +11 -10
- 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 +26 -26
- package/src/prelude/values/prototypes/symbol.hpp +5 -3
- 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
|
@@ -7,41 +7,39 @@ export function visitSourceFile(node, context) {
|
|
|
7
7
|
.filter(ts.isVariableStatement)
|
|
8
8
|
.flatMap((stmt) => stmt.declarationList.declarations);
|
|
9
9
|
const funcDecls = sourceFile.statements.filter(ts.isFunctionDeclaration);
|
|
10
|
-
const
|
|
10
|
+
const classDecls = sourceFile.statements.filter(ts.isClassDeclaration);
|
|
11
|
+
const hoistedSymbols = new Map();
|
|
11
12
|
// 1. Hoist function declarations
|
|
12
13
|
funcDecls.forEach((func) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
14
|
+
code += this.hoistDeclaration(func, hoistedSymbols);
|
|
15
|
+
});
|
|
16
|
+
// Hoist class declarations
|
|
17
|
+
classDecls.forEach((cls) => {
|
|
18
|
+
code += this.hoistDeclaration(cls, hoistedSymbols);
|
|
19
19
|
});
|
|
20
20
|
// Hoist variable declarations
|
|
21
21
|
varDecls.forEach((decl) => {
|
|
22
|
-
|
|
23
|
-
if (hoistedSymbols.has(name)) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
hoistedSymbols.add(name);
|
|
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`;
|
|
22
|
+
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
34
23
|
});
|
|
35
24
|
// 2. Assign all hoisted functions first
|
|
25
|
+
const contextForFunctions = {
|
|
26
|
+
...context,
|
|
27
|
+
currentScopeSymbols: new Map(context.currentScopeSymbols),
|
|
28
|
+
};
|
|
29
|
+
hoistedSymbols.forEach((v, k) => contextForFunctions.currentScopeSymbols.set(k, v));
|
|
36
30
|
funcDecls.forEach((stmt) => {
|
|
37
31
|
const funcName = stmt.name?.getText();
|
|
38
32
|
if (funcName) {
|
|
39
|
-
const lambda = this.generateLambda(stmt,
|
|
33
|
+
const lambda = this.generateLambda(stmt, contextForFunctions, {
|
|
34
|
+
isAssignment: true,
|
|
35
|
+
});
|
|
40
36
|
code += `${this.indent()}*${funcName} = ${lambda};\n`;
|
|
41
37
|
}
|
|
42
38
|
});
|
|
43
39
|
// 3. Process other statements
|
|
44
40
|
sourceFile.statements.forEach((stmt) => {
|
|
41
|
+
const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.currentScopeSymbols);
|
|
42
|
+
const currentScopeSymbols = hoistedSymbols; // hoistedSymbols becomes new local
|
|
45
43
|
if (ts.isFunctionDeclaration(stmt)) {
|
|
46
44
|
// Already handled
|
|
47
45
|
}
|
|
@@ -51,6 +49,8 @@ export function visitSourceFile(node, context) {
|
|
|
51
49
|
0;
|
|
52
50
|
const contextForVisit = {
|
|
53
51
|
...context,
|
|
52
|
+
topLevelScopeSymbols,
|
|
53
|
+
currentScopeSymbols,
|
|
54
54
|
isAssignmentOnly: !isLetOrConst,
|
|
55
55
|
};
|
|
56
56
|
const assignments = this.visit(stmt.declarationList, contextForVisit);
|
|
@@ -59,7 +59,14 @@ export function visitSourceFile(node, context) {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
else {
|
|
62
|
-
code += this.visit(stmt,
|
|
62
|
+
code += this.visit(stmt, {
|
|
63
|
+
...context,
|
|
64
|
+
isFunctionBody: false,
|
|
65
|
+
topLevelScopeSymbols,
|
|
66
|
+
currentScopeSymbols,
|
|
67
|
+
// currentScopeSymbols: undefined, // clear the currentScopeSymbols for nested visit
|
|
68
|
+
// topLevelScopeSymbols: undefined, // clear the topLevelScopeSymbols for nested visit
|
|
69
|
+
});
|
|
63
70
|
}
|
|
64
71
|
});
|
|
65
72
|
return code;
|
|
@@ -72,41 +79,39 @@ export function visitBlock(node, context) {
|
|
|
72
79
|
.filter(ts.isVariableStatement)
|
|
73
80
|
.flatMap((stmt) => stmt.declarationList.declarations);
|
|
74
81
|
const funcDecls = block.statements.filter(ts.isFunctionDeclaration);
|
|
75
|
-
const
|
|
82
|
+
const classDecls = block.statements.filter(ts.isClassDeclaration);
|
|
83
|
+
const hoistedSymbols = new Map();
|
|
76
84
|
// 1. Hoist all function declarations
|
|
77
85
|
funcDecls.forEach((func) => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
86
|
+
code += this.hoistDeclaration(func, hoistedSymbols);
|
|
87
|
+
});
|
|
88
|
+
// Hoist class declarations
|
|
89
|
+
classDecls.forEach((cls) => {
|
|
90
|
+
code += this.hoistDeclaration(cls, hoistedSymbols);
|
|
84
91
|
});
|
|
85
92
|
// Hoist variable declarations
|
|
86
93
|
varDecls.forEach((decl) => {
|
|
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`;
|
|
94
|
+
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
99
95
|
});
|
|
100
96
|
// 2. Assign all hoisted functions first
|
|
97
|
+
const contextForFunctions = {
|
|
98
|
+
...context,
|
|
99
|
+
currentScopeSymbols: new Map(context.currentScopeSymbols),
|
|
100
|
+
};
|
|
101
|
+
hoistedSymbols.forEach((v, k) => contextForFunctions.currentScopeSymbols.set(k, v));
|
|
101
102
|
funcDecls.forEach((stmt) => {
|
|
102
103
|
const funcName = stmt.name?.getText();
|
|
103
104
|
if (funcName) {
|
|
104
|
-
const lambda = this.generateLambda(stmt,
|
|
105
|
+
const lambda = this.generateLambda(stmt, contextForFunctions, {
|
|
106
|
+
isAssignment: true,
|
|
107
|
+
});
|
|
105
108
|
code += `${this.indent()}*${funcName} = ${lambda};\n`;
|
|
106
109
|
}
|
|
107
110
|
});
|
|
108
111
|
// 3. Process other statements
|
|
109
112
|
block.statements.forEach((stmt) => {
|
|
113
|
+
const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.currentScopeSymbols);
|
|
114
|
+
const currentScopeSymbols = hoistedSymbols; // hoistedSymbols becomes new local
|
|
110
115
|
if (ts.isFunctionDeclaration(stmt)) {
|
|
111
116
|
// Do nothing, already handled
|
|
112
117
|
}
|
|
@@ -116,6 +121,8 @@ export function visitBlock(node, context) {
|
|
|
116
121
|
0;
|
|
117
122
|
const contextForVisit = {
|
|
118
123
|
...context,
|
|
124
|
+
topLevelScopeSymbols,
|
|
125
|
+
currentScopeSymbols,
|
|
119
126
|
isAssignmentOnly: !isLetOrConst,
|
|
120
127
|
};
|
|
121
128
|
const assignments = this.visit(stmt.declarationList, contextForVisit);
|
|
@@ -124,13 +131,20 @@ export function visitBlock(node, context) {
|
|
|
124
131
|
}
|
|
125
132
|
}
|
|
126
133
|
else {
|
|
127
|
-
code += this.visit(stmt,
|
|
134
|
+
code += this.visit(stmt, {
|
|
135
|
+
...context,
|
|
136
|
+
isFunctionBody: false,
|
|
137
|
+
topLevelScopeSymbols,
|
|
138
|
+
currentScopeSymbols,
|
|
139
|
+
// currentScopeSymbols: undefined, // clear the currentScopeSymbols for nested visit
|
|
140
|
+
// topLevelScopeSymbols: undefined, // clear the topLevelScopeSymbols for nested visit
|
|
141
|
+
});
|
|
128
142
|
}
|
|
129
143
|
});
|
|
130
144
|
if (context.isFunctionBody) {
|
|
131
145
|
const lastStatement = block.statements[block.statements.length - 1];
|
|
132
146
|
if (!lastStatement || !ts.isReturnStatement(lastStatement)) {
|
|
133
|
-
code += `${this.indent()}${this.
|
|
147
|
+
code += `${this.indent()}${this.getReturnCommand(context)} jspp::AnyValue::make_undefined();\n`;
|
|
134
148
|
}
|
|
135
149
|
}
|
|
136
150
|
this.indentationLevel--;
|
|
@@ -142,156 +156,47 @@ export function visitVariableStatement(node, context) {
|
|
|
142
156
|
this.visit(node.declarationList, context) +
|
|
143
157
|
";\n");
|
|
144
158
|
}
|
|
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()`;
|
|
159
|
+
export function visitBreakStatement(node, context) {
|
|
160
|
+
if (node.label) {
|
|
161
|
+
return `${this.indent()}goto ${node.label.text}_break;\n`;
|
|
187
162
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
code += this.visit(forStmt.incrementor, context);
|
|
163
|
+
if (context.switchBreakLabel) {
|
|
164
|
+
return `${this.indent()}goto ${context.switchBreakLabel};\n`;
|
|
191
165
|
}
|
|
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;
|
|
166
|
+
return `${this.indent()}break;\n`;
|
|
199
167
|
}
|
|
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)})`;
|
|
168
|
+
export function visitContinueStatement(node, context) {
|
|
169
|
+
if (node.label) {
|
|
170
|
+
return `${this.indent()}goto ${node.label.text}_continue;\n`;
|
|
224
171
|
}
|
|
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;
|
|
172
|
+
return `${this.indent()}continue;\n`;
|
|
240
173
|
}
|
|
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
|
-
}
|
|
174
|
+
export function visitLabeledStatement(node, context) {
|
|
175
|
+
const label = node.label.text;
|
|
176
|
+
const statement = node.statement;
|
|
177
|
+
const isLoop = ts.isForStatement(statement) ||
|
|
178
|
+
ts.isForInStatement(statement) ||
|
|
179
|
+
ts.isForOfStatement(statement) ||
|
|
180
|
+
ts.isWhileStatement(statement) ||
|
|
181
|
+
ts.isDoStatement(statement);
|
|
182
|
+
const statementContext = { ...context, currentLabel: label };
|
|
183
|
+
if (ts.isSwitchStatement(statement)) {
|
|
184
|
+
return this.visit(statement, statementContext);
|
|
254
185
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// No explicit declaration here.
|
|
259
|
-
code += `${this.indent()}{\n`;
|
|
186
|
+
const statementCode = this.visit(statement, statementContext);
|
|
187
|
+
if (isLoop) {
|
|
188
|
+
return statementCode;
|
|
260
189
|
}
|
|
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`;
|
|
190
|
+
// A non-loop statement can only be broken from.
|
|
191
|
+
// We wrap it in a labeled block.
|
|
192
|
+
let code = `${this.indent()}${label}: {\n`;
|
|
276
193
|
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`;
|
|
194
|
+
code += statementCode;
|
|
284
195
|
this.indentationLevel--;
|
|
285
|
-
code += `${this.indent()}}
|
|
286
|
-
this.
|
|
196
|
+
code += `${this.indent()}}\n`;
|
|
197
|
+
code += `${this.indent()}${label}_break:; // break target for ${label}\n`;
|
|
287
198
|
return code;
|
|
288
199
|
}
|
|
289
|
-
export function visitBreakStatement(node, context) {
|
|
290
|
-
return "break;";
|
|
291
|
-
}
|
|
292
|
-
export function visitContinueStatement(node, context) {
|
|
293
|
-
return "continue;";
|
|
294
|
-
}
|
|
295
200
|
export function visitIfStatement(node, context) {
|
|
296
201
|
const ifStmt = node;
|
|
297
202
|
const condition = this.visit(ifStmt.expression, context);
|
|
@@ -317,7 +222,7 @@ export function visitExpressionStatement(node, context) {
|
|
|
317
222
|
export function visitThrowStatement(node, context) {
|
|
318
223
|
const throwStmt = node;
|
|
319
224
|
const expr = this.visit(throwStmt.expression, context);
|
|
320
|
-
return `${this.indent()}throw jspp::
|
|
225
|
+
return `${this.indent()}throw jspp::Exception(${expr});
|
|
321
226
|
`;
|
|
322
227
|
}
|
|
323
228
|
export function visitTryStatement(node, context) {
|
|
@@ -371,7 +276,7 @@ export function visitTryStatement(node, context) {
|
|
|
371
276
|
else {
|
|
372
277
|
code += `${this.indent()}catch (...) { throw; }\n`;
|
|
373
278
|
}
|
|
374
|
-
code += `${this.indent()}${this.
|
|
279
|
+
code += `${this.indent()}${this.getReturnCommand(context)} jspp::AnyValue::make_undefined();\n`;
|
|
375
280
|
this.indentationLevel--;
|
|
376
281
|
code += `${this.indent()}})();\n`;
|
|
377
282
|
this.indentationLevel--;
|
|
@@ -420,9 +325,9 @@ export function visitCatchClause(node, context) {
|
|
|
420
325
|
this.indentationLevel++;
|
|
421
326
|
code += `${this.indent()}{\n`;
|
|
422
327
|
this.indentationLevel++;
|
|
423
|
-
//
|
|
328
|
+
// The JS exception variable is always local to the catch block
|
|
424
329
|
code +=
|
|
425
|
-
`${this.indent()}
|
|
330
|
+
`${this.indent()}jspp::AnyValue ${varName} = jspp::Exception::exception_to_any_value(${exceptionName});\n`;
|
|
426
331
|
// Shadow the C++ exception variable *only if* the names don't clash.
|
|
427
332
|
if (varName !== exceptionName) {
|
|
428
333
|
code +=
|
|
@@ -446,54 +351,77 @@ export function visitCatchClause(node, context) {
|
|
|
446
351
|
export function visitYieldExpression(node, context) {
|
|
447
352
|
if (node.expression) {
|
|
448
353
|
const expr = node.expression;
|
|
449
|
-
|
|
354
|
+
let exprText = this.visit(expr, context);
|
|
450
355
|
if (ts.isIdentifier(expr)) {
|
|
451
356
|
const scope = this.getScopeForNode(expr);
|
|
452
357
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
|
|
453
358
|
if (!typeInfo) {
|
|
454
|
-
return `${this.indent()}jspp::
|
|
359
|
+
return `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})\n`; // THROWS, not returns
|
|
455
360
|
}
|
|
456
361
|
if (typeInfo &&
|
|
457
362
|
!typeInfo.isParameter &&
|
|
458
363
|
!typeInfo.isBuiltin) {
|
|
459
|
-
|
|
364
|
+
exprText = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
|
|
460
365
|
}
|
|
461
366
|
}
|
|
367
|
+
// Handle `yield*` expression
|
|
368
|
+
if (!!node.asteriskToken) {
|
|
369
|
+
let code = `${this.indent()}{\n`;
|
|
370
|
+
this.indentationLevel++;
|
|
371
|
+
const declaredSymbols = this.getDeclaredSymbols(expr);
|
|
372
|
+
const iterableRef = this.generateUniqueName("__iter_ref", declaredSymbols);
|
|
373
|
+
const iterator = this.generateUniqueName("__iter", declaredSymbols);
|
|
374
|
+
const nextFunc = this.generateUniqueName("__next_func", declaredSymbols);
|
|
375
|
+
const nextRes = this.generateUniqueName("__next_res", declaredSymbols);
|
|
376
|
+
const varName = this.getJsVarName(expr);
|
|
377
|
+
code += `${this.indent()}auto ${iterableRef} = ${exprText};\n`;
|
|
378
|
+
code +=
|
|
379
|
+
`${this.indent()}auto ${iterator} = jspp::Access::get_object_value_iterator(${iterableRef}, ${varName});\n`;
|
|
380
|
+
code +=
|
|
381
|
+
`${this.indent()}auto ${nextFunc} = ${iterator}.get_own_property("next").as_function();\n`;
|
|
382
|
+
code +=
|
|
383
|
+
`${this.indent()}auto ${nextRes} = ${nextFunc}->call(${iterator}, {});\n`;
|
|
384
|
+
code +=
|
|
385
|
+
`${this.indent()}while (!${nextRes}.get_own_property("done").is_truthy()) {\n`;
|
|
386
|
+
this.indentationLevel++;
|
|
387
|
+
code +=
|
|
388
|
+
`${this.indent()}co_yield ${nextRes}.get_own_property("value");\n`;
|
|
389
|
+
code +=
|
|
390
|
+
`${this.indent()}${nextRes} = ${nextFunc}->call(${iterator}, {});\n`;
|
|
391
|
+
this.indentationLevel--;
|
|
392
|
+
code += `${this.indent()}}}\n`;
|
|
393
|
+
return code;
|
|
394
|
+
}
|
|
462
395
|
return `${this.indent()}co_yield ${exprText}`;
|
|
463
396
|
}
|
|
464
397
|
return `${this.indent()}co_yield jspp::AnyValue::make_undefined()`;
|
|
465
398
|
}
|
|
466
399
|
export function visitReturnStatement(node, context) {
|
|
467
400
|
if (context.isMainContext) {
|
|
468
|
-
return `${this.indent()}jspp::
|
|
401
|
+
return `${this.indent()}jspp::Exception::throw_invalid_return_statement();\n`;
|
|
469
402
|
}
|
|
470
403
|
const returnStmt = node;
|
|
471
|
-
const returnCmd = this.
|
|
404
|
+
const returnCmd = this.getReturnCommand(context);
|
|
472
405
|
if (context.isInsideTryCatchLambda && context.hasReturnedFlag) {
|
|
473
406
|
let returnCode = `${this.indent()}${context.hasReturnedFlag} = true;\n`;
|
|
474
407
|
if (returnStmt.expression) {
|
|
475
408
|
const expr = returnStmt.expression;
|
|
476
409
|
const exprText = this.visit(expr, context);
|
|
410
|
+
let finalExpr = exprText;
|
|
477
411
|
if (ts.isIdentifier(expr)) {
|
|
478
412
|
const scope = this.getScopeForNode(expr);
|
|
479
413
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
|
|
480
414
|
if (!typeInfo) {
|
|
481
415
|
returnCode +=
|
|
482
|
-
`${this.indent()}jspp::
|
|
416
|
+
`${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)});\n`; // THROWS, not returns
|
|
483
417
|
}
|
|
484
|
-
if (typeInfo &&
|
|
418
|
+
else if (typeInfo &&
|
|
485
419
|
!typeInfo.isParameter &&
|
|
486
420
|
!typeInfo.isBuiltin) {
|
|
487
|
-
|
|
488
|
-
`${this.indent()}${returnCmd} jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)});\n`;
|
|
421
|
+
finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
|
|
489
422
|
}
|
|
490
|
-
else {
|
|
491
|
-
returnCode += `${this.indent()}${returnCmd} ${exprText};\n`;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
else {
|
|
495
|
-
returnCode += `${this.indent()}${returnCmd} ${exprText};\n`;
|
|
496
423
|
}
|
|
424
|
+
returnCode += `${this.indent()}${returnCmd} ${finalExpr};\n`;
|
|
497
425
|
}
|
|
498
426
|
else {
|
|
499
427
|
returnCode +=
|
|
@@ -504,19 +432,20 @@ export function visitReturnStatement(node, context) {
|
|
|
504
432
|
if (returnStmt.expression) {
|
|
505
433
|
const expr = returnStmt.expression;
|
|
506
434
|
const exprText = this.visit(expr, context);
|
|
435
|
+
let finalExpr = exprText;
|
|
507
436
|
if (ts.isIdentifier(expr)) {
|
|
508
437
|
const scope = this.getScopeForNode(expr);
|
|
509
438
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
|
|
510
439
|
if (!typeInfo) {
|
|
511
|
-
return `${this.indent()}jspp::
|
|
440
|
+
return `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)});\n`; // THROWS, not returns
|
|
512
441
|
}
|
|
513
442
|
if (typeInfo &&
|
|
514
443
|
!typeInfo.isParameter &&
|
|
515
444
|
!typeInfo.isBuiltin) {
|
|
516
|
-
|
|
445
|
+
finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
|
|
517
446
|
}
|
|
518
447
|
}
|
|
519
|
-
return `${this.indent()}${returnCmd} ${
|
|
448
|
+
return `${this.indent()}${returnCmd} ${finalExpr};\n`;
|
|
520
449
|
}
|
|
521
450
|
return `${this.indent()}${returnCmd} jspp::AnyValue::make_undefined();\n`;
|
|
522
451
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import { CodeGenerator } from "./";
|
|
3
|
+
import { visitClassDeclaration } from "./class-handlers";
|
|
4
|
+
import { visitCaseClause, visitDefaultClause, visitDoStatement, visitForInStatement, visitForOfStatement, visitForStatement, visitSwitchStatement, visitWhileStatement, } from "./control-flow-handlers";
|
|
3
5
|
import { visitVariableDeclaration, visitVariableDeclarationList, } from "./declaration-handlers";
|
|
4
|
-
import { visitArrayLiteralExpression, visitBinaryExpression, visitCallExpression, visitElementAccessExpression, visitObjectLiteralExpression, visitParenthesizedExpression, visitPostfixUnaryExpression, visitPrefixUnaryExpression, visitPropertyAccessExpression, visitTemplateExpression, visitVoidExpression, } from "./expression-handlers";
|
|
6
|
+
import { visitArrayLiteralExpression, visitAwaitExpression, visitBinaryExpression, visitCallExpression, visitConditionalExpression, visitElementAccessExpression, visitNewExpression, visitObjectLiteralExpression, visitParenthesizedExpression, visitPostfixUnaryExpression, visitPrefixUnaryExpression, visitPropertyAccessExpression, visitTemplateExpression, visitTypeOfExpression, visitVoidExpression, } from "./expression-handlers";
|
|
5
7
|
import { visitArrowFunction, visitFunctionDeclaration, visitFunctionExpression, } from "./function-handlers";
|
|
6
|
-
import { visitFalseKeyword, visitIdentifier, visitNoSubstitutionTemplateLiteral, visitNullKeyword, visitNumericLiteral, visitStringLiteral, visitTrueKeyword, visitUndefinedKeyword, } from "./literal-handlers";
|
|
7
|
-
import { visitBlock, visitBreakStatement, visitCatchClause, visitContinueStatement, visitExpressionStatement,
|
|
8
|
+
import { visitFalseKeyword, visitIdentifier, visitNoSubstitutionTemplateLiteral, visitNullKeyword, visitNumericLiteral, visitStringLiteral, visitThisKeyword, visitTrueKeyword, visitUndefinedKeyword, } from "./literal-handlers";
|
|
9
|
+
import { visitBlock, visitBreakStatement, visitCatchClause, visitContinueStatement, visitExpressionStatement, visitIfStatement, visitLabeledStatement, visitReturnStatement, visitSourceFile, visitThrowStatement, visitTryStatement, visitVariableStatement, visitYieldExpression, } from "./statement-handlers";
|
|
8
10
|
export function visit(node, context) {
|
|
9
11
|
if (ts.isFunctionDeclaration(node)) {
|
|
10
12
|
return visitFunctionDeclaration.call(this, node, context);
|
|
@@ -14,6 +16,8 @@ export function visit(node, context) {
|
|
|
14
16
|
return visitArrowFunction.call(this, node, context);
|
|
15
17
|
case ts.SyntaxKind.FunctionExpression:
|
|
16
18
|
return visitFunctionExpression.call(this, node, context);
|
|
19
|
+
case ts.SyntaxKind.ClassDeclaration:
|
|
20
|
+
return visitClassDeclaration.call(this, node, context);
|
|
17
21
|
case ts.SyntaxKind.SourceFile:
|
|
18
22
|
return visitSourceFile.call(this, node, context);
|
|
19
23
|
case ts.SyntaxKind.Block:
|
|
@@ -34,10 +38,22 @@ export function visit(node, context) {
|
|
|
34
38
|
return visitForInStatement.call(this, node, context);
|
|
35
39
|
case ts.SyntaxKind.ForOfStatement:
|
|
36
40
|
return visitForOfStatement.call(this, node, context);
|
|
41
|
+
case ts.SyntaxKind.WhileStatement:
|
|
42
|
+
return visitWhileStatement.call(this, node, context);
|
|
43
|
+
case ts.SyntaxKind.DoStatement:
|
|
44
|
+
return visitDoStatement.call(this, node, context);
|
|
45
|
+
case ts.SyntaxKind.SwitchStatement:
|
|
46
|
+
return visitSwitchStatement.call(this, node, context);
|
|
47
|
+
case ts.SyntaxKind.CaseClause:
|
|
48
|
+
return visitCaseClause.call(this, node, context);
|
|
49
|
+
case ts.SyntaxKind.DefaultClause:
|
|
50
|
+
return visitDefaultClause.call(this, node, context);
|
|
37
51
|
case ts.SyntaxKind.BreakStatement:
|
|
38
52
|
return visitBreakStatement.call(this, node, context);
|
|
39
53
|
case ts.SyntaxKind.ContinueStatement:
|
|
40
54
|
return visitContinueStatement.call(this, node, context);
|
|
55
|
+
case ts.SyntaxKind.LabeledStatement:
|
|
56
|
+
return visitLabeledStatement.call(this, node, context);
|
|
41
57
|
case ts.SyntaxKind.IfStatement:
|
|
42
58
|
return visitIfStatement.call(this, node, context);
|
|
43
59
|
case ts.SyntaxKind.PrefixUnaryExpression:
|
|
@@ -54,6 +70,8 @@ export function visit(node, context) {
|
|
|
54
70
|
return visitExpressionStatement.call(this, node, context);
|
|
55
71
|
case ts.SyntaxKind.BinaryExpression:
|
|
56
72
|
return visitBinaryExpression.call(this, node, context);
|
|
73
|
+
case ts.SyntaxKind.ConditionalExpression:
|
|
74
|
+
return visitConditionalExpression.call(this, node, context);
|
|
57
75
|
case ts.SyntaxKind.ThrowStatement:
|
|
58
76
|
return visitThrowStatement.call(this, node, context);
|
|
59
77
|
case ts.SyntaxKind.TryStatement:
|
|
@@ -76,6 +94,12 @@ export function visit(node, context) {
|
|
|
76
94
|
return visitNoSubstitutionTemplateLiteral.call(this, node);
|
|
77
95
|
case ts.SyntaxKind.TemplateExpression:
|
|
78
96
|
return visitTemplateExpression.call(this, node, context);
|
|
97
|
+
case ts.SyntaxKind.AwaitExpression:
|
|
98
|
+
return visitAwaitExpression.call(this, node, context);
|
|
99
|
+
case ts.SyntaxKind.NewExpression:
|
|
100
|
+
return visitNewExpression.call(this, node, context);
|
|
101
|
+
case ts.SyntaxKind.TypeOfExpression:
|
|
102
|
+
return visitTypeOfExpression.call(this, node, context);
|
|
79
103
|
case ts.SyntaxKind.TrueKeyword:
|
|
80
104
|
return visitTrueKeyword.call(this);
|
|
81
105
|
case ts.SyntaxKind.FalseKeyword:
|
|
@@ -86,6 +110,8 @@ export function visit(node, context) {
|
|
|
86
110
|
return visitUndefinedKeyword.call(this);
|
|
87
111
|
case ts.SyntaxKind.NullKeyword:
|
|
88
112
|
return visitNullKeyword.call(this);
|
|
113
|
+
case ts.SyntaxKind.ThisKeyword:
|
|
114
|
+
return visitThisKeyword.call(this);
|
|
89
115
|
default:
|
|
90
116
|
return `/* Unhandled node: ${ts.SyntaxKind[node.kind]} */`;
|
|
91
117
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ugo-studio/jspp",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "A modern
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "A modern transpiler that converts JavaScript code into high-performance, standard C++23.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "src/index.ts",
|
|
7
7
|
"type": "module",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dev": "bun run src/cli.ts",
|
|
17
17
|
"typecheck": "tsc --noEmit",
|
|
18
18
|
"precompile": "bun run scripts/precompile-headers.ts",
|
|
19
|
-
"test": "bun
|
|
19
|
+
"test": "bun test",
|
|
20
20
|
"build": "tsc",
|
|
21
21
|
"prepack": "bun run build",
|
|
22
22
|
"publish:npm": "npm publish --access=public"
|