@ugo-studio/jspp 0.2.5 → 0.2.7
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 +96 -43
- package/dist/ast/symbols.js +34 -24
- 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/core/codegen/class-handlers.js +8 -8
- package/dist/core/codegen/control-flow-handlers.js +19 -9
- package/dist/core/codegen/declaration-handlers.js +30 -10
- package/dist/core/codegen/expression-handlers.js +649 -161
- package/dist/core/codegen/function-handlers.js +107 -103
- package/dist/core/codegen/helpers.js +61 -14
- package/dist/core/codegen/index.js +13 -9
- package/dist/core/codegen/literal-handlers.js +4 -2
- package/dist/core/codegen/statement-handlers.js +147 -55
- package/dist/core/codegen/visitor.js +22 -2
- package/dist/core/constants.js +16 -0
- package/dist/core/error.js +58 -0
- package/dist/index.js +6 -3
- package/package.json +3 -3
- package/src/prelude/any_value.hpp +89 -59
- package/src/prelude/any_value_access.hpp +1 -1
- package/src/prelude/any_value_helpers.hpp +85 -43
- package/src/prelude/index.hpp +1 -0
- package/src/prelude/library/array.hpp +3 -2
- package/src/prelude/scheduler.hpp +144 -144
- package/src/prelude/types.hpp +8 -8
- package/src/prelude/utils/access.hpp +62 -6
- package/src/prelude/utils/assignment_operators.hpp +14 -14
- package/src/prelude/utils/log_any_value/array.hpp +0 -15
- package/src/prelude/utils/log_any_value/object.hpp +12 -10
- package/src/prelude/utils/log_any_value/primitives.hpp +2 -0
- package/src/prelude/utils/operators.hpp +117 -474
- package/src/prelude/utils/operators_primitive.hpp +337 -0
- package/src/prelude/values/helpers/array.hpp +4 -4
- package/src/prelude/values/helpers/async_iterator.hpp +2 -2
- package/src/prelude/values/helpers/function.hpp +3 -3
- package/src/prelude/values/helpers/iterator.hpp +2 -2
- package/src/prelude/values/helpers/object.hpp +3 -3
- package/src/prelude/values/helpers/promise.hpp +1 -1
- package/src/prelude/values/helpers/string.hpp +1 -1
- package/src/prelude/values/helpers/symbol.hpp +1 -1
- package/src/prelude/values/prototypes/array.hpp +1125 -853
- package/src/prelude/values/prototypes/async_iterator.hpp +32 -14
- package/src/prelude/values/prototypes/function.hpp +30 -18
- package/src/prelude/values/prototypes/iterator.hpp +40 -17
- package/src/prelude/values/prototypes/number.hpp +119 -62
- package/src/prelude/values/prototypes/object.hpp +10 -4
- package/src/prelude/values/prototypes/promise.hpp +167 -109
- package/src/prelude/values/prototypes/string.hpp +407 -231
- package/src/prelude/values/prototypes/symbol.hpp +45 -23
|
@@ -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,8 +10,9 @@ 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) => {
|
|
@@ -19,6 +22,10 @@ export function visitSourceFile(node, context) {
|
|
|
19
22
|
classDecls.forEach((cls) => {
|
|
20
23
|
code += this.hoistDeclaration(cls, hoistedSymbols, node);
|
|
21
24
|
});
|
|
25
|
+
// Hoist enum declarations
|
|
26
|
+
enumDecls.forEach((enm) => {
|
|
27
|
+
code += this.hoistDeclaration(enm, hoistedSymbols, node);
|
|
28
|
+
});
|
|
22
29
|
// Hoist variable declarations (var)
|
|
23
30
|
varDecls.forEach((decl) => {
|
|
24
31
|
code += this.hoistDeclaration(decl, hoistedSymbols, node);
|
|
@@ -45,21 +52,31 @@ export function visitSourceFile(node, context) {
|
|
|
45
52
|
// Mark before further visits
|
|
46
53
|
this.markSymbolAsInitialized(funcName, contextForFunctions.globalScopeSymbols, contextForFunctions.localScopeSymbols);
|
|
47
54
|
this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
|
|
48
|
-
//
|
|
55
|
+
// Update features in the symbol registry
|
|
49
56
|
const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
|
|
50
|
-
hoistedSymbols.update(funcName, {
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
hoistedSymbols.update(funcName, {
|
|
58
|
+
features: {
|
|
59
|
+
native: {
|
|
60
|
+
type: "lambda",
|
|
61
|
+
name: nativeName,
|
|
62
|
+
parameters: this.validateFunctionParams(stmt.parameters),
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
// Generate lambda components
|
|
67
|
+
const lambdaComps = this.generateLambdaComponents(stmt, contextForFunctions, {
|
|
53
68
|
isAssignment: true,
|
|
54
|
-
generateOnlyLambda: true,
|
|
55
69
|
nativeName,
|
|
70
|
+
noTypeSignature: true,
|
|
56
71
|
});
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
// Generate native lambda
|
|
73
|
+
const nativeLambda = this.generateNativeLambda(lambdaComps);
|
|
74
|
+
code += `${this.indent()}auto ${nativeName} = ${nativeLambda};\n`;
|
|
75
|
+
// Generate AnyValue wrapped lamda
|
|
59
76
|
if (this.isFunctionUsedAsValue(stmt, node) ||
|
|
60
77
|
this.isFunctionUsedBeforeDeclaration(funcName, node)) {
|
|
61
|
-
const
|
|
62
|
-
code += `${this.indent()}*${funcName} = ${
|
|
78
|
+
const wrappedLambda = this.generateWrappedLambda(lambdaComps);
|
|
79
|
+
code += `${this.indent()}*${funcName} = ${wrappedLambda};\n`;
|
|
63
80
|
}
|
|
64
81
|
});
|
|
65
82
|
// 3. Process other statements
|
|
@@ -68,19 +85,11 @@ export function visitSourceFile(node, context) {
|
|
|
68
85
|
// Already handled
|
|
69
86
|
}
|
|
70
87
|
else if (ts.isVariableStatement(stmt)) {
|
|
71
|
-
|
|
72
|
-
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
|
|
73
|
-
0;
|
|
74
|
-
const contextForVisit = {
|
|
88
|
+
code += this.visit(stmt, {
|
|
75
89
|
...context,
|
|
76
90
|
globalScopeSymbols,
|
|
77
91
|
localScopeSymbols,
|
|
78
|
-
|
|
79
|
-
};
|
|
80
|
-
const assignments = this.visit(stmt.declarationList, contextForVisit);
|
|
81
|
-
if (assignments) {
|
|
82
|
-
code += `${this.indent()}${assignments};\n`;
|
|
83
|
-
}
|
|
92
|
+
});
|
|
84
93
|
}
|
|
85
94
|
else {
|
|
86
95
|
code += this.visit(stmt, {
|
|
@@ -100,8 +109,9 @@ export function visitBlock(node, context) {
|
|
|
100
109
|
const block = node;
|
|
101
110
|
// Collect ONLY block-scoped declarations (let/const)
|
|
102
111
|
const blockScopedDecls = collectBlockScopedDeclarations(block.statements);
|
|
103
|
-
const funcDecls = block.statements.filter(ts.isFunctionDeclaration);
|
|
112
|
+
const funcDecls = block.statements.filter((s) => ts.isFunctionDeclaration(s) && !!s.body);
|
|
104
113
|
const classDecls = block.statements.filter(ts.isClassDeclaration);
|
|
114
|
+
const enumDecls = block.statements.filter(ts.isEnumDeclaration);
|
|
105
115
|
const hoistedSymbols = new DeclaredSymbols();
|
|
106
116
|
// 1. Hoist all function declarations
|
|
107
117
|
funcDecls.forEach((func) => {
|
|
@@ -111,6 +121,10 @@ export function visitBlock(node, context) {
|
|
|
111
121
|
classDecls.forEach((cls) => {
|
|
112
122
|
code += this.hoistDeclaration(cls, hoistedSymbols, node);
|
|
113
123
|
});
|
|
124
|
+
// Hoist enum declarations
|
|
125
|
+
enumDecls.forEach((enm) => {
|
|
126
|
+
code += this.hoistDeclaration(enm, hoistedSymbols, node);
|
|
127
|
+
});
|
|
114
128
|
// Hoist variable declarations (let/const only)
|
|
115
129
|
blockScopedDecls.forEach((decl) => {
|
|
116
130
|
code += this.hoistDeclaration(decl, hoistedSymbols, node);
|
|
@@ -133,21 +147,31 @@ export function visitBlock(node, context) {
|
|
|
133
147
|
// Mark before further visits
|
|
134
148
|
this.markSymbolAsInitialized(funcName, contextForFunctions.globalScopeSymbols, contextForFunctions.localScopeSymbols);
|
|
135
149
|
this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
|
|
136
|
-
//
|
|
150
|
+
// Update features in the symbol registry
|
|
137
151
|
const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
|
|
138
|
-
hoistedSymbols.update(funcName, {
|
|
139
|
-
|
|
140
|
-
|
|
152
|
+
hoistedSymbols.update(funcName, {
|
|
153
|
+
features: {
|
|
154
|
+
native: {
|
|
155
|
+
type: "lambda",
|
|
156
|
+
name: nativeName,
|
|
157
|
+
parameters: this.validateFunctionParams(stmt.parameters),
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
// Generate lambda components
|
|
162
|
+
const lambdaComps = this.generateLambdaComponents(stmt, contextForFunctions, {
|
|
141
163
|
isAssignment: true,
|
|
142
|
-
generateOnlyLambda: true,
|
|
143
164
|
nativeName,
|
|
165
|
+
noTypeSignature: true,
|
|
144
166
|
});
|
|
145
|
-
|
|
146
|
-
|
|
167
|
+
// Generate native lambda
|
|
168
|
+
const nativeLambda = this.generateNativeLambda(lambdaComps);
|
|
169
|
+
code += `${this.indent()}auto ${nativeName} = ${nativeLambda};\n`;
|
|
170
|
+
// Generate AnyValue wrapped lamda
|
|
147
171
|
if (this.isFunctionUsedAsValue(stmt, node) ||
|
|
148
172
|
this.isFunctionUsedBeforeDeclaration(funcName, node)) {
|
|
149
|
-
const
|
|
150
|
-
code += `${this.indent()}*${funcName} = ${
|
|
173
|
+
const wrappedLambda = this.generateWrappedLambda(lambdaComps);
|
|
174
|
+
code += `${this.indent()}*${funcName} = ${wrappedLambda};\n`;
|
|
151
175
|
}
|
|
152
176
|
});
|
|
153
177
|
// 3. Process other statements
|
|
@@ -156,19 +180,11 @@ export function visitBlock(node, context) {
|
|
|
156
180
|
// Do nothing, already handled
|
|
157
181
|
}
|
|
158
182
|
else if (ts.isVariableStatement(stmt)) {
|
|
159
|
-
|
|
160
|
-
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
|
|
161
|
-
0;
|
|
162
|
-
const contextForVisit = {
|
|
183
|
+
code += this.visit(stmt, {
|
|
163
184
|
...context,
|
|
164
185
|
globalScopeSymbols,
|
|
165
186
|
localScopeSymbols,
|
|
166
|
-
|
|
167
|
-
};
|
|
168
|
-
const assignments = this.visit(stmt.declarationList, contextForVisit);
|
|
169
|
-
if (assignments) {
|
|
170
|
-
code += `${this.indent()}${assignments};\n`;
|
|
171
|
-
}
|
|
187
|
+
});
|
|
172
188
|
}
|
|
173
189
|
else {
|
|
174
190
|
code += this.visit(stmt, {
|
|
@@ -190,9 +206,87 @@ export function visitBlock(node, context) {
|
|
|
190
206
|
return code;
|
|
191
207
|
}
|
|
192
208
|
export function visitVariableStatement(node, context) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
209
|
+
if (shouldIgnoreStatement(node)) {
|
|
210
|
+
return "";
|
|
211
|
+
}
|
|
212
|
+
const isLetOrConst = (node.declarationList.flags &
|
|
213
|
+
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
|
|
214
|
+
0;
|
|
215
|
+
const visitContext = {
|
|
216
|
+
...context,
|
|
217
|
+
isAssignmentOnly: !isLetOrConst,
|
|
218
|
+
};
|
|
219
|
+
const assignments = this.visit(node.declarationList, visitContext);
|
|
220
|
+
if (assignments) {
|
|
221
|
+
return `${this.indent()}${assignments};\n`;
|
|
222
|
+
}
|
|
223
|
+
return "";
|
|
224
|
+
}
|
|
225
|
+
export function visitTypeAliasDeclaration(node, context) {
|
|
226
|
+
return "";
|
|
227
|
+
}
|
|
228
|
+
export function visitInterfaceDeclaration(node, context) {
|
|
229
|
+
return "";
|
|
230
|
+
}
|
|
231
|
+
export function visitEnumDeclaration(node, context) {
|
|
232
|
+
const name = node.name.getText();
|
|
233
|
+
const scope = this.getScopeForNode(node);
|
|
234
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
235
|
+
// Mark as initialized
|
|
236
|
+
this.markSymbolAsInitialized(name, context.globalScopeSymbols, context.localScopeSymbols);
|
|
237
|
+
const enumVar = typeInfo.needsHeapAllocation ? `(*${name})` : name;
|
|
238
|
+
let code = `${this.indent()}${enumVar} = jspp::AnyValue::make_object({});\n`;
|
|
239
|
+
code += `${this.indent()}{\n`;
|
|
240
|
+
this.indentationLevel++;
|
|
241
|
+
code +=
|
|
242
|
+
`${this.indent()}jspp::AnyValue lastVal = jspp::AnyValue::make_number(-1);\n`; // Previous value tracker
|
|
243
|
+
for (const member of node.members) {
|
|
244
|
+
const memberName = member.name.getText();
|
|
245
|
+
let valueCode = "";
|
|
246
|
+
// Handle member name (it could be a string literal or identifier)
|
|
247
|
+
let key = "";
|
|
248
|
+
if (ts.isIdentifier(member.name)) {
|
|
249
|
+
key = `"${memberName}"`;
|
|
250
|
+
}
|
|
251
|
+
else if (ts.isStringLiteral(member.name)) {
|
|
252
|
+
key = member.name.getText(); // Includes quotes
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
// Computed property names or numeric literals in enums are rarer but possible
|
|
256
|
+
// For now assume simple enum
|
|
257
|
+
key = `"${memberName}"`;
|
|
258
|
+
}
|
|
259
|
+
if (member.initializer) {
|
|
260
|
+
// Visit initializer
|
|
261
|
+
valueCode = this.visit(member.initializer, context);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
// Auto-increment
|
|
265
|
+
valueCode = `jspp::add(lastVal, 1)`;
|
|
266
|
+
}
|
|
267
|
+
code += `${this.indent()}lastVal = ${valueCode};\n`;
|
|
268
|
+
code +=
|
|
269
|
+
`${this.indent()}${enumVar}.set_own_property(${key}, lastVal);\n`;
|
|
270
|
+
// Reverse mapping for numeric enums
|
|
271
|
+
code += `${this.indent()}if (lastVal.is_number()) {\n`;
|
|
272
|
+
this.indentationLevel++;
|
|
273
|
+
code +=
|
|
274
|
+
`${this.indent()}${enumVar}.set_own_property(lastVal, jspp::AnyValue::make_string(${key}));\n`;
|
|
275
|
+
this.indentationLevel--;
|
|
276
|
+
code += `${this.indent()}}\n`;
|
|
277
|
+
}
|
|
278
|
+
this.indentationLevel--;
|
|
279
|
+
code += `${this.indent()}}\n`;
|
|
280
|
+
return code;
|
|
281
|
+
}
|
|
282
|
+
export function visitModuleDeclaration(node, context) {
|
|
283
|
+
return "";
|
|
284
|
+
}
|
|
285
|
+
export function visitImportDeclaration(node, context) {
|
|
286
|
+
return "";
|
|
287
|
+
}
|
|
288
|
+
export function visitImportEqualsDeclaration(node, context) {
|
|
289
|
+
return "";
|
|
196
290
|
}
|
|
197
291
|
export function visitBreakStatement(node, context) {
|
|
198
292
|
if (node.label) {
|
|
@@ -237,7 +331,12 @@ export function visitLabeledStatement(node, context) {
|
|
|
237
331
|
}
|
|
238
332
|
export function visitIfStatement(node, context) {
|
|
239
333
|
const ifStmt = node;
|
|
240
|
-
const
|
|
334
|
+
const isBinaryExpression = ts.isBinaryExpression(ifStmt.expression) &&
|
|
335
|
+
constants.booleanOperators.includes(ifStmt.expression.operatorToken.kind);
|
|
336
|
+
const condition = this.visit(ifStmt.expression, {
|
|
337
|
+
...context,
|
|
338
|
+
supportedNativeLiterals: isBinaryExpression ? ["boolean"] : undefined,
|
|
339
|
+
});
|
|
241
340
|
const thenStmt = this.visit(ifStmt.thenStatement, {
|
|
242
341
|
...context,
|
|
243
342
|
isFunctionBody: false,
|
|
@@ -250,15 +349,8 @@ export function visitIfStatement(node, context) {
|
|
|
250
349
|
isFunctionBody: false,
|
|
251
350
|
});
|
|
252
351
|
}
|
|
253
|
-
if (
|
|
254
|
-
|
|
255
|
-
const op = binExpr.operatorToken.getText();
|
|
256
|
-
const isBoolean = op === "==" || op === "!=" || op === "===" ||
|
|
257
|
-
op === "!==" || op === "<" || op === ">" || op === "<=" ||
|
|
258
|
-
op === ">=" || op === "instanceof" || op === "in";
|
|
259
|
-
if (isBoolean) {
|
|
260
|
-
return `${this.indent()}if ((${condition}).as_boolean()) ${thenStmt}${elseStmt}`;
|
|
261
|
-
}
|
|
352
|
+
if (isBinaryExpression) {
|
|
353
|
+
return `${this.indent()}if (${condition}) ${thenStmt}${elseStmt}`;
|
|
262
354
|
}
|
|
263
355
|
return `${this.indent()}if (jspp::is_truthy(${condition})) ${thenStmt}${elseStmt}`;
|
|
264
356
|
}
|
|
@@ -540,7 +632,7 @@ export function visitCatchClause(node, context) {
|
|
|
540
632
|
const exceptionName = context.exceptionName;
|
|
541
633
|
if (!exceptionName) {
|
|
542
634
|
// This should not happen if it's coming from a TryStatement
|
|
543
|
-
throw new
|
|
635
|
+
throw new CompilerError("exceptionName not found in context for CatchClause", node, "CompilerBug");
|
|
544
636
|
}
|
|
545
637
|
if (catchClause.variableDeclaration) {
|
|
546
638
|
const varName = catchClause.variableDeclaration.name.getText();
|
|
@@ -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/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.7",
|
|
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",
|