@ugo-studio/jspp 0.1.4 → 0.1.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.
- package/dist/analysis/scope.js +17 -0
- package/dist/analysis/typeAnalyzer.js +7 -1
- package/dist/ast/symbols.js +32 -0
- package/dist/ast/types.js +0 -6
- package/dist/cli-utils/args.js +57 -0
- package/dist/cli-utils/colors.js +9 -0
- package/dist/cli-utils/file-utils.js +20 -0
- package/dist/cli-utils/spinner.js +55 -0
- package/dist/cli.js +105 -30
- package/dist/core/codegen/class-handlers.js +10 -6
- package/dist/core/codegen/control-flow-handlers.js +57 -28
- package/dist/core/codegen/declaration-handlers.js +10 -6
- package/dist/core/codegen/expression-handlers.js +206 -61
- package/dist/core/codegen/function-handlers.js +203 -76
- package/dist/core/codegen/helpers.js +125 -28
- package/dist/core/codegen/index.js +23 -15
- package/dist/core/codegen/literal-handlers.js +15 -6
- package/dist/core/codegen/statement-handlers.js +282 -84
- package/dist/core/codegen/visitor.js +3 -1
- package/package.json +1 -1
- package/src/prelude/any_value.hpp +221 -342
- package/src/prelude/any_value_access.hpp +168 -81
- package/src/prelude/any_value_defines.hpp +74 -35
- package/src/prelude/any_value_helpers.hpp +75 -180
- package/src/prelude/exception.hpp +1 -0
- package/src/prelude/exception_helpers.hpp +4 -4
- package/src/prelude/index.hpp +12 -2
- package/src/prelude/library/array.hpp +190 -0
- package/src/prelude/library/console.hpp +6 -5
- package/src/prelude/library/error.hpp +10 -8
- package/src/prelude/library/function.hpp +10 -0
- package/src/prelude/library/global.hpp +20 -0
- package/src/prelude/library/math.hpp +308 -0
- package/src/prelude/library/object.hpp +288 -0
- package/src/prelude/library/performance.hpp +1 -1
- package/src/prelude/library/process.hpp +39 -0
- package/src/prelude/library/promise.hpp +57 -55
- package/src/prelude/library/symbol.hpp +45 -57
- package/src/prelude/library/timer.hpp +6 -6
- package/src/prelude/types.hpp +54 -0
- package/src/prelude/utils/access.hpp +215 -11
- package/src/prelude/utils/assignment_operators.hpp +99 -0
- package/src/prelude/utils/log_any_value/array.hpp +8 -8
- package/src/prelude/utils/log_any_value/function.hpp +6 -4
- package/src/prelude/utils/log_any_value/object.hpp +41 -24
- package/src/prelude/utils/log_any_value/primitives.hpp +3 -1
- package/src/prelude/utils/operators.hpp +750 -274
- package/src/prelude/utils/well_known_symbols.hpp +12 -0
- package/src/prelude/values/array.hpp +8 -6
- package/src/prelude/values/async_iterator.hpp +79 -0
- package/src/prelude/values/descriptors.hpp +2 -2
- package/src/prelude/values/function.hpp +72 -62
- package/src/prelude/values/helpers/array.hpp +64 -28
- package/src/prelude/values/helpers/async_iterator.hpp +275 -0
- package/src/prelude/values/helpers/function.hpp +81 -92
- package/src/prelude/values/helpers/iterator.hpp +3 -3
- package/src/prelude/values/helpers/object.hpp +54 -9
- package/src/prelude/values/helpers/promise.hpp +13 -6
- package/src/prelude/values/iterator.hpp +1 -1
- package/src/prelude/values/object.hpp +10 -3
- package/src/prelude/values/promise.hpp +7 -11
- package/src/prelude/values/prototypes/array.hpp +851 -12
- package/src/prelude/values/prototypes/async_iterator.hpp +50 -0
- package/src/prelude/values/prototypes/function.hpp +2 -2
- package/src/prelude/values/prototypes/iterator.hpp +5 -5
- package/src/prelude/values/prototypes/number.hpp +153 -0
- package/src/prelude/values/prototypes/object.hpp +2 -2
- package/src/prelude/values/prototypes/promise.hpp +40 -30
- package/src/prelude/values/prototypes/string.hpp +28 -28
- package/src/prelude/values/prototypes/symbol.hpp +20 -3
- package/src/prelude/values/shape.hpp +52 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
+
import { DeclaredSymbols } from "../../ast/symbols";
|
|
2
3
|
import { CodeGenerator } from "./";
|
|
4
|
+
import { collectFunctionScopedDeclarations } from "./helpers";
|
|
3
5
|
export function generateLambda(node, context, options) {
|
|
4
6
|
const isAssignment = options?.isAssignment || false;
|
|
5
7
|
const capture = options?.capture || "[=]";
|
|
@@ -11,76 +13,175 @@ export function generateLambda(node, context, options) {
|
|
|
11
13
|
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
12
14
|
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
13
15
|
});
|
|
14
|
-
const funcReturnType = isInsideGeneratorFunction
|
|
15
|
-
? "jspp::
|
|
16
|
-
: (
|
|
16
|
+
const funcReturnType = (isInsideGeneratorFunction && isInsideAsyncFunction)
|
|
17
|
+
? "jspp::JsAsyncIterator<jspp::AnyValue>"
|
|
18
|
+
: (isInsideGeneratorFunction
|
|
19
|
+
? "jspp::JsIterator<jspp::AnyValue>"
|
|
20
|
+
: (isInsideAsyncFunction ? "jspp::JsPromise" : "jspp::AnyValue"));
|
|
17
21
|
const isArrow = ts.isArrowFunction(node);
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
const paramThisType =
|
|
21
|
-
|
|
22
|
-
: "const jspp::AnyValue&";
|
|
23
|
-
const paramArgsType = (isInsideGeneratorFunction || isInsideAsyncFunction)
|
|
24
|
-
? "std::vector<jspp::AnyValue>"
|
|
25
|
-
: "const std::vector<jspp::AnyValue>&";
|
|
22
|
+
// Lambda arguments are ALWAYS const references/spans to avoid copy overhead for normal functions.
|
|
23
|
+
// For generators/async, we manually copy them inside the body.
|
|
24
|
+
const paramThisType = "const jspp::AnyValue&";
|
|
25
|
+
const paramArgsType = "std::span<const jspp::AnyValue>";
|
|
26
26
|
const thisArgParam = isArrow
|
|
27
|
-
? "const jspp::AnyValue&" // Arrow functions are never generators in this parser
|
|
27
|
+
? "const jspp::AnyValue&" // Arrow functions use captured 'this' or are never generators in this parser context
|
|
28
28
|
: `${paramThisType} ${this.globalThisVar}`;
|
|
29
29
|
let lambda = `${capture}(${thisArgParam}, ${paramArgsType} ${argsName}) mutable -> ${funcReturnType} `;
|
|
30
|
-
const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.
|
|
30
|
+
const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.localScopeSymbols);
|
|
31
31
|
const visitContext = {
|
|
32
32
|
isMainContext: false,
|
|
33
33
|
isInsideFunction: true,
|
|
34
34
|
isFunctionBody: false,
|
|
35
35
|
lambdaName: undefined,
|
|
36
36
|
topLevelScopeSymbols,
|
|
37
|
-
|
|
37
|
+
localScopeSymbols: new DeclaredSymbols(),
|
|
38
38
|
superClassVar: context.superClassVar,
|
|
39
|
+
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
40
|
+
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
39
41
|
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
42
|
+
// Name of 'this' and 'args' to be used in the body.
|
|
43
|
+
// If generator/async, we use the copied versions.
|
|
44
|
+
let bodyThisVar = this.globalThisVar;
|
|
45
|
+
let bodyArgsVar = argsName;
|
|
46
|
+
// Preamble to copy arguments for coroutines
|
|
47
|
+
let preamble = "";
|
|
48
|
+
if (isInsideGeneratorFunction || isInsideAsyncFunction) {
|
|
49
|
+
const thisCopy = this.generateUniqueName("__this_copy", declaredSymbols);
|
|
50
|
+
const argsCopy = this.generateUniqueName("__args_copy", declaredSymbols);
|
|
51
|
+
if (!isArrow) {
|
|
52
|
+
preamble +=
|
|
53
|
+
`${this.indent()}jspp::AnyValue ${thisCopy} = ${this.globalThisVar};\n`;
|
|
54
|
+
bodyThisVar = thisCopy;
|
|
55
|
+
}
|
|
56
|
+
preamble +=
|
|
57
|
+
`${this.indent()}std::vector<jspp::AnyValue> ${argsCopy}(${argsName}.begin(), ${argsName}.end());\n`;
|
|
58
|
+
bodyArgsVar = argsCopy;
|
|
59
|
+
// Update visitContext to use the new 'this' variable if needed?
|
|
60
|
+
// CodeGenerator.globalThisVar is a member of the class, so we can't easily change it for the recursive visit
|
|
61
|
+
// without changing the class state or passing it down.
|
|
62
|
+
// BUT: visitThisKeyword uses `this.globalThisVar`.
|
|
63
|
+
// We need to temporarily swap `this.globalThisVar` or handle it.
|
|
64
|
+
// Actually, `this.globalThisVar` is set once in `generate`.
|
|
65
|
+
// Wait, `visitThisKeyword` uses `this.globalThisVar`.
|
|
66
|
+
// If we change the variable name for 'this', we must ensure `visitThisKeyword` generates the correct name.
|
|
67
|
+
// `generateLambda` does NOT create a new CodeGenerator instance, so `this.globalThisVar` is the global one.
|
|
68
|
+
//
|
|
69
|
+
// We need to support shadowing `globalThisVar` for the duration of the visit.
|
|
70
|
+
// The current `CodeGenerator` doesn't seem to support changing `globalThisVar` recursively easily
|
|
71
|
+
// because it's a property of the class, not `VisitContext`.
|
|
72
|
+
//
|
|
73
|
+
// However, `visitFunctionDeclaration` etc don't update `globalThisVar`.
|
|
74
|
+
// `visitThisKeyword` just returns `this.globalThisVar`.
|
|
75
|
+
//
|
|
76
|
+
// If we are inside a function, `this` should refer to the function's `this`.
|
|
77
|
+
// `this.globalThisVar` is generated in `generate()`: `__this_val__...`.
|
|
78
|
+
// And `generateLambda` uses `this.globalThisVar` as the parameter name.
|
|
79
|
+
//
|
|
80
|
+
// So, if we copy it to `__this_copy`, `visitThisKeyword` will still print `__this_val__...`.
|
|
81
|
+
// This is BAD for generators because `__this_val__...` is a reference that dies.
|
|
82
|
+
//
|
|
83
|
+
// FIX: We must reuse the SAME name for the local copy, and shadow the parameter.
|
|
84
|
+
// But we can't declare a variable with the same name as the parameter in the same scope.
|
|
85
|
+
//
|
|
86
|
+
// We can rename the PARAMETER to something else, and declare the local variable with `this.globalThisVar`.
|
|
87
|
+
//
|
|
88
|
+
}
|
|
89
|
+
// Adjust parameter names for generator/async to allow shadowing/copying
|
|
90
|
+
let finalThisParamName = isArrow ? "" : this.globalThisVar;
|
|
91
|
+
let finalArgsParamName = argsName;
|
|
92
|
+
if (isInsideGeneratorFunction || isInsideAsyncFunction) {
|
|
93
|
+
if (!isArrow) {
|
|
94
|
+
finalThisParamName = this.generateUniqueName("__this_ref", declaredSymbols);
|
|
95
|
+
}
|
|
96
|
+
finalArgsParamName = this.generateUniqueName("__args_ref", declaredSymbols);
|
|
97
|
+
}
|
|
98
|
+
const thisArgParamFinal = isArrow
|
|
99
|
+
? "const jspp::AnyValue&"
|
|
100
|
+
: `${paramThisType} ${finalThisParamName}`;
|
|
101
|
+
// Re-construct lambda header with potentially new param names
|
|
102
|
+
lambda =
|
|
103
|
+
`${capture}(${thisArgParamFinal}, ${paramArgsType} ${finalArgsParamName}) mutable -> ${funcReturnType} `;
|
|
104
|
+
// Regenerate preamble
|
|
105
|
+
preamble = "";
|
|
106
|
+
if (isInsideGeneratorFunction || isInsideAsyncFunction) {
|
|
107
|
+
if (!isArrow) {
|
|
108
|
+
preamble +=
|
|
109
|
+
`${this.indent()}jspp::AnyValue ${this.globalThisVar} = ${finalThisParamName};\n`;
|
|
110
|
+
}
|
|
111
|
+
preamble +=
|
|
112
|
+
`${this.indent()}std::vector<jspp::AnyValue> ${argsName}(${finalArgsParamName}.begin(), ${finalArgsParamName}.end());\n`;
|
|
113
|
+
}
|
|
114
|
+
// Now 'argsName' refers to the vector (if copied) or the span (if not).
|
|
115
|
+
// And 'this.globalThisVar' refers to the copy (if copied) or the param (if not).
|
|
116
|
+
// So subsequent code using `argsName` and `visit` (using `globalThisVar`) works correctly.
|
|
117
|
+
const paramExtractor = (parameters) => {
|
|
118
|
+
let code = "";
|
|
119
|
+
parameters.forEach((p, i) => {
|
|
120
|
+
const name = p.name.getText();
|
|
121
|
+
const defaultValue = p.initializer
|
|
122
|
+
? this.visit(p.initializer, visitContext)
|
|
123
|
+
: "jspp::Constants::UNDEFINED";
|
|
124
|
+
const scope = this.getScopeForNode(p);
|
|
125
|
+
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
126
|
+
// Handle rest parameters
|
|
127
|
+
if (!!p.dotDotDotToken) {
|
|
128
|
+
if (parameters.length - 1 !== i) {
|
|
129
|
+
throw new SyntaxError("Rest parameter must be last formal parameter.");
|
|
130
|
+
}
|
|
131
|
+
if (typeInfo.needsHeapAllocation) {
|
|
132
|
+
code +=
|
|
133
|
+
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
|
|
78
134
|
}
|
|
79
135
|
else {
|
|
80
|
-
|
|
81
|
-
`${this.indent()}
|
|
136
|
+
code +=
|
|
137
|
+
`${this.indent()}jspp::AnyValue ${name} = jspp::Constants::UNDEFINED;\n`;
|
|
82
138
|
}
|
|
139
|
+
// Extract rest parameters
|
|
140
|
+
const tempName = `temp_${name}`;
|
|
141
|
+
code += `${this.indent()}{\n`;
|
|
142
|
+
this.indentationLevel++;
|
|
143
|
+
code +=
|
|
144
|
+
`${this.indent()}std::vector<std::optional<jspp::AnyValue>> ${tempName};\n`;
|
|
145
|
+
code += `${this.indent()}if (${argsName}.size() > ${i}) {\n`;
|
|
146
|
+
this.indentationLevel++;
|
|
147
|
+
code +=
|
|
148
|
+
`${this.indent()}${tempName}.reserve(${argsName}.size() - ${i});\n`;
|
|
149
|
+
this.indentationLevel--;
|
|
150
|
+
code += `${this.indent()}}\n`;
|
|
151
|
+
code +=
|
|
152
|
+
`${this.indent()}for (size_t j = ${i}; j < ${argsName}.size(); j++) {\n`;
|
|
153
|
+
this.indentationLevel++;
|
|
154
|
+
code +=
|
|
155
|
+
`${this.indent()}${tempName}.push_back(${argsName}[j]);\n`;
|
|
156
|
+
this.indentationLevel--;
|
|
157
|
+
code += `${this.indent()}}\n`;
|
|
158
|
+
code += `${this.indent()}${typeInfo.needsHeapAllocation ? "*" : ""}${name} = jspp::AnyValue::make_array(std::move(${tempName}));\n`;
|
|
159
|
+
this.indentationLevel--;
|
|
160
|
+
code += `${this.indent()}}\n`;
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// Normal parameter
|
|
164
|
+
const initValue = `${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue}`;
|
|
165
|
+
if (typeInfo && typeInfo.needsHeapAllocation) {
|
|
166
|
+
code +=
|
|
167
|
+
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
code +=
|
|
171
|
+
`${this.indent()}jspp::AnyValue ${name} = ${initValue};\n`;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
return code;
|
|
175
|
+
};
|
|
176
|
+
if (node.body) {
|
|
177
|
+
if (ts.isBlock(node.body)) {
|
|
178
|
+
// Hoist var declarations in the function body
|
|
179
|
+
const varDecls = collectFunctionScopedDeclarations(node.body);
|
|
180
|
+
varDecls.forEach((decl) => {
|
|
181
|
+
preamble += this.hoistDeclaration(decl, visitContext.localScopeSymbols);
|
|
83
182
|
});
|
|
183
|
+
this.indentationLevel++;
|
|
184
|
+
const paramExtraction = paramExtractor(node.parameters);
|
|
84
185
|
this.indentationLevel--;
|
|
85
186
|
const blockContent = this.visit(node.body, {
|
|
86
187
|
...visitContext,
|
|
@@ -90,20 +191,15 @@ export function generateLambda(node, context, options) {
|
|
|
90
191
|
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
91
192
|
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
92
193
|
});
|
|
93
|
-
// The block visitor already adds braces, so we need to inject the param extraction.
|
|
94
|
-
lambda += "{\n" + paramExtraction +
|
|
194
|
+
// The block visitor already adds braces, so we need to inject the preamble and param extraction.
|
|
195
|
+
lambda += "{\n" + preamble + paramExtraction +
|
|
196
|
+
blockContent.trimStart().substring(2);
|
|
95
197
|
}
|
|
96
198
|
else {
|
|
97
199
|
lambda += "{\n";
|
|
98
200
|
this.indentationLevel++;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const defaultValue = p.initializer
|
|
102
|
-
? this.visit(p.initializer, visitContext)
|
|
103
|
-
: "jspp::AnyValue::make_undefined()";
|
|
104
|
-
lambda +=
|
|
105
|
-
`${this.indent()}auto ${name} = ${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue};\n`;
|
|
106
|
-
});
|
|
201
|
+
lambda += preamble;
|
|
202
|
+
lambda += paramExtractor(node.parameters);
|
|
107
203
|
lambda += `${this.indent()}${returnCmd} ${this.visit(node.body, {
|
|
108
204
|
...visitContext,
|
|
109
205
|
isMainContext: false,
|
|
@@ -111,34 +207,41 @@ export function generateLambda(node, context, options) {
|
|
|
111
207
|
isFunctionBody: false,
|
|
112
208
|
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
113
209
|
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
114
|
-
})}
|
|
115
|
-
`;
|
|
210
|
+
})};\n`;
|
|
116
211
|
this.indentationLevel--;
|
|
117
212
|
lambda += `${this.indent()}}`;
|
|
118
213
|
}
|
|
119
214
|
}
|
|
120
215
|
else {
|
|
121
|
-
lambda += `{ ${returnCmd} jspp::
|
|
216
|
+
lambda += `{ ${preamble} ${returnCmd} jspp::Constants::UNDEFINED; }\n`;
|
|
122
217
|
}
|
|
123
218
|
let signature = "";
|
|
124
219
|
let callable = "";
|
|
125
220
|
let method = "";
|
|
126
221
|
// Handle generator function
|
|
127
222
|
if (isInsideGeneratorFunction) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
223
|
+
if (isInsideAsyncFunction) {
|
|
224
|
+
signature =
|
|
225
|
+
"jspp::JsAsyncIterator<jspp::AnyValue>(const jspp::AnyValue&, std::span<const jspp::AnyValue>)";
|
|
226
|
+
callable = `std::function<${signature}>(${lambda})`;
|
|
227
|
+
method = `jspp::AnyValue::make_async_generator`;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
signature =
|
|
231
|
+
"jspp::JsIterator<jspp::AnyValue>(const jspp::AnyValue&, std::span<const jspp::AnyValue>)";
|
|
232
|
+
callable = `std::function<${signature}>(${lambda})`;
|
|
233
|
+
method = `jspp::AnyValue::make_generator`;
|
|
234
|
+
}
|
|
132
235
|
} // Handle async function
|
|
133
236
|
else if (isInsideAsyncFunction) {
|
|
134
237
|
signature =
|
|
135
|
-
"jspp::JsPromise(const jspp::AnyValue&,
|
|
238
|
+
"jspp::JsPromise(const jspp::AnyValue&, std::span<const jspp::AnyValue>)";
|
|
136
239
|
callable = `std::function<${signature}>(${lambda})`;
|
|
137
240
|
method = `jspp::AnyValue::make_async_function`;
|
|
138
241
|
} // Handle normal function
|
|
139
242
|
else {
|
|
140
243
|
signature =
|
|
141
|
-
`jspp::AnyValue(const jspp::AnyValue&,
|
|
244
|
+
`jspp::AnyValue(const jspp::AnyValue&, std::span<const jspp::AnyValue>)`;
|
|
142
245
|
callable = `std::function<${signature}>(${lambda})`;
|
|
143
246
|
if (options?.isClass) {
|
|
144
247
|
method = `jspp::AnyValue::make_class`;
|
|
@@ -147,9 +250,33 @@ export function generateLambda(node, context, options) {
|
|
|
147
250
|
method = `jspp::AnyValue::make_function`;
|
|
148
251
|
}
|
|
149
252
|
}
|
|
150
|
-
const funcName = node.name?.getText();
|
|
151
|
-
const
|
|
152
|
-
|
|
253
|
+
const funcName = context?.lambdaName || node.name?.getText();
|
|
254
|
+
const hasName = !!funcName && funcName.length > 0;
|
|
255
|
+
let args = callable;
|
|
256
|
+
const isMethod = ts.isMethodDeclaration(node);
|
|
257
|
+
const isAccessor = ts.isGetAccessor(node) || ts.isSetAccessor(node);
|
|
258
|
+
const isConstructor = !isArrow && !isMethod && !isAccessor;
|
|
259
|
+
// make_function(callable, name, is_constructor)
|
|
260
|
+
if (method === `jspp::AnyValue::make_function`) {
|
|
261
|
+
if (hasName) {
|
|
262
|
+
args += `, "${funcName}"`;
|
|
263
|
+
}
|
|
264
|
+
else if (!isConstructor) {
|
|
265
|
+
args += `, std::nullopt`;
|
|
266
|
+
}
|
|
267
|
+
if (!isConstructor) {
|
|
268
|
+
args += `, false`;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
// make_class, make_generator, make_async_function
|
|
273
|
+
if (hasName) {
|
|
274
|
+
args += `, "${funcName}"`;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const fullExpression = `${method}(${args})`;
|
|
278
|
+
if (ts.isFunctionDeclaration(node) && !isAssignment && node.name) {
|
|
279
|
+
const funcName = node.name?.getText();
|
|
153
280
|
return `${this.indent()}auto ${funcName} = ${fullExpression};\n`;
|
|
154
281
|
}
|
|
155
282
|
return fullExpression;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import { BUILTIN_OBJECTS, Scope } from "../../analysis/scope";
|
|
3
|
-
import { DeclaredSymbolType } from "../../ast/
|
|
3
|
+
import { DeclaredSymbols, DeclaredSymbolType } from "../../ast/symbols";
|
|
4
4
|
import { CodeGenerator } from "./";
|
|
5
5
|
export function isBuiltinObject(node) {
|
|
6
6
|
return BUILTIN_OBJECTS.values().some((obj) => obj.name === node.text);
|
|
@@ -33,23 +33,21 @@ export function getDeclaredSymbols(node) {
|
|
|
33
33
|
visitor(node);
|
|
34
34
|
return symbols;
|
|
35
35
|
}
|
|
36
|
-
export function generateUniqueName(prefix, namesToAvoid) {
|
|
37
|
-
let name = `${prefix}${this.
|
|
38
|
-
while (namesToAvoid.has(name)) {
|
|
39
|
-
this.
|
|
40
|
-
name = `${prefix}${this.
|
|
36
|
+
export function generateUniqueName(prefix, ...namesToAvoid) {
|
|
37
|
+
let name = `${prefix}${this.uniqueNameCounter}`;
|
|
38
|
+
while (namesToAvoid.some((names) => names.has(name))) {
|
|
39
|
+
this.uniqueNameCounter++;
|
|
40
|
+
name = `${prefix}${this.uniqueNameCounter}`;
|
|
41
41
|
}
|
|
42
|
-
this.
|
|
42
|
+
this.uniqueNameCounter++;
|
|
43
43
|
return name;
|
|
44
44
|
}
|
|
45
|
-
export function generateUniqueExceptionName(
|
|
46
|
-
let
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
exceptionName = `__caught_exception_${this.exceptionCounter}`;
|
|
45
|
+
export function generateUniqueExceptionName(exceptionNameToAvoid, ...otherNamesToAvoid) {
|
|
46
|
+
let prefix = `__caught_exception_`;
|
|
47
|
+
if (exceptionNameToAvoid) {
|
|
48
|
+
prefix += exceptionNameToAvoid;
|
|
50
49
|
}
|
|
51
|
-
this.
|
|
52
|
-
return exceptionName;
|
|
50
|
+
return this.generateUniqueName(prefix, ...otherNamesToAvoid);
|
|
53
51
|
}
|
|
54
52
|
export function getScopeForNode(node) {
|
|
55
53
|
let current = node;
|
|
@@ -80,19 +78,49 @@ export function escapeString(str) {
|
|
|
80
78
|
export function getJsVarName(node) {
|
|
81
79
|
return `"${node.text}"`;
|
|
82
80
|
}
|
|
83
|
-
export function getDerefCode(nodeText, varName, typeInfo) {
|
|
81
|
+
export function getDerefCode(nodeText, varName, context, typeInfo) {
|
|
84
82
|
// Make sure varName is incased in quotes
|
|
85
83
|
if (!varName.startsWith('"'))
|
|
86
84
|
varName = '"' + varName;
|
|
87
85
|
if (!varName.endsWith('"'))
|
|
88
86
|
varName = varName + '"';
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
const symbolName = varName.slice(1).slice(0, -1);
|
|
88
|
+
const symbol = context.localScopeSymbols.get(symbolName) ??
|
|
89
|
+
context.topLevelScopeSymbols.get(symbolName);
|
|
90
|
+
const checkedIfUninitialized = symbol?.checkedIfUninitialized ||
|
|
91
|
+
false;
|
|
92
|
+
// Mark the symbol as checked
|
|
93
|
+
this.markSymbolAsChecked(symbolName, context.topLevelScopeSymbols, context.localScopeSymbols);
|
|
94
|
+
// Apply deref code
|
|
95
|
+
if (checkedIfUninitialized) {
|
|
96
|
+
if (typeInfo && typeInfo.needsHeapAllocation) {
|
|
97
|
+
return `(*${nodeText})`;
|
|
98
|
+
}
|
|
99
|
+
return `${nodeText}`;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
if (typeInfo && typeInfo.needsHeapAllocation) {
|
|
103
|
+
return `jspp::Access::deref_ptr(${nodeText}, ${varName})`;
|
|
104
|
+
}
|
|
105
|
+
return `jspp::Access::deref_stack(${nodeText}, ${varName})`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export function markSymbolAsChecked(name, topLevel, local) {
|
|
109
|
+
if (topLevel.has(name)) {
|
|
110
|
+
topLevel.update(name, {
|
|
111
|
+
checkedIfUninitialized: true,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else if (local.has(name)) {
|
|
115
|
+
local.update(name, {
|
|
116
|
+
checkedIfUninitialized: true,
|
|
117
|
+
});
|
|
91
118
|
}
|
|
92
|
-
return `jspp::Access::deref_stack(${nodeText}, ${varName})`;
|
|
93
119
|
}
|
|
94
120
|
export function getReturnCommand(context) {
|
|
95
|
-
return (context.isInsideGeneratorFunction || context.isInsideAsyncFunction)
|
|
121
|
+
return (context.isInsideGeneratorFunction || context.isInsideAsyncFunction)
|
|
122
|
+
? "co_return"
|
|
123
|
+
: "return";
|
|
96
124
|
}
|
|
97
125
|
export function hoistDeclaration(decl, hoistedSymbols) {
|
|
98
126
|
const name = decl.name?.getText();
|
|
@@ -101,13 +129,16 @@ export function hoistDeclaration(decl, hoistedSymbols) {
|
|
|
101
129
|
}
|
|
102
130
|
const isLetOrConst = (decl.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
|
|
103
131
|
0;
|
|
132
|
+
// if (name === "letVal") console.log("hoistDeclaration: letVal isLetOrConst=", isLetOrConst, " flags=", decl.parent.flags);
|
|
104
133
|
const symbolType = isLetOrConst
|
|
105
134
|
? DeclaredSymbolType.letOrConst
|
|
106
135
|
: (ts.isFunctionDeclaration(decl)
|
|
107
136
|
? DeclaredSymbolType.function
|
|
108
|
-
: (ts.isClassDeclaration(decl)
|
|
137
|
+
: (ts.isClassDeclaration(decl)
|
|
138
|
+
? DeclaredSymbolType.letOrConst
|
|
139
|
+
: DeclaredSymbolType.var));
|
|
109
140
|
if (hoistedSymbols.has(name)) {
|
|
110
|
-
const existingType = hoistedSymbols.get(name);
|
|
141
|
+
const existingType = hoistedSymbols.get(name)?.type;
|
|
111
142
|
// Don't allow multiple declaration of `letOrConst` or `function` or `class` variables
|
|
112
143
|
if (existingType === DeclaredSymbolType.letOrConst ||
|
|
113
144
|
existingType === DeclaredSymbolType.function ||
|
|
@@ -117,12 +148,15 @@ export function hoistDeclaration(decl, hoistedSymbols) {
|
|
|
117
148
|
// `var` variables can be declared multiple times
|
|
118
149
|
return "";
|
|
119
150
|
}
|
|
120
|
-
hoistedSymbols.set(name,
|
|
151
|
+
hoistedSymbols.set(name, {
|
|
152
|
+
type: symbolType,
|
|
153
|
+
checkedIfUninitialized: false,
|
|
154
|
+
});
|
|
121
155
|
const scope = this.getScopeForNode(decl);
|
|
122
156
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
123
157
|
const initializer = isLetOrConst || ts.isClassDeclaration(decl)
|
|
124
|
-
? "jspp::
|
|
125
|
-
: "jspp::
|
|
158
|
+
? "jspp::Constants::UNINITIALIZED"
|
|
159
|
+
: "jspp::Constants::UNDEFINED";
|
|
126
160
|
if (typeInfo.needsHeapAllocation) {
|
|
127
161
|
return `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initializer});\n`;
|
|
128
162
|
}
|
|
@@ -142,10 +176,73 @@ export function isAsyncFunction(node) {
|
|
|
142
176
|
ts.isFunctionExpression(node) ||
|
|
143
177
|
ts.isMethodDeclaration(node) ||
|
|
144
178
|
ts.isArrowFunction(node)) &&
|
|
145
|
-
(ts.getCombinedModifierFlags(node) &
|
|
179
|
+
(ts.getCombinedModifierFlags(node) &
|
|
180
|
+
ts.ModifierFlags.Async) !== 0);
|
|
146
181
|
}
|
|
147
182
|
export function prepareScopeSymbolsForVisit(topLevel, local) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
183
|
+
// Join the top and local scopes
|
|
184
|
+
return new DeclaredSymbols(topLevel, local);
|
|
185
|
+
}
|
|
186
|
+
export function collectFunctionScopedDeclarations(node) {
|
|
187
|
+
const decls = [];
|
|
188
|
+
function visit(n) {
|
|
189
|
+
if (ts.isVariableStatement(n)) {
|
|
190
|
+
const isLetOrConst = (n.declarationList.flags &
|
|
191
|
+
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
|
|
192
|
+
if (!isLetOrConst) {
|
|
193
|
+
decls.push(...n.declarationList.declarations);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else if (ts.isForStatement(n)) {
|
|
197
|
+
if (n.initializer && ts.isVariableDeclarationList(n.initializer)) {
|
|
198
|
+
const isLetOrConst = (n.initializer.flags &
|
|
199
|
+
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
|
|
200
|
+
if (!isLetOrConst) {
|
|
201
|
+
decls.push(...n.initializer.declarations);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else if (ts.isForInStatement(n)) {
|
|
206
|
+
if (ts.isVariableDeclarationList(n.initializer)) {
|
|
207
|
+
const isLetOrConst = (n.initializer.flags &
|
|
208
|
+
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
|
|
209
|
+
if (!isLetOrConst) {
|
|
210
|
+
decls.push(...n.initializer.declarations);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
else if (ts.isForOfStatement(n)) {
|
|
215
|
+
if (ts.isVariableDeclarationList(n.initializer)) {
|
|
216
|
+
const isLetOrConst = (n.initializer.flags &
|
|
217
|
+
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
|
|
218
|
+
if (!isLetOrConst) {
|
|
219
|
+
decls.push(...n.initializer.declarations);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// Stop recursion at function boundaries (but not the root node if it is a function)
|
|
224
|
+
if (n !== node &&
|
|
225
|
+
(ts.isFunctionDeclaration(n) || ts.isFunctionExpression(n) ||
|
|
226
|
+
ts.isArrowFunction(n) || ts.isMethodDeclaration(n) ||
|
|
227
|
+
ts.isGetAccessor(n) || ts.isSetAccessor(n) ||
|
|
228
|
+
ts.isClassDeclaration(n))) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
ts.forEachChild(n, visit);
|
|
232
|
+
}
|
|
233
|
+
ts.forEachChild(node, visit);
|
|
234
|
+
return decls;
|
|
235
|
+
}
|
|
236
|
+
export function collectBlockScopedDeclarations(statements) {
|
|
237
|
+
const decls = [];
|
|
238
|
+
for (const stmt of statements) {
|
|
239
|
+
if (ts.isVariableStatement(stmt)) {
|
|
240
|
+
const isLetOrConst = (stmt.declarationList.flags &
|
|
241
|
+
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
|
|
242
|
+
if (isLetOrConst) {
|
|
243
|
+
decls.push(...stmt.declarationList.declarations);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return decls;
|
|
151
248
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DeclaredSymbols } from "../../ast/symbols";
|
|
2
2
|
import { generateLambda } from "./function-handlers";
|
|
3
|
-
import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isGeneratorFunction, prepareScopeSymbolsForVisit, } from "./helpers";
|
|
3
|
+
import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isGeneratorFunction, markSymbolAsChecked, prepareScopeSymbolsForVisit, } from "./helpers";
|
|
4
4
|
import { visit } from "./visitor";
|
|
5
|
-
const
|
|
5
|
+
const MODULE_NAME = "__main_function__";
|
|
6
6
|
export class CodeGenerator {
|
|
7
7
|
indentationLevel = 0;
|
|
8
8
|
typeAnalyzer;
|
|
9
9
|
globalThisVar;
|
|
10
|
-
|
|
10
|
+
uniqueNameCounter = 0;
|
|
11
11
|
// visitor
|
|
12
12
|
visit = visit;
|
|
13
13
|
// helpers
|
|
@@ -25,6 +25,7 @@ export class CodeGenerator {
|
|
|
25
25
|
isGeneratorFunction = isGeneratorFunction;
|
|
26
26
|
isAsyncFunction = isAsyncFunction;
|
|
27
27
|
prepareScopeSymbolsForVisit = prepareScopeSymbolsForVisit;
|
|
28
|
+
markSymbolAsChecked = markSymbolAsChecked;
|
|
28
29
|
// function handlers
|
|
29
30
|
generateLambda = generateLambda;
|
|
30
31
|
/**
|
|
@@ -34,7 +35,7 @@ export class CodeGenerator {
|
|
|
34
35
|
this.typeAnalyzer = analyzer;
|
|
35
36
|
this.globalThisVar = this.generateUniqueName("__this_val__", this.getDeclaredSymbols(ast));
|
|
36
37
|
const declarations = `#include "index.hpp"\n\n`;
|
|
37
|
-
let containerCode = `jspp::
|
|
38
|
+
let containerCode = `jspp::JsPromise ${MODULE_NAME}() {\n`;
|
|
38
39
|
this.indentationLevel++;
|
|
39
40
|
containerCode +=
|
|
40
41
|
`${this.indent()}jspp::AnyValue ${this.globalThisVar} = global;\n`;
|
|
@@ -42,24 +43,31 @@ export class CodeGenerator {
|
|
|
42
43
|
isMainContext: true,
|
|
43
44
|
isInsideFunction: true,
|
|
44
45
|
isFunctionBody: true,
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
isInsideAsyncFunction: true,
|
|
47
|
+
topLevelScopeSymbols: new DeclaredSymbols(),
|
|
48
|
+
localScopeSymbols: new DeclaredSymbols(),
|
|
47
49
|
});
|
|
48
50
|
this.indentationLevel--;
|
|
49
|
-
containerCode += "
|
|
51
|
+
containerCode += " co_return jspp::Constants::UNDEFINED;\n";
|
|
50
52
|
containerCode += "}\n\n";
|
|
51
|
-
let mainCode = "int main() {\n";
|
|
52
|
-
// std::ios::sync_with_stdio(false); // Removed to fix console output buffering
|
|
53
|
-
// std::cin.tie(nullptr); // Removed to fix console output buffering
|
|
53
|
+
let mainCode = "int main(int argc, char** argv) {\n";
|
|
54
54
|
mainCode += ` try {\n`;
|
|
55
|
-
mainCode += `
|
|
55
|
+
mainCode += ` jspp::setup_process_argv(argc, argv);\n`;
|
|
56
|
+
mainCode += ` auto p = ${MODULE_NAME}();\n`;
|
|
57
|
+
mainCode += ` p.then(nullptr, [](const jspp::AnyValue& err) {\n`;
|
|
58
|
+
mainCode +=
|
|
59
|
+
` auto error = std::make_shared<jspp::AnyValue>(err);\n`;
|
|
60
|
+
mainCode +=
|
|
61
|
+
` console.call_own_property("error", std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1));\n`;
|
|
62
|
+
mainCode += ` std::exit(1);\n`;
|
|
63
|
+
mainCode += ` });\n`;
|
|
56
64
|
mainCode += ` jspp::Scheduler::instance().run();\n`;
|
|
57
65
|
mainCode += ` } catch (const std::exception& ex) {\n`;
|
|
58
66
|
mainCode +=
|
|
59
|
-
" auto error = std::make_shared<jspp::AnyValue>(jspp::Exception::exception_to_any_value(ex));\n{\n";
|
|
67
|
+
" auto error = std::make_shared<jspp::AnyValue>(jspp::Exception::exception_to_any_value(ex));\n {\n";
|
|
60
68
|
mainCode +=
|
|
61
|
-
`
|
|
62
|
-
mainCode += `
|
|
69
|
+
` console.call_own_property("error", std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1));\n`;
|
|
70
|
+
mainCode += ` return 1;\n }\n`;
|
|
63
71
|
mainCode += ` }\n`;
|
|
64
72
|
mainCode += " return 0;\n}";
|
|
65
73
|
return declarations + containerCode + mainCode;
|