@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.
Files changed (54) hide show
  1. package/README.md +51 -36
  2. package/dist/analysis/scope.js +7 -0
  3. package/dist/analysis/typeAnalyzer.js +96 -43
  4. package/dist/ast/symbols.js +34 -24
  5. package/dist/cli/args.js +59 -0
  6. package/dist/cli/colors.js +9 -0
  7. package/dist/cli/file-utils.js +20 -0
  8. package/dist/cli/index.js +160 -0
  9. package/dist/cli/spinner.js +55 -0
  10. package/dist/core/codegen/class-handlers.js +8 -8
  11. package/dist/core/codegen/control-flow-handlers.js +19 -9
  12. package/dist/core/codegen/declaration-handlers.js +30 -10
  13. package/dist/core/codegen/expression-handlers.js +649 -161
  14. package/dist/core/codegen/function-handlers.js +107 -103
  15. package/dist/core/codegen/helpers.js +61 -14
  16. package/dist/core/codegen/index.js +13 -9
  17. package/dist/core/codegen/literal-handlers.js +4 -2
  18. package/dist/core/codegen/statement-handlers.js +147 -55
  19. package/dist/core/codegen/visitor.js +22 -2
  20. package/dist/core/constants.js +16 -0
  21. package/dist/core/error.js +58 -0
  22. package/dist/index.js +6 -3
  23. package/package.json +3 -3
  24. package/src/prelude/any_value.hpp +89 -59
  25. package/src/prelude/any_value_access.hpp +1 -1
  26. package/src/prelude/any_value_helpers.hpp +85 -43
  27. package/src/prelude/index.hpp +1 -0
  28. package/src/prelude/library/array.hpp +3 -2
  29. package/src/prelude/scheduler.hpp +144 -144
  30. package/src/prelude/types.hpp +8 -8
  31. package/src/prelude/utils/access.hpp +62 -6
  32. package/src/prelude/utils/assignment_operators.hpp +14 -14
  33. package/src/prelude/utils/log_any_value/array.hpp +0 -15
  34. package/src/prelude/utils/log_any_value/object.hpp +12 -10
  35. package/src/prelude/utils/log_any_value/primitives.hpp +2 -0
  36. package/src/prelude/utils/operators.hpp +117 -474
  37. package/src/prelude/utils/operators_primitive.hpp +337 -0
  38. package/src/prelude/values/helpers/array.hpp +4 -4
  39. package/src/prelude/values/helpers/async_iterator.hpp +2 -2
  40. package/src/prelude/values/helpers/function.hpp +3 -3
  41. package/src/prelude/values/helpers/iterator.hpp +2 -2
  42. package/src/prelude/values/helpers/object.hpp +3 -3
  43. package/src/prelude/values/helpers/promise.hpp +1 -1
  44. package/src/prelude/values/helpers/string.hpp +1 -1
  45. package/src/prelude/values/helpers/symbol.hpp +1 -1
  46. package/src/prelude/values/prototypes/array.hpp +1125 -853
  47. package/src/prelude/values/prototypes/async_iterator.hpp +32 -14
  48. package/src/prelude/values/prototypes/function.hpp +30 -18
  49. package/src/prelude/values/prototypes/iterator.hpp +40 -17
  50. package/src/prelude/values/prototypes/number.hpp +119 -62
  51. package/src/prelude/values/prototypes/object.hpp +10 -4
  52. package/src/prelude/values/prototypes/promise.hpp +167 -109
  53. package/src/prelude/values/prototypes/string.hpp +407 -231
  54. 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 generateLambda(node, context, options) {
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 returnCmd = this.getReturnCommand({
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 thisArgParamFinal = isArrow
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
- // Re-construct lambda header with potentially new param names
70
- lambda =
71
- `${capture}(${selfParamPart}${thisArgParamFinal}, ${paramArgsType} ${finalArgsParamName}) ${mutablePart}-> ${funcReturnType} `;
72
- // Regenerate preamble
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
- // Now 'argsName' refers to the vector (if copied) or the span (if not).
83
- // And 'this.globalThisVar' refers to the copy (if copied) or the param (if not).
84
- // So subsequent code using `argsName` and `visit` (using `globalThisVar`) works correctly.
85
- const paramExtractor = (parameters) => {
86
- let code = "";
87
- parameters.forEach((p, i) => {
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
- if (parameters.length - 1 !== i) {
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
- code +=
101
- `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(jspp::Constants::UNDEFINED);\n`;
95
+ paramsCode +=
96
+ `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
102
97
  }
103
98
  else {
104
- code +=
105
- `${this.indent()}jspp::AnyValue ${name} = jspp::Constants::UNDEFINED;\n`;
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
- code +=
107
+ paramsCode +=
135
108
  `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initValue});\n`;
136
109
  }
137
110
  else {
138
- code +=
111
+ paramsCode +=
139
112
  `${this.indent()}jspp::AnyValue ${name} = ${initValue};\n`;
140
113
  }
141
114
  });
142
- return code;
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
- preamble += this.hoistDeclaration(decl, visitContext.localScopeSymbols, node.body);
125
+ const hoistCode = this.hoistDeclaration(decl, visitContext.localScopeSymbols, node.body);
126
+ preamble += hoistCode;
127
+ nativePreamble += hoistCode;
150
128
  });
151
129
  this.indentationLevel++;
152
- const paramExtraction = paramExtractor(node.parameters);
130
+ paramsContent = generateParamsBuilder();
153
131
  this.indentationLevel--;
154
- const blockContent = this.visit(node.body, {
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
- lambda += preamble;
170
- lambda += paramExtractor(node.parameters);
171
- lambda += `${this.indent()}${returnCmd} ${this.visit(node.body, {
172
- ...visitContext,
173
- isMainContext: false,
174
- isInsideFunction: true,
175
- isFunctionBody: false,
176
- isInsideGeneratorFunction: isInsideGeneratorFunction,
177
- isInsideAsyncFunction: isInsideAsyncFunction,
178
- })};\n`;
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
- lambda += `${this.indent()}}`;
155
+ blockContentWithoutOpeningBrace += `${this.indent()}}`;
181
156
  }
182
157
  }
183
158
  else {
184
- lambda += `{ ${preamble} ${returnCmd} jspp::Constants::UNDEFINED; }\n`;
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 this.generateFullLambdaExpression(node, context, lambda, options);
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 generateFullLambdaExpression(node, context, lambda, options) {
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
- const isInsideGeneratorFunction = this.isGeneratorFunction(node);
196
- const isInsideAsyncFunction = this.isAsyncFunction(node);
197
- const isArrow = ts.isArrowFunction(node);
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.generateLambda(node, context);
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.generateLambda(node, context);
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.generateLambda(funcExpr, {
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.generateLambda(node, context);
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 Error("Compiler bug: Could not find a root scope.");
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?.checked.initialized ||
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
- checked: { initialized: true },
117
+ checks: { initialized: true },
113
118
  });
114
119
  }
115
120
  else if (local.has(name)) {
116
121
  local.update(name, {
117
- checked: { initialized: true },
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
- : DeclarationType.var;
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 `class` variables
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 SyntaxError(`Identifier '${name}' has already been declared.\n\n${" ".repeat(6) + decl.getText()}\n`);
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
- func: { isAsync, isGenerator },
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 funcName = decl.name?.getText();
271
- if (!funcName)
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 === funcName) {
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(funcName, root) {
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 === funcName) {
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 === funcName) {
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 { generateFullLambdaExpression, generateLambda, } from "./function-handlers.js";
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 = "__main_function__";
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
- generateLambda = generateLambda;
33
- generateFullLambdaExpression = generateFullLambdaExpression;
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 {\n";
75
+ " auto error = std::make_shared<jspp::AnyValue>(jspp::Exception::exception_to_any_value(ex));\n";
72
76
  mainCode +=
73
- ` console.call_own_property("error", std::span<const jspp::AnyValue>((const jspp::AnyValue[]){*error}, 1));\n`;
74
- mainCode += ` return 1;\n }\n`;
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
  }