@ugo-studio/jspp 0.2.5 → 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/README.md +51 -36
- package/dist/analysis/scope.js +7 -0
- package/dist/analysis/typeAnalyzer.js +96 -43
- package/dist/ast/symbols.js +34 -24
- package/dist/cli/args.js +59 -0
- package/dist/cli/colors.js +9 -0
- package/dist/cli/file-utils.js +20 -0
- package/dist/cli/index.js +160 -0
- package/dist/cli/spinner.js +55 -0
- package/dist/core/codegen/class-handlers.js +8 -8
- package/dist/core/codegen/control-flow-handlers.js +19 -9
- package/dist/core/codegen/declaration-handlers.js +30 -10
- package/dist/core/codegen/expression-handlers.js +649 -161
- package/dist/core/codegen/function-handlers.js +107 -103
- package/dist/core/codegen/helpers.js +61 -14
- package/dist/core/codegen/index.js +13 -9
- package/dist/core/codegen/literal-handlers.js +4 -2
- package/dist/core/codegen/statement-handlers.js +147 -55
- package/dist/core/codegen/visitor.js +22 -2
- package/dist/core/constants.js +16 -0
- package/dist/core/error.js +58 -0
- package/dist/index.js +6 -3
- package/package.json +3 -3
- 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/scheduler.hpp +144 -144
- 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/object.hpp +12 -10
- 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,15 +1,14 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
|
-
import { DeclaredSymbols } from "../../ast/symbols.js";
|
|
2
|
+
import { DeclarationType, DeclaredSymbols } from "../../ast/symbols.js";
|
|
3
3
|
import { collectFunctionScopedDeclarations } from "./helpers.js";
|
|
4
4
|
import { CodeGenerator } from "./index.js";
|
|
5
|
-
export function
|
|
6
|
-
const capture = options?.capture || "[=]";
|
|
7
|
-
const nativeName = options?.nativeName;
|
|
5
|
+
export function generateLambdaComponents(node, context, options) {
|
|
8
6
|
const declaredSymbols = this.getDeclaredSymbols(node);
|
|
9
7
|
const argsName = this.generateUniqueName("__args_", declaredSymbols, context.globalScopeSymbols, context.localScopeSymbols);
|
|
10
8
|
const isInsideGeneratorFunction = this.isGeneratorFunction(node);
|
|
11
9
|
const isInsideAsyncFunction = this.isAsyncFunction(node);
|
|
12
|
-
const
|
|
10
|
+
const isArrow = ts.isArrowFunction(node);
|
|
11
|
+
const returnCommand = this.getReturnCommand({
|
|
13
12
|
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
14
13
|
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
15
14
|
});
|
|
@@ -18,17 +17,10 @@ export function generateLambda(node, context, options) {
|
|
|
18
17
|
: (isInsideGeneratorFunction
|
|
19
18
|
? "jspp::JsIterator<jspp::AnyValue>"
|
|
20
19
|
: (isInsideAsyncFunction ? "jspp::JsPromise" : "jspp::AnyValue"));
|
|
21
|
-
const isArrow = ts.isArrowFunction(node);
|
|
22
20
|
// Lambda arguments are ALWAYS const references/spans to avoid copy overhead for normal functions.
|
|
23
21
|
// For generators/async, we manually copy them inside the body.
|
|
24
22
|
const paramThisType = "const jspp::AnyValue&";
|
|
25
23
|
const paramArgsType = "std::span<const jspp::AnyValue>";
|
|
26
|
-
const selfParamPart = nativeName ? `this auto&& ${nativeName}, ` : "";
|
|
27
|
-
const mutablePart = nativeName ? "" : "mutable ";
|
|
28
|
-
const thisArgParam = isArrow
|
|
29
|
-
? "const jspp::AnyValue&" // Arrow functions use captured 'this' or are never generators in this parser context
|
|
30
|
-
: `${paramThisType} ${this.globalThisVar}`;
|
|
31
|
-
let lambda = `${capture}(${selfParamPart}${thisArgParam}, ${paramArgsType} ${argsName}) ${mutablePart}-> ${funcReturnType} `;
|
|
32
24
|
const globalScopeSymbols = this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols);
|
|
33
25
|
const visitContext = {
|
|
34
26
|
currentScopeNode: context.currentScopeNode,
|
|
@@ -42,18 +34,6 @@ export function generateLambda(node, context, options) {
|
|
|
42
34
|
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
43
35
|
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
44
36
|
};
|
|
45
|
-
// Preamble to copy arguments for coroutines
|
|
46
|
-
let preamble = "";
|
|
47
|
-
if (isInsideGeneratorFunction || isInsideAsyncFunction) {
|
|
48
|
-
const thisCopy = this.generateUniqueName("__this_copy", declaredSymbols);
|
|
49
|
-
const argsCopy = this.generateUniqueName("__args_copy", declaredSymbols);
|
|
50
|
-
if (!isArrow) {
|
|
51
|
-
preamble +=
|
|
52
|
-
`${this.indent()}jspp::AnyValue ${thisCopy} = ${this.globalThisVar};\n`;
|
|
53
|
-
}
|
|
54
|
-
preamble +=
|
|
55
|
-
`${this.indent()}std::vector<jspp::AnyValue> ${argsCopy}(${argsName}.begin(), ${argsName}.end());\n`;
|
|
56
|
-
}
|
|
57
37
|
// Adjust parameter names for generator/async to allow shadowing/copying
|
|
58
38
|
let finalThisParamName = isArrow ? "" : this.globalThisVar;
|
|
59
39
|
let finalArgsParamName = argsName;
|
|
@@ -63,138 +43,161 @@ export function generateLambda(node, context, options) {
|
|
|
63
43
|
}
|
|
64
44
|
finalArgsParamName = this.generateUniqueName("__args_ref", declaredSymbols);
|
|
65
45
|
}
|
|
66
|
-
const
|
|
67
|
-
? "const jspp::AnyValue&"
|
|
46
|
+
const thisArgParam = isArrow
|
|
47
|
+
? "const jspp::AnyValue&" // Arrow functions use captured 'this' or are never generators in this parser context
|
|
68
48
|
: `${paramThisType} ${finalThisParamName}`;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
preamble = "";
|
|
49
|
+
const funcArgs = `${paramArgsType} ${finalArgsParamName}`;
|
|
50
|
+
// Preamble to copy arguments for coroutines
|
|
51
|
+
let preamble = "";
|
|
52
|
+
let nativePreamble = "";
|
|
74
53
|
if (isInsideGeneratorFunction || isInsideAsyncFunction) {
|
|
75
54
|
if (!isArrow) {
|
|
76
55
|
preamble +=
|
|
77
56
|
`${this.indent()}jspp::AnyValue ${this.globalThisVar} = ${finalThisParamName};\n`;
|
|
57
|
+
nativePreamble +=
|
|
58
|
+
`${this.indent()}jspp::AnyValue ${this.globalThisVar} = ${finalThisParamName};\n`;
|
|
78
59
|
}
|
|
60
|
+
// Note: Do not add argument copy to native lambda
|
|
79
61
|
preamble +=
|
|
80
62
|
`${this.indent()}std::vector<jspp::AnyValue> ${argsName}(${finalArgsParamName}.begin(), ${finalArgsParamName}.end());\n`;
|
|
81
63
|
}
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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) => {
|
|
88
80
|
const name = p.name.getText();
|
|
89
81
|
const defaultValue = p.initializer
|
|
90
82
|
? this.visit(p.initializer, visitContext)
|
|
91
83
|
: "jspp::Constants::UNDEFINED";
|
|
84
|
+
// Add paramerter to local context
|
|
85
|
+
visitContext.localScopeSymbols.add(name, {
|
|
86
|
+
type: DeclarationType.let,
|
|
87
|
+
checks: { initialized: true },
|
|
88
|
+
});
|
|
92
89
|
const scope = this.getScopeForNode(p);
|
|
93
90
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
|
|
94
91
|
// Handle rest parameters
|
|
95
92
|
if (!!p.dotDotDotToken) {
|
|
96
|
-
|
|
97
|
-
throw new SyntaxError("Rest parameter must be last formal parameter.");
|
|
98
|
-
}
|
|
93
|
+
const initValue = `jspp::AnyValue::make_array(${argsName}.subspan(${i}))`;
|
|
99
94
|
if (typeInfo.needsHeapAllocation) {
|
|
100
|
-
|
|
101
|
-
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(
|
|
95
|
+
paramsCode +=
|
|
96
|
+
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
|
|
102
97
|
}
|
|
103
98
|
else {
|
|
104
|
-
|
|
105
|
-
`${this.indent()}jspp::AnyValue ${name} =
|
|
99
|
+
paramsCode +=
|
|
100
|
+
`${this.indent()}jspp::AnyValue ${name} = ${initValue};\n`;
|
|
106
101
|
}
|
|
107
|
-
// Extract rest parameters
|
|
108
|
-
const tempName = `temp_${name}`;
|
|
109
|
-
code += `${this.indent()}{\n`;
|
|
110
|
-
this.indentationLevel++;
|
|
111
|
-
code +=
|
|
112
|
-
`${this.indent()}std::vector<std::optional<jspp::AnyValue>> ${tempName};\n`;
|
|
113
|
-
code += `${this.indent()}if (${argsName}.size() > ${i}) {\n`;
|
|
114
|
-
this.indentationLevel++;
|
|
115
|
-
code +=
|
|
116
|
-
`${this.indent()}${tempName}.reserve(${argsName}.size() - ${i});\n`;
|
|
117
|
-
this.indentationLevel--;
|
|
118
|
-
code += `${this.indent()}}\n`;
|
|
119
|
-
code +=
|
|
120
|
-
`${this.indent()}for (size_t j = ${i}; j < ${argsName}.size(); j++) {\n`;
|
|
121
|
-
this.indentationLevel++;
|
|
122
|
-
code +=
|
|
123
|
-
`${this.indent()}${tempName}.push_back(${argsName}[j]);\n`;
|
|
124
|
-
this.indentationLevel--;
|
|
125
|
-
code += `${this.indent()}}\n`;
|
|
126
|
-
code += `${this.indent()}${typeInfo.needsHeapAllocation ? "*" : ""}${name} = jspp::AnyValue::make_array(std::move(${tempName}));\n`;
|
|
127
|
-
this.indentationLevel--;
|
|
128
|
-
code += `${this.indent()}}\n`;
|
|
129
102
|
return;
|
|
130
103
|
}
|
|
131
104
|
// Normal parameter
|
|
132
105
|
const initValue = `${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue}`;
|
|
133
106
|
if (typeInfo && typeInfo.needsHeapAllocation) {
|
|
134
|
-
|
|
107
|
+
paramsCode +=
|
|
135
108
|
`${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
|
|
136
109
|
}
|
|
137
110
|
else {
|
|
138
|
-
|
|
111
|
+
paramsCode +=
|
|
139
112
|
`${this.indent()}jspp::AnyValue ${name} = ${initValue};\n`;
|
|
140
113
|
}
|
|
141
114
|
});
|
|
142
|
-
return
|
|
115
|
+
return paramsCode;
|
|
143
116
|
};
|
|
117
|
+
// Generate params and function body
|
|
118
|
+
let paramsContent = "";
|
|
119
|
+
let blockContentWithoutOpeningBrace = "";
|
|
144
120
|
if (node.body) {
|
|
145
121
|
if (ts.isBlock(node.body)) {
|
|
146
122
|
// Hoist var declarations in the function body
|
|
147
123
|
const varDecls = collectFunctionScopedDeclarations(node.body);
|
|
148
124
|
varDecls.forEach((decl) => {
|
|
149
|
-
|
|
125
|
+
const hoistCode = this.hoistDeclaration(decl, visitContext.localScopeSymbols, node.body);
|
|
126
|
+
preamble += hoistCode;
|
|
127
|
+
nativePreamble += hoistCode;
|
|
150
128
|
});
|
|
151
129
|
this.indentationLevel++;
|
|
152
|
-
|
|
130
|
+
paramsContent = generateParamsBuilder();
|
|
153
131
|
this.indentationLevel--;
|
|
154
|
-
|
|
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, {
|
|
155
134
|
...visitContext,
|
|
156
135
|
isMainContext: false,
|
|
157
136
|
isInsideFunction: true,
|
|
158
137
|
isFunctionBody: true,
|
|
159
138
|
isInsideGeneratorFunction: isInsideGeneratorFunction,
|
|
160
139
|
isInsideAsyncFunction: isInsideAsyncFunction,
|
|
161
|
-
});
|
|
162
|
-
// The block visitor already adds braces, so we need to inject the preamble and param extraction.
|
|
163
|
-
lambda += "{\n" + preamble + paramExtraction +
|
|
164
|
-
blockContent.trimStart().substring(2);
|
|
140
|
+
}).trimStart().substring(2);
|
|
165
141
|
}
|
|
166
142
|
else {
|
|
167
|
-
lambda += "{\n";
|
|
168
143
|
this.indentationLevel++;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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`;
|
|
179
154
|
this.indentationLevel--;
|
|
180
|
-
|
|
155
|
+
blockContentWithoutOpeningBrace += `${this.indent()}}`;
|
|
181
156
|
}
|
|
182
157
|
}
|
|
183
158
|
else {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
// Return only lambda if required
|
|
187
|
-
if (options?.generateOnlyLambda) {
|
|
188
|
-
return lambda.trimEnd();
|
|
159
|
+
blockContentWithoutOpeningBrace =
|
|
160
|
+
`${returnCommand} jspp::Constants::UNDEFINED; }\n`;
|
|
189
161
|
}
|
|
190
|
-
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
|
+
};
|
|
191
179
|
}
|
|
192
|
-
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 || "[=]";
|
|
193
195
|
const isAssignment = options?.isAssignment || false;
|
|
194
196
|
const noTypeSignature = options?.noTypeSignature || false;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
197
|
+
let lambda = `${capture}(${thisArgParam}, ${funcArgs}) mutable -> ${funcReturnType} {\n`;
|
|
198
|
+
lambda += preamble;
|
|
199
|
+
lambda += paramsContent;
|
|
200
|
+
lambda += blockContentWithoutOpeningBrace;
|
|
198
201
|
let callable = lambda;
|
|
199
202
|
let method = "";
|
|
200
203
|
// Handle generator function
|
|
@@ -236,6 +239,7 @@ export function generateFullLambdaExpression(node, context, lambda, options) {
|
|
|
236
239
|
const funcName = context?.lambdaName || node.name?.getText();
|
|
237
240
|
const hasName = !!funcName && funcName.length > 0;
|
|
238
241
|
let args = callable;
|
|
242
|
+
const isArrow = ts.isArrowFunction(node);
|
|
239
243
|
const isMethod = ts.isMethodDeclaration(node);
|
|
240
244
|
const isAccessor = ts.isGetAccessor(node) || ts.isSetAccessor(node);
|
|
241
245
|
const isConstructor = !isArrow && !isMethod && !isAccessor;
|
|
@@ -269,12 +273,12 @@ export function visitFunctionDeclaration(node, context) {
|
|
|
269
273
|
// This will now be handled by the Block visitor for hoisting.
|
|
270
274
|
// However, we still need to generate the lambda for assignment.
|
|
271
275
|
// The block visitor will wrap this in an assignment.
|
|
272
|
-
return this.
|
|
276
|
+
return this.generateWrappedLambda(this.generateLambdaComponents(node, context));
|
|
273
277
|
}
|
|
274
278
|
return "";
|
|
275
279
|
}
|
|
276
280
|
export function visitArrowFunction(node, context) {
|
|
277
|
-
return this.
|
|
281
|
+
return this.generateWrappedLambda(this.generateLambdaComponents(node, context));
|
|
278
282
|
}
|
|
279
283
|
export function visitFunctionExpression(node, context) {
|
|
280
284
|
const funcExpr = node;
|
|
@@ -284,18 +288,18 @@ export function visitFunctionExpression(node, context) {
|
|
|
284
288
|
this.indentationLevel++;
|
|
285
289
|
code +=
|
|
286
290
|
`${this.indent()}auto ${funcName} = std::make_shared<jspp::AnyValue>();\n`;
|
|
287
|
-
const lambda = this.
|
|
291
|
+
const lambda = this.generateWrappedLambda(this.generateLambdaComponents(funcExpr, {
|
|
288
292
|
...context,
|
|
289
293
|
lambdaName: funcName,
|
|
290
294
|
}, {
|
|
291
295
|
isAssignment: true,
|
|
292
296
|
capture: "[=]",
|
|
293
|
-
});
|
|
297
|
+
}));
|
|
294
298
|
code += `${this.indent()}*${funcName} = ${lambda};\n`;
|
|
295
299
|
code += `${this.indent()}return *${funcName};\n`;
|
|
296
300
|
this.indentationLevel--;
|
|
297
301
|
code += `${this.indent()}})()`;
|
|
298
302
|
return code;
|
|
299
303
|
}
|
|
300
|
-
return this.
|
|
304
|
+
return this.generateWrappedLambda(this.generateLambdaComponents(node, context));
|
|
301
305
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import ts from "typescript";
|
|
2
2
|
import { BUILTIN_OBJECTS, Scope } from "../../analysis/scope.js";
|
|
3
3
|
import { DeclarationType, DeclaredSymbols } from "../../ast/symbols.js";
|
|
4
|
+
import { CompilerError } from "../error.js";
|
|
4
5
|
import { CodeGenerator } from "./index.js";
|
|
5
6
|
export function isBuiltinObject(node) {
|
|
6
7
|
return BUILTIN_OBJECTS.values().some((obj) => obj.name === node.text);
|
|
@@ -20,6 +21,10 @@ export function getDeclaredSymbols(node) {
|
|
|
20
21
|
// Handles class declarations
|
|
21
22
|
symbols.add(child.name.getText());
|
|
22
23
|
}
|
|
24
|
+
else if (ts.isEnumDeclaration(child) && child.name) {
|
|
25
|
+
// Handles enum declarations
|
|
26
|
+
symbols.add(child.name.getText());
|
|
27
|
+
}
|
|
23
28
|
else if (ts.isParameter(child)) {
|
|
24
29
|
// Handles function parameters
|
|
25
30
|
symbols.add(child.name.getText());
|
|
@@ -60,7 +65,7 @@ export function getScopeForNode(node) {
|
|
|
60
65
|
}
|
|
61
66
|
const rootScope = this.typeAnalyzer.scopeManager.getAllScopes()[0];
|
|
62
67
|
if (!rootScope) {
|
|
63
|
-
throw new
|
|
68
|
+
throw new CompilerError("Could not find a root scope.", node, "CompilerBug");
|
|
64
69
|
}
|
|
65
70
|
return rootScope;
|
|
66
71
|
}
|
|
@@ -88,7 +93,7 @@ export function getDerefCode(nodeText, varName, context, typeInfo) {
|
|
|
88
93
|
const symbolName = varName.slice(1).slice(0, -1);
|
|
89
94
|
const symbol = context.localScopeSymbols.get(symbolName) ??
|
|
90
95
|
context.globalScopeSymbols.get(symbolName);
|
|
91
|
-
const isInitialized = symbol?.
|
|
96
|
+
const isInitialized = symbol?.checks.initialized ||
|
|
92
97
|
false;
|
|
93
98
|
// Mark the symbol as checked
|
|
94
99
|
this.markSymbolAsInitialized(symbolName, context.globalScopeSymbols, context.localScopeSymbols);
|
|
@@ -109,12 +114,12 @@ export function getDerefCode(nodeText, varName, context, typeInfo) {
|
|
|
109
114
|
export function markSymbolAsInitialized(name, topLevel, local) {
|
|
110
115
|
if (topLevel.has(name)) {
|
|
111
116
|
topLevel.update(name, {
|
|
112
|
-
|
|
117
|
+
checks: { initialized: true },
|
|
113
118
|
});
|
|
114
119
|
}
|
|
115
120
|
else if (local.has(name)) {
|
|
116
121
|
local.update(name, {
|
|
117
|
-
|
|
122
|
+
checks: { initialized: true },
|
|
118
123
|
});
|
|
119
124
|
}
|
|
120
125
|
}
|
|
@@ -138,16 +143,19 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
|
138
143
|
? DeclarationType.function
|
|
139
144
|
: ts.isClassDeclaration(decl)
|
|
140
145
|
? DeclarationType.class
|
|
141
|
-
:
|
|
146
|
+
: ts.isEnumDeclaration(decl)
|
|
147
|
+
? DeclarationType.enum
|
|
148
|
+
: DeclarationType.var;
|
|
142
149
|
if (hoistedSymbols.has(name)) {
|
|
143
150
|
const existingSymbol = hoistedSymbols.get(name);
|
|
144
|
-
// Don't allow multiple declaration of `let`,`const`,`function` or `
|
|
151
|
+
// Don't allow multiple declaration of `let`,`const`,`function`, `class` or `enum` variables
|
|
145
152
|
if (existingSymbol?.type === DeclarationType.let ||
|
|
146
153
|
existingSymbol?.type === DeclarationType.const ||
|
|
147
154
|
existingSymbol?.type === DeclarationType.function ||
|
|
148
155
|
existingSymbol?.type === DeclarationType.class ||
|
|
156
|
+
existingSymbol?.type === DeclarationType.enum ||
|
|
149
157
|
existingSymbol?.type !== declType) {
|
|
150
|
-
throw new
|
|
158
|
+
throw new CompilerError(`Identifier '${name}' has already been declared.`, decl, "SyntaxError");
|
|
151
159
|
}
|
|
152
160
|
// `var` variables can be declared multiple times
|
|
153
161
|
return "";
|
|
@@ -159,7 +167,7 @@ export function hoistDeclaration(decl, hoistedSymbols, scopeNode) {
|
|
|
159
167
|
const isGenerator = this.isGeneratorFunction(decl);
|
|
160
168
|
hoistedSymbols.add(name, {
|
|
161
169
|
type: declType,
|
|
162
|
-
|
|
170
|
+
features: { isAsync, isGenerator },
|
|
163
171
|
});
|
|
164
172
|
// Don't hoist functions not used as a variable
|
|
165
173
|
// they will be called with their native lambdas
|
|
@@ -203,10 +211,24 @@ export function prepareScopeSymbolsForVisit(topLevel, local) {
|
|
|
203
211
|
// Join the top and local scopes
|
|
204
212
|
return new DeclaredSymbols(topLevel, local);
|
|
205
213
|
}
|
|
214
|
+
export function shouldIgnoreStatement(stmt) {
|
|
215
|
+
// Ignore variable statements with 'declare' modifier
|
|
216
|
+
if (ts.isVariableStatement(stmt)) {
|
|
217
|
+
if (stmt.modifiers &&
|
|
218
|
+
stmt.modifiers.some((m) => m.kind === ts.SyntaxKind.DeclareKeyword)) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
206
224
|
export function collectFunctionScopedDeclarations(node) {
|
|
207
225
|
const decls = [];
|
|
208
226
|
function visit(n) {
|
|
209
227
|
if (ts.isVariableStatement(n)) {
|
|
228
|
+
// Ignore Declare modifier
|
|
229
|
+
if (shouldIgnoreStatement(n))
|
|
230
|
+
return;
|
|
231
|
+
// Only collect let/const declarations
|
|
210
232
|
const isLetOrConst = (n.declarationList.flags &
|
|
211
233
|
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
|
|
212
234
|
if (!isLetOrConst) {
|
|
@@ -257,6 +279,10 @@ export function collectBlockScopedDeclarations(statements) {
|
|
|
257
279
|
const decls = [];
|
|
258
280
|
for (const stmt of statements) {
|
|
259
281
|
if (ts.isVariableStatement(stmt)) {
|
|
282
|
+
// Ignore Declare modifier
|
|
283
|
+
if (shouldIgnoreStatement(stmt))
|
|
284
|
+
continue;
|
|
285
|
+
// Only collect let/const declarations
|
|
260
286
|
const isLetOrConst = (stmt.declarationList.flags &
|
|
261
287
|
(ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0;
|
|
262
288
|
if (isLetOrConst) {
|
|
@@ -267,14 +293,14 @@ export function collectBlockScopedDeclarations(statements) {
|
|
|
267
293
|
return decls;
|
|
268
294
|
}
|
|
269
295
|
export function isFunctionUsedAsValue(decl, root) {
|
|
270
|
-
const
|
|
271
|
-
if (!
|
|
296
|
+
const name = decl.name?.getText();
|
|
297
|
+
if (!name)
|
|
272
298
|
return false;
|
|
273
299
|
let isUsed = false;
|
|
274
300
|
const visitor = (node) => {
|
|
275
301
|
if (isUsed)
|
|
276
302
|
return;
|
|
277
|
-
if (ts.isIdentifier(node) && node.text ===
|
|
303
|
+
if (ts.isIdentifier(node) && node.text === name) {
|
|
278
304
|
const scope = this.getScopeForNode(node);
|
|
279
305
|
const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(node.text, scope);
|
|
280
306
|
if (typeInfo?.declaration === decl) {
|
|
@@ -310,14 +336,14 @@ export function isFunctionUsedAsValue(decl, root) {
|
|
|
310
336
|
ts.forEachChild(root, visitor);
|
|
311
337
|
return isUsed;
|
|
312
338
|
}
|
|
313
|
-
export function isFunctionUsedBeforeDeclaration(
|
|
339
|
+
export function isFunctionUsedBeforeDeclaration(name, root) {
|
|
314
340
|
let declPos = -1;
|
|
315
341
|
let foundDecl = false;
|
|
316
342
|
// Helper to find the function declaration position
|
|
317
343
|
function findDecl(node) {
|
|
318
344
|
if (foundDecl)
|
|
319
345
|
return;
|
|
320
|
-
if (ts.isFunctionDeclaration(node) && node.name?.text ===
|
|
346
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === name) {
|
|
321
347
|
declPos = node.getStart();
|
|
322
348
|
foundDecl = true;
|
|
323
349
|
}
|
|
@@ -333,7 +359,7 @@ export function isFunctionUsedBeforeDeclaration(funcName, root) {
|
|
|
333
359
|
function visit(node) {
|
|
334
360
|
if (isUsedBefore)
|
|
335
361
|
return;
|
|
336
|
-
if (ts.isIdentifier(node) && node.text ===
|
|
362
|
+
if (ts.isIdentifier(node) && node.text === name) {
|
|
337
363
|
const parent = node.parent;
|
|
338
364
|
// Ignore declarations where the identifier is the name being declared
|
|
339
365
|
if ((ts.isFunctionDeclaration(parent) ||
|
|
@@ -384,3 +410,24 @@ export function isFunctionUsedBeforeDeclaration(funcName, root) {
|
|
|
384
410
|
visit(root);
|
|
385
411
|
return isUsedBefore;
|
|
386
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,11 +1,12 @@
|
|
|
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
|
-
const MODULE_NAME = "
|
|
5
|
+
const MODULE_NAME = "__entry_point__";
|
|
6
6
|
export class CodeGenerator {
|
|
7
7
|
indentationLevel = 0;
|
|
8
8
|
typeAnalyzer;
|
|
9
|
+
isTypescript = false;
|
|
9
10
|
globalThisVar;
|
|
10
11
|
uniqueNameCounter = 0;
|
|
11
12
|
// visitor
|
|
@@ -28,14 +29,17 @@ export class CodeGenerator {
|
|
|
28
29
|
markSymbolAsInitialized = markSymbolAsInitialized;
|
|
29
30
|
isFunctionUsedAsValue = isFunctionUsedAsValue;
|
|
30
31
|
isFunctionUsedBeforeDeclaration = isFunctionUsedBeforeDeclaration;
|
|
32
|
+
validateFunctionParams = validateFunctionParams;
|
|
31
33
|
// function handlers
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
generateLambdaComponents = generateLambdaComponents;
|
|
35
|
+
generateNativeLambda = generateNativeLambda;
|
|
36
|
+
generateWrappedLambda = generateWrappedLambda;
|
|
34
37
|
/**
|
|
35
38
|
* Main entry point for the code generation process.
|
|
36
39
|
*/
|
|
37
|
-
generate(ast, analyzer) {
|
|
40
|
+
generate(ast, analyzer, isTypescript) {
|
|
38
41
|
this.typeAnalyzer = analyzer;
|
|
42
|
+
this.isTypescript = isTypescript;
|
|
39
43
|
this.globalThisVar = this.generateUniqueName("__this_val__", this.getDeclaredSymbols(ast));
|
|
40
44
|
const declarations = `#include "index.hpp"\n\n`;
|
|
41
45
|
let containerCode = `jspp::JsPromise ${MODULE_NAME}() {\n`;
|
|
@@ -68,10 +72,10 @@ export class CodeGenerator {
|
|
|
68
72
|
mainCode += ` jspp::Scheduler::instance().run();\n`;
|
|
69
73
|
mainCode += ` } catch (const std::exception& ex) {\n`;
|
|
70
74
|
mainCode +=
|
|
71
|
-
" auto error = std::make_shared<jspp::AnyValue>(jspp::Exception::exception_to_any_value(ex));\n
|
|
75
|
+
" auto error = std::make_shared<jspp::AnyValue>(jspp::Exception::exception_to_any_value(ex));\n";
|
|
72
76
|
mainCode +=
|
|
73
|
-
`
|
|
74
|
-
mainCode += `
|
|
77
|
+
` console.call_own_property("error", std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1));\n`;
|
|
78
|
+
mainCode += ` return 1;\n`;
|
|
75
79
|
mainCode += ` }\n`;
|
|
76
80
|
mainCode += " return 0;\n}";
|
|
77
81
|
return declarations + containerCode + mainCode;
|
|
@@ -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
|
}
|