@ugo-studio/jspp 0.1.0

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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/dist/analysis/scope.js +77 -0
  4. package/dist/analysis/typeAnalyzer.js +224 -0
  5. package/dist/ast/types.js +1 -0
  6. package/dist/cli.js +63 -0
  7. package/dist/core/codegen/declaration-handlers.js +49 -0
  8. package/dist/core/codegen/expression-handlers.js +333 -0
  9. package/dist/core/codegen/function-handlers.js +94 -0
  10. package/dist/core/codegen/helpers.js +83 -0
  11. package/dist/core/codegen/index.js +54 -0
  12. package/dist/core/codegen/literal-handlers.js +32 -0
  13. package/dist/core/codegen/statement-handlers.js +485 -0
  14. package/dist/core/codegen/visitor.js +86 -0
  15. package/dist/core/parser.js +6 -0
  16. package/dist/core/traverser.js +19 -0
  17. package/dist/index.js +16 -0
  18. package/package.json +41 -0
  19. package/src/prelude/access.hpp +86 -0
  20. package/src/prelude/any_value.hpp +734 -0
  21. package/src/prelude/descriptors.hpp +25 -0
  22. package/src/prelude/error.hpp +31 -0
  23. package/src/prelude/error_helpers.hpp +59 -0
  24. package/src/prelude/index.hpp +29 -0
  25. package/src/prelude/library/console.hpp +111 -0
  26. package/src/prelude/library/global.hpp +10 -0
  27. package/src/prelude/library/symbol.hpp +8 -0
  28. package/src/prelude/log_string.hpp +403 -0
  29. package/src/prelude/operators.hpp +256 -0
  30. package/src/prelude/types.hpp +50 -0
  31. package/src/prelude/values/array.hpp +50 -0
  32. package/src/prelude/values/function.hpp +19 -0
  33. package/src/prelude/values/non_values.hpp +20 -0
  34. package/src/prelude/values/object.hpp +17 -0
  35. package/src/prelude/values/operators/array.hpp +165 -0
  36. package/src/prelude/values/operators/function.hpp +34 -0
  37. package/src/prelude/values/operators/object.hpp +34 -0
  38. package/src/prelude/values/prototypes/array.hpp +228 -0
  39. package/src/prelude/values/prototypes/function.hpp +0 -0
  40. package/src/prelude/values/prototypes/object.hpp +0 -0
  41. package/src/prelude/values/prototypes/string.hpp +357 -0
  42. package/src/prelude/well_known_symbols.hpp +10 -0
