@ugo-studio/jspp 0.1.0

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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/dist/analysis/scope.js +77 -0
  4. package/dist/analysis/typeAnalyzer.js +224 -0
  5. package/dist/ast/types.js +1 -0
  6. package/dist/cli.js +63 -0
  7. package/dist/core/codegen/declaration-handlers.js +49 -0
  8. package/dist/core/codegen/expression-handlers.js +333 -0
  9. package/dist/core/codegen/function-handlers.js +94 -0
  10. package/dist/core/codegen/helpers.js +83 -0
  11. package/dist/core/codegen/index.js +54 -0
  12. package/dist/core/codegen/literal-handlers.js +32 -0
  13. package/dist/core/codegen/statement-handlers.js +485 -0
  14. package/dist/core/codegen/visitor.js +86 -0
  15. package/dist/core/parser.js +6 -0
  16. package/dist/core/traverser.js +19 -0
  17. package/dist/index.js +16 -0
  18. package/package.json +41 -0
  19. package/src/prelude/access.hpp +86 -0
  20. package/src/prelude/any_value.hpp +734 -0
  21. package/src/prelude/descriptors.hpp +25 -0
  22. package/src/prelude/error.hpp +31 -0
  23. package/src/prelude/error_helpers.hpp +59 -0
  24. package/src/prelude/index.hpp +29 -0
  25. package/src/prelude/library/console.hpp +111 -0
  26. package/src/prelude/library/global.hpp +10 -0
  27. package/src/prelude/library/symbol.hpp +8 -0
  28. package/src/prelude/log_string.hpp +403 -0
  29. package/src/prelude/operators.hpp +256 -0
  30. package/src/prelude/types.hpp +50 -0
  31. package/src/prelude/values/array.hpp +50 -0
  32. package/src/prelude/values/function.hpp +19 -0
  33. package/src/prelude/values/non_values.hpp +20 -0
  34. package/src/prelude/values/object.hpp +17 -0
  35. package/src/prelude/values/operators/array.hpp +165 -0
  36. package/src/prelude/values/operators/function.hpp +34 -0
  37. package/src/prelude/values/operators/object.hpp +34 -0
  38. package/src/prelude/values/prototypes/array.hpp +228 -0
  39. package/src/prelude/values/prototypes/function.hpp +0 -0
  40. package/src/prelude/values/prototypes/object.hpp +0 -0
  41. package/src/prelude/values/prototypes/string.hpp +357 -0
  42. package/src/prelude/well_known_symbols.hpp +10 -0
