@ugo-studio/jspp 0.3.2 → 0.3.4
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 +2 -6
- package/dist/cli/args.js +5 -0
- package/dist/cli/compiler.js +1 -1
- package/dist/cli/index.js +4 -2
- package/dist/interpreter/analysis/typeAnalyzer.js +62 -25
- package/dist/interpreter/core/codegen/control-flow-handlers.js +34 -10
- package/dist/interpreter/core/codegen/expression-handlers.js +92 -21
- package/dist/interpreter/core/codegen/function-handlers.js +16 -11
- package/dist/interpreter/core/codegen/helpers.js +5 -1
- package/dist/interpreter/core/codegen/index.js +1 -1
- package/dist/interpreter/core/codegen/statement-handlers.js +15 -11
- package/dist/interpreter/core/traverser.js +1 -2
- package/package.json +2 -2
- package/scripts/precompile-headers.ts +9 -1
- package/src/prelude/library/array.cpp +2 -2
- package/src/prelude/library/math.cpp +8 -8
- package/src/prelude/utils/access.hpp +0 -34
- package/src/prelude/utils/assignment_operators.hpp +96 -0
- package/src/prelude/utils/operators.hpp +39 -4
- package/src/prelude/utils/operators_native.hpp +73 -62
- package/src/prelude/values/array.cpp +17 -17
- package/src/prelude/values/iterator.cpp +262 -210
- package/src/prelude/values/number.cpp +4 -4
- package/src/prelude/values/string.cpp +13 -13
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ Using these keywords as variable names will result in a `SyntaxError`.
|
|
|
61
61
|
To use JSPP as a command-line tool, install it globally via npm:
|
|
62
62
|
|
|
63
63
|
```sh
|
|
64
|
-
npm i @ugo-studio/jspp -g
|
|
64
|
+
npm i @ugo-studio/jspp@latest -g
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
## For Developers
|
|
@@ -86,7 +86,7 @@ To contribute to JSPP or run its test suite, follow these steps:
|
|
|
86
86
|
```sh
|
|
87
87
|
bun install
|
|
88
88
|
```
|
|
89
|
-
*Note: The `postinstall` script will automatically check for your C++ compiler and
|
|
89
|
+
*Note: The `postinstall` script will automatically check for your C++ compiler and setup emsdk for wasm support.*
|
|
90
90
|
|
|
91
91
|
## Usage
|
|
92
92
|
|
|
@@ -106,10 +106,6 @@ jspp my-code/test.ts
|
|
|
106
106
|
|
|
107
107
|
The transpiled C++ file and executable will be generated in the same directory as the input file and cleaned up after execution (unless `--keep-cpp` is used).
|
|
108
108
|
|
|
109
|
-
### Timing and Reports
|
|
110
|
-
|
|
111
|
-
In debug mode (default), JSPP provides a compilation time report using GCC's `-ftime-report`. This helps track the performance of the transpilation and compilation phases.
|
|
112
|
-
|
|
113
109
|
## Roadmap
|
|
114
110
|
|
|
115
111
|
This project is ambitious, and there is a long and exciting road ahead. Here is a high-level overview of the planned features and the project's current standing.
|
package/dist/cli/args.js
CHANGED
|
@@ -5,6 +5,7 @@ export function parseArgs(rawArgs) {
|
|
|
5
5
|
let jsFilePathArg = null;
|
|
6
6
|
let isRelease = false;
|
|
7
7
|
let keepCpp = false;
|
|
8
|
+
let shouldRunOutput = false;
|
|
8
9
|
let outputExePath = null;
|
|
9
10
|
let scriptArgs = [];
|
|
10
11
|
let target = "native";
|
|
@@ -22,6 +23,9 @@ export function parseArgs(rawArgs) {
|
|
|
22
23
|
else if (arg === "--keep-cpp") {
|
|
23
24
|
keepCpp = true;
|
|
24
25
|
}
|
|
26
|
+
else if (arg === "--run-output") {
|
|
27
|
+
shouldRunOutput = true;
|
|
28
|
+
}
|
|
25
29
|
else if (arg === "-t" || arg === "--target") {
|
|
26
30
|
if (i + 1 < rawArgs.length) {
|
|
27
31
|
const targetValue = rawArgs[i + 1]?.toLowerCase();
|
|
@@ -72,6 +76,7 @@ export function parseArgs(rawArgs) {
|
|
|
72
76
|
jsFilePath: path.resolve(process.cwd(), jsFilePathArg),
|
|
73
77
|
isRelease,
|
|
74
78
|
keepCpp,
|
|
79
|
+
shouldRunOutput,
|
|
75
80
|
outputExePath: outputExePath
|
|
76
81
|
? path.resolve(process.cwd(), outputExePath)
|
|
77
82
|
: null,
|
package/dist/cli/compiler.js
CHANGED
|
@@ -49,5 +49,5 @@ export async function compileCpp(cppFilePath, exeFilePath, pchDir, preludePath,
|
|
|
49
49
|
}
|
|
50
50
|
const compileEndTime = performance.now();
|
|
51
51
|
const compileTime = msToHumanReadable(compileEndTime - compileStartTime);
|
|
52
|
-
spinner.succeed(`Compiled to ${COLORS.green}${COLORS.bold}${
|
|
52
|
+
spinner.succeed(`Compiled to ${COLORS.green}${COLORS.bold}${exeFilePath}${COLORS.reset} ${COLORS.dim}[${compileTime}]${COLORS.reset}`);
|
|
53
53
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -17,7 +17,7 @@ const emsdkEnv = {
|
|
|
17
17
|
PATH: `${path.join(pkgDir, ".emsdk")}${path.delimiter}${path.join(pkgDir, ".emsdk", "upstream", "emscripten")}${path.delimiter}${process.env.PATH}`,
|
|
18
18
|
};
|
|
19
19
|
async function main() {
|
|
20
|
-
const { jsFilePath, isRelease, keepCpp, outputExePath, scriptArgs, target, } = parseArgs(process.argv.slice(2));
|
|
20
|
+
const { jsFilePath, isRelease, keepCpp, shouldRunOutput, outputExePath, scriptArgs, target, } = parseArgs(process.argv.slice(2));
|
|
21
21
|
const isWasm = target === "wasm";
|
|
22
22
|
const ext = path.extname(jsFilePath);
|
|
23
23
|
const jsFileName = path.basename(jsFilePath, ext);
|
|
@@ -80,7 +80,9 @@ async function main() {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
// 4. Execution Phase
|
|
83
|
-
|
|
83
|
+
if (shouldRunOutput) {
|
|
84
|
+
await runOutput(exeFilePath, scriptArgs, isWasm);
|
|
85
|
+
}
|
|
84
86
|
}
|
|
85
87
|
catch (error) {
|
|
86
88
|
if (error instanceof CompilerError) {
|
|
@@ -568,7 +568,7 @@ export class TypeAnalyzer {
|
|
|
568
568
|
};
|
|
569
569
|
const visitor = {
|
|
570
570
|
// Enter new scope for any block-like structure
|
|
571
|
-
Block: {
|
|
571
|
+
[ts.SyntaxKind.Block]: {
|
|
572
572
|
enter: (node, parent) => {
|
|
573
573
|
const currentFuncNode = this.functionStack[this.functionStack.length - 1] ??
|
|
574
574
|
null;
|
|
@@ -591,7 +591,7 @@ export class TypeAnalyzer {
|
|
|
591
591
|
},
|
|
592
592
|
exit: () => this.scopeManager.exitScope(),
|
|
593
593
|
},
|
|
594
|
-
ForStatement: {
|
|
594
|
+
[ts.SyntaxKind.ForStatement]: {
|
|
595
595
|
enter: (node) => {
|
|
596
596
|
this.loopDepth++;
|
|
597
597
|
const currentFuncNode = this.functionStack[this.functionStack.length - 1] ??
|
|
@@ -604,7 +604,7 @@ export class TypeAnalyzer {
|
|
|
604
604
|
this.scopeManager.exitScope();
|
|
605
605
|
},
|
|
606
606
|
},
|
|
607
|
-
ForOfStatement: {
|
|
607
|
+
[ts.SyntaxKind.ForOfStatement]: {
|
|
608
608
|
enter: (node) => {
|
|
609
609
|
this.loopDepth++;
|
|
610
610
|
const currentFuncNode = this.functionStack[this.functionStack.length - 1] ??
|
|
@@ -617,7 +617,7 @@ export class TypeAnalyzer {
|
|
|
617
617
|
this.scopeManager.exitScope();
|
|
618
618
|
},
|
|
619
619
|
},
|
|
620
|
-
ForInStatement: {
|
|
620
|
+
[ts.SyntaxKind.ForInStatement]: {
|
|
621
621
|
enter: (node) => {
|
|
622
622
|
this.loopDepth++;
|
|
623
623
|
const currentFuncNode = this.functionStack[this.functionStack.length - 1] ??
|
|
@@ -649,7 +649,7 @@ export class TypeAnalyzer {
|
|
|
649
649
|
this.scopeManager.exitScope();
|
|
650
650
|
},
|
|
651
651
|
},
|
|
652
|
-
WhileStatement: {
|
|
652
|
+
[ts.SyntaxKind.WhileStatement]: {
|
|
653
653
|
enter: (node) => {
|
|
654
654
|
this.loopDepth++;
|
|
655
655
|
const currentFuncNode = this.functionStack[this.functionStack.length - 1] ??
|
|
@@ -662,7 +662,7 @@ export class TypeAnalyzer {
|
|
|
662
662
|
this.scopeManager.exitScope();
|
|
663
663
|
},
|
|
664
664
|
},
|
|
665
|
-
DoStatement: {
|
|
665
|
+
[ts.SyntaxKind.DoStatement]: {
|
|
666
666
|
enter: (node) => {
|
|
667
667
|
this.loopDepth++;
|
|
668
668
|
const currentFuncNode = this.functionStack[this.functionStack.length - 1] ??
|
|
@@ -675,7 +675,7 @@ export class TypeAnalyzer {
|
|
|
675
675
|
this.scopeManager.exitScope();
|
|
676
676
|
},
|
|
677
677
|
},
|
|
678
|
-
SwitchStatement: {
|
|
678
|
+
[ts.SyntaxKind.SwitchStatement]: {
|
|
679
679
|
enter: (node) => {
|
|
680
680
|
this.switchDepth++;
|
|
681
681
|
const currentFuncNode = this.functionStack[this.functionStack.length - 1] ??
|
|
@@ -688,7 +688,7 @@ export class TypeAnalyzer {
|
|
|
688
688
|
this.scopeManager.exitScope();
|
|
689
689
|
},
|
|
690
690
|
},
|
|
691
|
-
LabeledStatement: {
|
|
691
|
+
[ts.SyntaxKind.LabeledStatement]: {
|
|
692
692
|
enter: (node) => {
|
|
693
693
|
this.nodeToScope.set(node, this.scopeManager.currentScope);
|
|
694
694
|
this.labelStack.push(node.label.text);
|
|
@@ -697,7 +697,7 @@ export class TypeAnalyzer {
|
|
|
697
697
|
this.labelStack.pop();
|
|
698
698
|
},
|
|
699
699
|
},
|
|
700
|
-
BreakStatement: {
|
|
700
|
+
[ts.SyntaxKind.BreakStatement]: {
|
|
701
701
|
enter: (node) => {
|
|
702
702
|
const breakNode = node;
|
|
703
703
|
if (breakNode.label) {
|
|
@@ -712,7 +712,7 @@ export class TypeAnalyzer {
|
|
|
712
712
|
}
|
|
713
713
|
},
|
|
714
714
|
},
|
|
715
|
-
ContinueStatement: {
|
|
715
|
+
[ts.SyntaxKind.ContinueStatement]: {
|
|
716
716
|
enter: (node) => {
|
|
717
717
|
const continueNode = node;
|
|
718
718
|
if (continueNode.label) {
|
|
@@ -729,7 +729,7 @@ export class TypeAnalyzer {
|
|
|
729
729
|
}
|
|
730
730
|
},
|
|
731
731
|
},
|
|
732
|
-
ArrowFunction: {
|
|
732
|
+
[ts.SyntaxKind.ArrowFunction]: {
|
|
733
733
|
enter: (node) => {
|
|
734
734
|
if (ts.isArrowFunction(node)) {
|
|
735
735
|
const funcType = {
|
|
@@ -760,7 +760,7 @@ export class TypeAnalyzer {
|
|
|
760
760
|
this.scopeManager.exitScope();
|
|
761
761
|
},
|
|
762
762
|
},
|
|
763
|
-
FunctionExpression: {
|
|
763
|
+
[ts.SyntaxKind.FunctionExpression]: {
|
|
764
764
|
enter: (node) => {
|
|
765
765
|
if (ts.isFunctionExpression(node)) {
|
|
766
766
|
const funcType = {
|
|
@@ -795,7 +795,7 @@ export class TypeAnalyzer {
|
|
|
795
795
|
this.scopeManager.exitScope();
|
|
796
796
|
},
|
|
797
797
|
},
|
|
798
|
-
FunctionDeclaration: {
|
|
798
|
+
[ts.SyntaxKind.FunctionDeclaration]: {
|
|
799
799
|
enter: (node) => {
|
|
800
800
|
if (ts.isFunctionDeclaration(node)) {
|
|
801
801
|
// Define the function in the current scope.
|
|
@@ -831,7 +831,44 @@ export class TypeAnalyzer {
|
|
|
831
831
|
this.scopeManager.exitScope();
|
|
832
832
|
},
|
|
833
833
|
},
|
|
834
|
-
|
|
834
|
+
[ts.SyntaxKind.GetAccessor]: {
|
|
835
|
+
enter: (node) => {
|
|
836
|
+
if (ts.isGetAccessorDeclaration(node)) {
|
|
837
|
+
this.scopeManager.enterScope(node);
|
|
838
|
+
this.nodeToScope.set(node, this.scopeManager.currentScope);
|
|
839
|
+
this.functionStack.push(node);
|
|
840
|
+
}
|
|
841
|
+
},
|
|
842
|
+
exit: (node) => {
|
|
843
|
+
if (ts.isGetAccessorDeclaration(node)) {
|
|
844
|
+
this.functionStack.pop();
|
|
845
|
+
}
|
|
846
|
+
this.scopeManager.exitScope();
|
|
847
|
+
},
|
|
848
|
+
},
|
|
849
|
+
[ts.SyntaxKind.SetAccessor]: {
|
|
850
|
+
enter: (node) => {
|
|
851
|
+
if (ts.isSetAccessorDeclaration(node)) {
|
|
852
|
+
this.scopeManager.enterScope(node);
|
|
853
|
+
this.nodeToScope.set(node, this.scopeManager.currentScope);
|
|
854
|
+
// Define parameters in the new scope
|
|
855
|
+
node.parameters.forEach((p) => {
|
|
856
|
+
if (p.getText() == "this") { // Catch invalid parameters
|
|
857
|
+
throw new CompilerError("Cannot use 'this' as a parameter name.", p, "SyntaxError");
|
|
858
|
+
}
|
|
859
|
+
this.defineParameter(p.name, p);
|
|
860
|
+
});
|
|
861
|
+
this.functionStack.push(node);
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
exit: (node) => {
|
|
865
|
+
if (ts.isSetAccessorDeclaration(node)) {
|
|
866
|
+
this.functionStack.pop();
|
|
867
|
+
}
|
|
868
|
+
this.scopeManager.exitScope();
|
|
869
|
+
},
|
|
870
|
+
},
|
|
871
|
+
[ts.SyntaxKind.ClassDeclaration]: {
|
|
835
872
|
enter: (node) => {
|
|
836
873
|
const classNode = node;
|
|
837
874
|
if (classNode.name) {
|
|
@@ -853,7 +890,7 @@ export class TypeAnalyzer {
|
|
|
853
890
|
this.scopeManager.exitScope();
|
|
854
891
|
},
|
|
855
892
|
},
|
|
856
|
-
EnumDeclaration: {
|
|
893
|
+
[ts.SyntaxKind.EnumDeclaration]: {
|
|
857
894
|
enter: (node) => {
|
|
858
895
|
const enumNode = node;
|
|
859
896
|
if (enumNode.name) {
|
|
@@ -872,7 +909,7 @@ export class TypeAnalyzer {
|
|
|
872
909
|
this.nodeToScope.set(node, this.scopeManager.currentScope);
|
|
873
910
|
},
|
|
874
911
|
},
|
|
875
|
-
MethodDeclaration: {
|
|
912
|
+
[ts.SyntaxKind.MethodDeclaration]: {
|
|
876
913
|
enter: (node) => {
|
|
877
914
|
if (ts.isMethodDeclaration(node)) {
|
|
878
915
|
const funcType = {
|
|
@@ -898,7 +935,7 @@ export class TypeAnalyzer {
|
|
|
898
935
|
this.scopeManager.exitScope();
|
|
899
936
|
},
|
|
900
937
|
},
|
|
901
|
-
Constructor: {
|
|
938
|
+
[ts.SyntaxKind.Constructor]: {
|
|
902
939
|
enter: (node) => {
|
|
903
940
|
if (ts.isConstructorDeclaration(node)) {
|
|
904
941
|
const funcType = {
|
|
@@ -923,7 +960,7 @@ export class TypeAnalyzer {
|
|
|
923
960
|
this.scopeManager.exitScope();
|
|
924
961
|
},
|
|
925
962
|
},
|
|
926
|
-
VariableDeclaration: {
|
|
963
|
+
[ts.SyntaxKind.VariableDeclaration]: {
|
|
927
964
|
enter: (node) => {
|
|
928
965
|
if (ts.isVariableDeclaration(node)) {
|
|
929
966
|
// Check if it is an ambient declaration (declare var/let/const ...)
|
|
@@ -990,8 +1027,8 @@ export class TypeAnalyzer {
|
|
|
990
1027
|
}
|
|
991
1028
|
},
|
|
992
1029
|
},
|
|
993
|
-
Identifier: {
|
|
994
|
-
enter: (node
|
|
1030
|
+
[ts.SyntaxKind.Identifier]: {
|
|
1031
|
+
enter: (node) => {
|
|
995
1032
|
if (ts.isIdentifier(node)) {
|
|
996
1033
|
if (isBuiltinObject.call(this, node))
|
|
997
1034
|
return;
|
|
@@ -1027,7 +1064,7 @@ export class TypeAnalyzer {
|
|
|
1027
1064
|
}
|
|
1028
1065
|
},
|
|
1029
1066
|
},
|
|
1030
|
-
BinaryExpression: {
|
|
1067
|
+
[ts.SyntaxKind.BinaryExpression]: {
|
|
1031
1068
|
enter: (node) => {
|
|
1032
1069
|
if (ts.isBinaryExpression(node)) {
|
|
1033
1070
|
const isAssignment = node.operatorToken.kind >=
|
|
@@ -1050,7 +1087,7 @@ export class TypeAnalyzer {
|
|
|
1050
1087
|
}
|
|
1051
1088
|
},
|
|
1052
1089
|
},
|
|
1053
|
-
CallExpression: {
|
|
1090
|
+
[ts.SyntaxKind.CallExpression]: {
|
|
1054
1091
|
enter: (node) => {
|
|
1055
1092
|
if (ts.isCallExpression(node)) {
|
|
1056
1093
|
const callee = node.expression;
|
|
@@ -1070,7 +1107,7 @@ export class TypeAnalyzer {
|
|
|
1070
1107
|
}
|
|
1071
1108
|
},
|
|
1072
1109
|
},
|
|
1073
|
-
ReturnStatement: {
|
|
1110
|
+
[ts.SyntaxKind.ReturnStatement]: {
|
|
1074
1111
|
enter: (node) => {
|
|
1075
1112
|
if (ts.isReturnStatement(node) && node.expression) {
|
|
1076
1113
|
const currentFuncNode = this.functionStack[this.functionStack.length - 1];
|
|
@@ -1086,14 +1123,14 @@ export class TypeAnalyzer {
|
|
|
1086
1123
|
}
|
|
1087
1124
|
},
|
|
1088
1125
|
},
|
|
1089
|
-
PostfixUnaryExpression: {
|
|
1126
|
+
[ts.SyntaxKind.PostfixUnaryExpression]: {
|
|
1090
1127
|
enter: (node) => {
|
|
1091
1128
|
if (ts.isPostfixUnaryExpression(node)) {
|
|
1092
1129
|
crossScopeModificationVisitor(node.operand);
|
|
1093
1130
|
}
|
|
1094
1131
|
},
|
|
1095
1132
|
},
|
|
1096
|
-
PrefixUnaryExpression: {
|
|
1133
|
+
[ts.SyntaxKind.PrefixUnaryExpression]: {
|
|
1097
1134
|
enter: (node) => {
|
|
1098
1135
|
if (ts.isPrefixUnaryExpression(node)) {
|
|
1099
1136
|
const op = node.operator;
|
|
@@ -153,16 +153,19 @@ export function visitForInStatement(node, context) {
|
|
|
153
153
|
derefExpr = this.getDerefCode(exprText, this.getJsVarName(expr), context, typeInfo);
|
|
154
154
|
}
|
|
155
155
|
const keysVar = this.generateUniqueName("__keys_", new Set([varName]));
|
|
156
|
+
const visitContext = {
|
|
157
|
+
...context,
|
|
158
|
+
globalScopeSymbols: this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols),
|
|
159
|
+
localScopeSymbols: new DeclaredSymbols(),
|
|
160
|
+
currentLabel: undefined,
|
|
161
|
+
isFunctionBody: false,
|
|
162
|
+
};
|
|
156
163
|
code +=
|
|
157
164
|
`${this.indent()}std::vector<jspp::AnyValue> ${keysVar} = jspp::Access::get_object_keys(${derefExpr});\n`;
|
|
158
165
|
code += `${this.indent()}for (const auto& ${varName}_val : ${keysVar}) {\n`;
|
|
159
166
|
this.indentationLevel++;
|
|
160
167
|
code += `${this.indent()}${assignmentTarget} = ${varName}_val;\n`;
|
|
161
|
-
code += this.visit(forIn.statement,
|
|
162
|
-
...context,
|
|
163
|
-
currentLabel: undefined,
|
|
164
|
-
isFunctionBody: false,
|
|
165
|
-
});
|
|
168
|
+
code += this.visit(forIn.statement, visitContext);
|
|
166
169
|
this.indentationLevel--;
|
|
167
170
|
if (context.currentLabel) {
|
|
168
171
|
code += `${this.indent()}${context.currentLabel}_continue:;\n`;
|
|
@@ -188,6 +191,7 @@ export function visitForOfStatement(node, context) {
|
|
|
188
191
|
this.indentationLevel++; // Enter a new scope for the for-of loop
|
|
189
192
|
let elemName = "";
|
|
190
193
|
let assignmentTarget = "";
|
|
194
|
+
let declarationType = DeclarationType.const;
|
|
191
195
|
code += `${this.indent()}{\n`;
|
|
192
196
|
if (ts.isVariableDeclarationList(forOf.initializer)) {
|
|
193
197
|
const decl = forOf.initializer.declarations[0];
|
|
@@ -205,6 +209,11 @@ export function visitForOfStatement(node, context) {
|
|
|
205
209
|
`${this.indent()}jspp::AnyValue ${elemName} = jspp::Constants::UNDEFINED;\n`;
|
|
206
210
|
assignmentTarget = elemName;
|
|
207
211
|
}
|
|
212
|
+
declarationType = (decl.parent.flags & (ts.NodeFlags.Let)) !== 0
|
|
213
|
+
? DeclarationType.let
|
|
214
|
+
: (decl.parent.flags & (ts.NodeFlags.Const)) !== 0
|
|
215
|
+
? DeclarationType.const
|
|
216
|
+
: DeclarationType.var;
|
|
208
217
|
}
|
|
209
218
|
}
|
|
210
219
|
else if (ts.isIdentifier(forOf.initializer)) {
|
|
@@ -214,6 +223,12 @@ export function visitForOfStatement(node, context) {
|
|
|
214
223
|
assignmentTarget = typeInfo?.needsHeapAllocation
|
|
215
224
|
? `*${elemName}`
|
|
216
225
|
: elemName;
|
|
226
|
+
declarationType =
|
|
227
|
+
(forOf.initializer.parent.flags & (ts.NodeFlags.Let)) !== 0
|
|
228
|
+
? DeclarationType.let
|
|
229
|
+
: (forOf.initializer.parent.flags & (ts.NodeFlags.Const)) !== 0
|
|
230
|
+
? DeclarationType.const
|
|
231
|
+
: DeclarationType.var;
|
|
217
232
|
}
|
|
218
233
|
const iterableExpr = this.visit(forOf.expression, context);
|
|
219
234
|
let derefIterable = iterableExpr;
|
|
@@ -230,6 +245,19 @@ export function visitForOfStatement(node, context) {
|
|
|
230
245
|
const nextRes = this.generateUniqueName("__next_res", declaredSymbols);
|
|
231
246
|
const isAwait = forOf.awaitModifier !== undefined;
|
|
232
247
|
const varName = this.getJsVarName(forOf.expression);
|
|
248
|
+
const visitContext = {
|
|
249
|
+
...context,
|
|
250
|
+
globalScopeSymbols: this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols),
|
|
251
|
+
localScopeSymbols: new DeclaredSymbols(),
|
|
252
|
+
currentLabel: undefined,
|
|
253
|
+
isFunctionBody: false,
|
|
254
|
+
};
|
|
255
|
+
if (elemName) {
|
|
256
|
+
visitContext.localScopeSymbols.add(elemName, {
|
|
257
|
+
type: declarationType,
|
|
258
|
+
checks: { initialized: true },
|
|
259
|
+
});
|
|
260
|
+
}
|
|
233
261
|
code += `${this.indent()}auto ${iterableRef} = ${derefIterable};\n`;
|
|
234
262
|
if (isAwait) {
|
|
235
263
|
code +=
|
|
@@ -254,11 +282,7 @@ export function visitForOfStatement(node, context) {
|
|
|
254
282
|
this.indentationLevel++;
|
|
255
283
|
code +=
|
|
256
284
|
`${this.indent()}${assignmentTarget} = ${nextRes}.get_own_property("value");\n`;
|
|
257
|
-
code += this.visit(forOf.statement,
|
|
258
|
-
...context,
|
|
259
|
-
currentLabel: undefined,
|
|
260
|
-
isFunctionBody: false,
|
|
261
|
-
});
|
|
285
|
+
code += this.visit(forOf.statement, visitContext);
|
|
262
286
|
if (context.currentLabel) {
|
|
263
287
|
code += `${this.indent()}${context.currentLabel}_continue:;\n`;
|
|
264
288
|
}
|
|
@@ -247,6 +247,17 @@ export function visitPrefixUnaryExpression(node, context) {
|
|
|
247
247
|
const operand = this.visit(prefixUnaryExpr.operand, context);
|
|
248
248
|
const operator = ts.tokenToString(prefixUnaryExpr.operator);
|
|
249
249
|
if (operator === "++" || operator === "--") {
|
|
250
|
+
const opFunc = operator === "++" ? "add" : "sub";
|
|
251
|
+
if (ts.isPropertyAccessExpression(prefixUnaryExpr.operand)) {
|
|
252
|
+
const obj = this.visit(prefixUnaryExpr.operand.expression, context);
|
|
253
|
+
const prop = visitObjectPropertyName.call(this, prefixUnaryExpr.operand.name, context);
|
|
254
|
+
return `${obj}.set_own_property(${prop}, jspp::${opFunc}(${obj}.get_own_property(${prop}), 1.0))`;
|
|
255
|
+
}
|
|
256
|
+
else if (ts.isElementAccessExpression(prefixUnaryExpr.operand)) {
|
|
257
|
+
const obj = this.visit(prefixUnaryExpr.operand.expression, context);
|
|
258
|
+
const prop = visitObjectPropertyName.call(this, prefixUnaryExpr.operand.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
|
|
259
|
+
return `${obj}.set_own_property(${prop}, jspp::${opFunc}(${obj}.get_own_property(${prop}), 1.0))`;
|
|
260
|
+
}
|
|
250
261
|
let target = operand;
|
|
251
262
|
if (ts.isIdentifier(prefixUnaryExpr.operand)) {
|
|
252
263
|
const scope = this.getScopeForNode(prefixUnaryExpr.operand);
|
|
@@ -289,6 +300,20 @@ export function visitPostfixUnaryExpression(node, context) {
|
|
|
289
300
|
const postfixUnaryExpr = node;
|
|
290
301
|
const operand = this.visit(postfixUnaryExpr.operand, context);
|
|
291
302
|
const operator = ts.tokenToString(postfixUnaryExpr.operator);
|
|
303
|
+
if (ts.isPropertyAccessExpression(postfixUnaryExpr.operand)) {
|
|
304
|
+
const obj = this.visit(postfixUnaryExpr.operand.expression, context);
|
|
305
|
+
const prop = visitObjectPropertyName.call(this, postfixUnaryExpr.operand.name, context);
|
|
306
|
+
const opFunc = operator === "++" ? "add" : "sub";
|
|
307
|
+
// Postfix needs IILE to return old value
|
|
308
|
+
return `([&]() { auto oldVal = ${obj}.get_own_property(${prop}); ${obj}.set_own_property(${prop}, jspp::${opFunc}(oldVal, 1.0)); return oldVal; })()`;
|
|
309
|
+
}
|
|
310
|
+
else if (ts.isElementAccessExpression(postfixUnaryExpr.operand)) {
|
|
311
|
+
const obj = this.visit(postfixUnaryExpr.operand.expression, context);
|
|
312
|
+
const prop = visitObjectPropertyName.call(this, postfixUnaryExpr.operand.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
|
|
313
|
+
const opFunc = operator === "++" ? "add" : "sub";
|
|
314
|
+
// Postfix needs IILE to return old value
|
|
315
|
+
return `([&]() { auto oldVal = ${obj}.get_own_property(${prop}); ${obj}.set_own_property(${prop}, jspp::${opFunc}(oldVal, 1.0)); return oldVal; })()`;
|
|
316
|
+
}
|
|
292
317
|
let target = operand;
|
|
293
318
|
if (ts.isIdentifier(postfixUnaryExpr.operand)) {
|
|
294
319
|
const scope = this.getScopeForNode(postfixUnaryExpr.operand);
|
|
@@ -402,42 +427,36 @@ export function visitBinaryExpression(node, context) {
|
|
|
402
427
|
if (assignmentOperators.includes(opToken.kind)) {
|
|
403
428
|
if (opToken.kind ===
|
|
404
429
|
ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
|
|
405
|
-
const leftText = this.visit(binExpr.left, visitContext);
|
|
406
430
|
const rightText = this.visit(binExpr.right, visitContext);
|
|
407
|
-
let target = leftText;
|
|
408
431
|
if (ts.isIdentifier(binExpr.left)) {
|
|
432
|
+
const leftText = this.visit(binExpr.left, visitContext);
|
|
409
433
|
const scope = this.getScopeForNode(binExpr.left);
|
|
410
434
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
|
|
411
|
-
target = typeInfo?.needsHeapAllocation
|
|
435
|
+
const target = typeInfo?.needsHeapAllocation
|
|
412
436
|
? `*${leftText}`
|
|
413
437
|
: leftText;
|
|
414
|
-
return
|
|
438
|
+
return `jspp::unsigned_right_shift_assign(${target}, ${rightText})`;
|
|
415
439
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
if (ts.
|
|
422
|
-
const
|
|
423
|
-
const
|
|
424
|
-
|
|
425
|
-
? `*${leftText}`
|
|
426
|
-
: leftText;
|
|
427
|
-
return `${target} = jspp::pow(${target}, ${rightText})`;
|
|
440
|
+
else if (ts.isPropertyAccessExpression(binExpr.left)) {
|
|
441
|
+
const obj = this.visit(binExpr.left.expression, context);
|
|
442
|
+
const prop = visitObjectPropertyName.call(this, binExpr.left.name, context);
|
|
443
|
+
return `${obj}.set_own_property(${prop}, jspp::unsigned_right_shift(${obj}.get_own_property(${prop}), ${rightText}))`;
|
|
444
|
+
}
|
|
445
|
+
else if (ts.isElementAccessExpression(binExpr.left)) {
|
|
446
|
+
const obj = this.visit(binExpr.left.expression, context);
|
|
447
|
+
const prop = visitObjectPropertyName.call(this, binExpr.left.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
|
|
448
|
+
return `${obj}.set_own_property(${prop}, jspp::unsigned_right_shift(${obj}.get_own_property(${prop}), ${rightText}))`;
|
|
428
449
|
}
|
|
429
|
-
// For complex LHS, we need a different approach, but this is a start.
|
|
430
450
|
}
|
|
431
451
|
if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
432
452
|
opToken.kind === ts.SyntaxKind.BarBarEqualsToken ||
|
|
433
453
|
opToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
|
|
434
|
-
const leftText = this.visit(binExpr.left, visitContext);
|
|
435
454
|
const rightText = this.visit(binExpr.right, visitContext);
|
|
436
|
-
let target = leftText;
|
|
437
455
|
if (ts.isIdentifier(binExpr.left)) {
|
|
456
|
+
const leftText = this.visit(binExpr.left, visitContext);
|
|
438
457
|
const scope = this.getScopeForNode(binExpr.left);
|
|
439
458
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
|
|
440
|
-
target = typeInfo?.needsHeapAllocation
|
|
459
|
+
const target = typeInfo?.needsHeapAllocation
|
|
441
460
|
? `*${leftText}`
|
|
442
461
|
: leftText;
|
|
443
462
|
if (opToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
|
|
@@ -450,6 +469,29 @@ export function visitBinaryExpression(node, context) {
|
|
|
450
469
|
return `jspp::nullish_coalesce_assign(${target}, ${rightText})`;
|
|
451
470
|
}
|
|
452
471
|
}
|
|
472
|
+
else if (ts.isPropertyAccessExpression(binExpr.left)) {
|
|
473
|
+
const obj = this.visit(binExpr.left.expression, context);
|
|
474
|
+
const prop = visitObjectPropertyName.call(this, binExpr.left.name, context);
|
|
475
|
+
const func = opToken.kind ===
|
|
476
|
+
ts.SyntaxKind.AmpersandAmpersandEqualsToken
|
|
477
|
+
? "logical_and"
|
|
478
|
+
: (opToken.kind === ts.SyntaxKind.BarBarEqualsToken
|
|
479
|
+
? "logical_or"
|
|
480
|
+
: "nullish_coalesce");
|
|
481
|
+
// Logical assignments return newVal, set_own_property returns newVal.
|
|
482
|
+
return `${obj}.set_own_property(${prop}, jspp::${func}(${obj}.get_own_property(${prop}), ${rightText}))`;
|
|
483
|
+
}
|
|
484
|
+
else if (ts.isElementAccessExpression(binExpr.left)) {
|
|
485
|
+
const obj = this.visit(binExpr.left.expression, context);
|
|
486
|
+
const prop = visitObjectPropertyName.call(this, binExpr.left.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
|
|
487
|
+
const func = opToken.kind ===
|
|
488
|
+
ts.SyntaxKind.AmpersandAmpersandEqualsToken
|
|
489
|
+
? "logical_and"
|
|
490
|
+
: (opToken.kind === ts.SyntaxKind.BarBarEqualsToken
|
|
491
|
+
? "logical_or"
|
|
492
|
+
: "nullish_coalesce");
|
|
493
|
+
return `${obj}.set_own_property(${prop}, jspp::${func}(${obj}.get_own_property(${prop}), ${rightText}))`;
|
|
494
|
+
}
|
|
453
495
|
}
|
|
454
496
|
const leftText = this.visit(binExpr.left, visitContext);
|
|
455
497
|
let rightText = ts.isNumericLiteral(binExpr.right)
|
|
@@ -460,6 +502,35 @@ export function visitBinaryExpression(node, context) {
|
|
|
460
502
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope);
|
|
461
503
|
rightText = this.getDerefCode(rightText, this.getJsVarName(binExpr.right), visitContext, typeInfo);
|
|
462
504
|
}
|
|
505
|
+
if (ts.isPropertyAccessExpression(binExpr.left)) {
|
|
506
|
+
const obj = this.visit(binExpr.left.expression, context);
|
|
507
|
+
const prop = visitObjectPropertyName.call(this, binExpr.left.name, context);
|
|
508
|
+
const opBase = op.slice(0, -1); // "+=", "-=" -> "+", "-"
|
|
509
|
+
const opFunc = opBase === "+" ? "add" : (opBase === "-" ? "sub" : (opBase === "*" ? "mul" : (opBase === "/" ? "div" : (opBase === "%" ? "mod" : (opBase === "&" ? "bitwise_and" : (opBase === "|" ? "bitwise_or" : (opBase === "^" ? "bitwise_xor" : (opBase === "<<" ? "left_shift" : (opBase === ">>"
|
|
510
|
+
? "right_shift"
|
|
511
|
+
: "")))))))));
|
|
512
|
+
if (opFunc) {
|
|
513
|
+
return `${obj}.set_own_property(${prop}, jspp::${opFunc}(${obj}.get_own_property(${prop}), ${rightText}))`;
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
// Fallback to IILE if we don't have an opFunc mapping
|
|
517
|
+
return `([&]() { auto val = ${obj}.get_own_property(${prop}); val ${op} ${rightText}; ${obj}.set_own_property(${prop}, val); return val; })()`;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
else if (ts.isElementAccessExpression(binExpr.left)) {
|
|
521
|
+
const obj = this.visit(binExpr.left.expression, context);
|
|
522
|
+
const prop = visitObjectPropertyName.call(this, binExpr.left.argumentExpression, { ...context, isBracketNotationPropertyAccess: true });
|
|
523
|
+
const opBase = op.slice(0, -1);
|
|
524
|
+
const opFunc = opBase === "+" ? "add" : (opBase === "-" ? "sub" : (opBase === "*" ? "mul" : (opBase === "/" ? "div" : (opBase === "%" ? "mod" : (opBase === "&" ? "bitwise_and" : (opBase === "|" ? "bitwise_or" : (opBase === "^" ? "bitwise_xor" : (opBase === "<<" ? "left_shift" : (opBase === ">>"
|
|
525
|
+
? "right_shift"
|
|
526
|
+
: "")))))))));
|
|
527
|
+
if (opFunc) {
|
|
528
|
+
return `${obj}.set_own_property(${prop}, jspp::${opFunc}(${obj}.get_own_property(${prop}), ${rightText}))`;
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
return `([&]() { auto val = ${obj}.get_own_property(${prop}); val ${op} ${rightText}; ${obj}.set_own_property(${prop}, val); return val; })()`;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
463
534
|
let target = leftText;
|
|
464
535
|
if (ts.isIdentifier(binExpr.left)) {
|
|
465
536
|
const scope = this.getScopeForNode(binExpr.left);
|
|
@@ -1266,7 +1337,7 @@ export function visitTypeOfExpression(node, context) {
|
|
|
1266
1337
|
derefExpr = this.getDerefCode(exprText, this.getJsVarName(typeOfExpr.expression), context, typeInfo);
|
|
1267
1338
|
}
|
|
1268
1339
|
}
|
|
1269
|
-
return `jspp::
|
|
1340
|
+
return `jspp::type_of(${derefExpr})`;
|
|
1270
1341
|
}
|
|
1271
1342
|
export function visitAwaitExpression(node, context) {
|
|
1272
1343
|
const awaitExpr = node;
|
|
@@ -123,7 +123,7 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
123
123
|
}
|
|
124
124
|
});
|
|
125
125
|
// Extract lambda parameters from arguments span/vector
|
|
126
|
-
const
|
|
126
|
+
const wrappedFuncParamsBuilder = () => {
|
|
127
127
|
let paramsCode = "";
|
|
128
128
|
this.validateFunctionParams(node.parameters).forEach((p, i) => {
|
|
129
129
|
if (ts.isIdentifier(p.name)) {
|
|
@@ -152,14 +152,19 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
152
152
|
return;
|
|
153
153
|
}
|
|
154
154
|
// Normal parameter
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
155
|
+
const isUsedInFuncBody = !!p.initializer ||
|
|
156
|
+
this.isDeclarationUsedAsValue(p, node) ||
|
|
157
|
+
this.isDeclarationCalledAsFunction(p, node);
|
|
158
|
+
if (isUsedInFuncBody) {
|
|
159
|
+
const initValue = `${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue}`;
|
|
160
|
+
if (typeInfo?.needsHeapAllocation) {
|
|
161
|
+
paramsCode +=
|
|
162
|
+
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
paramsCode +=
|
|
166
|
+
`${this.indent()}jspp::AnyValue ${name} = ${initValue};\n`;
|
|
167
|
+
}
|
|
163
168
|
}
|
|
164
169
|
}
|
|
165
170
|
else {
|
|
@@ -225,7 +230,7 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
225
230
|
}
|
|
226
231
|
});
|
|
227
232
|
this.indentationLevel++;
|
|
228
|
-
paramsContent +=
|
|
233
|
+
paramsContent += wrappedFuncParamsBuilder();
|
|
229
234
|
this.indentationLevel--;
|
|
230
235
|
// The block visitor already adds braces, so we need to remove the opening brace to inject the preamble and param extraction.
|
|
231
236
|
const properIndentationLevel = this.indentationLevel;
|
|
@@ -250,7 +255,7 @@ export function generateLambdaComponents(node, context, options) {
|
|
|
250
255
|
}
|
|
251
256
|
else {
|
|
252
257
|
this.indentationLevel++;
|
|
253
|
-
paramsContent +=
|
|
258
|
+
paramsContent += wrappedFuncParamsBuilder();
|
|
254
259
|
const properIndentationLevel = this.indentationLevel;
|
|
255
260
|
const nodeBody = node.body;
|
|
256
261
|
getBlockContentWithoutOpeningBrace = (isInsideNativeLambda) => {
|
|
@@ -799,7 +799,11 @@ export function findEnclosingFunctionDeclarationFromReturnStatement(node) {
|
|
|
799
799
|
let current = node;
|
|
800
800
|
let foundReturn = false;
|
|
801
801
|
while (current) {
|
|
802
|
-
if (ts.isFunctionDeclaration(current)
|
|
802
|
+
if (ts.isFunctionDeclaration(current) ||
|
|
803
|
+
ts.isFunctionExpression(current) ||
|
|
804
|
+
ts.isArrowFunction(current) ||
|
|
805
|
+
ts.isMethodDeclaration(current) ||
|
|
806
|
+
ts.isConstructorDeclaration(current)) {
|
|
803
807
|
if (foundReturn) {
|
|
804
808
|
return current;
|
|
805
809
|
}
|