@ugo-studio/jspp 0.2.3 → 0.2.5

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,7 +1,7 @@
1
1
  import ts from "typescript";
2
- import { DeclaredSymbols } from "../../ast/symbols";
3
- import { CodeGenerator } from "./";
4
- import { collectBlockScopedDeclarations, collectFunctionScopedDeclarations, } from "./helpers";
2
+ import { DeclaredSymbols } from "../../ast/symbols.js";
3
+ import { collectBlockScopedDeclarations, collectFunctionScopedDeclarations, } from "./helpers.js";
4
+ import { CodeGenerator } from "./index.js";
5
5
  export function visitSourceFile(node, context) {
6
6
  const sourceFile = node;
7
7
  let code = "";
@@ -13,22 +13,22 @@ export function visitSourceFile(node, context) {
13
13
  const hoistedSymbols = new DeclaredSymbols();
14
14
  // Hoist function declarations
15
15
  funcDecls.forEach((func) => {
16
- code += this.hoistDeclaration(func, hoistedSymbols);
16
+ code += this.hoistDeclaration(func, hoistedSymbols, node);
17
17
  });
18
18
  // Hoist class declarations
19
19
  classDecls.forEach((cls) => {
20
- code += this.hoistDeclaration(cls, hoistedSymbols);
20
+ code += this.hoistDeclaration(cls, hoistedSymbols, node);
21
21
  });
22
22
  // Hoist variable declarations (var)
23
23
  varDecls.forEach((decl) => {
24
- code += this.hoistDeclaration(decl, hoistedSymbols);
24
+ code += this.hoistDeclaration(decl, hoistedSymbols, node);
25
25
  });
26
26
  // Hoist top-level let/const
27
27
  topLevelLetConst.forEach((decl) => {
28
- code += this.hoistDeclaration(decl, hoistedSymbols);
28
+ code += this.hoistDeclaration(decl, hoistedSymbols, node);
29
29
  });
30
30
  // Compile symbols for other statements (excluding function)
31
- const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
31
+ const globalScopeSymbols = this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols);
32
32
  const localScopeSymbols = new DeclaredSymbols(hoistedSymbols); // hoistedSymbols becomes new local
33
33
  // 2. Assign all hoisted functions first
34
34
  const contextForFunctions = {
@@ -37,13 +37,29 @@ export function visitSourceFile(node, context) {
37
37
  };
38
38
  funcDecls.forEach((stmt) => {
39
39
  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`;
40
+ if (!funcName)
41
+ return;
42
+ const symbol = hoistedSymbols.get(funcName);
43
+ if (!symbol)
44
+ return;
45
+ // Mark before further visits
46
+ this.markSymbolAsInitialized(funcName, contextForFunctions.globalScopeSymbols, contextForFunctions.localScopeSymbols);
47
+ this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
48
+ // Generate and update self name
49
+ const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
50
+ hoistedSymbols.update(funcName, { func: { nativeName } });
51
+ // Generate lambda
52
+ const lambda = this.generateLambda(stmt, contextForFunctions, {
53
+ isAssignment: true,
54
+ generateOnlyLambda: true,
55
+ nativeName,
56
+ });
57
+ code += `${this.indent()}auto ${nativeName} = ${lambda};\n`;
58
+ // Generate AnyValue wrapper
59
+ if (this.isFunctionUsedAsValue(stmt, node) ||
60
+ this.isFunctionUsedBeforeDeclaration(funcName, node)) {
61
+ const fullExpression = this.generateFullLambdaExpression(stmt, contextForFunctions, nativeName, { isAssignment: true, noTypeSignature: true });
62
+ code += `${this.indent()}*${funcName} = ${fullExpression};\n`;
47
63
  }
48
64
  });
49
65
  // 3. Process other statements
@@ -57,7 +73,7 @@ export function visitSourceFile(node, context) {
57
73
  0;
58
74
  const contextForVisit = {
59
75
  ...context,
60
- topLevelScopeSymbols,
76
+ globalScopeSymbols,
61
77
  localScopeSymbols,
62
78
  isAssignmentOnly: !isLetOrConst,
63
79
  };
@@ -70,7 +86,7 @@ export function visitSourceFile(node, context) {
70
86
  code += this.visit(stmt, {
71
87
  ...context,
72
88
  isFunctionBody: false,
73
- topLevelScopeSymbols,
89
+ globalScopeSymbols,
74
90
  localScopeSymbols,
75
91
  });
76
92
  }
@@ -78,6 +94,7 @@ export function visitSourceFile(node, context) {
78
94
  return code;
79
95
  }
80
96
  export function visitBlock(node, context) {
97
+ context.currentScopeNode = node; // Update scope node
81
98
  let code = `${this.indent()}{\n`;
82
99
  this.indentationLevel++;
83
100
  const block = node;
@@ -88,18 +105,18 @@ export function visitBlock(node, context) {
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);
96
113
  });
97
114
  // Hoist variable declarations (let/const only)
98
115
  blockScopedDecls.forEach((decl) => {
99
- code += this.hoistDeclaration(decl, hoistedSymbols);
116
+ code += this.hoistDeclaration(decl, hoistedSymbols, node);
100
117
  });
101
118
  // Compile symbols for other statements (excluding function)
102
- const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
119
+ const globalScopeSymbols = this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols);
103
120
  const localScopeSymbols = new DeclaredSymbols(hoistedSymbols); // hoistedSymbols becomes new local
104
121
  // 2. Assign all hoisted functions first
105
122
  const contextForFunctions = {
@@ -108,13 +125,29 @@ export function visitBlock(node, context) {
108
125
  };
109
126
  funcDecls.forEach((stmt) => {
110
127
  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`;
128
+ if (!funcName)
129
+ return;
130
+ const symbol = hoistedSymbols.get(funcName);
131
+ if (!symbol)
132
+ return;
133
+ // Mark before further visits
134
+ this.markSymbolAsInitialized(funcName, contextForFunctions.globalScopeSymbols, contextForFunctions.localScopeSymbols);
135
+ this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
136
+ // Generate and update self name
137
+ const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
138
+ hoistedSymbols.update(funcName, { func: { nativeName } });
139
+ // Generate lambda
140
+ const lambda = this.generateLambda(stmt, contextForFunctions, {
141
+ isAssignment: true,
142
+ generateOnlyLambda: true,
143
+ nativeName,
144
+ });
145
+ code += `${this.indent()}auto ${nativeName} = ${lambda};\n`;
146
+ // Generate AnyValue wrapper
147
+ if (this.isFunctionUsedAsValue(stmt, node) ||
148
+ this.isFunctionUsedBeforeDeclaration(funcName, node)) {
149
+ const fullExpression = this.generateFullLambdaExpression(stmt, contextForFunctions, nativeName, { isAssignment: true, noTypeSignature: true });
150
+ code += `${this.indent()}*${funcName} = ${fullExpression};\n`;
118
151
  }
119
152
  });
120
153
  // 3. Process other statements
@@ -128,7 +161,7 @@ export function visitBlock(node, context) {
128
161
  0;
129
162
  const contextForVisit = {
130
163
  ...context,
131
- topLevelScopeSymbols,
164
+ globalScopeSymbols,
132
165
  localScopeSymbols,
133
166
  isAssignmentOnly: !isLetOrConst,
134
167
  };
@@ -141,7 +174,7 @@ export function visitBlock(node, context) {
141
174
  code += this.visit(stmt, {
142
175
  ...context,
143
176
  isFunctionBody: false,
144
- topLevelScopeSymbols,
177
+ globalScopeSymbols,
145
178
  localScopeSymbols,
146
179
  });
147
180
  }
@@ -217,7 +250,17 @@ export function visitIfStatement(node, context) {
217
250
  isFunctionBody: false,
218
251
  });
219
252
  }
220
- return `${this.indent()}if (is_truthy(${condition})) ${thenStmt}${elseStmt}`;
253
+ if (ts.isBinaryExpression(ifStmt.expression)) {
254
+ const binExpr = ifStmt.expression;
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
+ }
262
+ }
263
+ return `${this.indent()}if (jspp::is_truthy(${condition})) ${thenStmt}${elseStmt}`;
221
264
  }
