@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
|
@@ -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,14 +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 +=
|
|
134
|
-
`${this.indent()}return jspp::AnyValue::make_undefined();\n`;
|
|
147
|
+
code += `${this.indent()}${this.getReturnCommand(context)} jspp::AnyValue::make_undefined();\n`;
|
|
135
148
|
}
|
|
136
149
|
}
|
|
137
150
|
this.indentationLevel--;
|
|
@@ -143,137 +156,45 @@ export function visitVariableStatement(node, context) {
|
|
|
143
156
|
this.visit(node.declarationList, context) +
|
|
144
157
|
";\n");
|
|
145
158
|
}
|
|
146
|
-
export function
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
this.indentationLevel++; // Enter a new scope for the for loop
|
|
150
|
-
// Handle initializer
|
|
151
|
-
let initializerCode = "";
|
|
152
|
-
if (forStmt.initializer) {
|
|
153
|
-
if (ts.isVariableDeclarationList(forStmt.initializer)) {
|
|
154
|
-
const varDeclList = forStmt.initializer;
|
|
155
|
-
const isLetOrConst = (varDeclList.flags &
|
|
156
|
-
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
|
|
157
|
-
0;
|
|
158
|
-
if (isLetOrConst) {
|
|
159
|
-
// For `let` or `const` in for loop, they are block-scoped to the loop.
|
|
160
|
-
// Declare the variable within the loop's scope.
|
|
161
|
-
// The C++ for loop initializer can contain a declaration.
|
|
162
|
-
const decl = varDeclList.declarations[0]; // Assuming single declaration for simplicity
|
|
163
|
-
if (decl) {
|
|
164
|
-
const name = decl.name.getText();
|
|
165
|
-
const initValue = decl.initializer
|
|
166
|
-
? this.visit(decl.initializer, context)
|
|
167
|
-
: "jspp::AnyValue::make_undefined()";
|
|
168
|
-
initializerCode =
|
|
169
|
-
`auto ${name} = std::make_unique<jspp::AnyValue>(${initValue})`;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
// For 'var', it's already hoisted, so this is an assignment.
|
|
174
|
-
initializerCode = this.visit(forStmt.initializer, {
|
|
175
|
-
...context,
|
|
176
|
-
isAssignmentOnly: true,
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
// If it's an expression (e.g., `i = 0`)
|
|
182
|
-
initializerCode = this.visit(forStmt.initializer, context);
|
|
183
|
-
}
|
|
159
|
+
export function visitBreakStatement(node, context) {
|
|
160
|
+
if (node.label) {
|
|
161
|
+
return `${this.indent()}goto ${node.label.text}_break;\n`;
|
|
184
162
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
code += `(${this.visit(forStmt.condition, context)}).is_truthy()`;
|
|
163
|
+
if (context.switchBreakLabel) {
|
|
164
|
+
return `${this.indent()}goto ${context.switchBreakLabel};\n`;
|
|
188
165
|
}
|
|
189
|
-
|
|
190
|
-
if (forStmt.incrementor) {
|
|
191
|
-
code += this.visit(forStmt.incrementor, context);
|
|
192
|
-
}
|
|
193
|
-
code += ") ";
|
|
194
|
-
code += this.visit(forStmt.statement, {
|
|
195
|
-
...context,
|
|
196
|
-
isFunctionBody: false,
|
|
197
|
-
});
|
|
198
|
-
this.indentationLevel--; // Exit the scope for the for loop
|
|
199
|
-
return code;
|
|
166
|
+
return `${this.indent()}break;\n`;
|
|
200
167
|
}
|
|
201
|
-
export function
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
this.indentationLevel++; // Enter a new scope for the for-in loop
|
|
205
|
-
let varName = "";
|
|
206
|
-
if (ts.isVariableDeclarationList(forIn.initializer)) {
|
|
207
|
-
const decl = forIn.initializer.declarations[0];
|
|
208
|
-
if (decl) {
|
|
209
|
-
varName = decl.name.getText();
|
|
210
|
-
// Declare the shared_ptr before the loop
|
|
211
|
-
code +=
|
|
212
|
-
`${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
else if (ts.isIdentifier(forIn.initializer)) {
|
|
216
|
-
varName = forIn.initializer.getText();
|
|
217
|
-
// Assume it's already declared in an outer scope, just assign to it.
|
|
218
|
-
// No explicit declaration here.
|
|
219
|
-
}
|
|
220
|
-
const expr = forIn.expression;
|
|
221
|
-
const exprText = this.visit(expr, context);
|
|
222
|
-
let derefExpr = exprText;
|
|
223
|
-
if (ts.isIdentifier(expr)) {
|
|
224
|
-
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`;
|
|
225
171
|
}
|
|
226
|
-
|
|
227
|
-
code +=
|
|
228
|
-
`${this.indent()}std::vector<std::string> ${keysVar} = jspp::Access::get_object_keys(${derefExpr});\n`;
|
|
229
|
-
code += `${this.indent()}for (const auto& ${varName}_str : ${keysVar}) {\n`;
|
|
230
|
-
this.indentationLevel++;
|
|
231
|
-
code +=
|
|
232
|
-
`${this.indent()}*${varName} = jspp::AnyValue::make_string(${varName}_str);\n`;
|
|
233
|
-
code += this.visit(forIn.statement, {
|
|
234
|
-
...context,
|
|
235
|
-
isFunctionBody: false,
|
|
236
|
-
});
|
|
237
|
-
this.indentationLevel--;
|
|
238
|
-
code += `${this.indent()}}}\n`;
|
|
239
|
-
this.indentationLevel--; // Exit the scope for the for-in loop
|
|
240
|
-
return code;
|
|
172
|
+
return `${this.indent()}continue;\n`;
|
|
241
173
|
}
|
|
242
|
-
export function
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
`${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
|
|
254
|
-
}
|
|
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);
|
|
255
185
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
// No explicit declaration here.
|
|
186
|
+
const statementCode = this.visit(statement, statementContext);
|
|
187
|
+
if (isLoop) {
|
|
188
|
+
return statementCode;
|
|
260
189
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
code +=
|
|
265
|
-
`${this.indent()}{ auto ${arrayPtr} = std::any_cast<std::shared_ptr<jspp::JsArray>>(${derefIterable});\n`;
|
|
266
|
-
code +=
|
|
267
|
-
`${this.indent()}for (const auto& ${varName}_val : ${arrayPtr}->items) {\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`;
|
|
268
193
|
this.indentationLevel++;
|
|
269
|
-
code +=
|
|
270
|
-
code += this.visit(forOf.statement, {
|
|
271
|
-
...context,
|
|
272
|
-
isFunctionBody: false,
|
|
273
|
-
});
|
|
194
|
+
code += statementCode;
|
|
274
195
|
this.indentationLevel--;
|
|
275
|
-
code += `${this.indent()}}
|
|
276
|
-
this.
|
|
196
|
+
code += `${this.indent()}}\n`;
|
|
197
|
+
code += `${this.indent()}${label}_break:; // break target for ${label}\n`;
|
|
277
198
|
return code;
|
|
278
199
|
}
|
|
279
200
|
export function visitIfStatement(node, context) {
|
|
@@ -301,7 +222,7 @@ export function visitExpressionStatement(node, context) {
|
|
|
301
222
|
export function visitThrowStatement(node, context) {
|
|
302
223
|
const throwStmt = node;
|
|
303
224
|
const expr = this.visit(throwStmt.expression, context);
|
|
304
|
-
return `${this.indent()}throw jspp::
|
|
225
|
+
return `${this.indent()}throw jspp::Exception(${expr});
|
|
305
226
|
`;
|
|
306
227
|
}
|
|
307
228
|
export function visitTryStatement(node, context) {
|
|
@@ -355,7 +276,7 @@ export function visitTryStatement(node, context) {
|
|
|
355
276
|
else {
|
|
356
277
|
code += `${this.indent()}catch (...) { throw; }\n`;
|
|
357
278
|
}
|
|
358
|
-
code += `${this.indent()}
|
|
279
|
+
code += `${this.indent()}${this.getReturnCommand(context)} jspp::AnyValue::make_undefined();\n`;
|
|
359
280
|
this.indentationLevel--;
|
|
360
281
|
code += `${this.indent()}})();\n`;
|
|
361
282
|
this.indentationLevel--;
|
|
@@ -404,9 +325,9 @@ export function visitCatchClause(node, context) {
|
|
|
404
325
|
this.indentationLevel++;
|
|
405
326
|
code += `${this.indent()}{\n`;
|
|
406
327
|
this.indentationLevel++;
|
|
407
|
-
//
|
|
328
|
+
// The JS exception variable is always local to the catch block
|
|
408
329
|
code +=
|
|
409
|
-
`${this.indent()}
|
|
330
|
+
`${this.indent()}jspp::AnyValue ${varName} = jspp::Exception::exception_to_any_value(${exceptionName});\n`;
|
|
410
331
|
// Shadow the C++ exception variable *only if* the names don't clash.
|
|
411
332
|
if (varName !== exceptionName) {
|
|
412
333
|
code +=
|
|
@@ -427,59 +348,104 @@ export function visitCatchClause(node, context) {
|
|
|
427
348
|
return code;
|
|
428
349
|
}
|
|
429
350
|
}
|
|
351
|
+
export function visitYieldExpression(node, context) {
|
|
352
|
+
if (node.expression) {
|
|
353
|
+
const expr = node.expression;
|
|
354
|
+
let exprText = this.visit(expr, context);
|
|
355
|
+
if (ts.isIdentifier(expr)) {
|
|
356
|
+
const scope = this.getScopeForNode(expr);
|
|
357
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
|
|
358
|
+
if (!typeInfo) {
|
|
359
|
+
return `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)})\n`; // THROWS, not returns
|
|
360
|
+
}
|
|
361
|
+
if (typeInfo &&
|
|
362
|
+
!typeInfo.isParameter &&
|
|
363
|
+
!typeInfo.isBuiltin) {
|
|
364
|
+
exprText = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
|
|
365
|
+
}
|
|
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
|
+
}
|
|
395
|
+
return `${this.indent()}co_yield ${exprText}`;
|
|
396
|
+
}
|
|
397
|
+
return `${this.indent()}co_yield jspp::AnyValue::make_undefined()`;
|
|
398
|
+
}
|
|
430
399
|
export function visitReturnStatement(node, context) {
|
|
431
400
|
if (context.isMainContext) {
|
|
432
|
-
return `${this.indent()}jspp::
|
|
401
|
+
return `${this.indent()}jspp::Exception::throw_invalid_return_statement();\n`;
|
|
433
402
|
}
|
|
434
403
|
const returnStmt = node;
|
|
404
|
+
const returnCmd = this.getReturnCommand(context);
|
|
435
405
|
if (context.isInsideTryCatchLambda && context.hasReturnedFlag) {
|
|
436
406
|
let returnCode = `${this.indent()}${context.hasReturnedFlag} = true;\n`;
|
|
437
407
|
if (returnStmt.expression) {
|
|
438
408
|
const expr = returnStmt.expression;
|
|
439
409
|
const exprText = this.visit(expr, context);
|
|
410
|
+
let finalExpr = exprText;
|
|
440
411
|
if (ts.isIdentifier(expr)) {
|
|
441
412
|
const scope = this.getScopeForNode(expr);
|
|
442
413
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
|
|
443
414
|
if (!typeInfo) {
|
|
444
415
|
returnCode +=
|
|
445
|
-
`${this.indent()}jspp::
|
|
416
|
+
`${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)});\n`; // THROWS, not returns
|
|
446
417
|
}
|
|
447
|
-
if (typeInfo &&
|
|
418
|
+
else if (typeInfo &&
|
|
448
419
|
!typeInfo.isParameter &&
|
|
449
420
|
!typeInfo.isBuiltin) {
|
|
450
|
-
|
|
451
|
-
`${this.indent()}return jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)});\n`;
|
|
421
|
+
finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
|
|
452
422
|
}
|
|
453
|
-
else {
|
|
454
|
-
returnCode += `${this.indent()}return ${exprText};\n`;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
else {
|
|
458
|
-
returnCode += `${this.indent()}return ${exprText};\n`;
|
|
459
423
|
}
|
|
424
|
+
returnCode += `${this.indent()}${returnCmd} ${finalExpr};\n`;
|
|
460
425
|
}
|
|
461
426
|
else {
|
|
462
427
|
returnCode +=
|
|
463
|
-
`${this.indent()}
|
|
428
|
+
`${this.indent()}${returnCmd} jspp::AnyValue::make_undefined();\n`;
|
|
464
429
|
}
|
|
465
430
|
return returnCode;
|
|
466
431
|
}
|
|
467
432
|
if (returnStmt.expression) {
|
|
468
433
|
const expr = returnStmt.expression;
|
|
469
434
|
const exprText = this.visit(expr, context);
|
|
435
|
+
let finalExpr = exprText;
|
|
470
436
|
if (ts.isIdentifier(expr)) {
|
|
471
437
|
const scope = this.getScopeForNode(expr);
|
|
472
438
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
|
|
473
439
|
if (!typeInfo) {
|
|
474
|
-
return `${this.indent()}jspp::
|
|
440
|
+
return `${this.indent()}jspp::Exception::throw_unresolved_reference(${this.getJsVarName(expr)});\n`; // THROWS, not returns
|
|
475
441
|
}
|
|
476
442
|
if (typeInfo &&
|
|
477
443
|
!typeInfo.isParameter &&
|
|
478
444
|
!typeInfo.isBuiltin) {
|
|
479
|
-
|
|
445
|
+
finalExpr = this.getDerefCode(exprText, this.getJsVarName(expr), typeInfo);
|
|
480
446
|
}
|
|
481
447
|
}
|
|
482
|
-
return `${this.indent()}
|
|
448
|
+
return `${this.indent()}${returnCmd} ${finalExpr};\n`;
|
|
483
449
|
}
|
|
484
|
-
return `${this.indent()}
|
|
450
|
+
return `${this.indent()}${returnCmd} jspp::AnyValue::make_undefined();\n`;
|
|
485
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,
|
|
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,6 +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);
|
|
51
|
+
case ts.SyntaxKind.BreakStatement:
|
|
52
|
+
return visitBreakStatement.call(this, node, context);
|
|
53
|
+
case ts.SyntaxKind.ContinueStatement:
|
|
54
|
+
return visitContinueStatement.call(this, node, context);
|
|
55
|
+
case ts.SyntaxKind.LabeledStatement:
|
|
56
|
+
return visitLabeledStatement.call(this, node, context);
|
|
37
57
|
case ts.SyntaxKind.IfStatement:
|
|
38
58
|
return visitIfStatement.call(this, node, context);
|
|
39
59
|
case ts.SyntaxKind.PrefixUnaryExpression:
|
|
@@ -50,6 +70,8 @@ export function visit(node, context) {
|
|
|
50
70
|
return visitExpressionStatement.call(this, node, context);
|
|
51
71
|
case ts.SyntaxKind.BinaryExpression:
|
|
52
72
|
return visitBinaryExpression.call(this, node, context);
|
|
73
|
+
case ts.SyntaxKind.ConditionalExpression:
|
|
74
|
+
return visitConditionalExpression.call(this, node, context);
|
|
53
75
|
case ts.SyntaxKind.ThrowStatement:
|
|
54
76
|
return visitThrowStatement.call(this, node, context);
|
|
55
77
|
case ts.SyntaxKind.TryStatement:
|
|
@@ -58,6 +80,8 @@ export function visit(node, context) {
|
|
|
58
80
|
return visitCatchClause.call(this, node, context);
|
|
59
81
|
case ts.SyntaxKind.CallExpression:
|
|
60
82
|
return visitCallExpression.call(this, node, context);
|
|
83
|
+
case ts.SyntaxKind.YieldExpression:
|
|
84
|
+
return visitYieldExpression.call(this, node, context);
|
|
61
85
|
case ts.SyntaxKind.ReturnStatement:
|
|
62
86
|
return visitReturnStatement.call(this, node, context);
|
|
63
87
|
case ts.SyntaxKind.Identifier:
|
|
@@ -70,6 +94,12 @@ export function visit(node, context) {
|
|
|
70
94
|
return visitNoSubstitutionTemplateLiteral.call(this, node);
|
|
71
95
|
case ts.SyntaxKind.TemplateExpression:
|
|
72
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);
|
|
73
103
|
case ts.SyntaxKind.TrueKeyword:
|
|
74
104
|
return visitTrueKeyword.call(this);
|
|
75
105
|
case ts.SyntaxKind.FalseKeyword:
|
|
@@ -80,6 +110,8 @@ export function visit(node, context) {
|
|
|
80
110
|
return visitUndefinedKeyword.call(this);
|
|
81
111
|
case ts.SyntaxKind.NullKeyword:
|
|
82
112
|
return visitNullKeyword.call(this);
|
|
113
|
+
case ts.SyntaxKind.ThisKeyword:
|
|
114
|
+
return visitThisKeyword.call(this);
|
|
83
115
|
default:
|
|
84
116
|
return `/* Unhandled node: ${ts.SyntaxKind[node.kind]} */`;
|
|
85
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"
|