@ugo-studio/jspp 0.2.4 → 0.2.5
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 +32 -0
- package/dist/ast/symbols.js +57 -15
- package/dist/cli.js +1 -1
- package/dist/core/codegen/control-flow-handlers.js +139 -51
- package/dist/core/codegen/declaration-handlers.js +37 -12
- package/dist/core/codegen/expression-handlers.js +38 -3
- package/dist/core/codegen/function-handlers.js +39 -56
- package/dist/core/codegen/helpers.js +169 -31
- package/dist/core/codegen/index.js +8 -4
- package/dist/core/codegen/statement-handlers.js +78 -35
- package/dist/core/parser.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/src/prelude/utils/access.hpp +2 -2
- package/src/prelude/utils/log_any_value/primitives.hpp +7 -0
- package/src/prelude/utils/operators.hpp +4 -6
- package/src/prelude/values/prototypes/function.hpp +18 -0
|
@@ -13,22 +13,22 @@ export function visitSourceFile(node, context) {
|
|
|
13
13
|
const hoistedSymbols = new DeclaredSymbols();
|
|
14
14
|
// Hoist function declarations
|
|
15
15
|
funcDecls.forEach((func) => {
|
|
16
|
-
code += this.hoistDeclaration(func, hoistedSymbols);
|
|
16
|
+
code += this.hoistDeclaration(func, hoistedSymbols, node);
|
|
17
17
|
});
|
|
18
18
|
// Hoist class declarations
|
|
19
19
|
classDecls.forEach((cls) => {
|
|
20
|
-
code += this.hoistDeclaration(cls, hoistedSymbols);
|
|
20
|
+
code += this.hoistDeclaration(cls, hoistedSymbols, node);
|
|
21
21
|
});
|
|
22
22
|
// Hoist variable declarations (var)
|
|
23
23
|
varDecls.forEach((decl) => {
|
|
24
|
-
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
24
|
+
code += this.hoistDeclaration(decl, hoistedSymbols, node);
|
|
25
25
|
});
|
|
26
26
|
// Hoist top-level let/const
|
|
27
27
|
topLevelLetConst.forEach((decl) => {
|
|
28
|
-
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
28
|
+
code += this.hoistDeclaration(decl, hoistedSymbols, node);
|
|
29
29
|
});
|
|
30
30
|
// Compile symbols for other statements (excluding function)
|
|
31
|
-
const
|
|
31
|
+
const globalScopeSymbols = this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols);
|
|
32
32
|
const localScopeSymbols = new DeclaredSymbols(hoistedSymbols); // hoistedSymbols becomes new local
|
|
33
33
|
// 2. Assign all hoisted functions first
|
|
34
34
|
const contextForFunctions = {
|
|
@@ -37,13 +37,29 @@ export function visitSourceFile(node, context) {
|
|
|
37
37
|
};
|
|
38
38
|
funcDecls.forEach((stmt) => {
|
|
39
39
|
const funcName = stmt.name?.getText();
|
|
40
|
-
if (funcName)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
if (!funcName)
|
|
41
|
+
return;
|
|
42
|
+
const symbol = hoistedSymbols.get(funcName);
|
|
43
|
+
if (!symbol)
|
|
44
|
+
return;
|
|
45
|
+
// Mark before further visits
|
|
46
|
+
this.markSymbolAsInitialized(funcName, contextForFunctions.globalScopeSymbols, contextForFunctions.localScopeSymbols);
|
|
47
|
+
this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
|
|
48
|
+
// Generate and update self name
|
|
49
|
+
const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
|
|
50
|
+
hoistedSymbols.update(funcName, { func: { nativeName } });
|
|
51
|
+
// Generate lambda
|
|
52
|
+
const lambda = this.generateLambda(stmt, contextForFunctions, {
|
|
53
|
+
isAssignment: true,
|
|
54
|
+
generateOnlyLambda: true,
|
|
55
|
+
nativeName,
|
|
56
|
+
});
|
|
57
|
+
code += `${this.indent()}auto ${nativeName} = ${lambda};\n`;
|
|
58
|
+
// Generate AnyValue wrapper
|
|
59
|
+
if (this.isFunctionUsedAsValue(stmt, node) ||
|
|
60
|
+
this.isFunctionUsedBeforeDeclaration(funcName, node)) {
|
|
61
|
+
const fullExpression = this.generateFullLambdaExpression(stmt, contextForFunctions, nativeName, { isAssignment: true, noTypeSignature: true });
|
|
62
|
+
code += `${this.indent()}*${funcName} = ${fullExpression};\n`;
|
|
47
63
|
}
|
|
48
64
|
});
|
|
49
65
|
// 3. Process other statements
|
|
@@ -57,7 +73,7 @@ export function visitSourceFile(node, context) {
|
|
|
57
73
|
0;
|
|
58
74
|
const contextForVisit = {
|
|
59
75
|
...context,
|
|
60
|
-
|
|
76
|
+
globalScopeSymbols,
|
|
61
77
|
localScopeSymbols,
|
|
62
78
|
isAssignmentOnly: !isLetOrConst,
|
|
63
79
|
};
|
|
@@ -70,7 +86,7 @@ export function visitSourceFile(node, context) {
|
|
|
70
86
|
code += this.visit(stmt, {
|
|
71
87
|
...context,
|
|
72
88
|
isFunctionBody: false,
|
|
73
|
-
|
|
89
|
+
globalScopeSymbols,
|
|
74
90
|
localScopeSymbols,
|
|
75
91
|
});
|
|
76
92
|
}
|
|
@@ -78,6 +94,7 @@ export function visitSourceFile(node, context) {
|
|
|
78
94
|
return code;
|
|
79
95
|
}
|
|
80
96
|
export function visitBlock(node, context) {
|
|
97
|
+
context.currentScopeNode = node; // Update scope node
|
|
81
98
|
let code = `${this.indent()}{\n`;
|
|
82
99
|
this.indentationLevel++;
|
|
83
100
|
const block = node;
|
|
@@ -88,18 +105,18 @@ export function visitBlock(node, context) {
|
|
|
88
105
|
const hoistedSymbols = new DeclaredSymbols();
|
|
89
106
|
// 1. Hoist all function declarations
|
|
90
107
|
funcDecls.forEach((func) => {
|
|
91
|
-
code += this.hoistDeclaration(func, hoistedSymbols);
|
|
108
|
+
code += this.hoistDeclaration(func, hoistedSymbols, node);
|
|
92
109
|
});
|
|
93
110
|
// Hoist class declarations
|
|
94
111
|
classDecls.forEach((cls) => {
|
|
95
|
-
code += this.hoistDeclaration(cls, hoistedSymbols);
|
|
112
|
+
code += this.hoistDeclaration(cls, hoistedSymbols, node);
|
|
96
113
|
});
|
|
97
114
|
// Hoist variable declarations (let/const only)
|
|
98
115
|
blockScopedDecls.forEach((decl) => {
|
|
99
|
-
code += this.hoistDeclaration(decl, hoistedSymbols);
|
|
116
|
+
code += this.hoistDeclaration(decl, hoistedSymbols, node);
|
|
100
117
|
});
|
|
101
118
|
// Compile symbols for other statements (excluding function)
|
|
102
|
-
const
|
|
119
|
+
const globalScopeSymbols = this.prepareScopeSymbolsForVisit(context.globalScopeSymbols, context.localScopeSymbols);
|
|
103
120
|
const localScopeSymbols = new DeclaredSymbols(hoistedSymbols); // hoistedSymbols becomes new local
|
|
104
121
|
// 2. Assign all hoisted functions first
|
|
105
122
|
const contextForFunctions = {
|
|
@@ -108,13 +125,29 @@ export function visitBlock(node, context) {
|
|
|
108
125
|
};
|
|
109
126
|
funcDecls.forEach((stmt) => {
|
|
110
127
|
const funcName = stmt.name?.getText();
|
|
111
|
-
if (funcName)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
128
|
+
if (!funcName)
|
|
129
|
+
return;
|
|
130
|
+
const symbol = hoistedSymbols.get(funcName);
|
|
131
|
+
if (!symbol)
|
|
132
|
+
return;
|
|
133
|
+
// Mark before further visits
|
|
134
|
+
this.markSymbolAsInitialized(funcName, contextForFunctions.globalScopeSymbols, contextForFunctions.localScopeSymbols);
|
|
135
|
+
this.markSymbolAsInitialized(funcName, globalScopeSymbols, localScopeSymbols);
|
|
136
|
+
// Generate and update self name
|
|
137
|
+
const nativeName = this.generateUniqueName(`__${funcName}_native_`, hoistedSymbols);
|
|
138
|
+
hoistedSymbols.update(funcName, { func: { nativeName } });
|
|
139
|
+
// Generate lambda
|
|
140
|
+
const lambda = this.generateLambda(stmt, contextForFunctions, {
|
|
141
|
+
isAssignment: true,
|
|
142
|
+
generateOnlyLambda: true,
|
|
143
|
+
nativeName,
|
|
144
|
+
});
|
|
145
|
+
code += `${this.indent()}auto ${nativeName} = ${lambda};\n`;
|
|
146
|
+
// Generate AnyValue wrapper
|
|
147
|
+
if (this.isFunctionUsedAsValue(stmt, node) ||
|
|
148
|
+
this.isFunctionUsedBeforeDeclaration(funcName, node)) {
|
|
149
|
+
const fullExpression = this.generateFullLambdaExpression(stmt, contextForFunctions, nativeName, { isAssignment: true, noTypeSignature: true });
|
|
150
|
+
code += `${this.indent()}*${funcName} = ${fullExpression};\n`;
|
|
118
151
|
}
|
|
119
152
|
});
|
|
120
153
|
// 3. Process other statements
|
|
@@ -128,7 +161,7 @@ export function visitBlock(node, context) {
|
|
|
128
161
|
0;
|
|
129
162
|
const contextForVisit = {
|
|
130
163
|
...context,
|
|
131
|
-
|
|
164
|
+
globalScopeSymbols,
|
|
132
165
|
localScopeSymbols,
|
|
133
166
|
isAssignmentOnly: !isLetOrConst,
|
|
134
167
|
};
|
|
@@ -141,7 +174,7 @@ export function visitBlock(node, context) {
|
|
|
141
174
|
code += this.visit(stmt, {
|
|
142
175
|
...context,
|
|
143
176
|
isFunctionBody: false,
|
|
144
|
-
|
|
177
|
+
globalScopeSymbols,
|
|
145
178
|
localScopeSymbols,
|
|
146
179
|
});
|
|
147
180
|
}
|
|
@@ -217,7 +250,17 @@ export function visitIfStatement(node, context) {
|
|
|
217
250
|
isFunctionBody: false,
|
|
218
251
|
});
|
|
219
252
|
}
|
|
220
|
-
|
|
253
|
+
if (ts.isBinaryExpression(ifStmt.expression)) {
|
|
254
|
+
const binExpr = ifStmt.expression;
|
|
255
|
+
const op = binExpr.operatorToken.getText();
|
|
256
|
+
const isBoolean = op === "==" || op === "!=" || op === "===" ||
|
|
257
|
+
op === "!==" || op === "<" || op === ">" || op === "<=" ||
|
|
258
|
+
op === ">=" || op === "instanceof" || op === "in";
|
|
259
|
+
if (isBoolean) {
|
|
260
|
+
return `${this.indent()}if ((${condition}).as_boolean()) ${thenStmt}${elseStmt}`;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return `${this.indent()}if (jspp::is_truthy(${condition})) ${thenStmt}${elseStmt}`;
|
|
221
264
|
}
|
|
222
265
|
export function visitExpressionStatement(node, context) {
|
|
223
266
|
return (this.indent() +
|
|
@@ -234,7 +277,7 @@ export function visitTryStatement(node, context) {
|
|
|
234
277
|
const tryStmt = node;
|
|
235
278
|
if (context.isInsideAsyncFunction) {
|
|
236
279
|
if (tryStmt.finallyBlock) {
|
|
237
|
-
const declaredSymbols = new Set(context.
|
|
280
|
+
const declaredSymbols = new Set(context.globalScopeSymbols.names);
|
|
238
281
|
this.getDeclaredSymbols(tryStmt.tryBlock).forEach((s) => declaredSymbols.add(s));
|
|
239
282
|
if (tryStmt.catchClause) {
|
|
240
283
|
this.getDeclaredSymbols(tryStmt.catchClause).forEach((s) => declaredSymbols.add(s));
|
|
@@ -339,7 +382,7 @@ export function visitTryStatement(node, context) {
|
|
|
339
382
|
return code;
|
|
340
383
|
}
|
|
341
384
|
else {
|
|
342
|
-
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.
|
|
385
|
+
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.globalScopeSymbols, context.localScopeSymbols);
|
|
343
386
|
const newContext = {
|
|
344
387
|
...context,
|
|
345
388
|
isFunctionBody: false,
|
|
@@ -405,7 +448,7 @@ export function visitTryStatement(node, context) {
|
|
|
405
448
|
}
|
|
406
449
|
}
|
|
407
450
|
if (tryStmt.finallyBlock) {
|
|
408
|
-
const declaredSymbols = new Set(context.
|
|
451
|
+
const declaredSymbols = new Set(context.globalScopeSymbols.names);
|
|
409
452
|
this.getDeclaredSymbols(tryStmt.tryBlock).forEach((s) => declaredSymbols.add(s));
|
|
410
453
|
if (tryStmt.catchClause) {
|
|
411
454
|
this.getDeclaredSymbols(tryStmt.catchClause).forEach((s) => declaredSymbols.add(s));
|
|
@@ -440,7 +483,7 @@ export function visitTryStatement(node, context) {
|
|
|
440
483
|
this.indentationLevel--;
|
|
441
484
|
code += `${this.indent()}}\n`;
|
|
442
485
|
if (tryStmt.catchClause) {
|
|
443
|
-
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText(), context.
|
|
486
|
+
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause.variableDeclaration?.name.getText(), context.globalScopeSymbols, context.localScopeSymbols);
|
|
444
487
|
const catchContext = { ...innerContext, exceptionName };
|
|
445
488
|
code +=
|
|
446
489
|
`${this.indent()}catch (const std::exception& ${exceptionName}) {\n`;
|
|
@@ -476,7 +519,7 @@ export function visitTryStatement(node, context) {
|
|
|
476
519
|
return code;
|
|
477
520
|
}
|
|
478
521
|
else {
|
|
479
|
-
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.
|
|
522
|
+
const exceptionName = this.generateUniqueExceptionName(tryStmt.catchClause?.variableDeclaration?.name.getText(), context.globalScopeSymbols, context.localScopeSymbols);
|
|
480
523
|
const newContext = {
|
|
481
524
|
...context,
|
|
482
525
|
isFunctionBody: false,
|
|
@@ -540,7 +583,7 @@ export function visitYieldExpression(node, context) {
|
|
|
540
583
|
let code = `${this.indent()}{\n`;
|
|
541
584
|
this.indentationLevel++;
|
|
542
585
|
const declaredSymbols = this.getDeclaredSymbols(expr);
|
|
543
|
-
context.
|
|
586
|
+
context.globalScopeSymbols.names.forEach((s) => declaredSymbols.add(s));
|
|
544
587
|
const iterableRef = this.generateUniqueName("__iter_ref", declaredSymbols);
|
|
545
588
|
const iterator = this.generateUniqueName("__iter", declaredSymbols);
|
|
546
589
|
const nextFunc = this.generateUniqueName("__next_func", declaredSymbols);
|
|
@@ -566,7 +609,7 @@ export function visitYieldExpression(node, context) {
|
|
|
566
609
|
`${this.indent()}auto ${nextRes} = ${nextFunc}.call(${iterator}, {}, "next");\n`;
|
|
567
610
|
}
|
|
568
611
|
code +=
|
|
569
|
-
`${this.indent()}while (!is_truthy(${nextRes}.get_own_property("done"))) {\n`;
|
|
612
|
+
`${this.indent()}while (!jspp::is_truthy(${nextRes}.get_own_property("done"))) {\n`;
|
|
570
613
|
this.indentationLevel++;
|
|
571
614
|
if (context.isInsideAsyncFunction) {
|
|
572
615
|
code +=
|
package/dist/core/parser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as ts from "typescript";
|
|
2
2
|
export class Parser {
|
|
3
|
-
parse(sourceCode) {
|
|
4
|
-
return ts.createSourceFile("temp.js", sourceCode, ts.ScriptTarget.Latest, true);
|
|
3
|
+
parse(sourceCode, fileName) {
|
|
4
|
+
return ts.createSourceFile(fileName || "temp.js", sourceCode, ts.ScriptTarget.Latest, true);
|
|
5
5
|
}
|
|
6
6
|
}
|
package/dist/index.js
CHANGED
|
@@ -6,8 +6,8 @@ export class Interpreter {
|
|
|
6
6
|
parser = new Parser();
|
|
7
7
|
analyzer = new TypeAnalyzer();
|
|
8
8
|
generator = new CodeGenerator();
|
|
9
|
-
interpret(jsCode) {
|
|
10
|
-
const ast = this.parser.parse(jsCode);
|
|
9
|
+
interpret(jsCode, fileName) {
|
|
10
|
+
const ast = this.parser.parse(jsCode, fileName);
|
|
11
11
|
this.analyzer.analyze(ast);
|
|
12
12
|
const cppCode = this.generator.generate(ast, this.analyzer);
|
|
13
13
|
const preludePath = path.resolve(import.meta.dirname, "..", "src", "prelude");
|
package/package.json
CHANGED
|
@@ -231,7 +231,7 @@ namespace jspp
|
|
|
231
231
|
{
|
|
232
232
|
throw jspp::Exception::make_exception("Function has non-object prototype in instanceof check", "TypeError");
|
|
233
233
|
}
|
|
234
|
-
|
|
234
|
+
|
|
235
235
|
AnyValue current = lhs;
|
|
236
236
|
|
|
237
237
|
while (true)
|
|
@@ -253,7 +253,7 @@ namespace jspp
|
|
|
253
253
|
{
|
|
254
254
|
break;
|
|
255
255
|
}
|
|
256
|
-
|
|
256
|
+
|
|
257
257
|
if (proto.is_null() || proto.is_undefined())
|
|
258
258
|
break;
|
|
259
259
|
if (is_strictly_equal_to_primitive(proto, targetProto))
|
|
@@ -27,7 +27,14 @@ namespace jspp
|
|
|
27
27
|
if (val.is_symbol())
|
|
28
28
|
return Color::BLUE + val.to_std_string() + Color::RESET;
|
|
29
29
|
if (val.is_accessor_descriptor())
|
|
30
|
+
{
|
|
31
|
+
auto desc = val.as_accessor_descriptor();
|
|
32
|
+
if (desc->get.has_value() && !desc->set.has_value())
|
|
33
|
+
return Color::BLUE + std::string("[Getter]") + Color::RESET;
|
|
34
|
+
if (!desc->get.has_value() && desc->set.has_value())
|
|
35
|
+
return Color::BLUE + std::string("[Setter]") + Color::RESET;
|
|
30
36
|
return Color::BLUE + std::string("[Getter/Setter]") + Color::RESET;
|
|
37
|
+
}
|
|
31
38
|
|
|
32
39
|
if (val.is_string())
|
|
33
40
|
{
|
|
@@ -93,16 +93,14 @@ namespace jspp
|
|
|
93
93
|
{
|
|
94
94
|
switch (val.get_type())
|
|
95
95
|
{
|
|
96
|
-
case JsType::Boolean:
|
|
97
|
-
return val.as_boolean();
|
|
98
96
|
case JsType::Number:
|
|
99
97
|
return is_truthy(val.as_double());
|
|
100
98
|
case JsType::String:
|
|
101
|
-
return
|
|
102
|
-
case JsType::
|
|
103
|
-
return
|
|
99
|
+
return is_truthy(val.as_string()->value);
|
|
100
|
+
case JsType::Boolean:
|
|
101
|
+
return val.as_boolean();
|
|
104
102
|
case JsType::Null:
|
|
105
|
-
|
|
103
|
+
case JsType::Undefined:
|
|
106
104
|
case JsType::Uninitialized:
|
|
107
105
|
return false;
|
|
108
106
|
default:
|
|
@@ -20,6 +20,24 @@ namespace jspp
|
|
|
20
20
|
key);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// --- call() method ---
|
|
24
|
+
if (key == "call")
|
|
25
|
+
{
|
|
26
|
+
return AnyValue::make_function([](const AnyValue &thisVal, std::span<const AnyValue> args) -> AnyValue
|
|
27
|
+
{
|
|
28
|
+
AnyValue thisArg = Constants::UNDEFINED;
|
|
29
|
+
std::span<const AnyValue> fnArgs;
|
|
30
|
+
|
|
31
|
+
if (!args.empty())
|
|
32
|
+
{
|
|
33
|
+
thisArg = args[0];
|
|
34
|
+
fnArgs = args.subspan(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return thisVal.call(thisArg, fnArgs);
|
|
38
|
+
}, key);
|
|
39
|
+
}
|
|
40
|
+
|
|
23
41
|
return std::nullopt;
|
|
24
42
|
}
|
|
25
43
|
}
|