222
265
  export function visitExpressionStatement(node, context) {
223
266
  return (this.indent() +
@@ -234,7 +277,7 @@ export function visitTryStatement(node, context) {
234
277
  const tryStmt = node;
235
278
  if (context.isInsideAsyncFunction) {
236
279
  if (tryStmt.finallyBlock) {
237
- const declaredSymbols = new Set(context.topLevelScopeSymbols.toSet());
280
+ const declaredSymbols = new Set(context.globalScopeSymbols.names);
238
281
  this.getDeclaredSymbols(tryStmt.tryBlock).forEach((s) => declaredSymbols.add(s));
239
282
  if (tryStmt.catchClause) {
240
283
  this.getDeclaredSymbols(tryStmt.catchClause).forEach((s) => declaredSymbols.add(s));
@@ -339,7 +382,7 @@ export function visitTryStatement(node, context) {
339
382
  return code;
340
383
  }
341
384
  else {
342
- const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.topLevelScopeSymbols, context.localScopeSymbols);
385
+ const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.globalScopeSymbols, context.localScopeSymbols);
343
386
  const newContext = {
344
387
  ...context,
345
388
  isFunctionBody: false,
@@ -405,7 +448,7 @@ export function visitTryStatement(node, context) {
405
448
  }
406
449
  }
407
450
  if (tryStmt.finallyBlock) {
408
- const declaredSymbols = new Set(context.topLevelScopeSymbols.toSet());
451
+ const declaredSymbols = new Set(context.globalScopeSymbols.names);
409
452
  this.getDeclaredSymbols(tryStmt.tryBlock).forEach((s) => declaredSymbols.add(s));
410
453
  if (tryStmt.catchClause) {
411
454
  this.getDeclaredSymbols(tryStmt.catchClause).forEach((s) => declaredSymbols.add(s));
@@ -440,7 +483,7 @@ export function visitTryStatement(node, context) {
440
483
  this.indentationLevel--;
441
484
  code += `${this.indent()}}\n`;
442
485
  if (tryStmt.catchClause) {
443
- const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText(), context.topLevelScopeSymbols, context.localScopeSymbols);
486
+ const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText(), context.globalScopeSymbols, context.localScopeSymbols);
444
487
  const catchContext = { ...innerContext, exceptionName };
445
488
  code +=
446
489
  `${this.indent()}catch (const std::exception& ${exceptionName}) {\n`;
@@ -476,7 +519,7 @@ export function visitTryStatement(node, context) {
476
519
  return code;
477
520
  }
478
521
  else {
479
- const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.topLevelScopeSymbols, context.localScopeSymbols);
522
+ const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.globalScopeSymbols, context.localScopeSymbols);
480
523
  const newContext = {
481
524
  ...context,
482
525
  isFunctionBody: false,
@@ -540,7 +583,7 @@ export function visitYieldExpression(node, context) {
540
583
  let code = `${this.indent()}{\n`;
541
584
  this.indentationLevel++;
542
585
  const declaredSymbols = this.getDeclaredSymbols(expr);
543
- context.topLevelScopeSymbols.toSet().forEach((s) => declaredSymbols.add(s));
586
+ context.globalScopeSymbols.names.forEach((s) => declaredSymbols.add(s));
544
587
  const iterableRef = this.generateUniqueName("__iter_ref", declaredSymbols);
545
588
  const iterator = this.generateUniqueName("__iter", declaredSymbols);
546
589
  const nextFunc = this.generateUniqueName("__next_func", declaredSymbols);
@@ -566,7 +609,7 @@ export function visitYieldExpression(node, context) {
566
609
  `${this.indent()}auto ${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
567
610
  }
568
611
  code +=
569
- `${this.indent()}while (!is_truthy(${nextRes}.get_own_property("done"))) {\n`;
612
+ `${this.indent()}while (!jspp::is_truthy(${nextRes}.get_own_property("done"))) {\n`;
570
613
  this.indentationLevel++;
571
614
  if (context.isInsideAsyncFunction) {
572
615
  code +=
@@ -1,12 +1,12 @@
1
1
  import ts from "typescript";
2
- import { CodeGenerator } from "./";
3
- import { visitClassDeclaration } from "./class-handlers";
4
- import { visitCaseClause, visitDefaultClause, visitDoStatement, visitForInStatement, visitForOfStatement, visitForStatement, visitSwitchStatement, visitWhileStatement, } from "./control-flow-handlers";
5
- import { visitVariableDeclaration, visitVariableDeclarationList, } from "./declaration-handlers";
6
- import { visitArrayLiteralExpression, visitAwaitExpression, visitBinaryExpression, visitCallExpression, visitConditionalExpression, visitDeleteExpression, visitElementAccessExpression, visitNewExpression, visitObjectLiteralExpression, visitParenthesizedExpression, visitPostfixUnaryExpression, visitPrefixUnaryExpression, visitPropertyAccessExpression, visitTemplateExpression, visitTypeOfExpression, visitVoidExpression, } from "./expression-handlers";
7
- import { visitArrowFunction, visitFunctionDeclaration, visitFunctionExpression, } from "./function-handlers";
8
- import { visitFalseKeyword, visitIdentifier, visitNoSubstitutionTemplateLiteral, visitNullKeyword, visitNumericLiteral, visitStringLiteral, visitThisKeyword, visitTrueKeyword, visitUndefinedKeyword, } from "./literal-handlers";
9
- import { visitBlock, visitBreakStatement, visitCatchClause, visitContinueStatement, visitExpressionStatement, visitIfStatement, visitLabeledStatement, visitReturnStatement, visitSourceFile, visitThrowStatement, visitTryStatement, visitVariableStatement, visitYieldExpression, } from "./statement-handlers";
2
+ import { visitClassDeclaration } from "./class-handlers.js";
3
+ import { visitCaseClause, visitDefaultClause, visitDoStatement, visitForInStatement, visitForOfStatement, visitForStatement, visitSwitchStatement, visitWhileStatement, } from "./control-flow-handlers.js";
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";
6
+ import { visitArrowFunction, visitFunctionDeclaration, visitFunctionExpression, } from "./function-handlers.js";
7
+ import { CodeGenerator } from "./index.js";
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";
10
10
  export function visit(node, context) {
11
11
  if (ts.isFunctionDeclaration(node)) {
12
12
  return visitFunctionDeclaration.call(this, node, context);
@@ -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
@@ -1,16 +1,16 @@
1
1
  import path from "path";
2
- import { TypeAnalyzer } from "./analysis/typeAnalyzer";
3
- import { CodeGenerator } from "./core/codegen";
4
- import { Parser } from "./core/parser";
2
+ import { TypeAnalyzer } from "./analysis/typeAnalyzer.js";
3
+ import { CodeGenerator } from "./core/codegen/index.js";
4
+ import { Parser } from "./core/parser.js";
5
5
  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(jsCode, fileName) {
10
+ const ast = this.parser.parse(jsCode, fileName);
11
11
  this.analyzer.analyze(ast);
12
12
  const cppCode = this.generator.generate(ast, this.analyzer);
13
- const preludePath = path.resolve(__dirname, "..", "src", "prelude");
13
+ const preludePath = path.resolve(import.meta.dirname, "..", "src", "prelude");
14
14
  return { cppCode, preludePath };
15
15
  }
16
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ugo-studio/jspp",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
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",
@@ -231,7 +231,7 @@ namespace jspp
231
231
  {
232
232
  throw jspp::Exception::make_exception("Function has non-object prototype in instanceof check", "TypeError");
233
233
  }
234
-
234
+
235
235
  AnyValue current = lhs;
236
236
 
237
237
  while (true)
@@ -253,7 +253,7 @@ namespace jspp
253
253
  {
254
254
  break;
255
255
  }
256
-
256
+
257
257
  if (proto.is_null() || proto.is_undefined())
258
258
  break;
259
259
  if (is_strictly_equal_to_primitive(proto, targetProto))
@@ -27,7 +27,14 @@ namespace jspp
27
27
  if (val.is_symbol())
28
28
  return Color::BLUE + val.to_std_string() + Color::RESET;
29
29
  if (val.is_accessor_descriptor())
30
+ {
31
+ auto desc = val.as_accessor_descriptor();
32
+ if (desc->get.has_value() && !desc->set.has_value())
33
+ return Color::BLUE + std::string("[Getter]") + Color::RESET;
34
+ if (!desc->get.has_value() && desc->set.has_value())
35
+ return Color::BLUE + std::string("[Setter]") + Color::RESET;
30
36
  return Color::BLUE + std::string("[Getter/Setter]") + Color::RESET;
37
+ }
31
38
 
32
39
  if (val.is_string())
33
40
  {
@@ -93,16 +93,14 @@ namespace jspp
93
93
  {
94
94
  switch (val.get_type())
95
95
  {
96
- case JsType::Boolean:
97
- return val.as_boolean();
98
96
  case JsType::Number:
99
97
  return is_truthy(val.as_double());
100
98
  case JsType::String:
101
- return !val.as_string()->value.empty();
102
- case JsType::Undefined:
103
- return false;
99
+ return is_truthy(val.as_string()->value);
100
+ case JsType::Boolean:
101
+ return val.as_boolean();
104
102
  case JsType::Null:
105
- return false;
103
+ case JsType::Undefined:
106
104
  case JsType::Uninitialized:
107
105
  return false;
108
106
  default:
@@ -20,6 +20,24 @@ namespace jspp
20
20
  key);
21
21
  }
22
22
 
23
+ // --- call() method ---
24
+ if (key == "call")
25
+ {
26
+ return AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
27
+ {
28
+ AnyValue thisArg = Constants::UNDEFINED;
29
+ std::span<const AnyValue> fnArgs;
30
+
31
+ if (!args.empty())
32
+ {
33
+ thisArg = args[0];
34
+ fnArgs = args.subspan(1);
35
+ }
36
+
37
+ return thisVal.call(thisArg, fnArgs);
38
+ }, key);
39
+ }
40
+
23
41
  return std::nullopt;
24
42
  }
25
43
  }