@@ -0,0 +1,333 @@
1
+ import ts from "typescript";
2
+ import { CodeGenerator } from "./";
3
+ function visitObjectPropertyName(node, context) {
4
+ if (ts.isStringLiteral(node)) {
5
+ return `"${node.getText().substring(1, node.getText().length - 1) // remove trailing "' from original name
6
+ }"`;
7
+ }
8
+ else if (ts.isComputedPropertyName(node)) {
9
+ let name = ts.isIdentifier(node.expression)
10
+ ? `jspp::Access::deref(${node.expression.getText()},${this.getJsVarName(node.expression)})`
11
+ : this.visit(node.expression, context);
12
+ name += ".to_std_string()";
13
+ return name;
14
+ }
15
+ return context.isPropertyNameAccess
16
+ ? node.getText()
17
+ : `"${node.getText()}"`;
18
+ }
19
+ export function visitObjectLiteralExpression(node, context) {
20
+ const obj = node;
21
+ let props = "";
22
+ for (const prop of obj.properties) {
23
+ // console.log("Property kind:", ts.SyntaxKind[prop.kind]);
24
+ if (ts.isPropertyAssignment(prop)) {
25
+ const key = visitObjectPropertyName.call(this, prop.name, context);
26
+ const value = ts.isIdentifier(prop.initializer)
27
+ ? `jspp::Access::deref(${this.visit(prop.initializer, context)}, ${this.getJsVarName(prop.initializer)})`
28
+ : this.visit(prop.initializer, context);
29
+ props += `{${key}, ${value}},`;
30
+ }
31
+ else if (ts.isShorthandPropertyAssignment(prop)) {
32
+ const key = visitObjectPropertyName.call(this, prop.name, context);
33
+ const value = `jspp::Access::deref(${this.visit(prop.name, context)}, ${this.getJsVarName(prop.name)})`;
34
+ props += `{${key}, ${value}},`;
35
+ }
36
+ }
37
+ return `jspp::AnyValue::make_object({${props}})`;
38
+ }
39
+ export function visitArrayLiteralExpression(node, context) {
40
+ const elements = node.elements
41
+ .map((elem) => ts.isIdentifier(elem)
42
+ ? `jspp::Access::deref(${this.visit(elem, context)}, ${this.getJsVarName(elem)})`
43
+ : this.visit(elem, context))
44
+ .join(", ");
45
+ return `jspp::AnyValue::make_array({${elements}})`;
46
+ }
47
+ export function visitPrefixUnaryExpression(node, context) {
48
+ const prefixUnaryExpr = node;
49
+ const operand = this.visit(prefixUnaryExpr.operand, context);
50
+ const operator = ts.tokenToString(prefixUnaryExpr.operator);
51
+ if (operator === "++" || operator === "--") {
52
+ return `${operator}(*${operand})`;
53
+ }
54
+ if (operator === "~") {
55
+ return `${operator}(*${operand})`;
56
+ }
57
+ return `${operator}${operand}`;
58
+ }
59
+ export function visitPostfixUnaryExpression(node, context) {
60
+ const postfixUnaryExpr = node;
61
+ const operand = this.visit(postfixUnaryExpr.operand, context);
62
+ const operator = ts.tokenToString(postfixUnaryExpr.operator);
63
+ return `(*${operand})${operator}`;
64
+ }
65
+ export function visitParenthesizedExpression(node, context) {
66
+ const parenExpr = node;
67
+ return `(${this.visit(parenExpr.expression, context)})`;
68
+ }
69
+ export function visitPropertyAccessExpression(node, context) {
70
+ const propAccess = node;
71
+ const exprText = this.visit(propAccess.expression, context);
72
+ const propName = propAccess.name.getText();
73
+ const scope = this.getScopeForNode(propAccess.expression);
74
+ const typeInfo = ts.isIdentifier(propAccess.expression)
75
+ ? this.typeAnalyzer.scopeManager.lookupFromScope(propAccess.expression.getText(), scope)
76
+ : null;
77
+ if (ts.isIdentifier(propAccess.expression) && !typeInfo &&
78
+ !this.isBuiltinObject(propAccess.expression)) {
79
+ return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(propAccess.expression)})`;
80
+ }
81
+ let finalExpr = "";
82
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
83
+ finalExpr = `jspp::Access::deref(${exprText}, ${this.getJsVarName(propAccess.expression)})`;
84
+ }
85
+ else {
86
+ finalExpr = exprText;
87
+ }
88
+ return `${finalExpr}.get_own_property("${propName}")`;
89
+ // return `${finalExpr}["${propName}"]`;
90
+ }
91
+ export function visitElementAccessExpression(node, context) {
92
+ const elemAccess = node;
93
+ const exprText = this.visit(elemAccess.expression, context);
94
+ let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isPropertyNameAccess: true });
95
+ // Dereference the expression being accessed
96
+ const exprScope = this.getScopeForNode(elemAccess.expression);
97
+ const exprTypeInfo = ts.isIdentifier(elemAccess.expression)
98
+ ? this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.expression.getText(), exprScope)
99
+ : null;
100
+ if (ts.isIdentifier(elemAccess.expression) && !exprTypeInfo &&
101
+ !this.isBuiltinObject(elemAccess.expression)) {
102
+ return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(elemAccess.expression)})`;
103
+ }
104
+ const finalExpr = exprTypeInfo && !exprTypeInfo.isParameter && !exprTypeInfo.isBuiltin
105
+ ? `jspp::Access::deref(${exprText}, ${this.getJsVarName(elemAccess.expression)})`
106
+ : exprText;
107
+ // Dereference the argument expression if it's an identifier
108
+ if (ts.isIdentifier(elemAccess.argumentExpression)) {
109
+ const argScope = this.getScopeForNode(elemAccess.argumentExpression);
110
+ const argTypeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.argumentExpression.getText(), argScope);
111
+ if (!argTypeInfo && !this.isBuiltinObject(elemAccess.argumentExpression)) {
112
+ return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(elemAccess.argumentExpression)})`;
113
+ }
114
+ if (argTypeInfo && !argTypeInfo.isParameter && !argTypeInfo.isBuiltin) {
115
+ argText = `jspp::Access::deref(${argText}, ${this.getJsVarName(elemAccess.argumentExpression)})`;
116
+ }
117
+ }
118
+ return `${finalExpr}.get_own_property(${argText})`;
119
+ // return `${finalExpr}[${argText}]`;
120
+ }
121
+ export function visitBinaryExpression(node, context) {
122
+ const binExpr = node;
123
+ const opToken = binExpr.operatorToken;
124
+ let op = opToken.getText();
125
+ if (opToken.kind === ts.SyntaxKind.PlusEqualsToken ||
126
+ opToken.kind === ts.SyntaxKind.MinusEqualsToken ||
127
+ opToken.kind === ts.SyntaxKind.AsteriskEqualsToken ||
128
+ opToken.kind === ts.SyntaxKind.SlashEqualsToken ||
129
+ opToken.kind === ts.SyntaxKind.PercentEqualsToken) {
130
+ const leftText = this.visit(binExpr.left, context);
131
+ const rightText = this.visit(binExpr.right, context);
132
+ return `*${leftText} ${op} ${rightText}`;
133
+ }
134
+ if (opToken.kind === ts.SyntaxKind.EqualsToken) {
135
+ const rightText = this.visit(binExpr.right, context);
136
+ if (ts.isPropertyAccessExpression(binExpr.left)) {
137
+ const propAccess = binExpr.left;
138
+ const objExprText = this.visit(propAccess.expression, context);
139
+ const propName = propAccess.name.getText();
140
+ const scope = this.getScopeForNode(propAccess.expression);
141
+ const typeInfo = ts.isIdentifier(propAccess.expression)
142
+ ? this.typeAnalyzer.scopeManager.lookupFromScope(propAccess.expression.getText(), scope)
143
+ : null;
144
+ const finalObjExpr = typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin
145
+ ? `jspp::Access::deref(${objExprText}, ${this.getJsVarName(propAccess.expression)})`
146
+ : objExprText;
147
+ let finalRightText = rightText;
148
+ if (ts.isIdentifier(binExpr.right)) {
149
+ const rightScope = this.getScopeForNode(binExpr.right);
150
+ const rightTypeInfo = this.typeAnalyzer.scopeManager
151
+ .lookupFromScope(binExpr.right.getText(), rightScope);
152
+ if (rightTypeInfo &&
153
+ !rightTypeInfo.isParameter &&
154
+ !rightTypeInfo.isBuiltin) {
155
+ finalRightText = `jspp::Access::deref(${rightText}, ${this.getJsVarName(binExpr.right)})`;
156
+ }
157
+ }
158
+ return `${finalObjExpr}.set_own_property("${propName}", ${finalRightText})`;
159
+ // return `${finalObjExpr}["${propName}"] = ${finalRightText}`;
160
+ }
161
+ else if (ts.isElementAccessExpression(binExpr.left)) {
162
+ const elemAccess = binExpr.left;
163
+ const objExprText = this.visit(elemAccess.expression, context);
164
+ let argText = visitObjectPropertyName.call(this, elemAccess.argumentExpression, { ...context, isPropertyNameAccess: true });
165
+ const scope = this.getScopeForNode(elemAccess.expression);
166
+ const typeInfo = ts.isIdentifier(elemAccess.expression)
167
+ ? this.typeAnalyzer.scopeManager.lookupFromScope(elemAccess.expression.getText(), scope)
168
+ : null;
169
+ const finalObjExpr = typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin
170
+ ? `jspp::Access::deref(${objExprText}, ${this.getJsVarName(elemAccess.expression)})`
171
+ : objExprText;
172
+ if (ts.isIdentifier(elemAccess.argumentExpression)) {
173
+ const argScope = this.getScopeForNode(elemAccess.argumentExpression);
174
+ const argTypeInfo = this.typeAnalyzer.scopeManager
175
+ .lookupFromScope(elemAccess.argumentExpression.getText(), argScope);
176
+ if (argTypeInfo &&
177
+ !argTypeInfo.isParameter &&
178
+ !argTypeInfo.isBuiltin) {
179
+ argText = `jspp::Access::deref(${argText}, ${this.getJsVarName(elemAccess.argumentExpression)})`;
180
+ }
181
+ }
182
+ let finalRightText = rightText;
183
+ if (ts.isIdentifier(binExpr.right)) {
184
+ const rightScope = this.getScopeForNode(binExpr.right);
185
+ const rightTypeInfo = this.typeAnalyzer.scopeManager
186
+ .lookupFromScope(binExpr.right.getText(), rightScope);
187
+ if (rightTypeInfo &&
188
+ !rightTypeInfo.isParameter &&
189
+ !rightTypeInfo.isBuiltin) {
190
+ finalRightText = `jspp::Access::deref(${rightText}, ${this.getJsVarName(binExpr.right)})`;
191
+ }
192
+ }
193
+ return `${finalObjExpr}.set_own_property(${argText}, ${finalRightText})`;
194
+ // return `${finalObjExpr}[${argText}] = ${finalRightText}`;
195
+ }
196
+ const leftText = this.visit(binExpr.left, context);
197
+ const scope = this.getScopeForNode(binExpr.left);
198
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.text, scope);
199
+ if (!typeInfo && !this.isBuiltinObject(binExpr.left)) {
200
+ return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(binExpr.left)})`;
201
+ }
202
+ if (typeInfo?.isConst) {
203
+ return `jspp::RuntimeError::throw_immutable_assignment_error()`;
204
+ }
205
+ return `*${leftText} ${op} ${rightText}`;
206
+ }
207
+ const leftText = this.visit(binExpr.left, context);
208
+ const rightText = this.visit(binExpr.right, context);
209
+ const leftIsIdentifier = ts.isIdentifier(binExpr.left);
210
+ const rightIsIdentifier = ts.isIdentifier(binExpr.right);
211
+ const scope = this.getScopeForNode(node);
212
+ const leftTypeInfo = leftIsIdentifier
213
+ ? this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.left.getText(), scope)
214
+ : null;
215
+ const rightTypeInfo = rightIsIdentifier
216
+ ? this.typeAnalyzer.scopeManager.lookupFromScope(binExpr.right.getText(), scope)
217
+ : null;
218
+ const finalLeft = leftIsIdentifier && leftTypeInfo && !leftTypeInfo.isParameter &&
219
+ !leftTypeInfo.isBuiltin
220
+ ? `jspp::Access::deref(${leftText}, ${this.getJsVarName(binExpr.left)})`
221
+ : leftText;
222
+ const finalRight = rightIsIdentifier && rightTypeInfo && !rightTypeInfo.isParameter &&
223
+ !rightTypeInfo.isBuiltin
224
+ ? `jspp::Access::deref(${rightText}, ${this.getJsVarName(binExpr.right)})`
225
+ : rightText;
226
+ if (leftIsIdentifier && !leftTypeInfo &&
227
+ !this.isBuiltinObject(binExpr.left)) {
228
+ return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(binExpr.left)})`;
229
+ }
230
+ if (rightIsIdentifier && !rightTypeInfo &&
231
+ !this.isBuiltinObject(binExpr.right)) {
232
+ return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(binExpr.right)})`;
233
+ }
234
+ if (opToken.kind === ts.SyntaxKind.EqualsEqualsEqualsToken) {
235
+ return `${finalLeft}.is_strictly_equal_to_primitive(${finalRight})`;
236
+ }
237
+ if (opToken.kind === ts.SyntaxKind.EqualsEqualsToken) {
238
+ return `${finalLeft}.is_equal_to_primitive(${finalRight})`;
239
+ }
240
+ if (opToken.kind === ts.SyntaxKind.ExclamationEqualsEqualsToken) {
241
+ return `${finalLeft}.not_strictly_equal_to_primitive(${finalRight})`;
242
+ }
243
+ if (opToken.kind === ts.SyntaxKind.ExclamationEqualsToken) {
244
+ return `${finalLeft}.not_equal_to_primitive(${finalRight})`;
245
+ }
246
+ if (opToken.kind === ts.SyntaxKind.AsteriskAsteriskToken) {
247
+ return `jspp::pow(${finalLeft}, ${finalRight})`;
248
+ }
249
+ if (op === "+" ||
250
+ op === "-" ||
251
+ op === "*" ||
252
+ op === "/" ||
253
+ op === "%" ||
254
+ op === "^" ||
255
+ op === "&" ||
256
+ op === "|") {
257
+ return `(${finalLeft} ${op} ${finalRight})`;
258
+ }
259
+ return `${finalLeft} ${op} ${finalRight}`;
260
+ }
261
+ export function visitCallExpression(node, context) {
262
+ const callExpr = node;
263
+ const callee = callExpr.expression;
264
+ const calleeName = this.escapeString(callee.getText());
265
+ const args = callExpr.arguments
266
+ .map((arg) => {
267
+ const argText = this.visit(arg, context);
268
+ if (ts.isIdentifier(arg)) {
269
+ const scope = this.getScopeForNode(arg);
270
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(arg.text, scope);
271
+ if (!typeInfo) {
272
+ return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(arg)})`;
273
+ }
274
+ if (typeInfo && !typeInfo.isParameter && !typeInfo.isBuiltin) {
275
+ return `jspp::Access::deref(${argText}, ${this.getJsVarName(arg)})`;
276
+ }
277
+ }
278
+ return argText;
279
+ })
280
+ .join(", ");
281
+ const calleeCode = this.visit(callee, context);
282
+ let derefCallee;
283
+ if (ts.isIdentifier(callee)) {
284
+ const scope = this.getScopeForNode(callee);
285
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(callee.text, scope);
286
+ if (!typeInfo && !this.isBuiltinObject(callee)) {
287
+ return `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(callee)})`;
288
+ }
289
+ if (typeInfo?.isBuiltin) {
290
+ derefCallee = calleeCode;
291
+ }
292
+ else {
293
+ derefCallee = `jspp::Access::deref(${calleeCode}, ${this.getJsVarName(callee)})`;
294
+ }
295
+ }
296
+ else {
297
+ derefCallee = calleeCode;
298
+ }
299
+ return `${derefCallee}.as_function()->call({${args}})`;
300
+ // return `${derefCallee}.as_function("${calleeName}")->call({${args}})`;
301
+ }
302
+ export function visitVoidExpression(node, context) {
303
+ const voidExpr = node;
304
+ const exprText = this.visit(voidExpr.expression, context);
305
+ return `(${exprText}, jspp::AnyValue::make_undefined())`;
306
+ }
307
+ export function visitTemplateExpression(node, context) {
308
+ const templateExpr = node;
309
+ let result = `jspp::AnyValue::make_string("${this.escapeString(templateExpr.head.text)}")`;
310
+ for (const span of templateExpr.templateSpans) {
311
+ const expr = span.expression;
312
+ const exprText = this.visit(expr, context);
313
+ let finalExpr = exprText;
314
+ if (ts.isIdentifier(expr)) {
315
+ const scope = this.getScopeForNode(expr);
316
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(expr.text, scope);
317
+ if (!typeInfo && !this.isBuiltinObject(expr)) {
318
+ finalExpr =
319
+ `jspp::RuntimeError::throw_unresolved_reference_error(${this.getJsVarName(expr)})`;
320
+ }
321
+ else if (typeInfo &&
322
+ !typeInfo.isParameter &&
323
+ !typeInfo.isBuiltin) {
324
+ finalExpr = `jspp::Access::deref(${exprText}, ${this.getJsVarName(expr)})`;
325
+ }
326
+ }
327
+ result += ` + (${finalExpr})`;
328
+ if (span.literal.text) {
329
+ result += ` + jspp::AnyValue::make_string("${this.escapeString(span.literal.text)}")`;
330
+ }
331
+ }
332
+ return result;
333
+ }
@@ -0,0 +1,94 @@
1
+ import ts from "typescript";
2
+ import { CodeGenerator } from "./";
3
+ export function generateLambda(node, isAssignment = false, capture = "[=]") {
4
+ const declaredSymbols = this.getDeclaredSymbols(node);
5
+ const argsName = this.generateUniqueName("__args_", declaredSymbols);
6
+ let lambda = `${capture}(const std::vector<jspp::AnyValue>& ${argsName}) mutable -> jspp::AnyValue `;
7
+ const visitContext = {
8
+ isMainContext: false,
9
+ isInsideFunction: true,
10
+ isFunctionBody: false,
11
+ };
12
+ if (node.body) {
13
+ if (ts.isBlock(node.body)) {
14
+ let paramExtraction = "";
15
+ this.indentationLevel++;
16
+ node.parameters.forEach((p, i) => {
17
+ const name = p.name.getText();
18
+ const defaultValue = p.initializer
19
+ ? this.visit(p.initializer, visitContext)
20
+ : "jspp::AnyValue::make_undefined()";
21
+ paramExtraction +=
22
+ `${this.indent()}auto ${name} = ${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue};\n`;
23
+ });
24
+ this.indentationLevel--;
25
+ const blockContent = this.visit(node.body, {
26
+ isMainContext: false,
27
+ isInsideFunction: true,
28
+ isFunctionBody: true,
29
+ });
30
+ // The block visitor already adds braces, so we need to inject the param extraction.
31
+ lambda += "{\n" + paramExtraction + blockContent.substring(2);
32
+ }
33
+ else {
34
+ lambda += "{\n";
35
+ this.indentationLevel++;
36
+ node.parameters.forEach((p, i) => {
37
+ const name = p.name.getText();
38
+ const defaultValue = p.initializer
39
+ ? this.visit(p.initializer, visitContext)
40
+ : "jspp::AnyValue::make_undefined()";
41
+ lambda +=
42
+ `${this.indent()}auto ${name} = ${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue};\n`;
43
+ });
44
+ lambda += `${this.indent()}return ${this.visit(node.body, {
45
+ isMainContext: false,
46
+ isInsideFunction: true,
47
+ isFunctionBody: false,
48
+ })};
49
+ `;
50
+ this.indentationLevel--;
51
+ lambda += `${this.indent()}}`;
52
+ }
53
+ }
54
+ else {
55
+ lambda += "{ return jspp::AnyValue::make_undefined(); }\n";
56
+ }
57
+ const signature = `jspp::AnyValue(const std::vector<jspp::AnyValue>&)`;
58
+ const callable = `std::function<${signature}>(${lambda})`;
59
+ const funcName = node.name?.getText();
60
+ const fullExpression = `jspp::AnyValue::make_function(${callable}, "${funcName || ""}")`;
61
+ if (ts.isFunctionDeclaration(node) && !isAssignment && funcName) {
62
+ return `${this.indent()}auto ${funcName} = ${fullExpression};\n`;
63
+ }
64
+ return fullExpression;
65
+ }
66
+ export function visitFunctionDeclaration(node, context) {
67
+ if (context.isInsideFunction) {
68
+ // This will now be handled by the Block visitor for hoisting.
69
+ // However, we still need to generate the lambda for assignment.
70
+ // The block visitor will wrap this in an assignment.
71
+ return this.generateLambda(node);
72
+ }
73
+ return "";
74
+ }
75
+ export function visitArrowFunction(node, context) {
76
+ return this.generateLambda(node);
77
+ }
78
+ export function visitFunctionExpression(node, context) {
79
+ const funcExpr = node;
80
+ if (funcExpr.name) {
81
+ const funcName = funcExpr.name.getText();
82
+ let code = "([=]() -> jspp::AnyValue {\n";
83
+ this.indentationLevel++;
84
+ code +=
85
+ `${this.indent()}auto ${funcName} = std::make_shared<jspp::AnyValue>();\n`;
86
+ const lambda = this.generateLambda(funcExpr, false, "[=]");
87
+ code += `${this.indent()}*${funcName} = ${lambda};\n`;
88
+ code += `${this.indent()}return *${funcName};\n`;
89
+ this.indentationLevel--;
90
+ code += `${this.indent()}})()`;
91
+ return code;
92
+ }
93
+ return this.generateLambda(node);
94
+ }
@@ -0,0 +1,83 @@
1
+ import ts from "typescript";
2
+ import { Scope } from "../../analysis/scope";
3
+ import { CodeGenerator } from "./";
4
+ const BUILTIN_OBJECTS = new Set([
5
+ "global",
6
+ "globalThis",
7
+ "console",
8
+ "Symbol",
9
+ ]);
10
+ export function isBuiltinObject(node) {
11
+ return BUILTIN_OBJECTS.has(node.text);
12
+ }
13
+ export function getDeclaredSymbols(node) {
14
+ const symbols = new Set();
15
+ const visitor = (child) => {
16
+ if (ts.isVariableDeclaration(child)) {
17
+ // Handles let, const, var
18
+ symbols.add(child.name.getText());
19
+ }
20
+ else if (ts.isFunctionDeclaration(child) && child.name) {
21
+ // Handles function declarations
22
+ symbols.add(child.name.getText());
23
+ }
24
+ else if (ts.isParameter(child)) {
25
+ // Handles function parameters
26
+ symbols.add(child.name.getText());
27
+ }
28
+ else if (ts.isCatchClause(child) && child.variableDeclaration) {
29
+ // Handles catch clause variable
30
+ symbols.add(child.variableDeclaration.name.getText());
31
+ }
32
+ ts.forEachChild(child, visitor);
33
+ };
34
+ visitor(node);
35
+ return symbols;
36
+ }
37
+ export function generateUniqueName(prefix, namesToAvoid) {
38
+ let name = `${prefix}${this.exceptionCounter}`;
39
+ while (namesToAvoid.has(name)) {
40
+ this.exceptionCounter++;
41
+ name = `${prefix}${this.exceptionCounter}`;
42
+ }
43
+ this.exceptionCounter++;
44
+ return name;
45
+ }
46
+ export function generateUniqueExceptionName(nameToAvoid) {
47
+ let exceptionName = `__caught_exception_${this.exceptionCounter}`;
48
+ while (exceptionName === nameToAvoid) {
49
+ this.exceptionCounter++;
50
+ exceptionName = `__caught_exception_${this.exceptionCounter}`;
51
+ }
52
+ this.exceptionCounter++;
53
+ return exceptionName;
54
+ }
55
+ export function getScopeForNode(node) {
56
+ let current = node;
57
+ while (current) {
58
+ const scope = this.typeAnalyzer.nodeToScope.get(current);
59
+ if (scope) {
60
+ return scope;
61
+ }
62
+ current = current.parent;
63
+ }
64
+ const rootScope = this.typeAnalyzer.scopeManager.getAllScopes()[0];
65
+ if (!rootScope) {
66
+ throw new Error("Compiler bug: Could not find a root scope.");
67
+ }
68
+ return rootScope;
69
+ }
70
+ export function indent() {
71
+ return " ".repeat(this.indentationLevel);
72
+ }
73
+ export function escapeString(str) {
74
+ return str
75
+ .replace(/\\/g, "\\\\")
76
+ .replace(/"/g, '\\"')
77
+ .replace(/\n/g, "\\n")
78
+ .replace(/\r/g, "\\r")
79
+ .replace(/\t/g, "\\t");
80
+ }
81
+ export function getJsVarName(node) {
82
+ return `"${node.text}"`;
83
+ }
@@ -0,0 +1,54 @@
1
+ import { TypeAnalyzer } from "../../analysis/typeAnalyzer";
2
+ import { generateLambda } from "./function-handlers";
3
+ import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getJsVarName, getScopeForNode, indent, isBuiltinObject, } from "./helpers";
4
+ import { visit } from "./visitor";
5
+ const CONTAINER_FUNCTION_NAME = "__container__";
6
+ export class CodeGenerator {
7
+ indentationLevel = 0;
8
+ typeAnalyzer;
9
+ exceptionCounter = 0;
10
+ // visitor
11
+ visit = visit;
12
+ // helpers
13
+ getDeclaredSymbols = getDeclaredSymbols;
14
+ generateUniqueName = generateUniqueName;
15
+ generateUniqueExceptionName = generateUniqueExceptionName;
16
+ getScopeForNode = getScopeForNode;
17
+ indent = indent;
18
+ escapeString = escapeString;
19
+ getJsVarName = getJsVarName;
20
+ isBuiltinObject = isBuiltinObject;
21
+ // function handlers
22
+ generateLambda = generateLambda;
23
+ /**
24
+ * Main entry point for the code generation process.
25
+ */
26
+ generate(ast, analyzer) {
27
+ this.typeAnalyzer = analyzer;
28
+ const declarations = `#include "index.hpp"\n\n`;
29
+ let containerCode = `jspp::AnyValue ${CONTAINER_FUNCTION_NAME}() {\n`;
30
+ this.indentationLevel++;
31
+ containerCode += this.visit(ast, {
32
+ isMainContext: true,
33
+ isInsideFunction: true,
34
+ isFunctionBody: true,
35
+ });
36
+ this.indentationLevel--;
37
+ containerCode += " return jspp::AnyValue::make_undefined();\n";
38
+ containerCode += "}\n\n";
39
+ let mainCode = "int main() {\n";
40
+ mainCode += ` std::ios::sync_with_stdio(false);\n`;
41
+ mainCode += ` std::cin.tie(nullptr);\n`;
42
+ mainCode += ` try {\n`;
43
+ mainCode += ` ${CONTAINER_FUNCTION_NAME}();\n`;
44
+ mainCode += ` } catch (const std::exception& ex) {\n`;
45
+ mainCode +=
46
+ " auto error = std::make_shared<jspp::AnyValue>(jspp::RuntimeError::error_to_value(ex));\n{\n";
47
+ mainCode +=
48
+ ` console.get_own_property("error").as_function("console.error")->call({*error});\n`;
49
+ mainCode += ` return 1;\n}\n`;
50
+ mainCode += ` }\n`;
51
+ mainCode += " return 0;\n}";
52
+ return declarations + containerCode + mainCode;
53
+ }
54
+ }
@@ -0,0 +1,32 @@
1
+ import ts from "typescript";
2
+ import { CodeGenerator } from "./";
3
+ export function visitIdentifier(node) {
4
+ if (node.text === "NaN") {
5
+ return "jspp::AnyValue::make_nan()";
6
+ }
7
+ if (node.text === "undefined") {
8
+ return "jspp::AnyValue::make_undefined()";
9
+ }
10
+ return node.text;
11
+ }
12
+ export function visitNumericLiteral(node) {
13
+ return `jspp::AnyValue::make_number(${this.escapeString(node.text)})`;
14
+ }
15
+ export function visitStringLiteral(node) {
16
+ return `jspp::AnyValue::make_string("${this.escapeString(node.text)}")`;
17
+ }
18
+ export function visitNoSubstitutionTemplateLiteral(node) {
19
+ return `jspp::AnyValue::make_string("${this.escapeString(node.text)}")`;
20
+ }
21
+ export function visitTrueKeyword() {
22
+ return "jspp::AnyValue::make_boolean(true)";
23
+ }
24
+ export function visitFalseKeyword() {
25
+ return "jspp::AnyValue::make_boolean(false)";
26
+ }
27
+ export function visitUndefinedKeyword() {
28
+ return "jspp::AnyValue::make_undefined()";
29
+ }
30
+ export function visitNullKeyword() {
31
+ return "jspp::AnyValue::make_null()";
32
+ }