@@ -0,0 +1,485 @@
1
+ import ts from "typescript";
2
+ import { CodeGenerator } from "./";
3
+ export function visitSourceFile(node, context) {
4
+ const sourceFile = node;
5
+ let code = "";
6
+ const varDecls = sourceFile.statements
7
+ .filter(ts.isVariableStatement)
8
+ .flatMap((stmt) => stmt.declarationList.declarations);
9
+ const funcDecls = sourceFile.statements.filter(ts.isFunctionDeclaration);
10
+ const hoistedSymbols = new Set();
11
+ // 1. Hoist function declarations
12
+ funcDecls.forEach((func) => {
13
+ const funcName = func.name?.getText();
14
+ if (funcName && !hoistedSymbols.has(funcName)) {
15
+ hoistedSymbols.add(funcName);
16
+ code +=
17
+ `${this.indent()}auto ${funcName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
18
+ }
19
+ });
20
+ // Hoist variable declarations
21
+ varDecls.forEach((decl) => {
22
+ const name = decl.name.getText();
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`;
34
+ });
35
+ // 2. Assign all hoisted functions first
36
+ funcDecls.forEach((stmt) => {
37
+ const funcName = stmt.name?.getText();
38
+ if (funcName) {
39
+ const lambda = this.generateLambda(stmt, true);
40
+ code += `${this.indent()}*${funcName} = ${lambda};\n`;
41
+ }
42
+ });
43
+ // 3. Process other statements
44
+ sourceFile.statements.forEach((stmt) => {
45
+ if (ts.isFunctionDeclaration(stmt)) {
46
+ // Already handled
47
+ }
48
+ else if (ts.isVariableStatement(stmt)) {
49
+ const isLetOrConst = (stmt.declarationList.flags &
50
+ (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
51
+ 0;
52
+ const contextForVisit = {
53
+ ...context,
54
+ isAssignmentOnly: !isLetOrConst,
55
+ };
56
+ const assignments = this.visit(stmt.declarationList, contextForVisit);
57
+ if (assignments) {
58
+ code += `${this.indent()}${assignments};\n`;
59
+ }
60
+ }
61
+ else {
62
+ code += this.visit(stmt, context);
63
+ }
64
+ });
65
+ return code;
66
+ }
67
+ export function visitBlock(node, context) {
68
+ let code = "{\n";
69
+ this.indentationLevel++;
70
+ const block = node;
71
+ const varDecls = block.statements
72
+ .filter(ts.isVariableStatement)
73
+ .flatMap((stmt) => stmt.declarationList.declarations);
74
+ const funcDecls = block.statements.filter(ts.isFunctionDeclaration);
75
+ const hoistedSymbols = new Set();
76
+ // 1. Hoist all function declarations
77
+ funcDecls.forEach((func) => {
78
+ const funcName = func.name?.getText();
79
+ if (funcName && !hoistedSymbols.has(funcName)) {
80
+ hoistedSymbols.add(funcName);
81
+ code +=
82
+ `${this.indent()}auto ${funcName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
83
+ }
84
+ });
85
+ // Hoist variable declarations
86
+ varDecls.forEach((decl) => {
87
+ const name = decl.name.getText();
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`;
99
+ });
100
+ // 2. Assign all hoisted functions first
101
+ funcDecls.forEach((stmt) => {
102
+ const funcName = stmt.name?.getText();
103
+ if (funcName) {
104
+ const lambda = this.generateLambda(stmt, true);
105
+ code += `${this.indent()}*${funcName} = ${lambda};\n`;
106
+ }
107
+ });
108
+ // 3. Process other statements
109
+ block.statements.forEach((stmt) => {
110
+ if (ts.isFunctionDeclaration(stmt)) {
111
+ // Do nothing, already handled
112
+ }
113
+ else if (ts.isVariableStatement(stmt)) {
114
+ const isLetOrConst = (stmt.declarationList.flags &
115
+ (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
116
+ 0;
117
+ const contextForVisit = {
118
+ ...context,
119
+ isAssignmentOnly: !isLetOrConst,
120
+ };
121
+ const assignments = this.visit(stmt.declarationList, contextForVisit);
122
+ if (assignments) {
123
+ code += `${this.indent()}${assignments};\n`;
124
+ }
125
+ }
126
+ else {
127
+ code += this.visit(stmt, context);
128
+ }
129
+ });
130
+ if (context.isFunctionBody) {
131
+ const lastStatement = block.statements[block.statements.length - 1];
132
+ if (!lastStatement || !ts.isReturnStatement(lastStatement)) {
133
+ code +=
134
+ `${this.indent()}return jspp::AnyValue::make_undefined();\n`;
135
+ }
136
+ }
137
+ this.indentationLevel--;
138
+ code += `${this.indent()}}\n`;
139
+ return code;
140
+ }
141
+ export function visitVariableStatement(node, context) {
142
+ return (this.indent() +
143
+ this.visit(node.declarationList, context) +
144
+ ";\n");
145
+ }
146
+ export function visitForStatement(node, context) {
147
+ const forStmt = node;
148
+ let code = "";
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
+ }
184
+ }
185
+ code += `${this.indent()}for (${initializerCode}; `;
186
+ if (forStmt.condition) {
187
+ code += `(${this.visit(forStmt.condition, context)}).is_truthy()`;
188
+ }
189
+ code += "; ";
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;
200
+ }
201
+ export function visitForInStatement(node, context) {
202
+ const forIn = node;
203
+ let code = `${this.indent()}{\n`;
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)})`;
225
+ }
226
+ const keysVar = this.generateUniqueName("__keys_", new Set([varName]));
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;
241
+ }
242
+ export function visitForOfStatement(node, context) {
243
+ const forOf = node;
244
+ let code = "";
245
+ this.indentationLevel++; // Enter a new scope for the for-of loop
246
+ let varName = "";
247
+ if (ts.isVariableDeclarationList(forOf.initializer)) {
248
+ const decl = forOf.initializer.declarations[0];
249
+ if (decl) {
250
+ varName = decl.name.getText();
251
+ // Declare the shared_ptr before the loop
252
+ code +=
253
+ `${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
254
+ }
255
+ }
256
+ else if (ts.isIdentifier(forOf.initializer)) {
257
+ varName = forOf.initializer.getText();
258
+ // Assume it's already declared in an outer scope, just assign to it.
259
+ // No explicit declaration here.
260
+ }
261
+ const iterableExpr = this.visit(forOf.expression, context);
262
+ const derefIterable = `jspp::Access::deref(${iterableExpr}, ${this.getJsVarName(forOf.expression)})`;
263
+ const arrayPtr = this.generateUniqueName("__array_ptr_", new Set());
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`;
268
+ this.indentationLevel++;
269
+ code += `${this.indent()}*${varName} = ${varName}_val;\n`;
270
+ code += this.visit(forOf.statement, {
271
+ ...context,
272
+ isFunctionBody: false,
273
+ });
274
+ this.indentationLevel--;
275
+ code += `${this.indent()}}}\n`;
276
+ this.indentationLevel--; // Exit the scope for the for-of loop
277
+ return code;
278
+ }
279
+ export function visitIfStatement(node, context) {
280
+ const ifStmt = node;
281
+ const condition = this.visit(ifStmt.expression, context);
282
+ const thenStmt = this.visit(ifStmt.thenStatement, {
283
+ ...context,
284
+ isFunctionBody: false,
285
+ });
286
+ let elseStmt = "";
287
+ if (ifStmt.elseStatement) {
288
+ elseStmt = " else " +
289
+ this.visit(ifStmt.elseStatement, {
290
+ ...context,
291
+ isFunctionBody: false,
292
+ });
293
+ }
294
+ return `${this.indent()}if ((${condition}).is_truthy()) ${thenStmt}${elseStmt}`;
295
+ }
296
+ export function visitExpressionStatement(node, context) {
297
+ return (this.indent() +
298
+ this.visit(node.expression, context) +
299
+ ";\n");
300
+ }
301
+ export function visitThrowStatement(node, context) {
302
+ const throwStmt = node;
303
+ const expr = this.visit(throwStmt.expression, context);
304
+ return `${this.indent()}throw jspp::RuntimeError(${expr});
305
+ `;
306
+ }
307
+ export function visitTryStatement(node, context) {
308
+ const tryStmt = node;
309
+ if (tryStmt.finallyBlock) {
310
+ const declaredSymbols = new Set();
311
+ this.getDeclaredSymbols(tryStmt.tryBlock).forEach((s) => declaredSymbols.add(s));
312
+ if (tryStmt.catchClause) {
313
+ this.getDeclaredSymbols(tryStmt.catchClause).forEach((s) => declaredSymbols.add(s));
314
+ }
315
+ this.getDeclaredSymbols(tryStmt.finallyBlock).forEach((s) => declaredSymbols.add(s));
316
+ const finallyLambdaName = this.generateUniqueName("__finally_", declaredSymbols);
317
+ const resultVarName = this.generateUniqueName("__try_result_", declaredSymbols);
318
+ const hasReturnedFlagName = this.generateUniqueName("__try_has_returned_", declaredSymbols);
319
+ let code = `${this.indent()}{\n`;
320
+ this.indentationLevel++;
321
+ code += `${this.indent()}jspp::AnyValue ${resultVarName};\n`;
322
+ code += `${this.indent()}bool ${hasReturnedFlagName} = false;\n`;
323
+ const finallyBlockCode = this.visit(tryStmt.finallyBlock, {
324
+ ...context,
325
+ isFunctionBody: false,
326
+ });
327
+ code +=
328
+ `${this.indent()}auto ${finallyLambdaName} = [=]() ${finallyBlockCode.trim()};\n`;
329
+ code += `${this.indent()}try {\n`;
330
+ this.indentationLevel++;
331
+ code +=
332
+ `${this.indent()}${resultVarName} = ([=, &${hasReturnedFlagName}]() -> jspp::AnyValue {\n`;
333
+ this.indentationLevel++;
334
+ const innerContext = {
335
+ ...context,
336
+ isFunctionBody: false,
337
+ isInsideTryCatchLambda: true,
338
+ hasReturnedFlag: hasReturnedFlagName,
339
+ };
340
+ code += `${this.indent()}try {\n`;
341
+ this.indentationLevel++;
342
+ code += this.visit(tryStmt.tryBlock, innerContext);
343
+ this.indentationLevel--;
344
+ code += `${this.indent()}}\n`;
345
+ if (tryStmt.catchClause) {
346
+ const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText());
347
+ const catchContext = { ...innerContext, exceptionName };
348
+ code +=
349
+ `${this.indent()}catch (const std::exception& ${exceptionName}) {\n`;
350
+ this.indentationLevel++;
351
+ code += this.visit(tryStmt.catchClause.block, catchContext);
352
+ this.indentationLevel--;
353
+ code += `${this.indent()}}\n`;
354
+ }
355
+ else {
356
+ code += `${this.indent()}catch (...) { throw; }\n`;
357
+ }
358
+ code += `${this.indent()}return jspp::AnyValue::make_undefined();\n`;
359
+ this.indentationLevel--;
360
+ code += `${this.indent()}})();\n`;
361
+ this.indentationLevel--;
362
+ code += `${this.indent()}} catch (...) {\n`;
363
+ this.indentationLevel++;
364
+ code += `${this.indent()}${finallyLambdaName}();\n`;
365
+ code += `${this.indent()}throw;\n`;
366
+ this.indentationLevel--;
367
+ code += `${this.indent()}}\n`;
368
+ code += `${this.indent()}${finallyLambdaName}();\n`;
369
+ code += `${this.indent()}if (${hasReturnedFlagName}) {\n`;
370
+ this.indentationLevel++;
371
+ code += `${this.indent()}return ${resultVarName};\n`;
372
+ this.indentationLevel--;
373
+ code += `${this.indent()}}\n`;
374
+ this.indentationLevel--;
375
+ code += `${this.indent()}}\n`;
376
+ return code;
377
+ }
378
+ else {
379
+ const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText());
380
+ const newContext = {
381
+ ...context,
382
+ isFunctionBody: false,
383
+ exceptionName,
384
+ };
385
+ let code = `${this.indent()}try `;
386
+ code += this.visit(tryStmt.tryBlock, newContext);
387
+ if (tryStmt.catchClause) {
388
+ code += ` catch (const std::exception& ${exceptionName}) `;
389
+ code += this.visit(tryStmt.catchClause, newContext);
390
+ }
391
+ return code;
392
+ }
393
+ }
394
+ export function visitCatchClause(node, context) {
395
+ const catchClause = node;
396
+ const exceptionName = context.exceptionName;
397
+ if (!exceptionName) {
398
+ // This should not happen if it's coming from a TryStatement
399
+ throw new Error("Compiler bug: exceptionName not found in context for CatchClause");
400
+ }
401
+ if (catchClause.variableDeclaration) {
402
+ const varName = catchClause.variableDeclaration.name.getText();
403
+ let code = `{\n`;
404
+ this.indentationLevel++;
405
+ code += `${this.indent()}{\n`;
406
+ this.indentationLevel++;
407
+ // Always create the JS exception variable.
408
+ code +=
409
+ `${this.indent()}auto ${varName} = std::make_shared<jspp::AnyValue>(jspp::RuntimeError::error_to_value(${exceptionName}));\n`;
410
+ // Shadow the C++ exception variable *only if* the names don't clash.
411
+ if (varName !== exceptionName) {
412
+ code +=
413
+ `${this.indent()}auto ${exceptionName} = std::make_shared<jspp::AnyValue>(jspp::AnyValue::make_undefined());\n`;
414
+ }
415
+ code += this.visit(catchClause.block, context);
416
+ this.indentationLevel--;
417
+ code += `${this.indent()}}\n`;
418
+ this.indentationLevel--;
419
+ code += `${this.indent()}}\n`;
420
+ return code;
421
+ }
422
+ else {
423
+ // No variable in the catch clause, e.g., `catch { ... }`
424
+ let code = `{\n`; // Alway create block scope
425
+ code += this.visit(catchClause.block, context);
426
+ code += `${this.indent()}}\n`;
427
+ return code;
428
+ }
429
+ }
430
+ export function visitReturnStatement(node, context) {
431
+ if (context.isMainContext) {
432
+ return `${this.indent()}jspp::RuntimeError::throw_invalid_return_statement_error();\n`;
433
+ }
434
+ const returnStmt = node;
435
+ if (context.isInsideTryCatchLambda && context.hasReturnedFlag) {
436
+ let returnCode = `${this.indent()}${context.hasReturnedFlag} = true;\n`;
437
+ if (returnStmt.expression) {
438
+ const expr = returnStmt.expression;
439
+ const exprText = this.visit(expr, context);
440
+ if (ts.isIdentifier(expr)) {
441
+ const scope = this.getScopeForNode(expr);
442
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
443
+ if (!typeInfo) {
444
+ returnCode +=
445
+ `${this.indent()}jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)});\n`; // THROWS, not returns
446
+ }
447
+ if (typeInfo &&
448
+ !typeInfo.isParameter &&
449
+ !typeInfo.isBuiltin) {
450
+ returnCode +=
451
+ `${this.indent()}return jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)});\n`;
452
+ }
453
+ else {
454
+ returnCode += `${this.indent()}return ${exprText};\n`;
455
+ }
456
+ }
457
+ else {
458
+ returnCode += `${this.indent()}return ${exprText};\n`;
459
+ }
460
+ }
461
+ else {
462
+ returnCode +=
463
+ `${this.indent()}return jspp::AnyValue::make_undefined();\n`;
464
+ }
465
+ return returnCode;
466
+ }
467
+ if (returnStmt.expression) {
468
+ const expr = returnStmt.expression;
469
+ const exprText = this.visit(expr, context);
470
+ if (ts.isIdentifier(expr)) {
471
+ const scope = this.getScopeForNode(expr);
472
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
473
+ if (!typeInfo) {
474
+ return `${this.indent()}jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)});\n`; // THROWS, not returns
475
+ }
476
+ if (typeInfo &&
477
+ !typeInfo.isParameter &&
478
+ !typeInfo.isBuiltin) {
479
+ return `${this.indent()}return jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)});\n`;
480
+ }
481
+ }
482
+ return `${this.indent()}return ${exprText};\n`;
483
+ }
484
+ return `${this.indent()}return jspp::AnyValue::make_undefined();\n`;
485
+ }
@@ -0,0 +1,86 @@
1
+ import ts from "typescript";
2
+ import { CodeGenerator } from "./";
3
+ import { visitVariableDeclaration, visitVariableDeclarationList, } from "./declaration-handlers";
4
+ import { visitArrayLiteralExpression, visitBinaryExpression, visitCallExpression, visitElementAccessExpression, visitObjectLiteralExpression, visitParenthesizedExpression, visitPostfixUnaryExpression, visitPrefixUnaryExpression, visitPropertyAccessExpression, visitTemplateExpression, visitVoidExpression, } from "./expression-handlers";
5
+ import { visitArrowFunction, visitFunctionDeclaration, visitFunctionExpression, } from "./function-handlers";
6
+ import { visitFalseKeyword, visitIdentifier, visitNoSubstitutionTemplateLiteral, visitNullKeyword, visitNumericLiteral, visitStringLiteral, visitTrueKeyword, visitUndefinedKeyword, } from "./literal-handlers";
7
+ import { visitBlock, visitCatchClause, visitExpressionStatement, visitForInStatement, visitForOfStatement, visitForStatement, visitIfStatement, visitReturnStatement, visitSourceFile, visitThrowStatement, visitTryStatement, visitVariableStatement, } from "./statement-handlers";
8
+ export function visit(node, context) {
9
+ if (ts.isFunctionDeclaration(node)) {
10
+ return visitFunctionDeclaration.call(this, node, context);
11
+ }
12
+ switch (node.kind) {
13
+ case ts.SyntaxKind.ArrowFunction:
14
+ return visitArrowFunction.call(this, node, context);
15
+ case ts.SyntaxKind.FunctionExpression:
16
+ return visitFunctionExpression.call(this, node, context);
17
+ case ts.SyntaxKind.SourceFile:
18
+ return visitSourceFile.call(this, node, context);
19
+ case ts.SyntaxKind.Block:
20
+ return visitBlock.call(this, node, context);
21
+ case ts.SyntaxKind.VariableStatement:
22
+ return visitVariableStatement.call(this, node, context);
23
+ case ts.SyntaxKind.VariableDeclarationList:
24
+ return visitVariableDeclarationList.call(this, node, context);
25
+ case ts.SyntaxKind.VariableDeclaration:
26
+ return visitVariableDeclaration.call(this, node, context);
27
+ case ts.SyntaxKind.ObjectLiteralExpression:
28
+ return visitObjectLiteralExpression.call(this, node, context);
29
+ case ts.SyntaxKind.ArrayLiteralExpression:
30
+ return visitArrayLiteralExpression.call(this, node, context);
31
+ case ts.SyntaxKind.ForStatement:
32
+ return visitForStatement.call(this, node, context);
33
+ case ts.SyntaxKind.ForInStatement:
34
+ return visitForInStatement.call(this, node, context);
35
+ case ts.SyntaxKind.ForOfStatement:
36
+ return visitForOfStatement.call(this, node, context);
37
+ case ts.SyntaxKind.IfStatement:
38
+ return visitIfStatement.call(this, node, context);
39
+ case ts.SyntaxKind.PrefixUnaryExpression:
40
+ return visitPrefixUnaryExpression.call(this, node, context);
41
+ case ts.SyntaxKind.PostfixUnaryExpression:
42
+ return visitPostfixUnaryExpression.call(this, node, context);
43
+ case ts.SyntaxKind.ParenthesizedExpression:
44
+ return visitParenthesizedExpression.call(this, node, context);
45
+ case ts.SyntaxKind.PropertyAccessExpression:
46
+ return visitPropertyAccessExpression.call(this, node, context);
47
+ case ts.SyntaxKind.ElementAccessExpression:
48
+ return visitElementAccessExpression.call(this, node, context);
49
+ case ts.SyntaxKind.ExpressionStatement:
50
+ return visitExpressionStatement.call(this, node, context);
51
+ case ts.SyntaxKind.BinaryExpression:
52
+ return visitBinaryExpression.call(this, node, context);
53
+ case ts.SyntaxKind.ThrowStatement:
54
+ return visitThrowStatement.call(this, node, context);
55
+ case ts.SyntaxKind.TryStatement:
56
+ return visitTryStatement.call(this, node, context);
57
+ case ts.SyntaxKind.CatchClause:
58
+ return visitCatchClause.call(this, node, context);
59
+ case ts.SyntaxKind.CallExpression:
60
+ return visitCallExpression.call(this, node, context);
61
+ case ts.SyntaxKind.ReturnStatement:
62
+ return visitReturnStatement.call(this, node, context);
63
+ case ts.SyntaxKind.Identifier:
64
+ return visitIdentifier.call(this, node);
65
+ case ts.SyntaxKind.NumericLiteral:
66
+ return visitNumericLiteral.call(this, node);
67
+ case ts.SyntaxKind.StringLiteral:
68
+ return visitStringLiteral.call(this, node);
69
+ case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
70
+ return visitNoSubstitutionTemplateLiteral.call(this, node);
71
+ case ts.SyntaxKind.TemplateExpression:
72
+ return visitTemplateExpression.call(this, node, context);
73
+ case ts.SyntaxKind.TrueKeyword:
74
+ return visitTrueKeyword.call(this);
75
+ case ts.SyntaxKind.FalseKeyword:
76
+ return visitFalseKeyword.call(this);
77
+ case ts.SyntaxKind.VoidExpression:
78
+ return visitVoidExpression.call(this, node, context);
79
+ case ts.SyntaxKind.UndefinedKeyword:
80
+ return visitUndefinedKeyword.call(this);
81
+ case ts.SyntaxKind.NullKeyword:
82
+ return visitNullKeyword.call(this);
83
+ default:
84
+ return `/* Unhandled node: ${ts.SyntaxKind[node.kind]} */`;
85
+ }
86
+ }
@@ -0,0 +1,6 @@
1
+ import * as ts from "typescript";
2
+ export class Parser {
3
+ parse(sourceCode) {
4
+ return ts.createSourceFile("temp.js", sourceCode, ts.ScriptTarget.Latest, true);
5
+ }
6
+ }
@@ -0,0 +1,19 @@
1
+ import * as ts from "typescript";
2
+ export class Traverser {
3
+ traverse(node, visitor) {
4
+ this.traverseNode(node, null, visitor);
5
+ }
6
+ traverseNode(node, parent, visitor) {
7
+ const nodeKind = ts.SyntaxKind[node.kind];
8
+ const visitorActions = visitor[nodeKind];
9
+ if (visitorActions && visitorActions.enter) {
10
+ visitorActions.enter(node, parent);
11
+ }
12
+ ts.forEachChild(node, (childNode) => {
13
+ this.traverseNode(childNode, node, visitor);
14
+ });
15
+ if (visitorActions && visitorActions.exit) {
16
+ visitorActions.exit(node, parent);
17
+ }
18
+ }
19
+ }
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ import path from "path";
2
+ import { TypeAnalyzer } from "./analysis/typeAnalyzer";
3
+ import { CodeGenerator } from "./core/codegen";
4
+ import { Parser } from "./core/parser";
5
+ export class Interpreter {
6
+ parser = new Parser();
7
+ analyzer = new TypeAnalyzer();
8
+ generator = new CodeGenerator();
9
+ interpret(jsCode) {
10
+ const ast = this.parser.parse(jsCode);
11
+ this.analyzer.analyze(ast);
12
+ const cppCode = this.generator.generate(ast, this.analyzer);
13
+ const preludePath = path.resolve(__dirname, "..", "src", "prelude");
14
+ return { cppCode, preludePath };
15
+ }
16
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@ugo-studio/jspp",
3
+ "version": "0.1.0",
4
+ "description": "A modern, experimental transpiler that converts JavaScript code into high-performance, standard C++23.",
5
+ "main": "dist/index.js",
6
+ "module": "src/index.ts",
7
+ "type": "module",
8
+ "bin": {
9
+ "jspp": "dist/cli.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "src/prelude",
14
+ "prelude-build"
15
+ ],
16
+ "scripts": {
17
+ "dev": "bun run src/cli.ts",
18
+ "typecheck": "tsc --noEmit",
19
+ "precompile": "bun run scripts/precompile-headers.ts",
20
+ "test": "bun run precompile && bun test",
21
+ "build": "tsc",
22
+ "prepack": "bun run build",
23
+ "publish-package": "npm publish --access=public"
24
+ },
25
+ "devDependencies": {
26
+ "@types/bun": "latest"
27
+ },
28
+ "peerDependencies": {
29
+ "typescript": "^5"
30
+ },
31
+ "author": "Ugochukwu David",
32
+ "license": "MIT",
33
+ "homepage": "https://github.com/ugo-studio/jspp/",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/ugo-studio/jspp.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/ugo-studio/jspp/issues"
40
+ }
41
+ }