@ugo-studio/jspp 0.2.4 → 0.2.6
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 +51 -36
- package/dist/analysis/scope.js +7 -0
- package/dist/analysis/typeAnalyzer.js +58 -5
- package/dist/ast/symbols.js +58 -15
- package/dist/cli/args.js +59 -0
- package/dist/cli/colors.js +9 -0
- package/dist/cli/file-utils.js +20 -0
- package/dist/cli/index.js +160 -0
- package/dist/cli/spinner.js +55 -0
- package/dist/cli.js +1 -1
- package/dist/core/codegen/control-flow-handlers.js +139 -51
- package/dist/core/codegen/declaration-handlers.js +45 -12
- package/dist/core/codegen/expression-handlers.js +139 -48
- package/dist/core/codegen/function-handlers.js +53 -59
- package/dist/core/codegen/helpers.js +196 -32
- package/dist/core/codegen/index.js +15 -9
- package/dist/core/codegen/statement-handlers.js +178 -63
- package/dist/core/codegen/visitor.js +22 -2
- package/dist/core/constants.js +16 -0
- package/dist/core/error.js +58 -0
- package/dist/core/parser.js +2 -2
- package/dist/index.js +6 -3
- package/package.json +3 -3
- package/src/prelude/scheduler.hpp +144 -144
- package/src/prelude/utils/access.hpp +2 -2
- package/src/prelude/utils/log_any_value/object.hpp +12 -10
- package/src/prelude/utils/log_any_value/primitives.hpp +7 -0
- package/src/prelude/utils/operators.hpp +4 -6
- package/src/prelude/values/prototypes/function.hpp +18 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import { DeclaredSymbols } from "../../ast/symbols.js";
|
|
3
|
-
import {
|
|
3
|
+
import { constants } from "../constants.js";
|
|
4
|
+
import { CompilerError } from "../error.js";
|
|
5
|
+
import { collectBlockScopedDeclarations, collectFunctionScopedDeclarations, shouldIgnoreStatement, } from "./helpers.js";
|
|
4
6
|
import { CodeGenerator } from "./index.js";
|
|
5
7
|
export function visitSourceFile(node, context) {
|
|
6
8
|
const sourceFile = node;
|
|
@@ -8,27 +10,32 @@ export function visitSourceFile(node, context) {
|
|
|
8
10
|
// 1. Collect all var declarations (recursively) + top-level let/const
|
|
9
11
|
const varDecls = collectFunctionScopedDeclarations(sourceFile);
|
|
10
12
|
const topLevelLetConst = collectBlockScopedDeclarations(sourceFile.statements);
|
|
11
|
-
const funcDecls = sourceFile.statements.filter(ts.isFunctionDeclaration);
|
|
13
|
+
const funcDecls = sourceFile.statements.filter((s) => ts.isFunctionDeclaration(s) && !!s.body);
|
|
12
14
|
const classDecls = sourceFile.statements.filter(ts.isClassDeclaration);
|
|
15
|
+
const enumDecls = sourceFile.statements.filter(ts.isEnumDeclaration);
|
|
13
16
|
const hoistedSymbols = new DeclaredSymbols();
|
|
14
17
|
// Hoist function declarations
|
|
15
18
|
funcDecls.forEach((func) => {
|
|
16
|
-
code += this.hoistDeclaration(func, hoistedSymbols);
|
|
19
|
+
code += this.hoistDeclaration(func, hoistedSymbols, node);
|
|
17
20
|
});
|
|
18
21
|
// Hoist class declarations
|
|
19
22
|
classDecls.forEach((cls) => {
|
|
20
|
-
code += this.hoistDeclaration(cls, hoistedSymbols);
|
|
23
|
+
code += this.hoistDeclaration(cls, hoistedSymbols, node);
|
|
24
|
+
});
|
|
25
|
+
// Hoist enum declarations
|
|
26
|
+
enumDecls.forEach((enm) => {
|
|
27
|
+
code += this.hoistDeclaration(enm, hoistedSymbols, node);
|
|
21
28
|
});
|
|
22
29
|
// Hoist variable declarations (var)
|
|
23
30
|
varDecls.forEach((decl) => {
|
|
24
|
-
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
31
|
+
code += this.hoistDeclaration(decl, hoistedSymbols, node);
|
|
25
32
|
});
|
|
26
33
|
// Hoist top-level let/const
|
|
27
34
|
topLevelLetConst.forEach((decl) => {
|
|
28
|
-
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
35
|
+
code += this.hoistDeclaration(decl, hoistedSymbols, node);
|
|
29
36
|
});
|
|
30
37
|
// Compile symbols for other statements (excluding function)
|
|
31
|
-
const
|
|
38
|
+
const globalScopeSymbols = this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols);
|
|
32
39
|
const localScopeSymbols = new DeclaredSymbols(hoistedSymbols); // hoistedSymbols becomes new local
|
|
33
40
|
// 2. Assign all hoisted functions first
|
|
34
41
|
const contextForFunctions = {
|
|
@@ -37,13 +44,29 @@ export function visitSourceFile(node, context) {
|
|
|
37
44
|
};
|
|
38
45
|
funcDecls.forEach((stmt) => {
|
|
39
46
|
const funcName = stmt.name?.getText();
|
|
40
|
-
if (funcName)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
if (!funcName)
|
|
48
|
+
return;
|
|
49
|
+
const symbol = hoistedSymbols.get(funcName);
|
|
50
|
+
if (!symbol)
|
|
51
|
+
return;
|
|
52
|
+
// Mark before further visits
|
|
53
|
+
this.markSymbolAsInitialized(funcName, contextForFunctions.globalScopeSymbols, contextForFunctions.localScopeSymbols);
|
|
54
|
+
this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
|
|
55
|
+
// Generate and update self name
|
|
56
|
+
const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
|
|
57
|
+
hoistedSymbols.update(funcName, { func: { nativeName } });
|
|
58
|
+
// Generate lambda
|
|
59
|
+
const lambda = this.generateLambda(stmt, contextForFunctions, {
|
|
60
|
+
isAssignment: true,
|
|
61
|
+
generateOnlyLambda: true,
|
|
62
|
+
nativeName,
|
|
63
|
+
});
|
|
64
|
+
code += `${this.indent()}auto ${nativeName} = ${lambda};\n`;
|
|
65
|
+
// Generate AnyValue wrapper
|
|
66
|
+
if (this.isFunctionUsedAsValue(stmt, node) ||
|
|
67
|
+
this.isFunctionUsedBeforeDeclaration(funcName, node)) {
|
|
68
|
+
const fullExpression = this.generateFullLambdaExpression(stmt, contextForFunctions, nativeName, { isAssignment: true, noTypeSignature: true });
|
|
69
|
+
code += `${this.indent()}*${funcName} = ${fullExpression};\n`;
|
|
47
70
|
}
|
|
48
71
|
});
|
|
49
72
|
// 3. Process other statements
|
|
@@ -52,25 +75,17 @@ export function visitSourceFile(node, context) {
|
|
|
52
75
|
// Already handled
|
|
53
76
|
}
|
|
54
77
|
else if (ts.isVariableStatement(stmt)) {
|
|
55
|
-
|
|
56
|
-
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
|
|
57
|
-
0;
|
|
58
|
-
const contextForVisit = {
|
|
78
|
+
code += this.visit(stmt, {
|
|
59
79
|
...context,
|
|
60
|
-
|
|
80
|
+
globalScopeSymbols,
|
|
61
81
|
localScopeSymbols,
|
|
62
|
-
|
|
63
|
-
};
|
|
64
|
-
const assignments = this.visit(stmt.declarationList, contextForVisit);
|
|
65
|
-
if (assignments) {
|
|
66
|
-
code += `${this.indent()}${assignments};\n`;
|
|
67
|
-
}
|
|
82
|
+
});
|
|
68
83
|
}
|
|
69
84
|
else {
|
|
70
85
|
code += this.visit(stmt, {
|
|
71
86
|
...context,
|
|
72
87
|
isFunctionBody: false,
|
|
73
|
-
|
|
88
|
+
globalScopeSymbols,
|
|
74
89
|
localScopeSymbols,
|
|
75
90
|
});
|
|
76
91
|
}
|
|
@@ -78,28 +93,34 @@ export function visitSourceFile(node, context) {
|
|
|
78
93
|
return code;
|
|
79
94
|
}
|
|
80
95
|
export function visitBlock(node, context) {
|
|
96
|
+
context.currentScopeNode = node; // Update scope node
|
|
81
97
|
let code = `${this.indent()}{\n`;
|
|
82
98
|
this.indentationLevel++;
|
|
83
99
|
const block = node;
|
|
84
100
|
// Collect ONLY block-scoped declarations (let/const)
|
|
85
101
|
const blockScopedDecls = collectBlockScopedDeclarations(block.statements);
|
|
86
|
-
const funcDecls = block.statements.filter(ts.isFunctionDeclaration);
|
|
102
|
+
const funcDecls = block.statements.filter((s) => ts.isFunctionDeclaration(s) && !!s.body);
|
|
87
103
|
const classDecls = block.statements.filter(ts.isClassDeclaration);
|
|
104
|
+
const enumDecls = block.statements.filter(ts.isEnumDeclaration);
|
|
88
105
|
const hoistedSymbols = new DeclaredSymbols();
|
|
89
106
|
// 1. Hoist all function declarations
|
|
90
107
|
funcDecls.forEach((func) => {
|
|
91
|
-
code += this.hoistDeclaration(func, hoistedSymbols);
|
|
108
|
+
code += this.hoistDeclaration(func, hoistedSymbols, node);
|
|
92
109
|
});
|
|
93
110
|
// Hoist class declarations
|
|
94
111
|
classDecls.forEach((cls) => {
|
|
95
|
-
code += this.hoistDeclaration(cls, hoistedSymbols);
|
|
112
|
+
code += this.hoistDeclaration(cls, hoistedSymbols, node);
|
|
113
|
+
});
|
|
114
|
+
// Hoist enum declarations
|
|
115
|
+
enumDecls.forEach((enm) => {
|
|
116
|
+
code += this.hoistDeclaration(enm, hoistedSymbols, node);
|
|
96
117
|
});
|
|
97
118
|
// Hoist variable declarations (let/const only)
|
|
98
119
|
blockScopedDecls.forEach((decl) => {
|
|
99
|
-
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
120
|
+
code += this.hoistDeclaration(decl, hoistedSymbols, node);
|
|
100
121
|
});
|
|
101
122
|
// Compile symbols for other statements (excluding function)
|
|
102
|
-
const
|
|
123
|
+
const globalScopeSymbols = this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols);
|
|
103
124
|
const localScopeSymbols = new DeclaredSymbols(hoistedSymbols); // hoistedSymbols becomes new local
|
|
104
125
|
// 2. Assign all hoisted functions first
|
|
105
126
|
const contextForFunctions = {
|
|
@@ -108,13 +129,29 @@ export function visitBlock(node, context) {
|
|
|
108
129
|
};
|
|
109
130
|
funcDecls.forEach((stmt) => {
|
|
110
131
|
const funcName = stmt.name?.getText();
|
|
111
|
-
if (funcName)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
132
|
+
if (!funcName)
|
|
133
|
+
return;
|
|
134
|
+
const symbol = hoistedSymbols.get(funcName);
|
|
135
|
+
if (!symbol)
|
|
136
|
+
return;
|
|
137
|
+
// Mark before further visits
|
|
138
|
+
this.markSymbolAsInitialized(funcName, contextForFunctions.globalScopeSymbols, contextForFunctions.localScopeSymbols);
|
|
139
|
+
this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
|
|
140
|
+
// Generate and update self name
|
|
141
|
+
const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
|
|
142
|
+
hoistedSymbols.update(funcName, { func: { nativeName } });
|
|
143
|
+
// Generate lambda
|
|
144
|
+
const lambda = this.generateLambda(stmt, contextForFunctions, {
|
|
145
|
+
isAssignment: true,
|
|
146
|
+
generateOnlyLambda: true,
|
|
147
|
+
nativeName,
|
|
148
|
+
});
|
|
149
|
+
code += `${this.indent()}auto ${nativeName} = ${lambda};\n`;
|
|
150
|
+
// Generate AnyValue wrapper
|
|
151
|
+
if (this.isFunctionUsedAsValue(stmt, node) ||
|
|
152
|
+
this.isFunctionUsedBeforeDeclaration(funcName, node)) {
|
|
153
|
+
const fullExpression = this.generateFullLambdaExpression(stmt, contextForFunctions, nativeName, { isAssignment: true, noTypeSignature: true });
|
|
154
|
+
code += `${this.indent()}*${funcName} = ${fullExpression};\n`;
|
|
118
155
|
}
|
|
119
156
|
});
|
|
120
157
|
// 3. Process other statements
|
|
@@ -123,25 +160,17 @@ export function visitBlock(node, context) {
|
|
|
123
160
|
// Do nothing, already handled
|
|
124
161
|
}
|
|
125
162
|
else if (ts.isVariableStatement(stmt)) {
|
|
126
|
-
|
|
127
|
-
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
|
|
128
|
-
0;
|
|
129
|
-
const contextForVisit = {
|
|
163
|
+
code += this.visit(stmt, {
|
|
130
164
|
...context,
|
|
131
|
-
|
|
165
|
+
globalScopeSymbols,
|
|
132
166
|
localScopeSymbols,
|
|
133
|
-
|
|
134
|
-
};
|
|
135
|
-
const assignments = this.visit(stmt.declarationList, contextForVisit);
|
|
136
|
-
if (assignments) {
|
|
137
|
-
code += `${this.indent()}${assignments};\n`;
|
|
138
|
-
}
|
|
167
|
+
});
|
|
139
168
|
}
|
|
140
169
|
else {
|
|
141
170
|
code += this.visit(stmt, {
|
|
142
171
|
...context,
|
|
143
172
|
isFunctionBody: false,
|
|
144
|
-
|
|
173
|
+
globalScopeSymbols,
|
|
145
174
|
localScopeSymbols,
|
|
146
175
|
});
|
|
147
176
|
}
|
|
@@ -157,9 +186,87 @@ export function visitBlock(node, context) {
|
|
|
157
186
|
return code;
|
|
158
187
|
}
|
|
159
188
|
export function visitVariableStatement(node, context) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
189
|
+
if (shouldIgnoreStatement(node)) {
|
|
190
|
+
return "";
|
|
191
|
+
}
|
|
192
|
+
const isLetOrConst = (node.declarationList.flags &
|
|
193
|
+
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
|
|
194
|
+
0;
|
|
195
|
+
const visitContext = {
|
|
196
|
+
...context,
|
|
197
|
+
isAssignmentOnly: !isLetOrConst,
|
|
198
|
+
};
|
|
199
|
+
const assignments = this.visit(node.declarationList, visitContext);
|
|
200
|
+
if (assignments) {
|
|
201
|
+
return `${this.indent()}${assignments};\n`;
|
|
202
|
+
}
|
|
203
|
+
return "";
|
|
204
|
+
}
|
|
205
|
+
export function visitTypeAliasDeclaration(node, context) {
|
|
206
|
+
return "";
|
|
207
|
+
}
|
|
208
|
+
export function visitInterfaceDeclaration(node, context) {
|
|
209
|
+
return "";
|
|
210
|
+
}
|
|
211
|
+
export function visitEnumDeclaration(node, context) {
|
|
212
|
+
const name = node.name.getText();
|
|
213
|
+
const scope = this.getScopeForNode(node);
|
|
214
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
215
|
+
// Mark as initialized
|
|
216
|
+
this.markSymbolAsInitialized(name, context.globalScopeSymbols, context.localScopeSymbols);
|
|
217
|
+
const enumVar = typeInfo.needsHeapAllocation ? `(*${name})` : name;
|
|
218
|
+
let code = `${this.indent()}${enumVar} = jspp::AnyValue::make_object({});\n`;
|
|
219
|
+
code += `${this.indent()}{\n`;
|
|
220
|
+
this.indentationLevel++;
|
|
221
|
+
code +=
|
|
222
|
+
`${this.indent()}jspp::AnyValue lastVal = jspp::AnyValue::make_number(-1);\n`; // Previous value tracker
|
|
223
|
+
for (const member of node.members) {
|
|
224
|
+
const memberName = member.name.getText();
|
|
225
|
+
let valueCode = "";
|
|
226
|
+
// Handle member name (it could be a string literal or identifier)
|
|
227
|
+
let key = "";
|
|
228
|
+
if (ts.isIdentifier(member.name)) {
|
|
229
|
+
key = `"${memberName}"`;
|
|
230
|
+
}
|
|
231
|
+
else if (ts.isStringLiteral(member.name)) {
|
|
232
|
+
key = member.name.getText(); // Includes quotes
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
// Computed property names or numeric literals in enums are rarer but possible
|
|
236
|
+
// For now assume simple enum
|
|
237
|
+
key = `"${memberName}"`;
|
|
238
|
+
}
|
|
239
|
+
if (member.initializer) {
|
|
240
|
+
// Visit initializer
|
|
241
|
+
valueCode = this.visit(member.initializer, context);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
// Auto-increment
|
|
245
|
+
valueCode = `lastVal + 1`;
|
|
246
|
+
}
|
|
247
|
+
code += `${this.indent()}lastVal = ${valueCode};\n`;
|
|
248
|
+
code +=
|
|
249
|
+
`${this.indent()}${enumVar}.set_own_property(${key}, lastVal);\n`;
|
|
250
|
+
// Reverse mapping for numeric enums
|
|
251
|
+
code += `${this.indent()}if (lastVal.is_number()) {\n`;
|
|
252
|
+
this.indentationLevel++;
|
|
253
|
+
code +=
|
|
254
|
+
`${this.indent()}${enumVar}.set_own_property(lastVal, jspp::AnyValue::make_string(${key}));\n`;
|
|
255
|
+
this.indentationLevel--;
|
|
256
|
+
code += `${this.indent()}}\n`;
|
|
257
|
+
}
|
|
258
|
+
this.indentationLevel--;
|
|
259
|
+
code += `${this.indent()}}\n`;
|
|
260
|
+
return code;
|
|
261
|
+
}
|
|
262
|
+
export function visitModuleDeclaration(node, context) {
|
|
263
|
+
return "";
|
|
264
|
+
}
|
|
265
|
+
export function visitImportDeclaration(node, context) {
|
|
266
|
+
return "";
|
|
267
|
+
}
|
|
268
|
+
export function visitImportEqualsDeclaration(node, context) {
|
|
269
|
+
return "";
|
|
163
270
|
}
|
|
164
271
|
export function visitBreakStatement(node, context) {
|
|
165
272
|
if (node.label) {
|
|
@@ -204,7 +311,12 @@ export function visitLabeledStatement(node, context) {
|
|
|
204
311
|
}
|
|
205
312
|
export function visitIfStatement(node, context) {
|
|
206
313
|
const ifStmt = node;
|
|
207
|
-
const
|
|
314
|
+
const isBinaryExpression = ts.isBinaryExpression(ifStmt.expression) &&
|
|
315
|
+
constants.booleanOperators.includes(ifStmt.expression.operatorToken.kind);
|
|
316
|
+
const condition = this.visit(ifStmt.expression, {
|
|
317
|
+
...context,
|
|
318
|
+
supportedNativeLiterals: isBinaryExpression ? ["boolean"] : undefined,
|
|
319
|
+
});
|
|
208
320
|
const thenStmt = this.visit(ifStmt.thenStatement, {
|
|
209
321
|
...context,
|
|
210
322
|
isFunctionBody: false,
|
|
@@ -217,7 +329,10 @@ export function visitIfStatement(node, context) {
|
|
|
217
329
|
isFunctionBody: false,
|
|
218
330
|
});
|
|
219
331
|
}
|
|
220
|
-
|
|
332
|
+
if (isBinaryExpression) {
|
|
333
|
+
return `${this.indent()}if (${condition}) ${thenStmt}${elseStmt}`;
|
|
334
|
+
}
|
|
335
|
+
return `${this.indent()}if (jspp::is_truthy(${condition})) ${thenStmt}${elseStmt}`;
|
|
221
336
|
}
|
|
222
337
|
export function visitExpressionStatement(node, context) {
|
|
223
338
|
return (this.indent() +
|
|
@@ -234,7 +349,7 @@ export function visitTryStatement(node, context) {
|
|
|
234
349
|
const tryStmt = node;
|
|
235
350
|
if (context.isInsideAsyncFunction) {
|
|
236
351
|
if (tryStmt.finallyBlock) {
|
|
237
|
-
const declaredSymbols = new Set(context.
|
|
352
|
+
const declaredSymbols = new Set(context.globalScopeSymbols.names);
|
|
238
353
|
this.getDeclaredSymbols(tryStmt.tryBlock).forEach((s) => declaredSymbols.add(s));
|
|
239
354
|
if (tryStmt.catchClause) {
|
|
240
355
|
this.getDeclaredSymbols(tryStmt.catchClause).forEach((s) => declaredSymbols.add(s));
|
|
@@ -339,7 +454,7 @@ export function visitTryStatement(node, context) {
|
|
|
339
454
|
return code;
|
|
340
455
|
}
|
|
341
456
|
else {
|
|
342
|
-
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.
|
|
457
|
+
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.globalScopeSymbols, context.localScopeSymbols);
|
|
343
458
|
const newContext = {
|
|
344
459
|
...context,
|
|
345
460
|
isFunctionBody: false,
|
|
@@ -405,7 +520,7 @@ export function visitTryStatement(node, context) {
|
|
|
405
520
|
}
|
|
406
521
|
}
|
|
407
522
|
if (tryStmt.finallyBlock) {
|
|
408
|
-
const declaredSymbols = new Set(context.
|
|
523
|
+
const declaredSymbols = new Set(context.globalScopeSymbols.names);
|
|
409
524
|
this.getDeclaredSymbols(tryStmt.tryBlock).forEach((s) => declaredSymbols.add(s));
|
|
410
525
|
if (tryStmt.catchClause) {
|
|
411
526
|
this.getDeclaredSymbols(tryStmt.catchClause).forEach((s) => declaredSymbols.add(s));
|
|
@@ -440,7 +555,7 @@ export function visitTryStatement(node, context) {
|
|
|
440
555
|
this.indentationLevel--;
|
|
441
556
|
code += `${this.indent()}}\n`;
|
|
442
557
|
if (tryStmt.catchClause) {
|
|
443
|
-
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText(), context.
|
|
558
|
+
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText(), context.globalScopeSymbols, context.localScopeSymbols);
|
|
444
559
|
const catchContext = { ...innerContext, exceptionName };
|
|
445
560
|
code +=
|
|
446
561
|
`${this.indent()}catch (const std::exception& ${exceptionName}) {\n`;
|
|
@@ -476,7 +591,7 @@ export function visitTryStatement(node, context) {
|
|
|
476
591
|
return code;
|
|
477
592
|
}
|
|
478
593
|
else {
|
|
479
|
-
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.
|
|
594
|
+
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.globalScopeSymbols, context.localScopeSymbols);
|
|
480
595
|
const newContext = {
|
|
481
596
|
...context,
|
|
482
597
|
isFunctionBody: false,
|
|
@@ -497,7 +612,7 @@ export function visitCatchClause(node, context) {
|
|
|
497
612
|
const exceptionName = context.exceptionName;
|
|
498
613
|
if (!exceptionName) {
|
|
499
614
|
// This should not happen if it's coming from a TryStatement
|
|
500
|
-
throw new
|
|
615
|
+
throw new CompilerError("exceptionName not found in context for CatchClause", node, "CompilerBug");
|
|
501
616
|
}
|
|
502
617
|
if (catchClause.variableDeclaration) {
|
|
503
618
|
const varName = catchClause.variableDeclaration.name.getText();
|
|
@@ -540,7 +655,7 @@ export function visitYieldExpression(node, context) {
|
|
|
540
655
|
let code = `${this.indent()}{\n`;
|
|
541
656
|
this.indentationLevel++;
|
|
542
657
|
const declaredSymbols = this.getDeclaredSymbols(expr);
|
|
543
|
-
context.
|
|
658
|
+
context.globalScopeSymbols.names.forEach((s) => declaredSymbols.add(s));
|
|
544
659
|
const iterableRef = this.generateUniqueName("__iter_ref", declaredSymbols);
|
|
545
660
|
const iterator = this.generateUniqueName("__iter", declaredSymbols);
|
|
546
661
|
const nextFunc = this.generateUniqueName("__next_func", declaredSymbols);
|
|
@@ -566,7 +681,7 @@ export function visitYieldExpression(node, context) {
|
|
|
566
681
|
`${this.indent()}auto ${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
|
|
567
682
|
}
|
|
568
683
|
code +=
|
|
569
|
-
`${this.indent()}while (!is_truthy(${nextRes}.get_own_property("done"))) {\n`;
|
|
684
|
+
`${this.indent()}while (!jspp::is_truthy(${nextRes}.get_own_property("done"))) {\n`;
|
|
570
685
|
this.indentationLevel++;
|
|
571
686
|
if (context.isInsideAsyncFunction) {
|
|
572
687
|
code +=
|
|
@@ -2,11 +2,11 @@ import ts from "typescript";
|
|
|
2
2
|
import { visitClassDeclaration } from "./class-handlers.js";
|
|
3
3
|
import { visitCaseClause, visitDefaultClause, visitDoStatement, visitForInStatement, visitForOfStatement, visitForStatement, visitSwitchStatement, visitWhileStatement, } from "./control-flow-handlers.js";
|
|
4
4
|
import { visitVariableDeclaration, visitVariableDeclarationList, } from "./declaration-handlers.js";
|
|
5
|
-
import { visitArrayLiteralExpression, visitAwaitExpression, visitBinaryExpression, visitCallExpression, visitConditionalExpression, visitDeleteExpression, visitElementAccessExpression, visitNewExpression, visitObjectLiteralExpression, visitParenthesizedExpression, visitPostfixUnaryExpression, visitPrefixUnaryExpression, visitPropertyAccessExpression, visitTemplateExpression, visitTypeOfExpression, visitVoidExpression, } from "./expression-handlers.js";
|
|
5
|
+
import { visitArrayLiteralExpression, visitAsExpression, visitAwaitExpression, visitBinaryExpression, visitCallExpression, visitConditionalExpression, visitDeleteExpression, visitElementAccessExpression, visitNewExpression, visitNonNullExpression, visitObjectLiteralExpression, visitParenthesizedExpression, visitPostfixUnaryExpression, visitPrefixUnaryExpression, visitPropertyAccessExpression, visitSatisfiesExpression, visitTemplateExpression, visitTypeAssertionExpression, visitTypeOfExpression, visitVoidExpression, } from "./expression-handlers.js";
|
|
6
6
|
import { visitArrowFunction, visitFunctionDeclaration, visitFunctionExpression, } from "./function-handlers.js";
|
|
7
7
|
import { CodeGenerator } from "./index.js";
|
|
8
8
|
import { visitFalseKeyword, visitIdentifier, visitNoSubstitutionTemplateLiteral, visitNullKeyword, visitNumericLiteral, visitStringLiteral, visitThisKeyword, visitTrueKeyword, visitUndefinedKeyword, } from "./literal-handlers.js";
|
|
9
|
-
import { visitBlock, visitBreakStatement, visitCatchClause, visitContinueStatement, visitExpressionStatement, visitIfStatement, visitLabeledStatement, visitReturnStatement, visitSourceFile, visitThrowStatement, visitTryStatement, visitVariableStatement, visitYieldExpression, } from "./statement-handlers.js";
|
|
9
|
+
import { visitBlock, visitBreakStatement, visitCatchClause, visitContinueStatement, visitEnumDeclaration, visitExpressionStatement, visitIfStatement, visitImportDeclaration, visitImportEqualsDeclaration, visitInterfaceDeclaration, visitLabeledStatement, visitModuleDeclaration, visitReturnStatement, visitSourceFile, visitThrowStatement, visitTryStatement, visitTypeAliasDeclaration, visitVariableStatement, visitYieldExpression, } from "./statement-handlers.js";
|
|
10
10
|
export function visit(node, context) {
|
|
11
11
|
if (ts.isFunctionDeclaration(node)) {
|
|
12
12
|
return visitFunctionDeclaration.call(this, node, context);
|
|
@@ -114,6 +114,26 @@ export function visit(node, context) {
|
|
|
114
114
|
return visitNullKeyword.call(this);
|
|
115
115
|
case ts.SyntaxKind.ThisKeyword:
|
|
116
116
|
return visitThisKeyword.call(this);
|
|
117
|
+
case ts.SyntaxKind.AsExpression:
|
|
118
|
+
return visitAsExpression.call(this, node, context);
|
|
119
|
+
case ts.SyntaxKind.TypeAssertionExpression:
|
|
120
|
+
return visitTypeAssertionExpression.call(this, node, context);
|
|
121
|
+
case ts.SyntaxKind.NonNullExpression:
|
|
122
|
+
return visitNonNullExpression.call(this, node, context);
|
|
123
|
+
case ts.SyntaxKind.SatisfiesExpression:
|
|
124
|
+
return visitSatisfiesExpression.call(this, node, context);
|
|
125
|
+
case ts.SyntaxKind.TypeAliasDeclaration:
|
|
126
|
+
return visitTypeAliasDeclaration.call(this, node, context);
|
|
127
|
+
case ts.SyntaxKind.InterfaceDeclaration:
|
|
128
|
+
return visitInterfaceDeclaration.call(this, node, context);
|
|
129
|
+
case ts.SyntaxKind.EnumDeclaration:
|
|
130
|
+
return visitEnumDeclaration.call(this, node, context);
|
|
131
|
+
case ts.SyntaxKind.ModuleDeclaration:
|
|
132
|
+
return visitModuleDeclaration.call(this, node, context);
|
|
133
|
+
case ts.SyntaxKind.ImportDeclaration:
|
|
134
|
+
return visitImportDeclaration.call(this, node, context);
|
|
135
|
+
case ts.SyntaxKind.ImportEqualsDeclaration:
|
|
136
|
+
return visitImportEqualsDeclaration.call(this, node, context);
|
|
117
137
|
default:
|
|
118
138
|
return `/* Unhandled node: ${ts.SyntaxKind[node.kind]} */`;
|
|
119
139
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
const booleanOperators = [
|
|
3
|
+
ts.SyntaxKind.EqualsEqualsEqualsToken,
|
|
4
|
+
ts.SyntaxKind.EqualsEqualsToken,
|
|
5
|
+
ts.SyntaxKind.ExclamationEqualsEqualsToken,
|
|
6
|
+
ts.SyntaxKind.ExclamationEqualsToken,
|
|
7
|
+
ts.SyntaxKind.InstanceOfKeyword,
|
|
8
|
+
ts.SyntaxKind.InKeyword,
|
|
9
|
+
ts.SyntaxKind.LessThanToken,
|
|
10
|
+
ts.SyntaxKind.LessThanEqualsToken,
|
|
11
|
+
ts.SyntaxKind.GreaterThanToken,
|
|
12
|
+
ts.SyntaxKind.GreaterThanEqualsToken,
|
|
13
|
+
];
|
|
14
|
+
export const constants = Object.freeze({
|
|
15
|
+
booleanOperators,
|
|
16
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
const COLORS = {
|
|
3
|
+
reset: "\x1b[0m",
|
|
4
|
+
red: "\x1b[31m",
|
|
5
|
+
yellow: "\x1b[33m",
|
|
6
|
+
cyan: "\x1b[36m",
|
|
7
|
+
bold: "\x1b[1m",
|
|
8
|
+
dim: "\x1b[2m",
|
|
9
|
+
};
|
|
10
|
+
export class CompilerError extends Error {
|
|
11
|
+
node;
|
|
12
|
+
type;
|
|
13
|
+
constructor(message, node, type = "SyntaxError") {
|
|
14
|
+
super(message);
|
|
15
|
+
this.node = node;
|
|
16
|
+
this.type = type;
|
|
17
|
+
Object.setPrototypeOf(this, CompilerError.prototype);
|
|
18
|
+
}
|
|
19
|
+
getFormattedError() {
|
|
20
|
+
const sourceFile = this.node.getSourceFile();
|
|
21
|
+
if (!sourceFile) {
|
|
22
|
+
return `${COLORS.red}${this.type}: ${this.message}${COLORS.reset}`;
|
|
23
|
+
}
|
|
24
|
+
const start = this.node.getStart();
|
|
25
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(start);
|
|
26
|
+
// Get the full line content safely
|
|
27
|
+
const lineStartPos = sourceFile.getPositionOfLineAndCharacter(line, 0);
|
|
28
|
+
let lineEndPos;
|
|
29
|
+
try {
|
|
30
|
+
lineEndPos = sourceFile.getLineEndOfPosition(start);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
lineEndPos = sourceFile.text.length;
|
|
34
|
+
}
|
|
35
|
+
const lineContent = sourceFile.text.substring(lineStartPos, lineEndPos).replace(/\r/g, ''); // Remove CR
|
|
36
|
+
const fileName = sourceFile.fileName;
|
|
37
|
+
const lineNum = line + 1;
|
|
38
|
+
const charNum = character + 1;
|
|
39
|
+
let output = `\n${COLORS.red}${COLORS.bold}${this.type}:${COLORS.reset} ${this.message}\n`;
|
|
40
|
+
output += ` ${COLORS.dim}at ${fileName}:${lineNum}:${charNum}${COLORS.reset}\n\n`;
|
|
41
|
+
// Code frame
|
|
42
|
+
const lineNumStr = `${lineNum} | `;
|
|
43
|
+
output += `${COLORS.dim}${lineNumStr}${COLORS.reset}${lineContent}\n`;
|
|
44
|
+
// Adjust pointer position if there are tabs
|
|
45
|
+
let pointerPadding = "";
|
|
46
|
+
for (let i = 0; i < character; i++) {
|
|
47
|
+
if (lineContent[i] === '\t') {
|
|
48
|
+
pointerPadding += "\t";
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
pointerPadding += " ";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const padding = " ".repeat(lineNumStr.length) + pointerPadding;
|
|
55
|
+
output += `${padding}${COLORS.red}^${COLORS.reset}\n`;
|
|
56
|
+
return output;
|
|
57
|
+
}
|
|
58
|
+
}
|
package/dist/core/parser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as ts from "typescript";
|
|
2
2
|
export class Parser {
|
|
3
|
-
parse(sourceCode) {
|
|
4
|
-
return ts.createSourceFile("temp.js", sourceCode, ts.ScriptTarget.Latest, true);
|
|
3
|
+
parse(sourceCode, fileName) {
|
|
4
|
+
return ts.createSourceFile(fileName || "temp.js", sourceCode, ts.ScriptTarget.Latest, true);
|
|
5
5
|
}
|
|
6
6
|
}
|
package/dist/index.js
CHANGED
|
@@ -6,10 +6,13 @@ export class Interpreter {
|
|
|
6
6
|
parser = new Parser();
|
|
7
7
|
analyzer = new TypeAnalyzer();
|
|
8
8
|
generator = new CodeGenerator();
|
|
9
|
-
interpret(
|
|
10
|
-
const ast = this.parser.parse(
|
|
9
|
+
interpret(code, fileName) {
|
|
10
|
+
const ast = this.parser.parse(code, fileName);
|
|
11
11
|
this.analyzer.analyze(ast);
|
|
12
|
-
const
|
|
12
|
+
const isTypescript = fileName
|
|
13
|
+
? path.extname(fileName) === ".ts"
|
|
14
|
+
: false;
|
|
15
|
+
const cppCode = this.generator.generate(ast, this.analyzer, isTypescript);
|
|
13
16
|
const preludePath = path.resolve(import.meta.dirname, "..", "src", "prelude");
|
|
14
17
|
return { cppCode, preludePath };
|
|
15
18
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ugo-studio/jspp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
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",
|
|
8
8
|
"bin": {
|
|
9
|
-
"jspp": "dist/cli.js"
|
|
9
|
+
"jspp": "dist/cli/index.js"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
17
|
"postinstall": "bun run scripts/setup-compiler.ts",
|
|
18
|
-
"dev": "bun run src/cli.ts",
|
|
18
|
+
"dev": "bun run src/cli/index.ts",
|
|
19
19
|
"typecheck": "tsc --noEmit",
|
|
20
20
|
"precompile": "bun run scripts/precompile-headers.ts",
|
|
21
21
|
"test": "bun test",
|