@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.
@@ -1,6 +1,8 @@
1
1
  import ts from "typescript";
2
2
  import { DeclaredSymbols } from "../../ast/symbols.js";
3
- import { collectBlockScopedDeclarations, collectFunctionScopedDeclarations, } from "./helpers.js";
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 topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
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
- this.markSymbolAsChecked(funcName, contextForFunctions.topLevelScopeSymbols, contextForFunctions.localScopeSymbols);
42
- this.markSymbolAsChecked(funcName, topLevelScopeSymbols, localScopeSymbols);
43
- const lambda = this.generateLambda(stmt, contextForFunctions, {
44
- isAssignment: true,
45
- });
46
- code += `${this.indent()}*${funcName} = ${lambda};\n`;
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
- const isLetOrConst = (stmt.declarationList.flags &
56
- (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
57
- 0;
58
- const contextForVisit = {
78
+ code += this.visit(stmt, {
59
79
  ...context,
60
- topLevelScopeSymbols,
80
+ globalScopeSymbols,
61
81
  localScopeSymbols,
62
- isAssignmentOnly: !isLetOrConst,
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
- topLevelScopeSymbols,
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 topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
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
- this.markSymbolAsChecked(funcName, contextForFunctions.topLevelScopeSymbols, contextForFunctions.localScopeSymbols);
113
- this.markSymbolAsChecked(funcName, topLevelScopeSymbols, localScopeSymbols);
114
- const lambda = this.generateLambda(stmt, contextForFunctions, {
115
- isAssignment: true,
116
- });
117
- code += `${this.indent()}*${funcName} = ${lambda};\n`;
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
- const isLetOrConst = (stmt.declarationList.flags &
127
- (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
128
- 0;
129
- const contextForVisit = {
163
+ code += this.visit(stmt, {
130
164
  ...context,
131
- topLevelScopeSymbols,
165
+ globalScopeSymbols,
132
166
  localScopeSymbols,
133
- isAssignmentOnly: !isLetOrConst,
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
- topLevelScopeSymbols,
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
- return (this.indent() +
161
- this.visit(node.declarationList, context) +
162
- ";\n");
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 condition = this.visit(ifStmt.expression, context);
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
- return `${this.indent()}if (is_truthy(${condition})) ${thenStmt}${elseStmt}`;
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.topLevelScopeSymbols.toSet());
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.topLevelScopeSymbols, context.localScopeSymbols);
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.topLevelScopeSymbols.toSet());
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.topLevelScopeSymbols, context.localScopeSymbols);
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.topLevelScopeSymbols, context.localScopeSymbols);
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 Error("Compiler bug: exceptionName not found in context for CatchClause");
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.topLevelScopeSymbols.toSet().forEach((s) => declaredSymbols.add(s));
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
+ }
@@ -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(jsCode) {
10
- const ast = this.parser.parse(jsCode);
9
+ interpret(code, fileName) {
10
+ const ast = this.parser.parse(code, fileName);
11
11
  this.analyzer.analyze(ast);
12
- const cppCode = this.generator.generate(ast, this.analyzer);
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.4",
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",