@ugo-studio/jspp 0.2.6 → 0.2.7
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/typeAnalyzer.js +56 -24
- package/dist/ast/symbols.js +28 -19
- package/dist/cli/index.js +1 -1
- package/dist/core/codegen/class-handlers.js +8 -8
- package/dist/core/codegen/control-flow-handlers.js +17 -7
- package/dist/core/codegen/declaration-handlers.js +21 -9
- package/dist/core/codegen/expression-handlers.js +558 -126
- package/dist/core/codegen/function-handlers.js +101 -108
- package/dist/core/codegen/helpers.js +28 -7
- package/dist/core/codegen/index.js +6 -4
- package/dist/core/codegen/literal-handlers.js +4 -2
- package/dist/core/codegen/statement-handlers.js +39 -19
- package/package.json +1 -1
- package/src/prelude/any_value.hpp +89 -59
- package/src/prelude/any_value_access.hpp +1 -1
- package/src/prelude/any_value_helpers.hpp +85 -43
- package/src/prelude/index.hpp +1 -0
- package/src/prelude/library/array.hpp +3 -2
- package/src/prelude/types.hpp +8 -8
- package/src/prelude/utils/access.hpp +62 -6
- package/src/prelude/utils/assignment_operators.hpp +14 -14
- package/src/prelude/utils/log_any_value/array.hpp +0 -15
- package/src/prelude/utils/log_any_value/primitives.hpp +2 -0
- package/src/prelude/utils/operators.hpp +117 -474
- package/src/prelude/utils/operators_primitive.hpp +337 -0
- package/src/prelude/values/helpers/array.hpp +4 -4
- package/src/prelude/values/helpers/async_iterator.hpp +2 -2
- package/src/prelude/values/helpers/function.hpp +3 -3
- package/src/prelude/values/helpers/iterator.hpp +2 -2
- package/src/prelude/values/helpers/object.hpp +3 -3
- package/src/prelude/values/helpers/promise.hpp +1 -1
- package/src/prelude/values/helpers/string.hpp +1 -1
- package/src/prelude/values/helpers/symbol.hpp +1 -1
- package/src/prelude/values/prototypes/array.hpp +1125 -853
- package/src/prelude/values/prototypes/async_iterator.hpp +32 -14
- package/src/prelude/values/prototypes/function.hpp +30 -18
- package/src/prelude/values/prototypes/iterator.hpp +40 -17
- package/src/prelude/values/prototypes/number.hpp +119 -62
- package/src/prelude/values/prototypes/object.hpp +10 -4
- package/src/prelude/values/prototypes/promise.hpp +167 -109
- package/src/prelude/values/prototypes/string.hpp +407 -231
- package/src/prelude/values/prototypes/symbol.hpp +45 -23
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import { DeclarationType, DeclaredSymbols } from "../../ast/symbols.js";
|
|
3
|
-
import { CompilerError } from "../error.js";
|
|
4
3
|
import { collectFunctionScopedDeclarations } from "./helpers.js";
|
|
5
4
|
import { CodeGenerator } from "./index.js";
|
|
6
|
-
export function
|
|
7
|
-
const capture = options?.capture || "[=]";
|
|
8
|
-
const nativeName = options?.nativeName;
|
|
5
|
+
export function generateLambdaComponents(node, context, options) {
|
|
9
6
|
const declaredSymbols = this.getDeclaredSymbols(node);
|
|
10
7
|
const argsName = this.generateUniqueName("__args_", declaredSymbols, context.globalScopeSymbols, context.localScopeSymbols);
|
|
11
8
|
const isInsideGeneratorFunction = this.isGeneratorFunction(node);
|
|
12
9
|
const isInsideAsyncFunction = this.isAsyncFunction(node);
|
|
13
|
-
const
|
|
10
|
+
const isArrow = ts.isArrowFunction(node);
|
|
11
|
+
const returnCommand = this.getReturnCommand({
|
|
14
12
|
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
15
13
|
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
16
14
|
});
|
|
@@ -19,17 +17,10 @@ export function generateLambda(node, context, options) {
|
|
|
19
17
|
: (isInsideGeneratorFunction
|
|
20
18
|
? "jspp::JsIterator<jspp::AnyValue>"
|
|
21
19
|
: (isInsideAsyncFunction ? "jspp::JsPromise" : "jspp::AnyValue"));
|
|
22
|
-
const isArrow = ts.isArrowFunction(node);
|
|
23
20
|
// Lambda arguments are ALWAYS const references/spans to avoid copy overhead for normal functions.
|
|
24
21
|
// For generators/async, we manually copy them inside the body.
|
|
25
22
|
const paramThisType = "const jspp::AnyValue&";
|
|
26
23
|
const paramArgsType = "std::span<const jspp::AnyValue>";
|
|
27
|
-
const selfParamPart = nativeName ? `this auto&& ${nativeName}, ` : "";
|
|
28
|
-
const mutablePart = nativeName ? "" : "mutable ";
|
|
29
|
-
const thisArgParam = isArrow
|
|
30
|
-
? "const jspp::AnyValue&" // Arrow functions use captured 'this' or are never generators in this parser context
|
|
31
|
-
: `${paramThisType} ${this.globalThisVar}`;
|
|
32
|
-
let lambda = `${capture}(${selfParamPart}${thisArgParam}, ${paramArgsType} ${argsName}) ${mutablePart}-> ${funcReturnType} `;
|
|
33
24
|
const globalScopeSymbols = this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols);
|
|
34
25
|
const visitContext = {
|
|
35
26
|
currentScopeNode: context.currentScopeNode,
|
|
@@ -43,18 +34,6 @@ export function generateLambda(node, context, options) {
|
|
|
43
34
|
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
44
35
|
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
45
36
|
};
|
|
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
|
-
}
|
|
55
|
-
preamble +=
|
|
56
|
-
`${this.indent()}std::vector<jspp::AnyValue> ${argsCopy}(${argsName}.begin(), ${argsName}.end());\n`;
|
|
57
|
-
}
|
|
58
37
|
// Adjust parameter names for generator/async to allow shadowing/copying
|
|
59
38
|
let finalThisParamName = isArrow ? "" : this.globalThisVar;
|
|
60
39
|
let finalArgsParamName = argsName;
|
|
@@ -64,37 +43,44 @@ export function generateLambda(node, context, options) {
|
|
|
64
43
|
}
|
|
65
44
|
finalArgsParamName = this.generateUniqueName("__args_ref", declaredSymbols);
|
|
66
45
|
}
|
|
67
|
-
const
|
|
68
|
-
? "const jspp::AnyValue&"
|
|
46
|
+
const thisArgParam = isArrow
|
|
47
|
+
? "const jspp::AnyValue&" // Arrow functions use captured 'this' or are never generators in this parser context
|
|
69
48
|
: `${paramThisType} ${finalThisParamName}`;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
preamble = "";
|
|
49
|
+
const funcArgs = `${paramArgsType} ${finalArgsParamName}`;
|
|
50
|
+
// Preamble to copy arguments for coroutines
|
|
51
|
+
let preamble = "";
|
|
52
|
+
let nativePreamble = "";
|
|
75
53
|
if (isInsideGeneratorFunction || isInsideAsyncFunction) {
|
|
76
54
|
if (!isArrow) {
|
|
77
55
|
preamble +=
|
|
78
56
|
`${this.indent()}jspp::AnyValue ${this.globalThisVar} = ${finalThisParamName};\n`;
|
|
57
|
+
nativePreamble +=
|
|
58
|
+
`${this.indent()}jspp::AnyValue ${this.globalThisVar} = ${finalThisParamName};\n`;
|
|
79
59
|
}
|
|
60
|
+
// Note: Do not add argument copy to native lambda
|
|
80
61
|
preamble +=
|
|
81
62
|
`${this.indent()}std::vector<jspp::AnyValue> ${argsName}(${finalArgsParamName}.begin(), ${finalArgsParamName}.end());\n`;
|
|
82
63
|
}
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
64
|
+
// Native function arguments for native lambda
|
|
65
|
+
let nativeFuncArgs = "";
|
|
66
|
+
let nativeParamsContent = "";
|
|
67
|
+
this.validateFunctionParams(node.parameters).forEach((p, i) => {
|
|
68
|
+
const name = p.name.getText();
|
|
69
|
+
const defaultValue = p.initializer
|
|
70
|
+
? this.visit(p.initializer, visitContext)
|
|
71
|
+
: !!p.dotDotDotToken
|
|
72
|
+
? "jspp::AnyValue::make_array(std::vector<jspp::AnyValue>{})"
|
|
73
|
+
: "jspp::Constants::UNDEFINED";
|
|
74
|
+
nativeFuncArgs += `, const jspp::AnyValue& ${name} = ${defaultValue}`;
|
|
75
|
+
});
|
|
76
|
+
// Extract lambda parameters from arguments span/vector
|
|
77
|
+
const generateParamsBuilder = () => {
|
|
78
|
+
let paramsCode = "";
|
|
79
|
+
this.validateFunctionParams(node.parameters).forEach((p, i) => {
|
|
90
80
|
const name = p.name.getText();
|
|
91
81
|
const defaultValue = p.initializer
|
|
92
82
|
? this.visit(p.initializer, visitContext)
|
|
93
83
|
: "jspp::Constants::UNDEFINED";
|
|
94
|
-
// Catch invalid parameters
|
|
95
|
-
if (name === "this") {
|
|
96
|
-
throw new CompilerError(`Cannot use '${name}' as a parameter name.`, p, "SyntaxError");
|
|
97
|
-
}
|
|
98
84
|
// Add paramerter to local context
|
|
99
85
|
visitContext.localScopeSymbols.add(name, {
|
|
100
86
|
type: DeclarationType.let,
|
|
@@ -104,108 +90,114 @@ export function generateLambda(node, context, options) {
|
|
|
104
90
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
105
91
|
// Handle rest parameters
|
|
106
92
|
if (!!p.dotDotDotToken) {
|
|
107
|
-
|
|
108
|
-
throw new CompilerError("Rest parameter must be last formal parameter.", p, "SyntaxError");
|
|
109
|
-
}
|
|
93
|
+
const initValue = `jspp::AnyValue::make_array(${argsName}.subspan(${i}))`;
|
|
110
94
|
if (typeInfo.needsHeapAllocation) {
|
|
111
|
-
|
|
112
|
-
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(
|
|
95
|
+
paramsCode +=
|
|
96
|
+
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
|
|
113
97
|
}
|
|
114
98
|
else {
|
|
115
|
-
|
|
116
|
-
`${this.indent()}jspp::AnyValue ${name} =
|
|
99
|
+
paramsCode +=
|
|
100
|
+
`${this.indent()}jspp::AnyValue ${name} = ${initValue};\n`;
|
|
117
101
|
}
|
|
118
|
-
// Extract rest parameters
|
|
119
|
-
const tempName = `temp_${name}`;
|
|
120
|
-
code += `${this.indent()}{\n`;
|
|
121
|
-
this.indentationLevel++;
|
|
122
|
-
code +=
|
|
123
|
-
`${this.indent()}std::vector<std::optional<jspp::AnyValue>> ${tempName};\n`;
|
|
124
|
-
code += `${this.indent()}if (${argsName}.size() > ${i}) {\n`;
|
|
125
|
-
this.indentationLevel++;
|
|
126
|
-
code +=
|
|
127
|
-
`${this.indent()}${tempName}.reserve(${argsName}.size() - ${i});\n`;
|
|
128
|
-
this.indentationLevel--;
|
|
129
|
-
code += `${this.indent()}}\n`;
|
|
130
|
-
code +=
|
|
131
|
-
`${this.indent()}for (size_t j = ${i}; j < ${argsName}.size(); j++) {\n`;
|
|
132
|
-
this.indentationLevel++;
|
|
133
|
-
code +=
|
|
134
|
-
`${this.indent()}${tempName}.push_back(${argsName}[j]);\n`;
|
|
135
|
-
this.indentationLevel--;
|
|
136
|
-
code += `${this.indent()}}\n`;
|
|
137
|
-
code += `${this.indent()}${typeInfo.needsHeapAllocation ? "*" : ""}${name} = jspp::AnyValue::make_array(std::move(${tempName}));\n`;
|
|
138
|
-
this.indentationLevel--;
|
|
139
|
-
code += `${this.indent()}}\n`;
|
|
140
102
|
return;
|
|
141
103
|
}
|
|
142
104
|
// Normal parameter
|
|
143
105
|
const initValue = `${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue}`;
|
|
144
106
|
if (typeInfo && typeInfo.needsHeapAllocation) {
|
|
145
|
-
|
|
107
|
+
paramsCode +=
|
|
146
108
|
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
|
|
147
109
|
}
|
|
148
110
|
else {
|
|
149
|
-
|
|
111
|
+
paramsCode +=
|
|
150
112
|
`${this.indent()}jspp::AnyValue ${name} = ${initValue};\n`;
|
|
151
113
|
}
|
|
152
114
|
});
|
|
153
|
-
return
|
|
115
|
+
return paramsCode;
|
|
154
116
|
};
|
|
117
|
+
// Generate params and function body
|
|
118
|
+
let paramsContent = "";
|
|
119
|
+
let blockContentWithoutOpeningBrace = "";
|
|
155
120
|
if (node.body) {
|
|
156
121
|
if (ts.isBlock(node.body)) {
|
|
157
122
|
// Hoist var declarations in the function body
|
|
158
123
|
const varDecls = collectFunctionScopedDeclarations(node.body);
|
|
159
124
|
varDecls.forEach((decl) => {
|
|
160
|
-
|
|
125
|
+
const hoistCode = this.hoistDeclaration(decl, visitContext.localScopeSymbols, node.body);
|
|
126
|
+
preamble += hoistCode;
|
|
127
|
+
nativePreamble += hoistCode;
|
|
161
128
|
});
|
|
162
129
|
this.indentationLevel++;
|
|
163
|
-
|
|
130
|
+
paramsContent = generateParamsBuilder();
|
|
164
131
|
this.indentationLevel--;
|
|
165
|
-
|
|
132
|
+
// The block visitor already adds braces, so we need to remove the opening brace to inject the preamble and param extraction.
|
|
133
|
+
blockContentWithoutOpeningBrace = this.visit(node.body, {
|
|
166
134
|
...visitContext,
|
|
167
135
|
isMainContext: false,
|
|
168
136
|
isInsideFunction: true,
|
|
169
137
|
isFunctionBody: true,
|
|
170
138
|
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
171
139
|
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
172
|
-
});
|
|
173
|
-
// The block visitor already adds braces, so we need to inject the preamble and param extraction.
|
|
174
|
-
lambda += "{\n" + preamble + paramExtraction +
|
|
175
|
-
blockContent.trimStart().substring(2);
|
|
140
|
+
}).trimStart().substring(2);
|
|
176
141
|
}
|
|
177
142
|
else {
|
|
178
|
-
lambda += "{\n";
|
|
179
143
|
this.indentationLevel++;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
144
|
+
paramsContent = generateParamsBuilder();
|
|
145
|
+
blockContentWithoutOpeningBrace =
|
|
146
|
+
`${this.indent()}${returnCommand} ${this.visit(node.body, {
|
|
147
|
+
...visitContext,
|
|
148
|
+
isMainContext: false,
|
|
149
|
+
isInsideFunction: true,
|
|
150
|
+
isFunctionBody: false,
|
|
151
|
+
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
152
|
+
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
153
|
+
})};\n`;
|
|
190
154
|
this.indentationLevel--;
|
|
191
|
-
|
|
155
|
+
blockContentWithoutOpeningBrace += `${this.indent()}}`;
|
|
192
156
|
}
|
|
193
157
|
}
|
|
194
158
|
else {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
// Return only lambda if required
|
|
198
|
-
if (options?.generateOnlyLambda) {
|
|
199
|
-
return lambda.trimEnd();
|
|
159
|
+
blockContentWithoutOpeningBrace =
|
|
160
|
+
`${returnCommand} jspp::Constants::UNDEFINED; }\n`;
|
|
200
161
|
}
|
|
201
|
-
return
|
|
162
|
+
return {
|
|
163
|
+
node,
|
|
164
|
+
context,
|
|
165
|
+
options,
|
|
166
|
+
visitContext,
|
|
167
|
+
thisArgParam,
|
|
168
|
+
funcArgs,
|
|
169
|
+
nativeFuncArgs,
|
|
170
|
+
funcReturnType,
|
|
171
|
+
preamble,
|
|
172
|
+
nativePreamble,
|
|
173
|
+
paramsContent,
|
|
174
|
+
nativeParamsContent,
|
|
175
|
+
blockContentWithoutOpeningBrace,
|
|
176
|
+
isInsideGeneratorFunction,
|
|
177
|
+
isInsideAsyncFunction,
|
|
178
|
+
};
|
|
202
179
|
}
|
|
203
|
-
export function
|
|
180
|
+
export function generateNativeLambda(comps) {
|
|
181
|
+
const { options, thisArgParam, nativeFuncArgs, funcReturnType, nativePreamble, nativeParamsContent, blockContentWithoutOpeningBrace, } = comps;
|
|
182
|
+
const capture = options?.capture || "[=]";
|
|
183
|
+
const nativeName = options?.nativeName;
|
|
184
|
+
const selfParam = nativeName ? `this auto&& ${nativeName}, ` : "";
|
|
185
|
+
const mutableLabel = nativeName ? "" : "mutable ";
|
|
186
|
+
let lambda = `${capture}(${selfParam}${thisArgParam}${nativeFuncArgs}) ${mutableLabel}-> ${funcReturnType} {\n`;
|
|
187
|
+
lambda += nativePreamble;
|
|
188
|
+
lambda += nativeParamsContent;
|
|
189
|
+
lambda += blockContentWithoutOpeningBrace;
|
|
190
|
+
return lambda;
|
|
191
|
+
}
|
|
192
|
+
export function generateWrappedLambda(comps) {
|
|
193
|
+
const { node, context, options, thisArgParam, funcArgs, funcReturnType, preamble, paramsContent, blockContentWithoutOpeningBrace, isInsideGeneratorFunction, isInsideAsyncFunction, } = comps;
|
|
194
|
+
const capture = options?.capture || "[=]";
|
|
204
195
|
const isAssignment = options?.isAssignment || false;
|
|
205
196
|
const noTypeSignature = options?.noTypeSignature || false;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
197
|
+
let lambda = `${capture}(${thisArgParam}, ${funcArgs}) mutable -> ${funcReturnType} {\n`;
|
|
198
|
+
lambda += preamble;
|
|
199
|
+
lambda += paramsContent;
|
|
200
|
+
lambda += blockContentWithoutOpeningBrace;
|
|
209
201
|
let callable = lambda;
|
|
210
202
|
let method = "";
|
|
211
203
|
// Handle generator function
|
|
@@ -247,6 +239,7 @@ export function generateFullLambdaExpression(node, context, lambda, options) {
|
|
|
247
239
|
const funcName = context?.lambdaName || node.name?.getText();
|
|
248
240
|
const hasName = !!funcName && funcName.length > 0;
|
|
249
241
|
let args = callable;
|
|
242
|
+
const isArrow = ts.isArrowFunction(node);
|
|
250
243
|
const isMethod = ts.isMethodDeclaration(node);
|
|
251
244
|
const isAccessor = ts.isGetAccessor(node) || ts.isSetAccessor(node);
|
|
252
245
|
const isConstructor = !isArrow && !isMethod && !isAccessor;
|
|
@@ -280,12 +273,12 @@ export function visitFunctionDeclaration(node, context) {
|
|
|
280
273
|
// This will now be handled by the Block visitor for hoisting.
|
|
281
274
|
// However, we still need to generate the lambda for assignment.
|
|
282
275
|
// The block visitor will wrap this in an assignment.
|
|
283
|
-
return this.
|
|
276
|
+
return this.generateWrappedLambda(this.generateLambdaComponents(node, context));
|
|
284
277
|
}
|
|
285
278
|
return "";
|
|
286
279
|
}
|
|
287
280
|
export function visitArrowFunction(node, context) {
|
|
288
|
-
return this.
|
|
281
|
+
return this.generateWrappedLambda(this.generateLambdaComponents(node, context));
|
|
289
282
|
}
|
|
290
283
|
export function visitFunctionExpression(node, context) {
|
|
291
284
|
const funcExpr = node;
|
|
@@ -295,18 +288,18 @@ export function visitFunctionExpression(node, context) {
|
|
|
295
288
|
this.indentationLevel++;
|
|
296
289
|
code +=
|
|
297
290
|
`${this.indent()}auto ${funcName} = std::make_shared<jspp::AnyValue>();\n`;
|
|
298
|
-
const lambda = this.
|
|
291
|
+
const lambda = this.generateWrappedLambda(this.generateLambdaComponents(funcExpr, {
|
|
299
292
|
...context,
|
|
300
293
|
lambdaName: funcName,
|
|
301
294
|
}, {
|
|
302
295
|
isAssignment: true,
|
|
303
296
|
capture: "[=]",
|
|
304
|
-
});
|
|
297
|
+
}));
|
|
305
298
|
code += `${this.indent()}*${funcName} = ${lambda};\n`;
|
|
306
299
|
code += `${this.indent()}return *${funcName};\n`;
|
|
307
300
|
this.indentationLevel--;
|
|
308
301
|
code += `${this.indent()}})()`;
|
|
309
302
|
return code;
|
|
310
303
|
}
|
|
311
|
-
return this.
|
|
304
|
+
return this.generateWrappedLambda(this.generateLambdaComponents(node, context));
|
|
312
305
|
}
|
|
@@ -167,7 +167,7 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
|
167
167
|
const isGenerator = this.isGeneratorFunction(decl);
|
|
168
168
|
hoistedSymbols.add(name, {
|
|
169
169
|
type: declType,
|
|
170
|
-
|
|
170
|
+
features: { isAsync, isGenerator },
|
|
171
171
|
});
|
|
172
172
|
// Don't hoist functions not used as a variable
|
|
173
173
|
// they will be called with their native lambdas
|
|
@@ -293,14 +293,14 @@ export function collectBlockScopedDeclarations(statements) {
|
|
|
293
293
|
return decls;
|
|
294
294
|
}
|
|
295
295
|
export function isFunctionUsedAsValue(decl, root) {
|
|
296
|
-
const
|
|
297
|
-
if (!
|
|
296
|
+
const name = decl.name?.getText();
|
|
297
|
+
if (!name)
|
|
298
298
|
return false;
|
|
299
299
|
let isUsed = false;
|
|
300
300
|
const visitor = (node) => {
|
|
301
301
|
if (isUsed)
|
|
302
302
|
return;
|
|
303
|
-
if (ts.isIdentifier(node) && node.text ===
|
|
303
|
+
if (ts.isIdentifier(node) && node.text === name) {
|
|
304
304
|
const scope = this.getScopeForNode(node);
|
|
305
305
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(node.text, scope);
|
|
306
306
|
if (typeInfo?.declaration === decl) {
|
|
@@ -336,14 +336,14 @@ export function isFunctionUsedAsValue(decl, root) {
|
|
|
336
336
|
ts.forEachChild(root, visitor);
|
|
337
337
|
return isUsed;
|
|
338
338
|
}
|
|
339
|
-
export function isFunctionUsedBeforeDeclaration(
|
|
339
|
+
export function isFunctionUsedBeforeDeclaration(name, root) {
|
|
340
340
|
let declPos = -1;
|
|
341
341
|
let foundDecl = false;
|
|
342
342
|
// Helper to find the function declaration position
|
|
343
343
|
function findDecl(node) {
|
|
344
344
|
if (foundDecl)
|
|
345
345
|
return;
|
|
346
|
-
if (ts.isFunctionDeclaration(node) && node.name?.text ===
|
|
346
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === name) {
|
|
347
347
|
declPos = node.getStart();
|
|
348
348
|
foundDecl = true;
|
|
349
349
|
}
|
|
@@ -359,7 +359,7 @@ export function isFunctionUsedBeforeDeclaration(funcName, root) {
|
|
|
359
359
|
function visit(node) {
|
|
360
360
|
if (isUsedBefore)
|
|
361
361
|
return;
|
|
362
|
-
if (ts.isIdentifier(node) && node.text ===
|
|
362
|
+
if (ts.isIdentifier(node) && node.text === name) {
|
|
363
363
|
const parent = node.parent;
|
|
364
364
|
// Ignore declarations where the identifier is the name being declared
|
|
365
365
|
if ((ts.isFunctionDeclaration(parent) ||
|
|
@@ -410,3 +410,24 @@ export function isFunctionUsedBeforeDeclaration(funcName, root) {
|
|
|
410
410
|
visit(root);
|
|
411
411
|
return isUsedBefore;
|
|
412
412
|
}
|
|
413
|
+
export function validateFunctionParams(parameters) {
|
|
414
|
+
return parameters.filter((p) => {
|
|
415
|
+
if (p.name.getText() === "this") {
|
|
416
|
+
if (!this.isTypescript) {
|
|
417
|
+
// Throw error for "this" parameters in javascript files
|
|
418
|
+
throw new CompilerError('Cannot use "this" as a parameter name.', p, "SyntaxError");
|
|
419
|
+
}
|
|
420
|
+
else
|
|
421
|
+
return false; // Ignore "this" parameters in typescript files
|
|
422
|
+
}
|
|
423
|
+
else
|
|
424
|
+
return true;
|
|
425
|
+
}).map((p, i, parameters) => {
|
|
426
|
+
if (!!p.dotDotDotToken) {
|
|
427
|
+
if (parameters.length - 1 !== i) {
|
|
428
|
+
throw new CompilerError("Rest parameter must be last formal parameter.", p, "SyntaxError");
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return p;
|
|
432
|
+
}) || [];
|
|
433
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DeclaredSymbols } from "../../ast/symbols.js";
|
|
2
|
-
import {
|
|
3
|
-
import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isFunctionUsedAsValue, isFunctionUsedBeforeDeclaration, isGeneratorFunction, markSymbolAsInitialized, prepareScopeSymbolsForVisit, } from "./helpers.js";
|
|
2
|
+
import { generateLambdaComponents, generateNativeLambda, generateWrappedLambda, } from "./function-handlers.js";
|
|
3
|
+
import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isFunctionUsedAsValue, isFunctionUsedBeforeDeclaration, isGeneratorFunction, markSymbolAsInitialized, prepareScopeSymbolsForVisit, validateFunctionParams, } from "./helpers.js";
|
|
4
4
|
import { visit } from "./visitor.js";
|
|
5
5
|
const MODULE_NAME = "__entry_point__";
|
|
6
6
|
export class CodeGenerator {
|
|
@@ -29,9 +29,11 @@ export class CodeGenerator {
|
|
|
29
29
|
markSymbolAsInitialized = markSymbolAsInitialized;
|
|
30
30
|
isFunctionUsedAsValue = isFunctionUsedAsValue;
|
|
31
31
|
isFunctionUsedBeforeDeclaration = isFunctionUsedBeforeDeclaration;
|
|
32
|
+
validateFunctionParams = validateFunctionParams;
|
|
32
33
|
// function handlers
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
generateLambdaComponents = generateLambdaComponents;
|
|
35
|
+
generateNativeLambda = generateNativeLambda;
|
|
36
|
+
generateWrappedLambda = generateWrappedLambda;
|
|
35
37
|
/**
|
|
36
38
|
* Main entry point for the code generation process.
|
|
37
39
|
*/
|
|
@@ -11,9 +11,11 @@ export function visitIdentifier(node) {
|
|
|
11
11
|
return node.text;
|
|
12
12
|
}
|
|
13
13
|
export function visitNumericLiteral(node) {
|
|
14
|
-
if (node.text === "0")
|
|
14
|
+
if (node.text === "0" || (node.text.startsWith("0.") &&
|
|
15
|
+
!node.text.substring(2).split("").some((c) => c !== "0")))
|
|
15
16
|
return "jspp::Constants::ZERO";
|
|
16
|
-
if (node.text === "1")
|
|
17
|
+
if (node.text === "1" || (node.text.startsWith("1.") &&
|
|
18
|
+
!node.text.substring(2).split("").some((c) => c !== "0")))
|
|
17
19
|
return "jspp::Constants::ONE";
|
|
18
20
|
return `jspp::AnyValue::make_number(${this.escapeString(node.text)})`;
|
|
19
21
|
}
|
|
@@ -52,21 +52,31 @@ export function visitSourceFile(node, context) {
|
|
|
52
52
|
// Mark before further visits
|
|
53
53
|
this.markSymbolAsInitialized(funcName, contextForFunctions.globalScopeSymbols, contextForFunctions.localScopeSymbols);
|
|
54
54
|
this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
|
|
55
|
-
//
|
|
55
|
+
// Update features in the symbol registry
|
|
56
56
|
const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
|
|
57
|
-
hoistedSymbols.update(funcName, {
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
hoistedSymbols.update(funcName, {
|
|
58
|
+
features: {
|
|
59
|
+
native: {
|
|
60
|
+
type: "lambda",
|
|
61
|
+
name: nativeName,
|
|
62
|
+
parameters: this.validateFunctionParams(stmt.parameters),
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
// Generate lambda components
|
|
67
|
+
const lambdaComps = this.generateLambdaComponents(stmt, contextForFunctions, {
|
|
60
68
|
isAssignment: true,
|
|
61
|
-
generateOnlyLambda: true,
|
|
62
69
|
nativeName,
|
|
70
|
+
noTypeSignature: true,
|
|
63
71
|
});
|
|
64
|
-
|
|
65
|
-
|
|
72
|
+
// Generate native lambda
|
|
73
|
+
const nativeLambda = this.generateNativeLambda(lambdaComps);
|
|
74
|
+
code += `${this.indent()}auto ${nativeName} = ${nativeLambda};\n`;
|
|
75
|
+
// Generate AnyValue wrapped lamda
|
|
66
76
|
if (this.isFunctionUsedAsValue(stmt, node) ||
|
|
67
77
|
this.isFunctionUsedBeforeDeclaration(funcName, node)) {
|
|
68
|
-
const
|
|
69
|
-
code += `${this.indent()}*${funcName} = ${
|
|
78
|
+
const wrappedLambda = this.generateWrappedLambda(lambdaComps);
|
|
79
|
+
code += `${this.indent()}*${funcName} = ${wrappedLambda};\n`;
|
|
70
80
|
}
|
|
71
81
|
});
|
|
72
82
|
// 3. Process other statements
|
|
@@ -137,21 +147,31 @@ export function visitBlock(node, context) {
|
|
|
137
147
|
// Mark before further visits
|
|
138
148
|
this.markSymbolAsInitialized(funcName, contextForFunctions.globalScopeSymbols, contextForFunctions.localScopeSymbols);
|
|
139
149
|
this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
|
|
140
|
-
//
|
|
150
|
+
// Update features in the symbol registry
|
|
141
151
|
const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
|
|
142
|
-
hoistedSymbols.update(funcName, {
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
hoistedSymbols.update(funcName, {
|
|
153
|
+
features: {
|
|
154
|
+
native: {
|
|
155
|
+
type: "lambda",
|
|
156
|
+
name: nativeName,
|
|
157
|
+
parameters: this.validateFunctionParams(stmt.parameters),
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
// Generate lambda components
|
|
162
|
+
const lambdaComps = this.generateLambdaComponents(stmt, contextForFunctions, {
|
|
145
163
|
isAssignment: true,
|
|
146
|
-
generateOnlyLambda: true,
|
|
147
164
|
nativeName,
|
|
165
|
+
noTypeSignature: true,
|
|
148
166
|
});
|
|
149
|
-
|
|
150
|
-
|
|
167
|
+
// Generate native lambda
|
|
168
|
+
const nativeLambda = this.generateNativeLambda(lambdaComps);
|
|
169
|
+
code += `${this.indent()}auto ${nativeName} = ${nativeLambda};\n`;
|
|
170
|
+
// Generate AnyValue wrapped lamda
|
|
151
171
|
if (this.isFunctionUsedAsValue(stmt, node) ||
|
|
152
172
|
this.isFunctionUsedBeforeDeclaration(funcName, node)) {
|
|
153
|
-
const
|
|
154
|
-
code += `${this.indent()}*${funcName} = ${
|
|
173
|
+
const wrappedLambda = this.generateWrappedLambda(lambdaComps);
|
|
174
|
+
code += `${this.indent()}*${funcName} = ${wrappedLambda};\n`;
|
|
155
175
|
}
|
|
156
176
|
});
|
|
157
177
|
// 3. Process other statements
|
|
@@ -242,7 +262,7 @@ export function visitEnumDeclaration(node, context) {
|
|
|
242
262
|
}
|
|
243
263
|
else {
|
|
244
264
|
// Auto-increment
|
|
245
|
-
valueCode = `lastVal
|
|
265
|
+
valueCode = `jspp::add(lastVal, 1)`;
|
|
246
266
|
}
|
|
247
267
|
code += `${this.indent()}lastVal = ${valueCode};\n`;
|
|
248
268
|
code +=
|
package/package.json
CHANGED