jslike 1.3.1 → 1.4.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.
- package/bin/jslike.js +1 -1
- package/dist/editor/monaco/index.cjs +239 -0
- package/{src/editor/monaco/index.js → dist/editor/monaco/index.d.cts} +4 -2
- package/dist/editor/monaco/index.d.ts +236 -0
- package/dist/editor/monaco/index.js +214 -0
- package/dist/editor/wang-prism.cjs +109 -0
- package/dist/editor/wang-prism.d.ts +136 -0
- package/dist/editor/wang-prism.js +109 -0
- package/dist/index.cjs +8106 -0
- package/{src/parser.js → dist/index.d.cts} +3276 -41
- package/dist/index.d.ts +9476 -0
- package/dist/index.js +8072 -0
- package/dist/validator/index.cjs +5753 -0
- package/dist/validator/index.d.cts +103 -0
- package/{src/validator/index.js → dist/validator/index.d.ts} +5 -2
- package/dist/validator/index.js +5727 -0
- package/package.json +49 -10
- package/src/ast/nodes.js +0 -236
- package/src/cli/wang-run.js +0 -89
- package/src/cli/wang-validate.js +0 -87
- package/src/errors/enhanced-error.js +0 -124
- package/src/index.js +0 -186
- package/src/interpreter/index.js +0 -201
- package/src/interpreter/interpreter.js +0 -2155
- package/src/metadata/index.js +0 -531
- package/src/resolvers/memory.js +0 -17
- package/src/runtime/builtins.js +0 -517
- package/src/runtime/environment.js +0 -75
- /package/{src/editor/wang-prism.js → dist/editor/wang-prism.d.cts} +0 -0
|
@@ -828,10 +828,10 @@ var pp$8 = Parser.prototype;
|
|
|
828
828
|
// to its body instead of creating a new node.
|
|
829
829
|
|
|
830
830
|
pp$8.parseTopLevel = function(node) {
|
|
831
|
-
var exports = Object.create(null);
|
|
831
|
+
var exports$1 = Object.create(null);
|
|
832
832
|
if (!node.body) { node.body = []; }
|
|
833
833
|
while (this.type !== types$1.eof) {
|
|
834
|
-
var stmt = this.parseStatement(null, true, exports);
|
|
834
|
+
var stmt = this.parseStatement(null, true, exports$1);
|
|
835
835
|
node.body.push(stmt);
|
|
836
836
|
}
|
|
837
837
|
if (this.inModule)
|
|
@@ -938,7 +938,7 @@ pp$8.isUsing = function(isFor) {
|
|
|
938
938
|
// `if (foo) /blah/.exec(foo)`, where looking at the previous token
|
|
939
939
|
// does not help.
|
|
940
940
|
|
|
941
|
-
pp$8.parseStatement = function(context, topLevel, exports) {
|
|
941
|
+
pp$8.parseStatement = function(context, topLevel, exports$1) {
|
|
942
942
|
var starttype = this.type, node = this.startNode(), kind;
|
|
943
943
|
|
|
944
944
|
if (this.isLet(context)) {
|
|
@@ -993,7 +993,7 @@ pp$8.parseStatement = function(context, topLevel, exports) {
|
|
|
993
993
|
if (!this.inModule)
|
|
994
994
|
{ this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'"); }
|
|
995
995
|
}
|
|
996
|
-
return starttype === types$1._import ? this.parseImport(node) : this.parseExport(node, exports)
|
|
996
|
+
return starttype === types$1._import ? this.parseImport(node) : this.parseExport(node, exports$1)
|
|
997
997
|
|
|
998
998
|
// If the statement does not start with a statement keyword or a
|
|
999
999
|
// brace, it's an ExpressionStatement or LabeledStatement. We
|
|
@@ -1749,11 +1749,11 @@ function checkKeyName(node, name) {
|
|
|
1749
1749
|
|
|
1750
1750
|
// Parses module export declaration.
|
|
1751
1751
|
|
|
1752
|
-
pp$8.parseExportAllDeclaration = function(node, exports) {
|
|
1752
|
+
pp$8.parseExportAllDeclaration = function(node, exports$1) {
|
|
1753
1753
|
if (this.options.ecmaVersion >= 11) {
|
|
1754
1754
|
if (this.eatContextual("as")) {
|
|
1755
1755
|
node.exported = this.parseModuleExportName();
|
|
1756
|
-
this.checkExport(exports, node.exported, this.lastTokStart);
|
|
1756
|
+
this.checkExport(exports$1, node.exported, this.lastTokStart);
|
|
1757
1757
|
} else {
|
|
1758
1758
|
node.exported = null;
|
|
1759
1759
|
}
|
|
@@ -1767,14 +1767,14 @@ pp$8.parseExportAllDeclaration = function(node, exports) {
|
|
|
1767
1767
|
return this.finishNode(node, "ExportAllDeclaration")
|
|
1768
1768
|
};
|
|
1769
1769
|
|
|
1770
|
-
pp$8.parseExport = function(node, exports) {
|
|
1770
|
+
pp$8.parseExport = function(node, exports$1) {
|
|
1771
1771
|
this.next();
|
|
1772
1772
|
// export * from '...'
|
|
1773
1773
|
if (this.eat(types$1.star)) {
|
|
1774
|
-
return this.parseExportAllDeclaration(node, exports)
|
|
1774
|
+
return this.parseExportAllDeclaration(node, exports$1)
|
|
1775
1775
|
}
|
|
1776
1776
|
if (this.eat(types$1._default)) { // export default ...
|
|
1777
|
-
this.checkExport(exports, "default", this.lastTokStart);
|
|
1777
|
+
this.checkExport(exports$1, "default", this.lastTokStart);
|
|
1778
1778
|
node.declaration = this.parseExportDefaultDeclaration();
|
|
1779
1779
|
return this.finishNode(node, "ExportDefaultDeclaration")
|
|
1780
1780
|
}
|
|
@@ -1782,16 +1782,16 @@ pp$8.parseExport = function(node, exports) {
|
|
|
1782
1782
|
if (this.shouldParseExportStatement()) {
|
|
1783
1783
|
node.declaration = this.parseExportDeclaration(node);
|
|
1784
1784
|
if (node.declaration.type === "VariableDeclaration")
|
|
1785
|
-
{ this.checkVariableExport(exports, node.declaration.declarations); }
|
|
1785
|
+
{ this.checkVariableExport(exports$1, node.declaration.declarations); }
|
|
1786
1786
|
else
|
|
1787
|
-
{ this.checkExport(exports, node.declaration.id, node.declaration.id.start); }
|
|
1787
|
+
{ this.checkExport(exports$1, node.declaration.id, node.declaration.id.start); }
|
|
1788
1788
|
node.specifiers = [];
|
|
1789
1789
|
node.source = null;
|
|
1790
1790
|
if (this.options.ecmaVersion >= 16)
|
|
1791
1791
|
{ node.attributes = []; }
|
|
1792
1792
|
} else { // export { x, y as z } [from '...']
|
|
1793
1793
|
node.declaration = null;
|
|
1794
|
-
node.specifiers = this.parseExportSpecifiers(exports);
|
|
1794
|
+
node.specifiers = this.parseExportSpecifiers(exports$1);
|
|
1795
1795
|
if (this.eatContextual("from")) {
|
|
1796
1796
|
if (this.type !== types$1.string) { this.unexpected(); }
|
|
1797
1797
|
node.source = this.parseExprAtom();
|
|
@@ -1841,47 +1841,47 @@ pp$8.parseExportDefaultDeclaration = function() {
|
|
|
1841
1841
|
}
|
|
1842
1842
|
};
|
|
1843
1843
|
|
|
1844
|
-
pp$8.checkExport = function(exports, name, pos) {
|
|
1845
|
-
if (!exports) { return }
|
|
1844
|
+
pp$8.checkExport = function(exports$1, name, pos) {
|
|
1845
|
+
if (!exports$1) { return }
|
|
1846
1846
|
if (typeof name !== "string")
|
|
1847
1847
|
{ name = name.type === "Identifier" ? name.name : name.value; }
|
|
1848
|
-
if (hasOwn(exports, name))
|
|
1848
|
+
if (hasOwn(exports$1, name))
|
|
1849
1849
|
{ this.raiseRecoverable(pos, "Duplicate export '" + name + "'"); }
|
|
1850
|
-
exports[name] = true;
|
|
1850
|
+
exports$1[name] = true;
|
|
1851
1851
|
};
|
|
1852
1852
|
|
|
1853
|
-
pp$8.checkPatternExport = function(exports, pat) {
|
|
1853
|
+
pp$8.checkPatternExport = function(exports$1, pat) {
|
|
1854
1854
|
var type = pat.type;
|
|
1855
1855
|
if (type === "Identifier")
|
|
1856
|
-
{ this.checkExport(exports, pat, pat.start); }
|
|
1856
|
+
{ this.checkExport(exports$1, pat, pat.start); }
|
|
1857
1857
|
else if (type === "ObjectPattern")
|
|
1858
1858
|
{ for (var i = 0, list = pat.properties; i < list.length; i += 1)
|
|
1859
1859
|
{
|
|
1860
1860
|
var prop = list[i];
|
|
1861
1861
|
|
|
1862
|
-
this.checkPatternExport(exports, prop);
|
|
1862
|
+
this.checkPatternExport(exports$1, prop);
|
|
1863
1863
|
} }
|
|
1864
1864
|
else if (type === "ArrayPattern")
|
|
1865
1865
|
{ for (var i$1 = 0, list$1 = pat.elements; i$1 < list$1.length; i$1 += 1) {
|
|
1866
1866
|
var elt = list$1[i$1];
|
|
1867
1867
|
|
|
1868
|
-
if (elt) { this.checkPatternExport(exports, elt); }
|
|
1868
|
+
if (elt) { this.checkPatternExport(exports$1, elt); }
|
|
1869
1869
|
} }
|
|
1870
1870
|
else if (type === "Property")
|
|
1871
|
-
{ this.checkPatternExport(exports, pat.value); }
|
|
1871
|
+
{ this.checkPatternExport(exports$1, pat.value); }
|
|
1872
1872
|
else if (type === "AssignmentPattern")
|
|
1873
|
-
{ this.checkPatternExport(exports, pat.left); }
|
|
1873
|
+
{ this.checkPatternExport(exports$1, pat.left); }
|
|
1874
1874
|
else if (type === "RestElement")
|
|
1875
|
-
{ this.checkPatternExport(exports, pat.argument); }
|
|
1875
|
+
{ this.checkPatternExport(exports$1, pat.argument); }
|
|
1876
1876
|
};
|
|
1877
1877
|
|
|
1878
|
-
pp$8.checkVariableExport = function(exports, decls) {
|
|
1879
|
-
if (!exports) { return }
|
|
1878
|
+
pp$8.checkVariableExport = function(exports$1, decls) {
|
|
1879
|
+
if (!exports$1) { return }
|
|
1880
1880
|
for (var i = 0, list = decls; i < list.length; i += 1)
|
|
1881
1881
|
{
|
|
1882
1882
|
var decl = list[i];
|
|
1883
1883
|
|
|
1884
|
-
this.checkPatternExport(exports, decl.id);
|
|
1884
|
+
this.checkPatternExport(exports$1, decl.id);
|
|
1885
1885
|
}
|
|
1886
1886
|
};
|
|
1887
1887
|
|
|
@@ -1896,13 +1896,13 @@ pp$8.shouldParseExportStatement = function() {
|
|
|
1896
1896
|
|
|
1897
1897
|
// Parses a comma-separated list of module exports.
|
|
1898
1898
|
|
|
1899
|
-
pp$8.parseExportSpecifier = function(exports) {
|
|
1899
|
+
pp$8.parseExportSpecifier = function(exports$1) {
|
|
1900
1900
|
var node = this.startNode();
|
|
1901
1901
|
node.local = this.parseModuleExportName();
|
|
1902
1902
|
|
|
1903
1903
|
node.exported = this.eatContextual("as") ? this.parseModuleExportName() : node.local;
|
|
1904
1904
|
this.checkExport(
|
|
1905
|
-
exports,
|
|
1905
|
+
exports$1,
|
|
1906
1906
|
node.exported,
|
|
1907
1907
|
node.exported.start
|
|
1908
1908
|
);
|
|
@@ -1910,7 +1910,7 @@ pp$8.parseExportSpecifier = function(exports) {
|
|
|
1910
1910
|
return this.finishNode(node, "ExportSpecifier")
|
|
1911
1911
|
};
|
|
1912
1912
|
|
|
1913
|
-
pp$8.parseExportSpecifiers = function(exports) {
|
|
1913
|
+
pp$8.parseExportSpecifiers = function(exports$1) {
|
|
1914
1914
|
var nodes = [], first = true;
|
|
1915
1915
|
// export { x, y as z } [from '...']
|
|
1916
1916
|
this.expect(types$1.braceL);
|
|
@@ -1920,7 +1920,7 @@ pp$8.parseExportSpecifiers = function(exports) {
|
|
|
1920
1920
|
if (this.afterTrailingComma(types$1.braceR)) { break }
|
|
1921
1921
|
} else { first = false; }
|
|
1922
1922
|
|
|
1923
|
-
nodes.push(this.parseExportSpecifier(exports));
|
|
1923
|
+
nodes.push(this.parseExportSpecifier(exports$1));
|
|
1924
1924
|
}
|
|
1925
1925
|
return nodes
|
|
1926
1926
|
};
|
|
@@ -6218,24 +6218,3259 @@ Parser.acorn = {
|
|
|
6218
6218
|
//
|
|
6219
6219
|
// [estree]: https://github.com/estree/estree
|
|
6220
6220
|
|
|
6221
|
-
function parse(input, options) {
|
|
6221
|
+
function parse$1(input, options) {
|
|
6222
6222
|
return Parser.parse(input, options)
|
|
6223
6223
|
}
|
|
6224
6224
|
|
|
6225
|
-
//
|
|
6226
|
-
// offset in a string. Useful for parsing mixed-language formats
|
|
6227
|
-
// that embed JavaScript expressions.
|
|
6225
|
+
// Runtime Environment for variable scoping and storage
|
|
6228
6226
|
|
|
6229
|
-
|
|
6230
|
-
|
|
6227
|
+
class Environment {
|
|
6228
|
+
constructor(parent = null) {
|
|
6229
|
+
this.parent = parent;
|
|
6230
|
+
this.vars = new Map();
|
|
6231
|
+
this.consts = new Set(); // Track const variables
|
|
6232
|
+
}
|
|
6233
|
+
|
|
6234
|
+
define(name, value, isConst = false) {
|
|
6235
|
+
if (this.vars.has(name)) {
|
|
6236
|
+
throw new Error(`Variable '${name}' already declared`);
|
|
6237
|
+
}
|
|
6238
|
+
this.vars.set(name, value);
|
|
6239
|
+
if (isConst) {
|
|
6240
|
+
this.consts.add(name);
|
|
6241
|
+
}
|
|
6242
|
+
return value;
|
|
6243
|
+
}
|
|
6244
|
+
|
|
6245
|
+
get(name) {
|
|
6246
|
+
if (this.vars.has(name)) {
|
|
6247
|
+
return this.vars.get(name);
|
|
6248
|
+
}
|
|
6249
|
+
if (this.parent) {
|
|
6250
|
+
return this.parent.get(name);
|
|
6251
|
+
}
|
|
6252
|
+
throw new ReferenceError(`Variable "${name}" is not defined`);
|
|
6253
|
+
}
|
|
6254
|
+
|
|
6255
|
+
set(name, value) {
|
|
6256
|
+
if (this.vars.has(name)) {
|
|
6257
|
+
// Check if trying to reassign a const variable
|
|
6258
|
+
if (this.consts.has(name)) {
|
|
6259
|
+
throw new TypeError(`Cannot reassign const variable '${name}'`);
|
|
6260
|
+
}
|
|
6261
|
+
this.vars.set(name, value);
|
|
6262
|
+
return value;
|
|
6263
|
+
}
|
|
6264
|
+
if (this.parent) {
|
|
6265
|
+
return this.parent.set(name, value);
|
|
6266
|
+
}
|
|
6267
|
+
throw new ReferenceError(`Variable "${name}" is not defined`);
|
|
6268
|
+
}
|
|
6269
|
+
|
|
6270
|
+
has(name) {
|
|
6271
|
+
return this.vars.has(name) || (this.parent ? this.parent.has(name) : false);
|
|
6272
|
+
}
|
|
6273
|
+
|
|
6274
|
+
// For let/const block scoping
|
|
6275
|
+
extend() {
|
|
6276
|
+
return new Environment(this);
|
|
6277
|
+
}
|
|
6278
|
+
}
|
|
6279
|
+
|
|
6280
|
+
// Special control flow signals
|
|
6281
|
+
class ReturnValue {
|
|
6282
|
+
constructor(value) {
|
|
6283
|
+
this.value = value;
|
|
6284
|
+
}
|
|
6285
|
+
}
|
|
6286
|
+
|
|
6287
|
+
class BreakSignal {
|
|
6288
|
+
constructor() {}
|
|
6289
|
+
}
|
|
6290
|
+
|
|
6291
|
+
class ContinueSignal {
|
|
6292
|
+
constructor() {}
|
|
6293
|
+
}
|
|
6294
|
+
|
|
6295
|
+
class ThrowSignal {
|
|
6296
|
+
constructor(value) {
|
|
6297
|
+
this.value = value;
|
|
6298
|
+
}
|
|
6299
|
+
}
|
|
6300
|
+
|
|
6301
|
+
/**
|
|
6302
|
+
* Enhanced error classes for better developer experience
|
|
6303
|
+
*/
|
|
6304
|
+
|
|
6305
|
+
// Simple Levenshtein distance for "did you mean" suggestions
|
|
6306
|
+
function levenshteinDistance(a, b) {
|
|
6307
|
+
const matrix = [];
|
|
6308
|
+
|
|
6309
|
+
for (let i = 0; i <= b.length; i++) {
|
|
6310
|
+
matrix[i] = [i];
|
|
6311
|
+
}
|
|
6312
|
+
|
|
6313
|
+
for (let j = 0; j <= a.length; j++) {
|
|
6314
|
+
matrix[0][j] = j;
|
|
6315
|
+
}
|
|
6316
|
+
|
|
6317
|
+
for (let i = 1; i <= b.length; i++) {
|
|
6318
|
+
for (let j = 1; j <= a.length; j++) {
|
|
6319
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
6320
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
6321
|
+
} else {
|
|
6322
|
+
matrix[i][j] = Math.min(
|
|
6323
|
+
matrix[i - 1][j - 1] + 1,
|
|
6324
|
+
matrix[i][j - 1] + 1,
|
|
6325
|
+
matrix[i - 1][j] + 1
|
|
6326
|
+
);
|
|
6327
|
+
}
|
|
6328
|
+
}
|
|
6329
|
+
}
|
|
6330
|
+
|
|
6331
|
+
return matrix[b.length][a.length];
|
|
6332
|
+
}
|
|
6333
|
+
|
|
6334
|
+
// Find similar strings for suggestions
|
|
6335
|
+
function findSimilar(target, candidates, maxDistance = 3) {
|
|
6336
|
+
const suggestions = [];
|
|
6337
|
+
|
|
6338
|
+
for (const candidate of candidates) {
|
|
6339
|
+
const distance = levenshteinDistance(target.toLowerCase(), candidate.toLowerCase());
|
|
6340
|
+
if (distance <= maxDistance && distance > 0) {
|
|
6341
|
+
suggestions.push({ name: candidate, distance });
|
|
6342
|
+
}
|
|
6343
|
+
}
|
|
6344
|
+
|
|
6345
|
+
return suggestions
|
|
6346
|
+
.sort((a, b) => a.distance - b.distance)
|
|
6347
|
+
.slice(0, 3)
|
|
6348
|
+
.map(s => s.name);
|
|
6349
|
+
}
|
|
6350
|
+
|
|
6351
|
+
// Get available methods/properties on an object
|
|
6352
|
+
function getAvailableMethods(obj) {
|
|
6353
|
+
if (obj === null || obj === undefined) return [];
|
|
6354
|
+
|
|
6355
|
+
const methods = new Set();
|
|
6356
|
+
|
|
6357
|
+
// Get own properties
|
|
6358
|
+
Object.getOwnPropertyNames(obj).forEach(name => {
|
|
6359
|
+
if (typeof obj[name] === 'function') {
|
|
6360
|
+
methods.add(name);
|
|
6361
|
+
}
|
|
6362
|
+
});
|
|
6363
|
+
|
|
6364
|
+
// Get prototype methods
|
|
6365
|
+
let proto = Object.getPrototypeOf(obj);
|
|
6366
|
+
while (proto && proto !== Object.prototype) {
|
|
6367
|
+
Object.getOwnPropertyNames(proto).forEach(name => {
|
|
6368
|
+
if (typeof proto[name] === 'function' && name !== 'constructor') {
|
|
6369
|
+
methods.add(name);
|
|
6370
|
+
}
|
|
6371
|
+
});
|
|
6372
|
+
proto = Object.getPrototypeOf(proto);
|
|
6373
|
+
}
|
|
6374
|
+
|
|
6375
|
+
return Array.from(methods).sort();
|
|
6376
|
+
}
|
|
6377
|
+
|
|
6378
|
+
class EnhancedTypeError extends TypeError {
|
|
6379
|
+
constructor(message, context = {}) {
|
|
6380
|
+
super(message);
|
|
6381
|
+
this.name = 'TypeError';
|
|
6382
|
+
this.objectName = context.objectName || 'object';
|
|
6383
|
+
this.methodName = context.methodName || 'method';
|
|
6384
|
+
this.objectValue = context.objectValue;
|
|
6385
|
+
this.availableMethods = context.availableMethods || [];
|
|
6386
|
+
this.suggestions = context.suggestions || [];
|
|
6387
|
+
}
|
|
6388
|
+
|
|
6389
|
+
getFormattedMessage() {
|
|
6390
|
+
let formatted = this.message;
|
|
6391
|
+
|
|
6392
|
+
// Add available methods
|
|
6393
|
+
if (this.availableMethods.length > 0) {
|
|
6394
|
+
const methodList = this.availableMethods.slice(0, 10).join(', ');
|
|
6395
|
+
const more = this.availableMethods.length > 10
|
|
6396
|
+
? ` (and ${this.availableMethods.length - 10} more)`
|
|
6397
|
+
: '';
|
|
6398
|
+
formatted += `\n\nAvailable methods on '${this.objectName}': ${methodList}${more}`;
|
|
6399
|
+
}
|
|
6400
|
+
|
|
6401
|
+
// Add suggestions
|
|
6402
|
+
if (this.suggestions.length > 0) {
|
|
6403
|
+
formatted += `\n\nDid you mean: ${this.suggestions.join(', ')}?`;
|
|
6404
|
+
}
|
|
6405
|
+
|
|
6406
|
+
return formatted;
|
|
6407
|
+
}
|
|
6408
|
+
}
|
|
6409
|
+
|
|
6410
|
+
function createMethodNotFoundError(objectName, methodName, objectValue) {
|
|
6411
|
+
const availableMethods = getAvailableMethods(objectValue);
|
|
6412
|
+
const suggestions = findSimilar(methodName, availableMethods);
|
|
6413
|
+
|
|
6414
|
+
const message = `TypeError when calling method '${methodName}' on object '${objectName}': ` +
|
|
6415
|
+
`Expected: function, Received: undefined`;
|
|
6416
|
+
|
|
6417
|
+
return new EnhancedTypeError(message, {
|
|
6418
|
+
objectName,
|
|
6419
|
+
methodName,
|
|
6420
|
+
objectValue,
|
|
6421
|
+
availableMethods,
|
|
6422
|
+
suggestions
|
|
6423
|
+
});
|
|
6424
|
+
}
|
|
6425
|
+
|
|
6426
|
+
class Interpreter {
|
|
6427
|
+
constructor(globalEnv, options = {}) {
|
|
6428
|
+
this.globalEnv = globalEnv;
|
|
6429
|
+
this.moduleResolver = options.moduleResolver;
|
|
6430
|
+
this.moduleCache = new Map(); // Cache loaded modules
|
|
6431
|
+
this.moduleExports = {}; // Track exports in current module
|
|
6432
|
+
this.abortSignal = options.abortSignal;
|
|
6433
|
+
}
|
|
6434
|
+
|
|
6435
|
+
// Check if execution should be aborted
|
|
6436
|
+
checkAbortSignal() {
|
|
6437
|
+
if (this.abortSignal && this.abortSignal.aborted) {
|
|
6438
|
+
const error = new Error('The operation was aborted');
|
|
6439
|
+
error.name = 'AbortError';
|
|
6440
|
+
throw error;
|
|
6441
|
+
}
|
|
6442
|
+
}
|
|
6443
|
+
|
|
6444
|
+
// Async evaluation for async functions - handles await expressions
|
|
6445
|
+
async evaluateAsync(node, env) {
|
|
6446
|
+
if (!node) return undefined;
|
|
6447
|
+
|
|
6448
|
+
// Check for abort signal before evaluating
|
|
6449
|
+
this.checkAbortSignal();
|
|
6450
|
+
|
|
6451
|
+
// Handle await expressions by actually awaiting the promise
|
|
6452
|
+
if (node.type === 'AwaitExpression') {
|
|
6453
|
+
const promise = await this.evaluateAsync(node.argument, env);
|
|
6454
|
+
return await promise;
|
|
6455
|
+
}
|
|
6456
|
+
|
|
6457
|
+
// For block statements, evaluate each statement async
|
|
6458
|
+
if (node.type === 'BlockStatement') {
|
|
6459
|
+
const blockEnv = new Environment(env);
|
|
6460
|
+
let result = undefined;
|
|
6461
|
+
for (const statement of node.body) {
|
|
6462
|
+
result = await this.evaluateAsync(statement, blockEnv);
|
|
6463
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal ||
|
|
6464
|
+
result instanceof BreakSignal || result instanceof ContinueSignal) {
|
|
6465
|
+
return result;
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
return result;
|
|
6469
|
+
}
|
|
6470
|
+
|
|
6471
|
+
// For expression statements, evaluate the expression async
|
|
6472
|
+
if (node.type === 'ExpressionStatement') {
|
|
6473
|
+
return await this.evaluateAsync(node.expression, env);
|
|
6474
|
+
}
|
|
6475
|
+
|
|
6476
|
+
// For variable declarations with await in init
|
|
6477
|
+
if (node.type === 'VariableDeclaration') {
|
|
6478
|
+
for (const declarator of node.declarations) {
|
|
6479
|
+
const value = declarator.init
|
|
6480
|
+
? await this.evaluateAsync(declarator.init, env)
|
|
6481
|
+
: undefined;
|
|
6482
|
+
|
|
6483
|
+
const isConst = node.kind === 'const';
|
|
6484
|
+
if (declarator.id.type === 'Identifier') {
|
|
6485
|
+
env.define(declarator.id.name, value, isConst);
|
|
6486
|
+
} else if (declarator.id.type === 'ObjectPattern') {
|
|
6487
|
+
this.bindObjectPattern(declarator.id, value, env, isConst);
|
|
6488
|
+
} else if (declarator.id.type === 'ArrayPattern') {
|
|
6489
|
+
this.bindArrayPattern(declarator.id, value, env, isConst);
|
|
6490
|
+
}
|
|
6491
|
+
}
|
|
6492
|
+
return undefined;
|
|
6493
|
+
}
|
|
6494
|
+
|
|
6495
|
+
// For Program nodes (evaluate all statements async)
|
|
6496
|
+
if (node.type === 'Program') {
|
|
6497
|
+
let result = undefined;
|
|
6498
|
+
for (const statement of node.body) {
|
|
6499
|
+
result = await this.evaluateAsync(statement, env);
|
|
6500
|
+
// Handle top-level return and throw
|
|
6501
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
6502
|
+
return result;
|
|
6503
|
+
}
|
|
6504
|
+
}
|
|
6505
|
+
return result;
|
|
6506
|
+
}
|
|
6507
|
+
|
|
6508
|
+
// For import declarations (always async)
|
|
6509
|
+
if (node.type === 'ImportDeclaration') {
|
|
6510
|
+
return await this.evaluateImportDeclaration(node, env);
|
|
6511
|
+
}
|
|
6512
|
+
|
|
6513
|
+
// For export declarations
|
|
6514
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
6515
|
+
return this.evaluateExportNamedDeclaration(node, env);
|
|
6516
|
+
}
|
|
6517
|
+
|
|
6518
|
+
if (node.type === 'ExportDefaultDeclaration') {
|
|
6519
|
+
return this.evaluateExportDefaultDeclaration(node, env);
|
|
6520
|
+
}
|
|
6521
|
+
|
|
6522
|
+
// For return statements with await
|
|
6523
|
+
if (node.type === 'ReturnStatement') {
|
|
6524
|
+
const value = node.argument ? await this.evaluateAsync(node.argument, env) : undefined;
|
|
6525
|
+
return new ReturnValue(value);
|
|
6526
|
+
}
|
|
6527
|
+
|
|
6528
|
+
// For binary/unary expressions that might contain awaits
|
|
6529
|
+
if (node.type === 'BinaryExpression') {
|
|
6530
|
+
const left = await this.evaluateAsync(node.left, env);
|
|
6531
|
+
const right = await this.evaluateAsync(node.right, env);
|
|
6532
|
+
return this.evaluateBinaryExpressionValues(node.operator, left, right);
|
|
6533
|
+
}
|
|
6534
|
+
|
|
6535
|
+
// For call expressions (might be calling async functions)
|
|
6536
|
+
if (node.type === 'CallExpression') {
|
|
6537
|
+
let thisContext = undefined;
|
|
6538
|
+
let callee;
|
|
6539
|
+
let objectName = null;
|
|
6540
|
+
let methodName = null;
|
|
6541
|
+
|
|
6542
|
+
if (node.callee.type === 'MemberExpression') {
|
|
6543
|
+
thisContext = await this.evaluateAsync(node.callee.object, env);
|
|
6544
|
+
const prop = node.callee.computed
|
|
6545
|
+
? await this.evaluateAsync(node.callee.property, env)
|
|
6546
|
+
: node.callee.property.name;
|
|
6547
|
+
callee = thisContext[prop];
|
|
6548
|
+
|
|
6549
|
+
// Capture names for enhanced error messages
|
|
6550
|
+
methodName = prop;
|
|
6551
|
+
objectName = this.getExpressionName(node.callee.object);
|
|
6552
|
+
} else {
|
|
6553
|
+
callee = await this.evaluateAsync(node.callee, env);
|
|
6554
|
+
}
|
|
6555
|
+
|
|
6556
|
+
// Handle optional call - if optional and callee is null/undefined, return undefined
|
|
6557
|
+
if (node.optional && (callee === null || callee === undefined)) {
|
|
6558
|
+
return undefined;
|
|
6559
|
+
}
|
|
6560
|
+
|
|
6561
|
+
const args = [];
|
|
6562
|
+
for (const arg of node.arguments) {
|
|
6563
|
+
args.push(await this.evaluateAsync(arg, env));
|
|
6564
|
+
}
|
|
6565
|
+
|
|
6566
|
+
if (typeof callee === 'function') {
|
|
6567
|
+
if (thisContext !== undefined) {
|
|
6568
|
+
return await callee.call(thisContext, ...args);
|
|
6569
|
+
}
|
|
6570
|
+
return await callee(...args);
|
|
6571
|
+
} else if (callee && callee.__isFunction) {
|
|
6572
|
+
return await this.callUserFunction(callee, args, env, thisContext);
|
|
6573
|
+
}
|
|
6574
|
+
|
|
6575
|
+
// Throw enhanced error for member expression calls
|
|
6576
|
+
if (objectName && methodName) {
|
|
6577
|
+
throw createMethodNotFoundError(objectName, methodName, thisContext);
|
|
6578
|
+
}
|
|
6579
|
+
|
|
6580
|
+
throw new TypeError(`${node.callee.name || 'Expression'} is not a function`);
|
|
6581
|
+
}
|
|
6582
|
+
|
|
6583
|
+
// For chain expressions (optional chaining)
|
|
6584
|
+
if (node.type === 'ChainExpression') {
|
|
6585
|
+
return await this.evaluateAsync(node.expression, env);
|
|
6586
|
+
}
|
|
6587
|
+
|
|
6588
|
+
// For member expressions in async context
|
|
6589
|
+
if (node.type === 'MemberExpression') {
|
|
6590
|
+
const obj = await this.evaluateAsync(node.object, env);
|
|
6591
|
+
|
|
6592
|
+
// Handle optional chaining
|
|
6593
|
+
if (node.optional && (obj === null || obj === undefined)) {
|
|
6594
|
+
return undefined;
|
|
6595
|
+
}
|
|
6596
|
+
|
|
6597
|
+
if (obj === null || obj === undefined) {
|
|
6598
|
+
throw new TypeError(`Cannot read property of ${obj}`);
|
|
6599
|
+
}
|
|
6600
|
+
|
|
6601
|
+
const prop = node.computed
|
|
6602
|
+
? await this.evaluateAsync(node.property, env)
|
|
6603
|
+
: node.property.name;
|
|
6604
|
+
|
|
6605
|
+
return obj[prop];
|
|
6606
|
+
}
|
|
6607
|
+
|
|
6608
|
+
// For template literals with await expressions
|
|
6609
|
+
if (node.type === 'TemplateLiteral') {
|
|
6610
|
+
let result = '';
|
|
6611
|
+
for (let i = 0; i < node.quasis.length; i++) {
|
|
6612
|
+
result += node.quasis[i].value.cooked || node.quasis[i].value.raw;
|
|
6613
|
+
if (i < node.expressions.length) {
|
|
6614
|
+
const exprValue = await this.evaluateAsync(node.expressions[i], env);
|
|
6615
|
+
result += String(exprValue);
|
|
6616
|
+
}
|
|
6617
|
+
}
|
|
6618
|
+
return result;
|
|
6619
|
+
}
|
|
6620
|
+
|
|
6621
|
+
// For logical expressions with async operands (await support)
|
|
6622
|
+
if (node.type === 'LogicalExpression') {
|
|
6623
|
+
const left = await this.evaluateAsync(node.left, env);
|
|
6624
|
+
|
|
6625
|
+
if (node.operator === '&&') {
|
|
6626
|
+
return left ? await this.evaluateAsync(node.right, env) : left;
|
|
6627
|
+
} else if (node.operator === '||') {
|
|
6628
|
+
return left ? left : await this.evaluateAsync(node.right, env);
|
|
6629
|
+
} else if (node.operator === '??') {
|
|
6630
|
+
return left !== null && left !== undefined ? left : await this.evaluateAsync(node.right, env);
|
|
6631
|
+
}
|
|
6632
|
+
|
|
6633
|
+
throw new Error(`Unknown logical operator: ${node.operator}`);
|
|
6634
|
+
}
|
|
6635
|
+
|
|
6636
|
+
// For try-catch-finally with async operations
|
|
6637
|
+
if (node.type === 'TryStatement') {
|
|
6638
|
+
let result;
|
|
6639
|
+
|
|
6640
|
+
try {
|
|
6641
|
+
result = await this.evaluateAsync(node.block, env);
|
|
6642
|
+
|
|
6643
|
+
if (result instanceof ThrowSignal) {
|
|
6644
|
+
throw result.value;
|
|
6645
|
+
}
|
|
6646
|
+
} catch (error) {
|
|
6647
|
+
if (node.handler) {
|
|
6648
|
+
const catchEnv = new Environment(env);
|
|
6649
|
+
if (node.handler.param) {
|
|
6650
|
+
catchEnv.define(node.handler.param.name, error);
|
|
6651
|
+
}
|
|
6652
|
+
result = await this.evaluateAsync(node.handler.body, catchEnv);
|
|
6653
|
+
} else {
|
|
6654
|
+
throw error;
|
|
6655
|
+
}
|
|
6656
|
+
} finally {
|
|
6657
|
+
if (node.finalizer) {
|
|
6658
|
+
const finalResult = await this.evaluateAsync(node.finalizer, env);
|
|
6659
|
+
// If finally block throws or returns, it overrides the try/catch result
|
|
6660
|
+
if (finalResult instanceof ThrowSignal || finalResult instanceof ReturnValue) {
|
|
6661
|
+
return finalResult;
|
|
6662
|
+
}
|
|
6663
|
+
}
|
|
6664
|
+
}
|
|
6665
|
+
|
|
6666
|
+
return result;
|
|
6667
|
+
}
|
|
6668
|
+
|
|
6669
|
+
// For new expressions (async constructors)
|
|
6670
|
+
if (node.type === 'NewExpression') {
|
|
6671
|
+
const result = this.evaluateNewExpression(node, env);
|
|
6672
|
+
// If it's a promise, await it
|
|
6673
|
+
if (result && typeof result.then === 'function') {
|
|
6674
|
+
return await result;
|
|
6675
|
+
}
|
|
6676
|
+
return result;
|
|
6677
|
+
}
|
|
6678
|
+
|
|
6679
|
+
// For ForStatement with async body
|
|
6680
|
+
if (node.type === 'ForStatement') {
|
|
6681
|
+
const forEnv = new Environment(env);
|
|
6682
|
+
if (node.init) {
|
|
6683
|
+
await this.evaluateAsync(node.init, forEnv);
|
|
6684
|
+
}
|
|
6685
|
+
while (!node.test || await this.evaluateAsync(node.test, forEnv)) {
|
|
6686
|
+
const result = await this.evaluateAsync(node.body, forEnv);
|
|
6687
|
+
if (result instanceof BreakSignal) {
|
|
6688
|
+
break;
|
|
6689
|
+
}
|
|
6690
|
+
if (result instanceof ContinueSignal) {
|
|
6691
|
+
if (node.update) {
|
|
6692
|
+
await this.evaluateAsync(node.update, forEnv);
|
|
6693
|
+
}
|
|
6694
|
+
continue;
|
|
6695
|
+
}
|
|
6696
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
6697
|
+
return result;
|
|
6698
|
+
}
|
|
6699
|
+
if (node.update) {
|
|
6700
|
+
await this.evaluateAsync(node.update, forEnv);
|
|
6701
|
+
}
|
|
6702
|
+
}
|
|
6703
|
+
return undefined;
|
|
6704
|
+
}
|
|
6705
|
+
|
|
6706
|
+
// For ForOfStatement with async body
|
|
6707
|
+
if (node.type === 'ForOfStatement') {
|
|
6708
|
+
const forEnv = new Environment(env);
|
|
6709
|
+
const iterable = await this.evaluateAsync(node.right, forEnv);
|
|
6710
|
+
const declarator = node.left.declarations[0];
|
|
6711
|
+
const isConst = node.left.kind === 'const';
|
|
6712
|
+
|
|
6713
|
+
for (const value of iterable) {
|
|
6714
|
+
const iterEnv = forEnv.extend();
|
|
6715
|
+
if (declarator.id.type === 'Identifier') {
|
|
6716
|
+
iterEnv.define(declarator.id.name, value, isConst);
|
|
6717
|
+
} else if (declarator.id.type === 'ArrayPattern') {
|
|
6718
|
+
this.bindArrayPattern(declarator.id, value, iterEnv, isConst);
|
|
6719
|
+
} else if (declarator.id.type === 'ObjectPattern') {
|
|
6720
|
+
this.bindObjectPattern(declarator.id, value, iterEnv, isConst);
|
|
6721
|
+
}
|
|
6722
|
+
const result = await this.evaluateAsync(node.body, iterEnv);
|
|
6723
|
+
if (result instanceof BreakSignal) {
|
|
6724
|
+
break;
|
|
6725
|
+
}
|
|
6726
|
+
if (result instanceof ContinueSignal) {
|
|
6727
|
+
continue;
|
|
6728
|
+
}
|
|
6729
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
6730
|
+
return result;
|
|
6731
|
+
}
|
|
6732
|
+
}
|
|
6733
|
+
return undefined;
|
|
6734
|
+
}
|
|
6735
|
+
|
|
6736
|
+
// For ForInStatement with async body
|
|
6737
|
+
if (node.type === 'ForInStatement') {
|
|
6738
|
+
const forEnv = new Environment(env);
|
|
6739
|
+
const obj = await this.evaluateAsync(node.right, forEnv);
|
|
6740
|
+
if (obj === null || obj === undefined) {
|
|
6741
|
+
throw new TypeError(`Cannot use 'in' operator to iterate over ${obj}`);
|
|
6742
|
+
}
|
|
6743
|
+
const varName = node.left.declarations[0].id.name;
|
|
6744
|
+
forEnv.define(varName, undefined);
|
|
6745
|
+
|
|
6746
|
+
for (const key in obj) {
|
|
6747
|
+
forEnv.set(varName, key);
|
|
6748
|
+
const result = await this.evaluateAsync(node.body, forEnv);
|
|
6749
|
+
if (result instanceof BreakSignal) {
|
|
6750
|
+
break;
|
|
6751
|
+
}
|
|
6752
|
+
if (result instanceof ContinueSignal) {
|
|
6753
|
+
continue;
|
|
6754
|
+
}
|
|
6755
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
6756
|
+
return result;
|
|
6757
|
+
}
|
|
6758
|
+
}
|
|
6759
|
+
return undefined;
|
|
6760
|
+
}
|
|
6761
|
+
|
|
6762
|
+
// For WhileStatement with async body
|
|
6763
|
+
if (node.type === 'WhileStatement') {
|
|
6764
|
+
while (await this.evaluateAsync(node.test, env)) {
|
|
6765
|
+
const result = await this.evaluateAsync(node.body, env);
|
|
6766
|
+
if (result instanceof BreakSignal) {
|
|
6767
|
+
break;
|
|
6768
|
+
}
|
|
6769
|
+
if (result instanceof ContinueSignal) {
|
|
6770
|
+
continue;
|
|
6771
|
+
}
|
|
6772
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
6773
|
+
return result;
|
|
6774
|
+
}
|
|
6775
|
+
}
|
|
6776
|
+
return undefined;
|
|
6777
|
+
}
|
|
6778
|
+
|
|
6779
|
+
// For DoWhileStatement with async body
|
|
6780
|
+
if (node.type === 'DoWhileStatement') {
|
|
6781
|
+
do {
|
|
6782
|
+
const result = await this.evaluateAsync(node.body, env);
|
|
6783
|
+
if (result instanceof BreakSignal) {
|
|
6784
|
+
break;
|
|
6785
|
+
}
|
|
6786
|
+
if (result instanceof ContinueSignal) {
|
|
6787
|
+
continue;
|
|
6788
|
+
}
|
|
6789
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
6790
|
+
return result;
|
|
6791
|
+
}
|
|
6792
|
+
} while (await this.evaluateAsync(node.test, env));
|
|
6793
|
+
return undefined;
|
|
6794
|
+
}
|
|
6795
|
+
|
|
6796
|
+
// For IfStatement with async branches
|
|
6797
|
+
if (node.type === 'IfStatement') {
|
|
6798
|
+
const test = await this.evaluateAsync(node.test, env);
|
|
6799
|
+
if (test) {
|
|
6800
|
+
return await this.evaluateAsync(node.consequent, env);
|
|
6801
|
+
} else if (node.alternate) {
|
|
6802
|
+
return await this.evaluateAsync(node.alternate, env);
|
|
6803
|
+
}
|
|
6804
|
+
return undefined;
|
|
6805
|
+
}
|
|
6806
|
+
|
|
6807
|
+
// For SwitchStatement with async cases
|
|
6808
|
+
if (node.type === 'SwitchStatement') {
|
|
6809
|
+
const discriminant = await this.evaluateAsync(node.discriminant, env);
|
|
6810
|
+
let matched = false;
|
|
6811
|
+
|
|
6812
|
+
for (const switchCase of node.cases) {
|
|
6813
|
+
if (!matched && switchCase.test) {
|
|
6814
|
+
const testValue = await this.evaluateAsync(switchCase.test, env);
|
|
6815
|
+
if (testValue === discriminant) {
|
|
6816
|
+
matched = true;
|
|
6817
|
+
}
|
|
6818
|
+
} else if (!switchCase.test) {
|
|
6819
|
+
matched = true;
|
|
6820
|
+
}
|
|
6821
|
+
|
|
6822
|
+
if (matched) {
|
|
6823
|
+
for (const statement of switchCase.consequent) {
|
|
6824
|
+
const result = await this.evaluateAsync(statement, env);
|
|
6825
|
+
if (result instanceof BreakSignal) {
|
|
6826
|
+
return undefined;
|
|
6827
|
+
}
|
|
6828
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
6829
|
+
return result;
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6832
|
+
}
|
|
6833
|
+
}
|
|
6834
|
+
return undefined;
|
|
6835
|
+
}
|
|
6836
|
+
|
|
6837
|
+
// For ConditionalExpression (ternary) with async operands
|
|
6838
|
+
if (node.type === 'ConditionalExpression') {
|
|
6839
|
+
const test = await this.evaluateAsync(node.test, env);
|
|
6840
|
+
return test
|
|
6841
|
+
? await this.evaluateAsync(node.consequent, env)
|
|
6842
|
+
: await this.evaluateAsync(node.alternate, env);
|
|
6843
|
+
}
|
|
6844
|
+
|
|
6845
|
+
// For AssignmentExpression with async value
|
|
6846
|
+
if (node.type === 'AssignmentExpression') {
|
|
6847
|
+
const value = await this.evaluateAsync(node.right, env);
|
|
6848
|
+
|
|
6849
|
+
if (node.left.type === 'Identifier') {
|
|
6850
|
+
const name = node.left.name;
|
|
6851
|
+
if (node.operator === '=') {
|
|
6852
|
+
if (env.has(name)) {
|
|
6853
|
+
env.set(name, value);
|
|
6854
|
+
} else {
|
|
6855
|
+
env.define(name, value);
|
|
6856
|
+
}
|
|
6857
|
+
return value;
|
|
6858
|
+
} else {
|
|
6859
|
+
const current = env.get(name);
|
|
6860
|
+
const newValue = this.applyCompoundAssignment(node.operator, current, value);
|
|
6861
|
+
env.set(name, newValue);
|
|
6862
|
+
return newValue;
|
|
6863
|
+
}
|
|
6864
|
+
} else if (node.left.type === 'MemberExpression') {
|
|
6865
|
+
const obj = await this.evaluateAsync(node.left.object, env);
|
|
6866
|
+
const prop = node.left.computed
|
|
6867
|
+
? await this.evaluateAsync(node.left.property, env)
|
|
6868
|
+
: node.left.property.name;
|
|
6869
|
+
|
|
6870
|
+
if (node.operator === '=') {
|
|
6871
|
+
obj[prop] = value;
|
|
6872
|
+
return value;
|
|
6873
|
+
} else {
|
|
6874
|
+
const newValue = this.applyCompoundAssignment(node.operator, obj[prop], value);
|
|
6875
|
+
obj[prop] = newValue;
|
|
6876
|
+
return newValue;
|
|
6877
|
+
}
|
|
6878
|
+
}
|
|
6879
|
+
throw new Error('Invalid assignment target');
|
|
6880
|
+
}
|
|
6881
|
+
|
|
6882
|
+
// For UnaryExpression with async argument
|
|
6883
|
+
if (node.type === 'UnaryExpression') {
|
|
6884
|
+
if (node.operator === 'delete' && node.argument.type === 'MemberExpression') {
|
|
6885
|
+
const obj = await this.evaluateAsync(node.argument.object, env);
|
|
6886
|
+
const prop = node.argument.computed
|
|
6887
|
+
? await this.evaluateAsync(node.argument.property, env)
|
|
6888
|
+
: node.argument.property.name;
|
|
6889
|
+
return delete obj[prop];
|
|
6890
|
+
}
|
|
6891
|
+
const argument = await this.evaluateAsync(node.argument, env);
|
|
6892
|
+
switch (node.operator) {
|
|
6893
|
+
case '+': return +argument;
|
|
6894
|
+
case '-': return -argument;
|
|
6895
|
+
case '!': return !argument;
|
|
6896
|
+
case '~': return ~argument;
|
|
6897
|
+
case 'typeof':
|
|
6898
|
+
if (argument && argument.__isFunction) {
|
|
6899
|
+
return 'function';
|
|
6900
|
+
}
|
|
6901
|
+
return typeof argument;
|
|
6902
|
+
case 'void': return undefined;
|
|
6903
|
+
case 'delete': return true;
|
|
6904
|
+
default:
|
|
6905
|
+
throw new Error(`Unknown unary operator: ${node.operator}`);
|
|
6906
|
+
}
|
|
6907
|
+
}
|
|
6908
|
+
|
|
6909
|
+
// For UpdateExpression with async member access
|
|
6910
|
+
if (node.type === 'UpdateExpression') {
|
|
6911
|
+
if (node.argument.type === 'Identifier') {
|
|
6912
|
+
const name = node.argument.name;
|
|
6913
|
+
const current = env.get(name);
|
|
6914
|
+
const numericCurrent = (current === null || current === undefined) ? 0 : Number(current);
|
|
6915
|
+
const newValue = node.operator === '++' ? numericCurrent + 1 : numericCurrent - 1;
|
|
6916
|
+
env.set(name, newValue);
|
|
6917
|
+
return node.prefix ? newValue : numericCurrent;
|
|
6918
|
+
} else if (node.argument.type === 'MemberExpression') {
|
|
6919
|
+
const obj = await this.evaluateAsync(node.argument.object, env);
|
|
6920
|
+
if (obj === null || obj === undefined) {
|
|
6921
|
+
throw new TypeError(
|
|
6922
|
+
`Cannot read properties of ${obj} (reading '${
|
|
6923
|
+
node.argument.computed
|
|
6924
|
+
? await this.evaluateAsync(node.argument.property, env)
|
|
6925
|
+
: node.argument.property.name
|
|
6926
|
+
}')`
|
|
6927
|
+
);
|
|
6928
|
+
}
|
|
6929
|
+
const prop = node.argument.computed
|
|
6930
|
+
? await this.evaluateAsync(node.argument.property, env)
|
|
6931
|
+
: node.argument.property.name;
|
|
6932
|
+
let current = obj[prop];
|
|
6933
|
+
const numericCurrent = (current === null || current === undefined) ? 0 : Number(current);
|
|
6934
|
+
const newValue = node.operator === '++' ? numericCurrent + 1 : numericCurrent - 1;
|
|
6935
|
+
obj[prop] = newValue;
|
|
6936
|
+
return node.prefix ? newValue : numericCurrent;
|
|
6937
|
+
}
|
|
6938
|
+
throw new Error('Invalid update expression target');
|
|
6939
|
+
}
|
|
6940
|
+
|
|
6941
|
+
// For ArrayExpression with async elements
|
|
6942
|
+
if (node.type === 'ArrayExpression') {
|
|
6943
|
+
const result = [];
|
|
6944
|
+
for (const elem of node.elements) {
|
|
6945
|
+
if (!elem) {
|
|
6946
|
+
result.push(undefined);
|
|
6947
|
+
} else if (elem.type === 'SpreadElement') {
|
|
6948
|
+
const spreadValue = await this.evaluateAsync(elem.argument, env);
|
|
6949
|
+
if (Array.isArray(spreadValue)) {
|
|
6950
|
+
result.push(...spreadValue);
|
|
6951
|
+
} else if (typeof spreadValue[Symbol.iterator] === 'function') {
|
|
6952
|
+
result.push(...spreadValue);
|
|
6953
|
+
} else {
|
|
6954
|
+
throw new TypeError('Spread syntax requires an iterable');
|
|
6955
|
+
}
|
|
6956
|
+
} else {
|
|
6957
|
+
result.push(await this.evaluateAsync(elem, env));
|
|
6958
|
+
}
|
|
6959
|
+
}
|
|
6960
|
+
return result;
|
|
6961
|
+
}
|
|
6962
|
+
|
|
6963
|
+
// For ObjectExpression with async values
|
|
6964
|
+
if (node.type === 'ObjectExpression') {
|
|
6965
|
+
const obj = {};
|
|
6966
|
+
for (const prop of node.properties) {
|
|
6967
|
+
if (prop.type === 'SpreadElement') {
|
|
6968
|
+
const spreadValue = await this.evaluateAsync(prop.argument, env);
|
|
6969
|
+
if (typeof spreadValue === 'object' && spreadValue !== null) {
|
|
6970
|
+
Object.assign(obj, spreadValue);
|
|
6971
|
+
}
|
|
6972
|
+
} else {
|
|
6973
|
+
const key = prop.key.type === 'Identifier' && !prop.computed
|
|
6974
|
+
? prop.key.name
|
|
6975
|
+
: await this.evaluateAsync(prop.key, env);
|
|
6976
|
+
const value = prop.value ? await this.evaluateAsync(prop.value, env) : env.get(key);
|
|
6977
|
+
if (prop.method && prop.value.type === 'FunctionExpression') {
|
|
6978
|
+
obj[key] = (...args) => {
|
|
6979
|
+
const funcValue = this.evaluate(prop.value, env);
|
|
6980
|
+
return this.callUserFunction(funcValue, args, env);
|
|
6981
|
+
};
|
|
6982
|
+
} else {
|
|
6983
|
+
obj[key] = value;
|
|
6984
|
+
}
|
|
6985
|
+
}
|
|
6986
|
+
}
|
|
6987
|
+
return obj;
|
|
6988
|
+
}
|
|
6989
|
+
|
|
6990
|
+
// For SequenceExpression with async expressions
|
|
6991
|
+
if (node.type === 'SequenceExpression') {
|
|
6992
|
+
let result;
|
|
6993
|
+
for (const expr of node.expressions) {
|
|
6994
|
+
result = await this.evaluateAsync(expr, env);
|
|
6995
|
+
}
|
|
6996
|
+
return result;
|
|
6997
|
+
}
|
|
6998
|
+
|
|
6999
|
+
// For ThrowStatement with async argument
|
|
7000
|
+
if (node.type === 'ThrowStatement') {
|
|
7001
|
+
return new ThrowSignal(await this.evaluateAsync(node.argument, env));
|
|
7002
|
+
}
|
|
7003
|
+
|
|
7004
|
+
// For FunctionDeclaration - define in environment
|
|
7005
|
+
if (node.type === 'FunctionDeclaration') {
|
|
7006
|
+
return this.evaluateFunctionDeclaration(node, env);
|
|
7007
|
+
}
|
|
7008
|
+
|
|
7009
|
+
// For FunctionExpression/ArrowFunctionExpression - create function
|
|
7010
|
+
if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
|
|
7011
|
+
return this.evaluateFunctionExpression(node, env);
|
|
7012
|
+
}
|
|
7013
|
+
|
|
7014
|
+
// For ClassDeclaration
|
|
7015
|
+
if (node.type === 'ClassDeclaration') {
|
|
7016
|
+
return this.evaluateClassDeclaration(node, env);
|
|
7017
|
+
}
|
|
7018
|
+
|
|
7019
|
+
// For ClassExpression
|
|
7020
|
+
if (node.type === 'ClassExpression') {
|
|
7021
|
+
return this.evaluateClassExpression(node, env);
|
|
7022
|
+
}
|
|
7023
|
+
|
|
7024
|
+
// Only leaf nodes should fall through to sync evaluate
|
|
7025
|
+
// These have no sub-expressions that could contain await
|
|
7026
|
+
if (['Literal', 'Identifier', 'BreakStatement', 'ContinueStatement',
|
|
7027
|
+
'EmptyStatement', 'ThisExpression', 'Super'].includes(node.type)) {
|
|
7028
|
+
return this.evaluate(node, env);
|
|
7029
|
+
}
|
|
7030
|
+
|
|
7031
|
+
// Safety check - if we get here, we missed a node type
|
|
7032
|
+
throw new Error(`Unhandled node type in evaluateAsync: ${node.type}`);
|
|
7033
|
+
}
|
|
7034
|
+
|
|
7035
|
+
evaluate(node, env) {
|
|
7036
|
+
if (!node) return undefined;
|
|
7037
|
+
|
|
7038
|
+
// Check for abort signal before evaluating
|
|
7039
|
+
this.checkAbortSignal();
|
|
7040
|
+
|
|
7041
|
+
switch (node.type) {
|
|
7042
|
+
case 'Program':
|
|
7043
|
+
return this.evaluateProgram(node, env);
|
|
7044
|
+
|
|
7045
|
+
case 'Literal':
|
|
7046
|
+
// Handle regex literals
|
|
7047
|
+
if (node.regex) {
|
|
7048
|
+
return new RegExp(node.regex.pattern, node.regex.flags);
|
|
7049
|
+
}
|
|
7050
|
+
return node.value;
|
|
7051
|
+
|
|
7052
|
+
case 'Identifier':
|
|
7053
|
+
return env.get(node.name);
|
|
7054
|
+
|
|
7055
|
+
case 'BinaryExpression':
|
|
7056
|
+
return this.evaluateBinaryExpression(node, env);
|
|
7057
|
+
|
|
7058
|
+
case 'UnaryExpression':
|
|
7059
|
+
return this.evaluateUnaryExpression(node, env);
|
|
7060
|
+
|
|
7061
|
+
case 'UpdateExpression':
|
|
7062
|
+
return this.evaluateUpdateExpression(node, env);
|
|
7063
|
+
|
|
7064
|
+
case 'AwaitExpression':
|
|
7065
|
+
return this.evaluateAwaitExpression(node, env);
|
|
7066
|
+
|
|
7067
|
+
case 'AssignmentExpression':
|
|
7068
|
+
return this.evaluateAssignmentExpression(node, env);
|
|
7069
|
+
|
|
7070
|
+
case 'LogicalExpression':
|
|
7071
|
+
return this.evaluateLogicalExpression(node, env);
|
|
7072
|
+
|
|
7073
|
+
case 'ConditionalExpression':
|
|
7074
|
+
return this.evaluateConditionalExpression(node, env);
|
|
7075
|
+
|
|
7076
|
+
case 'CallExpression':
|
|
7077
|
+
return this.evaluateCallExpression(node, env);
|
|
7078
|
+
|
|
7079
|
+
case 'MemberExpression':
|
|
7080
|
+
return this.evaluateMemberExpression(node, env);
|
|
7081
|
+
|
|
7082
|
+
case 'ChainExpression':
|
|
7083
|
+
return this.evaluateChainExpression(node, env);
|
|
7084
|
+
|
|
7085
|
+
case 'ArrayExpression':
|
|
7086
|
+
return this.evaluateArrayExpression(node, env);
|
|
7087
|
+
|
|
7088
|
+
case 'ObjectExpression':
|
|
7089
|
+
return this.evaluateObjectExpression(node, env);
|
|
7090
|
+
|
|
7091
|
+
case 'FunctionExpression':
|
|
7092
|
+
case 'ArrowFunctionExpression':
|
|
7093
|
+
return this.evaluateFunctionExpression(node, env);
|
|
7094
|
+
|
|
7095
|
+
case 'NewExpression':
|
|
7096
|
+
return this.evaluateNewExpression(node, env);
|
|
7097
|
+
|
|
7098
|
+
case 'ThisExpression':
|
|
7099
|
+
return this.evaluateThisExpression(node, env);
|
|
7100
|
+
|
|
7101
|
+
case 'Super':
|
|
7102
|
+
return this.evaluateSuperExpression(node, env);
|
|
7103
|
+
|
|
7104
|
+
case 'SequenceExpression':
|
|
7105
|
+
return this.evaluateSequenceExpression(node, env);
|
|
7106
|
+
|
|
7107
|
+
case 'VariableDeclaration':
|
|
7108
|
+
return this.evaluateVariableDeclaration(node, env);
|
|
7109
|
+
|
|
7110
|
+
case 'FunctionDeclaration':
|
|
7111
|
+
return this.evaluateFunctionDeclaration(node, env);
|
|
7112
|
+
|
|
7113
|
+
case 'ImportDeclaration':
|
|
7114
|
+
return this.evaluateImportDeclaration(node, env);
|
|
7115
|
+
|
|
7116
|
+
case 'ExportNamedDeclaration':
|
|
7117
|
+
return this.evaluateExportNamedDeclaration(node, env);
|
|
7118
|
+
|
|
7119
|
+
case 'ExportDefaultDeclaration':
|
|
7120
|
+
return this.evaluateExportDefaultDeclaration(node, env);
|
|
7121
|
+
|
|
7122
|
+
case 'BlockStatement':
|
|
7123
|
+
return this.evaluateBlockStatement(node, env);
|
|
7124
|
+
|
|
7125
|
+
case 'ExpressionStatement':
|
|
7126
|
+
return this.evaluate(node.expression, env);
|
|
7127
|
+
|
|
7128
|
+
case 'ReturnStatement':
|
|
7129
|
+
return new ReturnValue(node.argument ? this.evaluate(node.argument, env) : undefined);
|
|
7130
|
+
|
|
7131
|
+
case 'IfStatement':
|
|
7132
|
+
return this.evaluateIfStatement(node, env);
|
|
7133
|
+
|
|
7134
|
+
case 'WhileStatement':
|
|
7135
|
+
return this.evaluateWhileStatement(node, env);
|
|
7136
|
+
|
|
7137
|
+
case 'DoWhileStatement':
|
|
7138
|
+
return this.evaluateDoWhileStatement(node, env);
|
|
7139
|
+
|
|
7140
|
+
case 'ForStatement':
|
|
7141
|
+
return this.evaluateForStatement(node, env);
|
|
7142
|
+
|
|
7143
|
+
case 'ForInStatement':
|
|
7144
|
+
return this.evaluateForInStatement(node, env);
|
|
7145
|
+
|
|
7146
|
+
case 'ForOfStatement':
|
|
7147
|
+
return this.evaluateForOfStatement(node, env);
|
|
7148
|
+
|
|
7149
|
+
case 'BreakStatement':
|
|
7150
|
+
return new BreakSignal();
|
|
7151
|
+
|
|
7152
|
+
case 'ContinueStatement':
|
|
7153
|
+
return new ContinueSignal();
|
|
7154
|
+
|
|
7155
|
+
case 'ThrowStatement':
|
|
7156
|
+
return new ThrowSignal(this.evaluate(node.argument, env));
|
|
7157
|
+
|
|
7158
|
+
case 'TryStatement':
|
|
7159
|
+
return this.evaluateTryStatement(node, env);
|
|
7160
|
+
|
|
7161
|
+
case 'SwitchStatement':
|
|
7162
|
+
return this.evaluateSwitchStatement(node, env);
|
|
7163
|
+
|
|
7164
|
+
case 'EmptyStatement':
|
|
7165
|
+
return undefined;
|
|
7166
|
+
|
|
7167
|
+
// ES6+ Features
|
|
7168
|
+
case 'TemplateLiteral':
|
|
7169
|
+
return this.evaluateTemplateLiteral(node, env);
|
|
7170
|
+
|
|
7171
|
+
case 'ClassDeclaration':
|
|
7172
|
+
return this.evaluateClassDeclaration(node, env);
|
|
7173
|
+
|
|
7174
|
+
case 'ClassExpression':
|
|
7175
|
+
return this.evaluateClassExpression(node, env);
|
|
7176
|
+
|
|
7177
|
+
case 'MethodDefinition':
|
|
7178
|
+
return this.evaluateMethodDefinition(node, env);
|
|
7179
|
+
|
|
7180
|
+
case 'SpreadElement':
|
|
7181
|
+
return this.evaluateSpreadElement(node, env);
|
|
7182
|
+
|
|
7183
|
+
case 'RestElement':
|
|
7184
|
+
return this.evaluateRestElement(node, env);
|
|
7185
|
+
|
|
7186
|
+
case 'ObjectPattern':
|
|
7187
|
+
return this.evaluateObjectPattern(node, env);
|
|
7188
|
+
|
|
7189
|
+
case 'ArrayPattern':
|
|
7190
|
+
return this.evaluateArrayPattern(node, env);
|
|
7191
|
+
|
|
7192
|
+
case 'AssignmentPattern':
|
|
7193
|
+
return this.evaluateAssignmentPattern(node, env);
|
|
7194
|
+
|
|
7195
|
+
case 'Property':
|
|
7196
|
+
return this.evaluateProperty(node, env);
|
|
7197
|
+
|
|
7198
|
+
default:
|
|
7199
|
+
throw new Error(`Unknown node type: ${node.type}`);
|
|
7200
|
+
}
|
|
7201
|
+
}
|
|
7202
|
+
|
|
7203
|
+
evaluateProgram(node, env) {
|
|
7204
|
+
let result = undefined;
|
|
7205
|
+
for (let i = 0; i < node.body.length; i++) {
|
|
7206
|
+
const statement = node.body[i];
|
|
7207
|
+
const isLast = i === node.body.length - 1;
|
|
7208
|
+
|
|
7209
|
+
// Special case: Last statement is a BlockStatement that looks like object literal
|
|
7210
|
+
// Handle both shorthand { x, y } and full syntax { key: value, key2: value2 }
|
|
7211
|
+
if (isLast && statement.type === 'BlockStatement') {
|
|
7212
|
+
const objLiteral = this.tryConvertBlockToObjectLiteral(statement, env);
|
|
7213
|
+
if (objLiteral !== null) {
|
|
7214
|
+
return objLiteral;
|
|
7215
|
+
}
|
|
7216
|
+
}
|
|
7217
|
+
|
|
7218
|
+
const statementResult = this.evaluate(statement, env);
|
|
7219
|
+
if (statementResult instanceof ReturnValue || statementResult instanceof ThrowSignal) {
|
|
7220
|
+
return statementResult;
|
|
7221
|
+
}
|
|
7222
|
+
result = statementResult;
|
|
7223
|
+
}
|
|
7224
|
+
return result;
|
|
7225
|
+
}
|
|
7226
|
+
|
|
7227
|
+
// Try to convert a BlockStatement to an object literal
|
|
7228
|
+
// Returns null if the block doesn't look like an object literal
|
|
7229
|
+
tryConvertBlockToObjectLiteral(block, env) {
|
|
7230
|
+
if (block.body.length === 0) return null;
|
|
7231
|
+
|
|
7232
|
+
// Check if it's shorthand syntax: { x, y }
|
|
7233
|
+
if (block.body.length === 1 && block.body[0].type === 'ExpressionStatement') {
|
|
7234
|
+
const expr = block.body[0].expression;
|
|
7235
|
+
|
|
7236
|
+
// SequenceExpression of Identifiers: { x, y, z }
|
|
7237
|
+
if (expr.type === 'SequenceExpression' &&
|
|
7238
|
+
expr.expressions.every(e => e.type === 'Identifier')) {
|
|
7239
|
+
const obj = {};
|
|
7240
|
+
for (const identifier of expr.expressions) {
|
|
7241
|
+
obj[identifier.name] = env.get(identifier.name);
|
|
7242
|
+
}
|
|
7243
|
+
return obj;
|
|
7244
|
+
}
|
|
7245
|
+
|
|
7246
|
+
// Single Identifier: { x }
|
|
7247
|
+
if (expr.type === 'Identifier') {
|
|
7248
|
+
const obj = {};
|
|
7249
|
+
obj[expr.name] = env.get(expr.name);
|
|
7250
|
+
return obj;
|
|
7251
|
+
}
|
|
7252
|
+
}
|
|
7253
|
+
|
|
7254
|
+
// Check if it's labeled statements that look like object properties
|
|
7255
|
+
// Example: { first: first(arr), last: last(arr) }
|
|
7256
|
+
// This gets parsed as LabeledStatements in script mode
|
|
7257
|
+
const allLabeled = block.body.every(stmt => stmt.type === 'LabeledStatement');
|
|
7258
|
+
if (!allLabeled) return null;
|
|
7259
|
+
|
|
7260
|
+
// Convert labeled statements to object properties
|
|
7261
|
+
const obj = {};
|
|
7262
|
+
for (const stmt of block.body) {
|
|
7263
|
+
const label = stmt.label.name;
|
|
7264
|
+
|
|
7265
|
+
// The body of LabeledStatement should be ExpressionStatement
|
|
7266
|
+
if (stmt.body.type !== 'ExpressionStatement') {
|
|
7267
|
+
return null; // Not an object literal pattern
|
|
7268
|
+
}
|
|
7269
|
+
|
|
7270
|
+
const value = this.evaluate(stmt.body.expression, env);
|
|
7271
|
+
obj[label] = value;
|
|
7272
|
+
}
|
|
7273
|
+
|
|
7274
|
+
return obj;
|
|
7275
|
+
}
|
|
7276
|
+
|
|
7277
|
+
evaluateBinaryExpression(node, env) {
|
|
7278
|
+
const left = this.evaluate(node.left, env);
|
|
7279
|
+
const right = this.evaluate(node.right, env);
|
|
7280
|
+
return this.evaluateBinaryExpressionValues(node.operator, left, right);
|
|
7281
|
+
}
|
|
7282
|
+
|
|
7283
|
+
evaluateBinaryExpressionValues(operator, left, right) {
|
|
7284
|
+
switch (operator) {
|
|
7285
|
+
case '+': return left + right;
|
|
7286
|
+
case '-': return left - right;
|
|
7287
|
+
case '*': return left * right;
|
|
7288
|
+
case '/': return left / right;
|
|
7289
|
+
case '%': return left % right;
|
|
7290
|
+
case '**': return left ** right;
|
|
7291
|
+
case '<': return left < right;
|
|
7292
|
+
case '>': return left > right;
|
|
7293
|
+
case '<=': return left <= right;
|
|
7294
|
+
case '>=': return left >= right;
|
|
7295
|
+
case '==': return left == right;
|
|
7296
|
+
case '!=': return left != right;
|
|
7297
|
+
case '===': return left === right;
|
|
7298
|
+
case '!==': return left !== right;
|
|
7299
|
+
case '&': return left & right;
|
|
7300
|
+
case '|': return left | right;
|
|
7301
|
+
case '^': return left ^ right;
|
|
7302
|
+
case '<<': return left << right;
|
|
7303
|
+
case '>>': return left >> right;
|
|
7304
|
+
case '>>>': return left >>> right;
|
|
7305
|
+
case 'in': {
|
|
7306
|
+
// Check right operand is not null/undefined
|
|
7307
|
+
if (right === null || right === undefined) {
|
|
7308
|
+
throw new TypeError(
|
|
7309
|
+
'Cannot use "in" operator to search for property in null or undefined'
|
|
7310
|
+
);
|
|
7311
|
+
}
|
|
7312
|
+
// Coerce left operand to string/symbol for property key
|
|
7313
|
+
const key = String(left);
|
|
7314
|
+
return key in Object(right);
|
|
7315
|
+
}
|
|
7316
|
+
case 'instanceof': {
|
|
7317
|
+
// Check right operand is a constructor function or JSLike function
|
|
7318
|
+
if (typeof right !== 'function' && !(right && right.__isFunction)) {
|
|
7319
|
+
throw new TypeError(
|
|
7320
|
+
'Right-hand side of instanceof is not a constructor'
|
|
7321
|
+
);
|
|
7322
|
+
}
|
|
7323
|
+
// Primitives (null/undefined) always return false
|
|
7324
|
+
if (left === null || left === undefined) {
|
|
7325
|
+
return false;
|
|
7326
|
+
}
|
|
7327
|
+
// Special case: check if left is a JSLike function and right is Function constructor
|
|
7328
|
+
if (right === Function && left && left.__isFunction) {
|
|
7329
|
+
return true;
|
|
7330
|
+
}
|
|
7331
|
+
// Use JavaScript's instanceof for native objects
|
|
7332
|
+
if (typeof right === 'function') {
|
|
7333
|
+
return left instanceof right;
|
|
7334
|
+
}
|
|
7335
|
+
// For JSLike functions, check prototype chain
|
|
7336
|
+
return false;
|
|
7337
|
+
}
|
|
7338
|
+
default:
|
|
7339
|
+
throw new Error(`Unknown binary operator: ${operator}`);
|
|
7340
|
+
}
|
|
7341
|
+
}
|
|
7342
|
+
|
|
7343
|
+
evaluateUnaryExpression(node, env) {
|
|
7344
|
+
const argument = this.evaluate(node.argument, env);
|
|
7345
|
+
|
|
7346
|
+
switch (node.operator) {
|
|
7347
|
+
case '+': return +argument;
|
|
7348
|
+
case '-': return -argument;
|
|
7349
|
+
case '!': return !argument;
|
|
7350
|
+
case '~': return ~argument;
|
|
7351
|
+
case 'typeof':
|
|
7352
|
+
// JSLike functions should report as 'function'
|
|
7353
|
+
if (argument && argument.__isFunction) {
|
|
7354
|
+
return 'function';
|
|
7355
|
+
}
|
|
7356
|
+
return typeof argument;
|
|
7357
|
+
case 'void': return undefined;
|
|
7358
|
+
case 'delete':
|
|
7359
|
+
if (node.argument.type === 'MemberExpression') {
|
|
7360
|
+
const obj = this.evaluate(node.argument.object, env);
|
|
7361
|
+
const prop = node.argument.computed
|
|
7362
|
+
? this.evaluate(node.argument.property, env)
|
|
7363
|
+
: node.argument.property.name;
|
|
7364
|
+
return delete obj[prop];
|
|
7365
|
+
}
|
|
7366
|
+
return true;
|
|
7367
|
+
default:
|
|
7368
|
+
throw new Error(`Unknown unary operator: ${node.operator}`);
|
|
7369
|
+
}
|
|
7370
|
+
}
|
|
7371
|
+
|
|
7372
|
+
evaluateUpdateExpression(node, env) {
|
|
7373
|
+
if (node.argument.type === 'Identifier') {
|
|
7374
|
+
const name = node.argument.name;
|
|
7375
|
+
const current = env.get(name);
|
|
7376
|
+
// Wang feature: treat null/undefined as 0 for increment/decrement
|
|
7377
|
+
const numericCurrent = (current === null || current === undefined) ? 0 : Number(current);
|
|
7378
|
+
const newValue = node.operator === '++' ? numericCurrent + 1 : numericCurrent - 1;
|
|
7379
|
+
env.set(name, newValue);
|
|
7380
|
+
return node.prefix ? newValue : numericCurrent;
|
|
7381
|
+
} else if (node.argument.type === 'MemberExpression') {
|
|
7382
|
+
const obj = this.evaluate(node.argument.object, env);
|
|
7383
|
+
|
|
7384
|
+
// Check for null/undefined object
|
|
7385
|
+
if (obj === null || obj === undefined) {
|
|
7386
|
+
throw new TypeError(
|
|
7387
|
+
`Cannot read properties of ${obj} (reading '${
|
|
7388
|
+
node.argument.computed
|
|
7389
|
+
? this.evaluate(node.argument.property, env)
|
|
7390
|
+
: node.argument.property.name
|
|
7391
|
+
}')`
|
|
7392
|
+
);
|
|
7393
|
+
}
|
|
7394
|
+
|
|
7395
|
+
const prop = node.argument.computed
|
|
7396
|
+
? this.evaluate(node.argument.property, env)
|
|
7397
|
+
: node.argument.property.name;
|
|
7398
|
+
|
|
7399
|
+
// Get current value and convert to number
|
|
7400
|
+
let current = obj[prop];
|
|
7401
|
+
// Wang feature: treat null/undefined as 0 for increment/decrement
|
|
7402
|
+
const numericCurrent = (current === null || current === undefined) ? 0 : Number(current);
|
|
7403
|
+
const newValue = node.operator === '++' ? numericCurrent + 1 : numericCurrent - 1;
|
|
7404
|
+
obj[prop] = newValue;
|
|
7405
|
+
|
|
7406
|
+
return node.prefix ? newValue : numericCurrent;
|
|
7407
|
+
}
|
|
7408
|
+
throw new Error('Invalid update expression target');
|
|
7409
|
+
}
|
|
7410
|
+
|
|
7411
|
+
evaluateAwaitExpression(node, env) {
|
|
7412
|
+
// Evaluate the argument (should be a Promise)
|
|
7413
|
+
const promise = this.evaluate(node.argument, env);
|
|
7414
|
+
|
|
7415
|
+
// Return the promise - the caller must handle it
|
|
7416
|
+
// This is a simplified implementation that relies on the runtime being async
|
|
7417
|
+
return promise;
|
|
7418
|
+
}
|
|
7419
|
+
|
|
7420
|
+
evaluateAssignmentExpression(node, env) {
|
|
7421
|
+
const value = this.evaluate(node.right, env);
|
|
7422
|
+
|
|
7423
|
+
if (node.left.type === 'Identifier') {
|
|
7424
|
+
const name = node.left.name;
|
|
7425
|
+
|
|
7426
|
+
if (node.operator === '=') {
|
|
7427
|
+
if (env.has(name)) {
|
|
7428
|
+
env.set(name, value);
|
|
7429
|
+
} else {
|
|
7430
|
+
env.define(name, value);
|
|
7431
|
+
}
|
|
7432
|
+
return value;
|
|
7433
|
+
} else {
|
|
7434
|
+
const current = env.get(name);
|
|
7435
|
+
const newValue = this.applyCompoundAssignment(node.operator, current, value);
|
|
7436
|
+
env.set(name, newValue);
|
|
7437
|
+
return newValue;
|
|
7438
|
+
}
|
|
7439
|
+
} else if (node.left.type === 'MemberExpression') {
|
|
7440
|
+
const obj = this.evaluate(node.left.object, env);
|
|
7441
|
+
const prop = node.left.computed
|
|
7442
|
+
? this.evaluate(node.left.property, env)
|
|
7443
|
+
: node.left.property.name;
|
|
7444
|
+
|
|
7445
|
+
if (node.operator === '=') {
|
|
7446
|
+
obj[prop] = value;
|
|
7447
|
+
return value;
|
|
7448
|
+
} else {
|
|
7449
|
+
const newValue = this.applyCompoundAssignment(node.operator, obj[prop], value);
|
|
7450
|
+
obj[prop] = newValue;
|
|
7451
|
+
return newValue;
|
|
7452
|
+
}
|
|
7453
|
+
}
|
|
7454
|
+
|
|
7455
|
+
throw new Error('Invalid assignment target');
|
|
7456
|
+
}
|
|
7457
|
+
|
|
7458
|
+
applyCompoundAssignment(operator, left, right) {
|
|
7459
|
+
// For numeric operators, coerce undefined to 0 (like JavaScript does for += with numbers)
|
|
7460
|
+
// But keep undefined for string concatenation
|
|
7461
|
+
const isNumericOp = operator !== '+=';
|
|
7462
|
+
const leftVal = (isNumericOp && left === undefined) ? 0 : left;
|
|
7463
|
+
|
|
7464
|
+
switch (operator) {
|
|
7465
|
+
case '+=':
|
|
7466
|
+
// Special case: undefined + number should coerce undefined to 0
|
|
7467
|
+
if (left === undefined && typeof right === 'number') {
|
|
7468
|
+
return 0 + right;
|
|
7469
|
+
}
|
|
7470
|
+
return left + right;
|
|
7471
|
+
case '-=': return leftVal - right;
|
|
7472
|
+
case '*=': return leftVal * right;
|
|
7473
|
+
case '/=': return leftVal / right;
|
|
7474
|
+
case '%=': return leftVal % right;
|
|
7475
|
+
default: throw new Error(`Unknown assignment operator: ${operator}`);
|
|
7476
|
+
}
|
|
7477
|
+
}
|
|
7478
|
+
|
|
7479
|
+
evaluateLogicalExpression(node, env) {
|
|
7480
|
+
const left = this.evaluate(node.left, env);
|
|
7481
|
+
|
|
7482
|
+
if (node.operator === '&&') {
|
|
7483
|
+
return left ? this.evaluate(node.right, env) : left;
|
|
7484
|
+
} else if (node.operator === '||') {
|
|
7485
|
+
return left ? left : this.evaluate(node.right, env);
|
|
7486
|
+
} else if (node.operator === '??') {
|
|
7487
|
+
return left !== null && left !== undefined ? left : this.evaluate(node.right, env);
|
|
7488
|
+
}
|
|
7489
|
+
|
|
7490
|
+
throw new Error(`Unknown logical operator: ${node.operator}`);
|
|
7491
|
+
}
|
|
7492
|
+
|
|
7493
|
+
evaluateConditionalExpression(node, env) {
|
|
7494
|
+
const test = this.evaluate(node.test, env);
|
|
7495
|
+
return test
|
|
7496
|
+
? this.evaluate(node.consequent, env)
|
|
7497
|
+
: this.evaluate(node.alternate, env);
|
|
7498
|
+
}
|
|
7499
|
+
|
|
7500
|
+
evaluateCallExpression(node, env) {
|
|
7501
|
+
// Determine thisContext for method calls
|
|
7502
|
+
let thisContext = undefined;
|
|
7503
|
+
let callee;
|
|
7504
|
+
let objectName = null;
|
|
7505
|
+
let methodName = null;
|
|
7506
|
+
|
|
7507
|
+
if (node.callee.type === 'MemberExpression') {
|
|
7508
|
+
// For method calls like obj.method(), set this to obj
|
|
7509
|
+
thisContext = this.evaluate(node.callee.object, env);
|
|
7510
|
+
const prop = node.callee.computed
|
|
7511
|
+
? this.evaluate(node.callee.property, env)
|
|
7512
|
+
: node.callee.property.name;
|
|
7513
|
+
callee = thisContext[prop];
|
|
7514
|
+
|
|
7515
|
+
// Capture names for enhanced error messages
|
|
7516
|
+
methodName = prop;
|
|
7517
|
+
objectName = this.getExpressionName(node.callee.object);
|
|
7518
|
+
} else {
|
|
7519
|
+
callee = this.evaluate(node.callee, env);
|
|
7520
|
+
}
|
|
7521
|
+
|
|
7522
|
+
// Handle optional call - if optional and callee is null/undefined, return undefined
|
|
7523
|
+
if (node.optional && (callee === null || callee === undefined)) {
|
|
7524
|
+
return undefined;
|
|
7525
|
+
}
|
|
7526
|
+
|
|
7527
|
+
const args = node.arguments.map(arg => this.evaluate(arg, env));
|
|
7528
|
+
|
|
7529
|
+
if (typeof callee === 'function') {
|
|
7530
|
+
// Native JavaScript function or class method
|
|
7531
|
+
if (thisContext !== undefined) {
|
|
7532
|
+
return callee.call(thisContext, ...args);
|
|
7533
|
+
}
|
|
7534
|
+
return callee(...args);
|
|
7535
|
+
} else if (callee && callee.__isFunction) {
|
|
7536
|
+
// User-defined function - pass thisContext
|
|
7537
|
+
return this.callUserFunction(callee, args, env, thisContext);
|
|
7538
|
+
}
|
|
7539
|
+
|
|
7540
|
+
// Throw enhanced error for member expression calls
|
|
7541
|
+
if (objectName && methodName) {
|
|
7542
|
+
throw createMethodNotFoundError(objectName, methodName, thisContext);
|
|
7543
|
+
}
|
|
7544
|
+
|
|
7545
|
+
throw new TypeError(`${node.callee.name || 'Expression'} is not a function`);
|
|
7546
|
+
}
|
|
7547
|
+
|
|
7548
|
+
// Helper to get a readable name for an expression (for error messages)
|
|
7549
|
+
getExpressionName(node) {
|
|
7550
|
+
if (!node) return 'object';
|
|
7551
|
+
|
|
7552
|
+
switch (node.type) {
|
|
7553
|
+
case 'Identifier':
|
|
7554
|
+
return node.name;
|
|
7555
|
+
case 'ThisExpression':
|
|
7556
|
+
return 'this';
|
|
7557
|
+
case 'MemberExpression':
|
|
7558
|
+
const objName = this.getExpressionName(node.object);
|
|
7559
|
+
const propName = node.computed ? '[...]' : node.property.name;
|
|
7560
|
+
return `${objName}.${propName}`;
|
|
7561
|
+
default:
|
|
7562
|
+
return 'object';
|
|
7563
|
+
}
|
|
7564
|
+
}
|
|
7565
|
+
|
|
7566
|
+
callUserFunction(func, args, callingEnv, thisContext = undefined) {
|
|
7567
|
+
// Extract metadata if function is wrapped
|
|
7568
|
+
const metadata = func.__metadata || func;
|
|
7569
|
+
const funcEnv = new Environment(metadata.closure);
|
|
7570
|
+
|
|
7571
|
+
// Bind 'this' if provided (for method calls)
|
|
7572
|
+
if (thisContext !== undefined) {
|
|
7573
|
+
funcEnv.define('this', thisContext);
|
|
7574
|
+
}
|
|
7575
|
+
|
|
7576
|
+
// Bind parameters
|
|
7577
|
+
for (let i = 0; i < metadata.params.length; i++) {
|
|
7578
|
+
const param = metadata.params[i];
|
|
7579
|
+
|
|
7580
|
+
if (param.type === 'Identifier') {
|
|
7581
|
+
// Simple parameter: function(x)
|
|
7582
|
+
funcEnv.define(param.name, args[i]);
|
|
7583
|
+
} else if (param.type === 'AssignmentPattern') {
|
|
7584
|
+
// Default parameter: function(x = defaultValue)
|
|
7585
|
+
const value = args[i] !== undefined ? args[i] : this.evaluate(param.right, funcEnv);
|
|
7586
|
+
funcEnv.define(param.left.name, value);
|
|
7587
|
+
} else if (param.type === 'RestElement') {
|
|
7588
|
+
// Rest parameter: function(...rest)
|
|
7589
|
+
funcEnv.define(param.argument.name, args.slice(i));
|
|
7590
|
+
break; // Rest element must be last
|
|
7591
|
+
} else if (param.type === 'ObjectPattern') {
|
|
7592
|
+
// Destructuring parameter: function({a, b})
|
|
7593
|
+
this.bindObjectPattern(param, args[i], funcEnv);
|
|
7594
|
+
} else if (param.type === 'ArrayPattern') {
|
|
7595
|
+
// Array destructuring parameter: function([a, b])
|
|
7596
|
+
this.bindArrayPattern(param, args[i], funcEnv);
|
|
7597
|
+
} else {
|
|
7598
|
+
// Fallback for simple parameter names
|
|
7599
|
+
funcEnv.define(param.name, args[i]);
|
|
7600
|
+
}
|
|
7601
|
+
}
|
|
7602
|
+
|
|
7603
|
+
// Execute function body
|
|
7604
|
+
// If async, use async evaluation and return a promise
|
|
7605
|
+
if (metadata.async) {
|
|
7606
|
+
return (async () => {
|
|
7607
|
+
if (metadata.expression) {
|
|
7608
|
+
// Arrow function with expression body
|
|
7609
|
+
const result = await this.evaluateAsync(metadata.body, funcEnv);
|
|
7610
|
+
// If the result is a ThrowSignal, throw the error
|
|
7611
|
+
if (result instanceof ThrowSignal) {
|
|
7612
|
+
throw result.value;
|
|
7613
|
+
}
|
|
7614
|
+
return result;
|
|
7615
|
+
} else {
|
|
7616
|
+
// Block statement body
|
|
7617
|
+
const result = await this.evaluateAsync(metadata.body, funcEnv);
|
|
7618
|
+
if (result instanceof ReturnValue) {
|
|
7619
|
+
return result.value;
|
|
7620
|
+
}
|
|
7621
|
+
// If the result is a ThrowSignal, throw the error
|
|
7622
|
+
if (result instanceof ThrowSignal) {
|
|
7623
|
+
throw result.value;
|
|
7624
|
+
}
|
|
7625
|
+
return undefined;
|
|
7626
|
+
}
|
|
7627
|
+
})();
|
|
7628
|
+
} else {
|
|
7629
|
+
// Synchronous evaluation for non-async functions
|
|
7630
|
+
if (metadata.expression) {
|
|
7631
|
+
const result = this.evaluate(metadata.body, funcEnv);
|
|
7632
|
+
// If the result is a ThrowSignal, throw the error
|
|
7633
|
+
if (result instanceof ThrowSignal) {
|
|
7634
|
+
throw result.value;
|
|
7635
|
+
}
|
|
7636
|
+
return result;
|
|
7637
|
+
} else {
|
|
7638
|
+
const result = this.evaluate(metadata.body, funcEnv);
|
|
7639
|
+
if (result instanceof ReturnValue) {
|
|
7640
|
+
return result.value;
|
|
7641
|
+
}
|
|
7642
|
+
// If the result is a ThrowSignal, throw the error
|
|
7643
|
+
if (result instanceof ThrowSignal) {
|
|
7644
|
+
throw result.value;
|
|
7645
|
+
}
|
|
7646
|
+
return undefined;
|
|
7647
|
+
}
|
|
7648
|
+
}
|
|
7649
|
+
}
|
|
7650
|
+
|
|
7651
|
+
evaluateMemberExpression(node, env) {
|
|
7652
|
+
const obj = this.evaluate(node.object, env);
|
|
7653
|
+
|
|
7654
|
+
// Handle optional chaining - if optional and obj is null/undefined, return undefined
|
|
7655
|
+
if (node.optional && (obj === null || obj === undefined)) {
|
|
7656
|
+
return undefined;
|
|
7657
|
+
}
|
|
7658
|
+
|
|
7659
|
+
if (obj === null || obj === undefined) {
|
|
7660
|
+
throw new TypeError(`Cannot read property of ${obj}`);
|
|
7661
|
+
}
|
|
7662
|
+
|
|
7663
|
+
const prop = node.computed
|
|
7664
|
+
? this.evaluate(node.property, env)
|
|
7665
|
+
: node.property.name;
|
|
7666
|
+
|
|
7667
|
+
return obj[prop];
|
|
7668
|
+
}
|
|
7669
|
+
|
|
7670
|
+
evaluateChainExpression(node, env) {
|
|
7671
|
+
// ChainExpression is a wrapper for optional chaining expressions
|
|
7672
|
+
// It contains the actual expression (MemberExpression or CallExpression with optional: true)
|
|
7673
|
+
// We just evaluate the inner expression, which will handle the optional logic
|
|
7674
|
+
return this.evaluate(node.expression, env);
|
|
7675
|
+
}
|
|
7676
|
+
|
|
7677
|
+
evaluateArrayExpression(node, env) {
|
|
7678
|
+
const result = [];
|
|
7679
|
+
for (const elem of node.elements) {
|
|
7680
|
+
if (!elem) {
|
|
7681
|
+
// Hole in array [1, , 3]
|
|
7682
|
+
result.push(undefined);
|
|
7683
|
+
} else if (elem.type === 'SpreadElement') {
|
|
7684
|
+
// Spread syntax [...arr]
|
|
7685
|
+
const spreadValue = this.evaluate(elem.argument, env);
|
|
7686
|
+
if (Array.isArray(spreadValue)) {
|
|
7687
|
+
result.push(...spreadValue);
|
|
7688
|
+
} else if (typeof spreadValue[Symbol.iterator] === 'function') {
|
|
7689
|
+
result.push(...spreadValue);
|
|
7690
|
+
} else {
|
|
7691
|
+
throw new TypeError('Spread syntax requires an iterable');
|
|
7692
|
+
}
|
|
7693
|
+
} else {
|
|
7694
|
+
result.push(this.evaluate(elem, env));
|
|
7695
|
+
}
|
|
7696
|
+
}
|
|
7697
|
+
return result;
|
|
7698
|
+
}
|
|
7699
|
+
|
|
7700
|
+
evaluateObjectExpression(node, env) {
|
|
7701
|
+
const obj = {};
|
|
7702
|
+
for (const prop of node.properties) {
|
|
7703
|
+
if (prop.type === 'SpreadElement') {
|
|
7704
|
+
// Object spread {...other}
|
|
7705
|
+
const spreadValue = this.evaluate(prop.argument, env);
|
|
7706
|
+
if (typeof spreadValue === 'object' && spreadValue !== null) {
|
|
7707
|
+
Object.assign(obj, spreadValue);
|
|
7708
|
+
}
|
|
7709
|
+
} else {
|
|
7710
|
+
// Regular property or shorthand
|
|
7711
|
+
const key = prop.key.type === 'Identifier' && !prop.computed
|
|
7712
|
+
? prop.key.name
|
|
7713
|
+
: this.evaluate(prop.key, env);
|
|
7714
|
+
|
|
7715
|
+
// Handle shorthand properties {x} => {x: x}
|
|
7716
|
+
const value = prop.value ? this.evaluate(prop.value, env) : env.get(key);
|
|
7717
|
+
|
|
7718
|
+
// Handle method shorthand: method() {}
|
|
7719
|
+
if (prop.method && prop.value.type === 'FunctionExpression') {
|
|
7720
|
+
obj[key] = (...args) => {
|
|
7721
|
+
const funcValue = this.evaluate(prop.value, env);
|
|
7722
|
+
return this.callUserFunction(funcValue, args, env);
|
|
7723
|
+
};
|
|
7724
|
+
} else {
|
|
7725
|
+
obj[key] = value;
|
|
7726
|
+
}
|
|
7727
|
+
}
|
|
7728
|
+
}
|
|
7729
|
+
return obj;
|
|
7730
|
+
}
|
|
7731
|
+
|
|
7732
|
+
evaluateFunctionExpression(node, env) {
|
|
7733
|
+
const funcMetadata = {
|
|
7734
|
+
__isFunction: true,
|
|
7735
|
+
params: node.params,
|
|
7736
|
+
body: node.body,
|
|
7737
|
+
closure: env,
|
|
7738
|
+
expression: node.type === 'ArrowFunctionExpression' && node.expression,
|
|
7739
|
+
async: node.async || false
|
|
7740
|
+
};
|
|
7741
|
+
|
|
7742
|
+
// Wrap in actual JavaScript function so it can be called by native code
|
|
7743
|
+
const interpreter = this;
|
|
7744
|
+
|
|
7745
|
+
// Create async or sync wrapper based on function type
|
|
7746
|
+
// Note: using regular function (not arrow) to capture 'this' for method calls
|
|
7747
|
+
const wrappedFunc = funcMetadata.async
|
|
7748
|
+
? async function(...args) {
|
|
7749
|
+
return await interpreter.callUserFunction(funcMetadata, args, funcMetadata.closure, this);
|
|
7750
|
+
}
|
|
7751
|
+
: function(...args) {
|
|
7752
|
+
return interpreter.callUserFunction(funcMetadata, args, funcMetadata.closure, this);
|
|
7753
|
+
};
|
|
7754
|
+
|
|
7755
|
+
// Preserve metadata for JSLike's internal use
|
|
7756
|
+
wrappedFunc.__isFunction = true;
|
|
7757
|
+
wrappedFunc.__metadata = funcMetadata;
|
|
7758
|
+
|
|
7759
|
+
return wrappedFunc;
|
|
7760
|
+
}
|
|
7761
|
+
|
|
7762
|
+
evaluateNewExpression(node, env) {
|
|
7763
|
+
const constructor = this.evaluate(node.callee, env);
|
|
7764
|
+
const args = node.arguments.map(arg => this.evaluate(arg, env));
|
|
7765
|
+
|
|
7766
|
+
// Handle user-defined functions (including async and arrow functions)
|
|
7767
|
+
if (constructor && constructor.__isFunction) {
|
|
7768
|
+
const result = this.callUserFunction(constructor, args, env);
|
|
7769
|
+
// If result is a promise (async function), return it directly
|
|
7770
|
+
// The async context will handle it
|
|
7771
|
+
if (result && typeof result.then === 'function') {
|
|
7772
|
+
return result.then(res => {
|
|
7773
|
+
if (res && typeof res === 'object') {
|
|
7774
|
+
return res;
|
|
7775
|
+
}
|
|
7776
|
+
return {};
|
|
7777
|
+
});
|
|
7778
|
+
}
|
|
7779
|
+
// If the function returns an object, use it; otherwise create a new object
|
|
7780
|
+
if (result && typeof result === 'object') {
|
|
7781
|
+
return result;
|
|
7782
|
+
}
|
|
7783
|
+
// For arrow/async functions that don't return an object, create one
|
|
7784
|
+
return {};
|
|
7785
|
+
}
|
|
7786
|
+
|
|
7787
|
+
if (typeof constructor === 'function') {
|
|
7788
|
+
// For native functions and classes, try to construct
|
|
7789
|
+
// Arrow functions and async functions can't be constructed with 'new' in JavaScript,
|
|
7790
|
+
// but if they return an object, we can use that
|
|
7791
|
+
try {
|
|
7792
|
+
return new constructor(...args);
|
|
7793
|
+
} catch (err) {
|
|
7794
|
+
// If construction fails (e.g., arrow function, async function),
|
|
7795
|
+
// try calling it normally and see if it returns an object
|
|
7796
|
+
if (err.message && err.message.includes('not a constructor')) {
|
|
7797
|
+
const result = constructor(...args);
|
|
7798
|
+
// If result is a promise, handle it
|
|
7799
|
+
if (result && typeof result.then === 'function') {
|
|
7800
|
+
return result.then(res => {
|
|
7801
|
+
if (res && typeof res === 'object') {
|
|
7802
|
+
return res;
|
|
7803
|
+
}
|
|
7804
|
+
throw new TypeError(`Type mismatch in new expression: ${node.callee.name || 'Expression'} is not a constructor`);
|
|
7805
|
+
});
|
|
7806
|
+
}
|
|
7807
|
+
if (result && typeof result === 'object') {
|
|
7808
|
+
return result;
|
|
7809
|
+
}
|
|
7810
|
+
throw new TypeError(`Type mismatch in new expression: ${node.callee.name || 'Expression'} is not a constructor`);
|
|
7811
|
+
}
|
|
7812
|
+
throw err;
|
|
7813
|
+
}
|
|
7814
|
+
}
|
|
7815
|
+
|
|
7816
|
+
throw new TypeError(`Type mismatch in new expression: ${node.callee.name || 'Expression'} is not a constructor`);
|
|
7817
|
+
}
|
|
7818
|
+
|
|
7819
|
+
evaluateThisExpression(node, env) {
|
|
7820
|
+
try {
|
|
7821
|
+
return env.get('this');
|
|
7822
|
+
} catch (e) {
|
|
7823
|
+
// 'this' not defined in current scope
|
|
7824
|
+
return undefined;
|
|
7825
|
+
}
|
|
7826
|
+
}
|
|
7827
|
+
|
|
7828
|
+
evaluateSuperExpression(node, env) {
|
|
7829
|
+
// Super is used in class methods to access parent class
|
|
7830
|
+
try {
|
|
7831
|
+
return env.get('super');
|
|
7832
|
+
} catch (e) {
|
|
7833
|
+
throw new ReferenceError("'super' keyword is unexpected here");
|
|
7834
|
+
}
|
|
7835
|
+
}
|
|
7836
|
+
|
|
7837
|
+
evaluateSequenceExpression(node, env) {
|
|
7838
|
+
let result;
|
|
7839
|
+
for (const expr of node.expressions) {
|
|
7840
|
+
result = this.evaluate(expr, env);
|
|
7841
|
+
}
|
|
7842
|
+
return result;
|
|
7843
|
+
}
|
|
7844
|
+
|
|
7845
|
+
evaluateVariableDeclaration(node, env) {
|
|
7846
|
+
const isConst = node.kind === 'const';
|
|
7847
|
+
|
|
7848
|
+
for (const declarator of node.declarations) {
|
|
7849
|
+
const value = declarator.init
|
|
7850
|
+
? this.evaluate(declarator.init, env)
|
|
7851
|
+
: undefined;
|
|
7852
|
+
|
|
7853
|
+
// Handle destructuring patterns
|
|
7854
|
+
if (declarator.id.type === 'ObjectPattern') {
|
|
7855
|
+
this.bindObjectPattern(declarator.id, value, env, isConst);
|
|
7856
|
+
} else if (declarator.id.type === 'ArrayPattern') {
|
|
7857
|
+
this.bindArrayPattern(declarator.id, value, env, isConst);
|
|
7858
|
+
} else {
|
|
7859
|
+
env.define(declarator.id.name, value, isConst);
|
|
7860
|
+
}
|
|
7861
|
+
}
|
|
7862
|
+
return undefined;
|
|
7863
|
+
}
|
|
7864
|
+
|
|
7865
|
+
bindObjectPattern(pattern, value, env, isConst = false) {
|
|
7866
|
+
if (value === null || value === undefined) {
|
|
7867
|
+
throw new TypeError('Cannot destructure undefined or null');
|
|
7868
|
+
}
|
|
7869
|
+
|
|
7870
|
+
for (const prop of pattern.properties) {
|
|
7871
|
+
if (prop.type === 'RestElement') {
|
|
7872
|
+
// Handle rest properties {...rest}
|
|
7873
|
+
const assignedKeys = pattern.properties
|
|
7874
|
+
.filter(p => p.type !== 'RestElement')
|
|
7875
|
+
.map(p => p.key.name || p.key.value);
|
|
7876
|
+
const restObj = {};
|
|
7877
|
+
for (const key in value) {
|
|
7878
|
+
if (!assignedKeys.includes(key)) {
|
|
7879
|
+
restObj[key] = value[key];
|
|
7880
|
+
}
|
|
7881
|
+
}
|
|
7882
|
+
env.define(prop.argument.name, restObj, isConst);
|
|
7883
|
+
} else {
|
|
7884
|
+
const key = prop.key.name || prop.key.value;
|
|
7885
|
+
const propValue = value[key];
|
|
7886
|
+
|
|
7887
|
+
if (prop.value.type === 'Identifier') {
|
|
7888
|
+
env.define(prop.value.name, propValue, isConst);
|
|
7889
|
+
} else if (prop.value.type === 'AssignmentPattern') {
|
|
7890
|
+
// Handle default values
|
|
7891
|
+
const finalValue = propValue !== undefined
|
|
7892
|
+
? propValue
|
|
7893
|
+
: this.evaluate(prop.value.right, env);
|
|
7894
|
+
env.define(prop.value.left.name, finalValue, isConst);
|
|
7895
|
+
} else if (prop.value.type === 'ObjectPattern') {
|
|
7896
|
+
this.bindObjectPattern(prop.value, propValue, env, isConst);
|
|
7897
|
+
} else if (prop.value.type === 'ArrayPattern') {
|
|
7898
|
+
this.bindArrayPattern(prop.value, propValue, env, isConst);
|
|
7899
|
+
}
|
|
7900
|
+
}
|
|
7901
|
+
}
|
|
7902
|
+
}
|
|
7903
|
+
|
|
7904
|
+
bindArrayPattern(pattern, value, env, isConst = false) {
|
|
7905
|
+
if (!Array.isArray(value)) {
|
|
7906
|
+
throw new TypeError('Cannot destructure non-iterable');
|
|
7907
|
+
}
|
|
7908
|
+
|
|
7909
|
+
for (let i = 0; i < pattern.elements.length; i++) {
|
|
7910
|
+
const element = pattern.elements[i];
|
|
7911
|
+
if (!element) continue; // Hole in pattern [a, , c]
|
|
7912
|
+
|
|
7913
|
+
if (element.type === 'RestElement') {
|
|
7914
|
+
// Handle rest elements [...rest]
|
|
7915
|
+
const restValues = value.slice(i);
|
|
7916
|
+
env.define(element.argument.name, restValues, isConst);
|
|
7917
|
+
break;
|
|
7918
|
+
} else if (element.type === 'Identifier') {
|
|
7919
|
+
env.define(element.name, value[i], isConst);
|
|
7920
|
+
} else if (element.type === 'AssignmentPattern') {
|
|
7921
|
+
// Handle default values
|
|
7922
|
+
const finalValue = value[i] !== undefined
|
|
7923
|
+
? value[i]
|
|
7924
|
+
: this.evaluate(element.right, env);
|
|
7925
|
+
env.define(element.left.name, finalValue, isConst);
|
|
7926
|
+
} else if (element.type === 'ObjectPattern') {
|
|
7927
|
+
this.bindObjectPattern(element, value[i], env, isConst);
|
|
7928
|
+
} else if (element.type === 'ArrayPattern') {
|
|
7929
|
+
this.bindArrayPattern(element, value[i], env, isConst);
|
|
7930
|
+
}
|
|
7931
|
+
}
|
|
7932
|
+
}
|
|
7933
|
+
|
|
7934
|
+
evaluateFunctionDeclaration(node, env) {
|
|
7935
|
+
const funcMetadata = {
|
|
7936
|
+
__isFunction: true,
|
|
7937
|
+
params: node.params,
|
|
7938
|
+
body: node.body,
|
|
7939
|
+
closure: env,
|
|
7940
|
+
expression: false,
|
|
7941
|
+
async: node.async || false
|
|
7942
|
+
};
|
|
7943
|
+
|
|
7944
|
+
// Wrap in actual JavaScript function so it can be called by native code
|
|
7945
|
+
const interpreter = this;
|
|
7946
|
+
|
|
7947
|
+
// Create async or sync wrapper based on function type
|
|
7948
|
+
// Note: using regular function (not arrow) to capture 'this' for method calls
|
|
7949
|
+
const wrappedFunc = funcMetadata.async
|
|
7950
|
+
? async function(...args) {
|
|
7951
|
+
return await interpreter.callUserFunction(funcMetadata, args, funcMetadata.closure, this);
|
|
7952
|
+
}
|
|
7953
|
+
: function(...args) {
|
|
7954
|
+
return interpreter.callUserFunction(funcMetadata, args, funcMetadata.closure, this);
|
|
7955
|
+
};
|
|
7956
|
+
|
|
7957
|
+
// Preserve metadata for JSLike's internal use
|
|
7958
|
+
wrappedFunc.__isFunction = true;
|
|
7959
|
+
wrappedFunc.__metadata = funcMetadata;
|
|
7960
|
+
|
|
7961
|
+
env.define(node.id.name, wrappedFunc);
|
|
7962
|
+
return undefined;
|
|
7963
|
+
}
|
|
7964
|
+
|
|
7965
|
+
async evaluateImportDeclaration(node, env) {
|
|
7966
|
+
// Get module path from import source
|
|
7967
|
+
const modulePath = node.source.value;
|
|
7968
|
+
|
|
7969
|
+
// Check if module resolver is configured
|
|
7970
|
+
if (!this.moduleResolver) {
|
|
7971
|
+
throw new Error('Module resolver not configured - cannot import modules');
|
|
7972
|
+
}
|
|
7973
|
+
|
|
7974
|
+
// Check if module is already cached
|
|
7975
|
+
let moduleExports;
|
|
7976
|
+
if (this.moduleCache.has(modulePath)) {
|
|
7977
|
+
moduleExports = this.moduleCache.get(modulePath);
|
|
7978
|
+
} else {
|
|
7979
|
+
// Resolve and load module code
|
|
7980
|
+
const resolution = await this.moduleResolver.resolve(modulePath);
|
|
7981
|
+
if (!resolution) {
|
|
7982
|
+
throw new Error(`Cannot find module '${modulePath}'`);
|
|
7983
|
+
}
|
|
7984
|
+
|
|
7985
|
+
// Handle both old (string) and new (ModuleResolution) formats
|
|
7986
|
+
const moduleCode = typeof resolution === 'string' ? resolution : resolution.code;
|
|
7987
|
+
|
|
7988
|
+
// Parse and execute module in its own environment
|
|
7989
|
+
const moduleAst = parse$1(moduleCode, {
|
|
7990
|
+
ecmaVersion: 2020,
|
|
7991
|
+
sourceType: 'module',
|
|
7992
|
+
locations: false
|
|
7993
|
+
});
|
|
7994
|
+
const moduleEnv = new Environment(this.globalEnv);
|
|
7995
|
+
|
|
7996
|
+
// Create a new interpreter for the module with shared module cache
|
|
7997
|
+
const moduleInterpreter = new Interpreter(this.globalEnv, {
|
|
7998
|
+
moduleResolver: this.moduleResolver
|
|
7999
|
+
});
|
|
8000
|
+
moduleInterpreter.moduleCache = this.moduleCache; // Share cache
|
|
8001
|
+
|
|
8002
|
+
// Execute module and collect exports
|
|
8003
|
+
await moduleInterpreter.evaluateAsync(moduleAst, moduleEnv);
|
|
8004
|
+
|
|
8005
|
+
// Cache the module exports
|
|
8006
|
+
moduleExports = moduleInterpreter.moduleExports;
|
|
8007
|
+
this.moduleCache.set(modulePath, moduleExports);
|
|
8008
|
+
}
|
|
8009
|
+
|
|
8010
|
+
// Import specified bindings into current environment
|
|
8011
|
+
for (const specifier of node.specifiers) {
|
|
8012
|
+
if (specifier.type === 'ImportSpecifier') {
|
|
8013
|
+
// Named import: import { foo, bar } from "module"
|
|
8014
|
+
const importedName = specifier.imported.name;
|
|
8015
|
+
const localName = specifier.local.name;
|
|
8016
|
+
|
|
8017
|
+
if (!(importedName in moduleExports)) {
|
|
8018
|
+
throw new Error(`Module '${modulePath}' has no export '${importedName}'`);
|
|
8019
|
+
}
|
|
8020
|
+
|
|
8021
|
+
env.define(localName, moduleExports[importedName]);
|
|
8022
|
+
} else if (specifier.type === 'ImportDefaultSpecifier') {
|
|
8023
|
+
// Default import: import foo from "module"
|
|
8024
|
+
const localName = specifier.local.name;
|
|
8025
|
+
|
|
8026
|
+
if (!('default' in moduleExports)) {
|
|
8027
|
+
throw new Error(`Module '${modulePath}' has no default export`);
|
|
8028
|
+
}
|
|
8029
|
+
|
|
8030
|
+
env.define(localName, moduleExports.default);
|
|
8031
|
+
} else if (specifier.type === 'ImportNamespaceSpecifier') {
|
|
8032
|
+
// Namespace import: import * as foo from "module"
|
|
8033
|
+
const localName = specifier.local.name;
|
|
8034
|
+
env.define(localName, moduleExports);
|
|
8035
|
+
}
|
|
8036
|
+
}
|
|
8037
|
+
|
|
8038
|
+
return undefined;
|
|
8039
|
+
}
|
|
8040
|
+
|
|
8041
|
+
evaluateExportNamedDeclaration(node, env) {
|
|
8042
|
+
// Handle export with declaration: export function foo() {} or export const x = 42
|
|
8043
|
+
if (node.declaration) {
|
|
8044
|
+
const result = this.evaluate(node.declaration, env);
|
|
8045
|
+
|
|
8046
|
+
// Register exported names
|
|
8047
|
+
if (node.declaration.type === 'FunctionDeclaration') {
|
|
8048
|
+
// export function foo() {}
|
|
8049
|
+
const name = node.declaration.id.name;
|
|
8050
|
+
this.moduleExports[name] = env.get(name);
|
|
8051
|
+
} else if (node.declaration.type === 'VariableDeclaration') {
|
|
8052
|
+
// export const x = 42, y = 10
|
|
8053
|
+
for (const declarator of node.declaration.declarations) {
|
|
8054
|
+
const name = declarator.id.name;
|
|
8055
|
+
this.moduleExports[name] = env.get(name);
|
|
8056
|
+
}
|
|
8057
|
+
} else if (node.declaration.type === 'ClassDeclaration') {
|
|
8058
|
+
// export class Foo {}
|
|
8059
|
+
const name = node.declaration.id.name;
|
|
8060
|
+
this.moduleExports[name] = env.get(name);
|
|
8061
|
+
}
|
|
8062
|
+
|
|
8063
|
+
return result;
|
|
8064
|
+
}
|
|
8065
|
+
|
|
8066
|
+
// Handle export list: export { foo, bar }
|
|
8067
|
+
if (node.specifiers && node.specifiers.length > 0) {
|
|
8068
|
+
for (const specifier of node.specifiers) {
|
|
8069
|
+
const exportedName = specifier.exported.name;
|
|
8070
|
+
const localName = specifier.local.name;
|
|
8071
|
+
this.moduleExports[exportedName] = env.get(localName);
|
|
8072
|
+
}
|
|
8073
|
+
}
|
|
8074
|
+
|
|
8075
|
+
return undefined;
|
|
8076
|
+
}
|
|
8077
|
+
|
|
8078
|
+
evaluateExportDefaultDeclaration(node, env) {
|
|
8079
|
+
// Evaluate the default export expression/declaration
|
|
8080
|
+
let value;
|
|
8081
|
+
|
|
8082
|
+
if (node.declaration.type === 'FunctionDeclaration' || node.declaration.type === 'ClassDeclaration') {
|
|
8083
|
+
// export default function foo() {} or export default class Foo {}
|
|
8084
|
+
value = this.evaluate(node.declaration, env);
|
|
8085
|
+
// If it has a name, it's also defined in the environment
|
|
8086
|
+
if (node.declaration.id) {
|
|
8087
|
+
value = env.get(node.declaration.id.name);
|
|
8088
|
+
}
|
|
8089
|
+
} else {
|
|
8090
|
+
// export default expression
|
|
8091
|
+
value = this.evaluate(node.declaration, env);
|
|
8092
|
+
}
|
|
8093
|
+
|
|
8094
|
+
// Register as default export
|
|
8095
|
+
this.moduleExports.default = value;
|
|
8096
|
+
|
|
8097
|
+
return undefined;
|
|
8098
|
+
}
|
|
8099
|
+
|
|
8100
|
+
evaluateBlockStatement(node, env) {
|
|
8101
|
+
const blockEnv = new Environment(env);
|
|
8102
|
+
let result;
|
|
8103
|
+
|
|
8104
|
+
for (const statement of node.body) {
|
|
8105
|
+
result = this.evaluate(statement, blockEnv);
|
|
8106
|
+
if (result instanceof ReturnValue ||
|
|
8107
|
+
result instanceof BreakSignal ||
|
|
8108
|
+
result instanceof ContinueSignal ||
|
|
8109
|
+
result instanceof ThrowSignal) {
|
|
8110
|
+
return result;
|
|
8111
|
+
}
|
|
8112
|
+
}
|
|
8113
|
+
|
|
8114
|
+
return result;
|
|
8115
|
+
}
|
|
8116
|
+
|
|
8117
|
+
evaluateIfStatement(node, env) {
|
|
8118
|
+
const test = this.evaluate(node.test, env);
|
|
8119
|
+
|
|
8120
|
+
if (test) {
|
|
8121
|
+
return this.evaluate(node.consequent, env);
|
|
8122
|
+
} else if (node.alternate) {
|
|
8123
|
+
return this.evaluate(node.alternate, env);
|
|
8124
|
+
}
|
|
8125
|
+
|
|
8126
|
+
return undefined;
|
|
8127
|
+
}
|
|
8128
|
+
|
|
8129
|
+
evaluateWhileStatement(node, env) {
|
|
8130
|
+
let result;
|
|
8131
|
+
|
|
8132
|
+
while (this.evaluate(node.test, env)) {
|
|
8133
|
+
result = this.evaluate(node.body, env);
|
|
8134
|
+
|
|
8135
|
+
if (result instanceof BreakSignal) {
|
|
8136
|
+
break;
|
|
8137
|
+
}
|
|
8138
|
+
if (result instanceof ContinueSignal) {
|
|
8139
|
+
continue;
|
|
8140
|
+
}
|
|
8141
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
8142
|
+
return result;
|
|
8143
|
+
}
|
|
8144
|
+
}
|
|
8145
|
+
|
|
8146
|
+
return undefined;
|
|
8147
|
+
}
|
|
8148
|
+
|
|
8149
|
+
evaluateDoWhileStatement(node, env) {
|
|
8150
|
+
let result;
|
|
8151
|
+
|
|
8152
|
+
do {
|
|
8153
|
+
result = this.evaluate(node.body, env);
|
|
8154
|
+
|
|
8155
|
+
if (result instanceof BreakSignal) {
|
|
8156
|
+
break;
|
|
8157
|
+
}
|
|
8158
|
+
if (result instanceof ContinueSignal) {
|
|
8159
|
+
continue;
|
|
8160
|
+
}
|
|
8161
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
8162
|
+
return result;
|
|
8163
|
+
}
|
|
8164
|
+
} while (this.evaluate(node.test, env));
|
|
8165
|
+
|
|
8166
|
+
return undefined;
|
|
8167
|
+
}
|
|
8168
|
+
|
|
8169
|
+
evaluateForStatement(node, env) {
|
|
8170
|
+
const forEnv = new Environment(env);
|
|
8171
|
+
let result;
|
|
8172
|
+
|
|
8173
|
+
if (node.init) {
|
|
8174
|
+
this.evaluate(node.init, forEnv);
|
|
8175
|
+
}
|
|
8176
|
+
|
|
8177
|
+
while (!node.test || this.evaluate(node.test, forEnv)) {
|
|
8178
|
+
result = this.evaluate(node.body, forEnv);
|
|
8179
|
+
|
|
8180
|
+
if (result instanceof BreakSignal) {
|
|
8181
|
+
break;
|
|
8182
|
+
}
|
|
8183
|
+
if (result instanceof ContinueSignal) {
|
|
8184
|
+
if (node.update) {
|
|
8185
|
+
this.evaluate(node.update, forEnv);
|
|
8186
|
+
}
|
|
8187
|
+
continue;
|
|
8188
|
+
}
|
|
8189
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
8190
|
+
return result;
|
|
8191
|
+
}
|
|
8192
|
+
|
|
8193
|
+
if (node.update) {
|
|
8194
|
+
this.evaluate(node.update, forEnv);
|
|
8195
|
+
}
|
|
8196
|
+
}
|
|
8197
|
+
|
|
8198
|
+
return undefined;
|
|
8199
|
+
}
|
|
8200
|
+
|
|
8201
|
+
evaluateForInStatement(node, env) {
|
|
8202
|
+
const forEnv = new Environment(env);
|
|
8203
|
+
const obj = this.evaluate(node.right, forEnv);
|
|
8204
|
+
let result;
|
|
8205
|
+
|
|
8206
|
+
// Check for null or undefined - JavaScript throws TypeError
|
|
8207
|
+
if (obj === null || obj === undefined) {
|
|
8208
|
+
throw new TypeError(`Cannot use 'in' operator to iterate over ${obj}`);
|
|
8209
|
+
}
|
|
8210
|
+
|
|
8211
|
+
// Get the variable name from the declaration
|
|
8212
|
+
const varName = node.left.declarations[0].id.name;
|
|
8213
|
+
|
|
8214
|
+
// Define the variable once before the loop
|
|
8215
|
+
forEnv.define(varName, undefined);
|
|
8216
|
+
|
|
8217
|
+
for (const key in obj) {
|
|
8218
|
+
// Update the variable value for each iteration
|
|
8219
|
+
forEnv.set(varName, key);
|
|
8220
|
+
result = this.evaluate(node.body, forEnv);
|
|
8221
|
+
|
|
8222
|
+
if (result instanceof BreakSignal) {
|
|
8223
|
+
break;
|
|
8224
|
+
}
|
|
8225
|
+
if (result instanceof ContinueSignal) {
|
|
8226
|
+
continue;
|
|
8227
|
+
}
|
|
8228
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
8229
|
+
return result;
|
|
8230
|
+
}
|
|
8231
|
+
}
|
|
8232
|
+
|
|
8233
|
+
return undefined;
|
|
8234
|
+
}
|
|
8235
|
+
|
|
8236
|
+
evaluateForOfStatement(node, env) {
|
|
8237
|
+
const forEnv = new Environment(env);
|
|
8238
|
+
const iterable = this.evaluate(node.right, forEnv);
|
|
8239
|
+
let result;
|
|
8240
|
+
|
|
8241
|
+
const declarator = node.left.declarations[0];
|
|
8242
|
+
const isConst = node.left.kind === 'const';
|
|
8243
|
+
|
|
8244
|
+
for (const value of iterable) {
|
|
8245
|
+
// Create a new child environment for each iteration to handle const properly
|
|
8246
|
+
const iterEnv = forEnv.extend();
|
|
8247
|
+
|
|
8248
|
+
// Bind the value using the appropriate pattern
|
|
8249
|
+
if (declarator.id.type === 'Identifier') {
|
|
8250
|
+
iterEnv.define(declarator.id.name, value, isConst);
|
|
8251
|
+
} else if (declarator.id.type === 'ArrayPattern') {
|
|
8252
|
+
this.bindArrayPattern(declarator.id, value, iterEnv, isConst);
|
|
8253
|
+
} else if (declarator.id.type === 'ObjectPattern') {
|
|
8254
|
+
this.bindObjectPattern(declarator.id, value, iterEnv, isConst);
|
|
8255
|
+
}
|
|
8256
|
+
|
|
8257
|
+
result = this.evaluate(node.body, iterEnv);
|
|
8258
|
+
|
|
8259
|
+
if (result instanceof BreakSignal) {
|
|
8260
|
+
break;
|
|
8261
|
+
}
|
|
8262
|
+
if (result instanceof ContinueSignal) {
|
|
8263
|
+
continue;
|
|
8264
|
+
}
|
|
8265
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
8266
|
+
return result;
|
|
8267
|
+
}
|
|
8268
|
+
}
|
|
8269
|
+
|
|
8270
|
+
return undefined;
|
|
8271
|
+
}
|
|
8272
|
+
|
|
8273
|
+
evaluateTryStatement(node, env) {
|
|
8274
|
+
let result;
|
|
8275
|
+
|
|
8276
|
+
try {
|
|
8277
|
+
result = this.evaluate(node.block, env);
|
|
8278
|
+
|
|
8279
|
+
if (result instanceof ThrowSignal) {
|
|
8280
|
+
throw result.value;
|
|
8281
|
+
}
|
|
8282
|
+
} catch (error) {
|
|
8283
|
+
if (node.handler) {
|
|
8284
|
+
const catchEnv = new Environment(env);
|
|
8285
|
+
if (node.handler.param) {
|
|
8286
|
+
catchEnv.define(node.handler.param.name, error);
|
|
8287
|
+
}
|
|
8288
|
+
result = this.evaluate(node.handler.body, catchEnv);
|
|
8289
|
+
} else {
|
|
8290
|
+
throw error;
|
|
8291
|
+
}
|
|
8292
|
+
} finally {
|
|
8293
|
+
if (node.finalizer) {
|
|
8294
|
+
const finalResult = this.evaluate(node.finalizer, env);
|
|
8295
|
+
// If finally block throws or returns, it overrides the try/catch result
|
|
8296
|
+
if (finalResult instanceof ThrowSignal || finalResult instanceof ReturnValue) {
|
|
8297
|
+
return finalResult;
|
|
8298
|
+
}
|
|
8299
|
+
}
|
|
8300
|
+
}
|
|
8301
|
+
|
|
8302
|
+
return result;
|
|
8303
|
+
}
|
|
8304
|
+
|
|
8305
|
+
evaluateSwitchStatement(node, env) {
|
|
8306
|
+
const discriminant = this.evaluate(node.discriminant, env);
|
|
8307
|
+
let matched = false;
|
|
8308
|
+
let result;
|
|
8309
|
+
|
|
8310
|
+
for (const switchCase of node.cases) {
|
|
8311
|
+
// Check if this case matches (or if we're in fall-through mode)
|
|
8312
|
+
if (!matched && switchCase.test) {
|
|
8313
|
+
const testValue = this.evaluate(switchCase.test, env);
|
|
8314
|
+
if (testValue === discriminant) {
|
|
8315
|
+
matched = true;
|
|
8316
|
+
}
|
|
8317
|
+
} else if (!switchCase.test) {
|
|
8318
|
+
// Default case
|
|
8319
|
+
matched = true;
|
|
8320
|
+
}
|
|
8321
|
+
|
|
8322
|
+
// Execute consequent if matched
|
|
8323
|
+
if (matched) {
|
|
8324
|
+
for (const statement of switchCase.consequent) {
|
|
8325
|
+
result = this.evaluate(statement, env);
|
|
8326
|
+
|
|
8327
|
+
if (result instanceof BreakSignal) {
|
|
8328
|
+
return undefined;
|
|
8329
|
+
}
|
|
8330
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
8331
|
+
return result;
|
|
8332
|
+
}
|
|
8333
|
+
}
|
|
8334
|
+
}
|
|
8335
|
+
}
|
|
8336
|
+
|
|
8337
|
+
return undefined;
|
|
8338
|
+
}
|
|
8339
|
+
|
|
8340
|
+
// ===== ES6+ Feature Implementations =====
|
|
8341
|
+
|
|
8342
|
+
evaluateTemplateLiteral(node, env) {
|
|
8343
|
+
let result = '';
|
|
8344
|
+
for (let i = 0; i < node.quasis.length; i++) {
|
|
8345
|
+
result += node.quasis[i].value.cooked || node.quasis[i].value.raw;
|
|
8346
|
+
if (i < node.expressions.length) {
|
|
8347
|
+
const exprValue = this.evaluate(node.expressions[i], env);
|
|
8348
|
+
result += String(exprValue);
|
|
8349
|
+
}
|
|
8350
|
+
}
|
|
8351
|
+
return result;
|
|
8352
|
+
}
|
|
8353
|
+
|
|
8354
|
+
evaluateClassDeclaration(node, env) {
|
|
8355
|
+
const className = node.id.name;
|
|
8356
|
+
const classFunc = this.createClass(node, env);
|
|
8357
|
+
env.define(className, classFunc);
|
|
8358
|
+
return undefined;
|
|
8359
|
+
}
|
|
8360
|
+
|
|
8361
|
+
evaluateClassExpression(node, env) {
|
|
8362
|
+
return this.createClass(node, env);
|
|
8363
|
+
}
|
|
8364
|
+
|
|
8365
|
+
createClass(node, env) {
|
|
8366
|
+
const className = node.id ? node.id.name : 'AnonymousClass';
|
|
8367
|
+
const superClass = node.superClass ? this.evaluate(node.superClass, env) : null;
|
|
8368
|
+
const interpreter = this; // Capture interpreter reference
|
|
8369
|
+
|
|
8370
|
+
// Find constructor
|
|
8371
|
+
let constructor = null;
|
|
8372
|
+
const methods = {};
|
|
8373
|
+
const staticMethods = {};
|
|
8374
|
+
|
|
8375
|
+
for (const member of node.body.body) {
|
|
8376
|
+
if (member.type === 'MethodDefinition') {
|
|
8377
|
+
const methodName = member.key.name || member.key.value;
|
|
8378
|
+
const methodFunc = this.createMethodFunction(member.value, env, className);
|
|
8379
|
+
|
|
8380
|
+
if (member.kind === 'constructor') {
|
|
8381
|
+
constructor = methodFunc;
|
|
8382
|
+
} else if (member.static) {
|
|
8383
|
+
staticMethods[methodName] = methodFunc;
|
|
8384
|
+
} else {
|
|
8385
|
+
methods[methodName] = methodFunc;
|
|
8386
|
+
}
|
|
8387
|
+
}
|
|
8388
|
+
}
|
|
8389
|
+
|
|
8390
|
+
// Create class constructor function
|
|
8391
|
+
const classConstructor = function(...args) {
|
|
8392
|
+
// Create instance
|
|
8393
|
+
const instance = Object.create(classConstructor.prototype);
|
|
8394
|
+
|
|
8395
|
+
// Call constructor - super() must be called explicitly inside constructor
|
|
8396
|
+
if (constructor) {
|
|
8397
|
+
const result = interpreter.callMethodFunction(constructor, instance, args, env, superClass);
|
|
8398
|
+
// Only use the returned object if it's an explicit return of an object (not the instance)
|
|
8399
|
+
if (result && result.__explicitReturn && result.value && typeof result.value === 'object' && result.value !== instance) {
|
|
8400
|
+
return result.value;
|
|
8401
|
+
}
|
|
8402
|
+
} else if (superClass) {
|
|
8403
|
+
// If no constructor defined but has superClass, implicitly call super()
|
|
8404
|
+
// Call the superClass constructor properly - it's a classConstructor function
|
|
8405
|
+
superClass.call(instance, ...args);
|
|
8406
|
+
}
|
|
8407
|
+
|
|
8408
|
+
return instance;
|
|
8409
|
+
};
|
|
8410
|
+
|
|
8411
|
+
// Store the constructor method on the classConstructor for super() to access
|
|
8412
|
+
if (constructor) {
|
|
8413
|
+
classConstructor.__constructor = constructor;
|
|
8414
|
+
}
|
|
8415
|
+
|
|
8416
|
+
// Set up prototype chain
|
|
8417
|
+
if (superClass) {
|
|
8418
|
+
classConstructor.prototype = Object.create(superClass.prototype);
|
|
8419
|
+
classConstructor.prototype.constructor = classConstructor;
|
|
8420
|
+
}
|
|
8421
|
+
|
|
8422
|
+
// Add methods to prototype
|
|
8423
|
+
for (const [name, method] of Object.entries(methods)) {
|
|
8424
|
+
classConstructor.prototype[name] = function(...args) {
|
|
8425
|
+
const result = interpreter.callMethodFunction(method, this, args, env);
|
|
8426
|
+
// Unwrap explicit return marker
|
|
8427
|
+
if (result && result.__explicitReturn) {
|
|
8428
|
+
return result.value;
|
|
8429
|
+
}
|
|
8430
|
+
return result;
|
|
8431
|
+
};
|
|
8432
|
+
}
|
|
8433
|
+
|
|
8434
|
+
// Add static methods
|
|
8435
|
+
for (const [name, method] of Object.entries(staticMethods)) {
|
|
8436
|
+
classConstructor[name] = function(...args) {
|
|
8437
|
+
const result = interpreter.callMethodFunction(method, classConstructor, args, env);
|
|
8438
|
+
// Unwrap explicit return marker
|
|
8439
|
+
if (result && result.__explicitReturn) {
|
|
8440
|
+
return result.value;
|
|
8441
|
+
}
|
|
8442
|
+
return result;
|
|
8443
|
+
};
|
|
8444
|
+
}
|
|
8445
|
+
|
|
8446
|
+
classConstructor.__className = className;
|
|
8447
|
+
return classConstructor;
|
|
8448
|
+
}
|
|
8449
|
+
|
|
8450
|
+
createMethodFunction(funcNode, env, className) {
|
|
8451
|
+
const func = {
|
|
8452
|
+
__isFunction: true,
|
|
8453
|
+
__params: funcNode.params,
|
|
8454
|
+
__body: funcNode.body,
|
|
8455
|
+
__env: env,
|
|
8456
|
+
__className: className
|
|
8457
|
+
};
|
|
8458
|
+
return func;
|
|
8459
|
+
}
|
|
8460
|
+
|
|
8461
|
+
callMethodFunction(methodFunc, thisContext, args, env, superClass = null) {
|
|
8462
|
+
const funcEnv = new Environment(methodFunc.__env || env);
|
|
8463
|
+
|
|
8464
|
+
// Bind 'this'
|
|
8465
|
+
funcEnv.define('this', thisContext);
|
|
8466
|
+
|
|
8467
|
+
// Bind 'super' if superClass exists
|
|
8468
|
+
if (superClass) {
|
|
8469
|
+
// Create a super function that calls the parent constructor
|
|
8470
|
+
const superFunc = (...superArgs) => {
|
|
8471
|
+
// Call the parent constructor method if it exists
|
|
8472
|
+
if (superClass.__constructor) {
|
|
8473
|
+
this.callMethodFunction(superClass.__constructor, thisContext, superArgs, env, null);
|
|
8474
|
+
}
|
|
8475
|
+
// Otherwise, call superClass as a regular constructor (for native/external classes)
|
|
8476
|
+
else {
|
|
8477
|
+
// For native constructors like Error, we need to use Reflect.construct
|
|
8478
|
+
// to properly initialize the instance properties
|
|
8479
|
+
const tempInstance = Reflect.construct(superClass, superArgs, thisContext.constructor);
|
|
8480
|
+
// Copy properties from the temp instance to our thisContext
|
|
8481
|
+
Object.getOwnPropertyNames(tempInstance).forEach(name => {
|
|
8482
|
+
thisContext[name] = tempInstance[name];
|
|
8483
|
+
});
|
|
8484
|
+
}
|
|
8485
|
+
return undefined;
|
|
8486
|
+
};
|
|
8487
|
+
// Store both the function and mark it as super
|
|
8488
|
+
superFunc.__isSuperConstructor = true;
|
|
8489
|
+
superFunc.__superClass = superClass;
|
|
8490
|
+
funcEnv.define('super', superFunc);
|
|
8491
|
+
}
|
|
8492
|
+
|
|
8493
|
+
// Bind parameters
|
|
8494
|
+
for (let i = 0; i < methodFunc.__params.length; i++) {
|
|
8495
|
+
const param = methodFunc.__params[i];
|
|
8496
|
+
|
|
8497
|
+
if (param.type === 'Identifier') {
|
|
8498
|
+
// Simple parameter: function(x)
|
|
8499
|
+
funcEnv.define(param.name, args[i]);
|
|
8500
|
+
} else if (param.type === 'AssignmentPattern') {
|
|
8501
|
+
// Default parameter: function(x = defaultValue)
|
|
8502
|
+
const value = args[i] !== undefined ? args[i] : this.evaluate(param.right, funcEnv);
|
|
8503
|
+
funcEnv.define(param.left.name, value);
|
|
8504
|
+
} else if (param.type === 'RestElement') {
|
|
8505
|
+
// Rest parameter: function(...rest)
|
|
8506
|
+
funcEnv.define(param.argument.name, args.slice(i));
|
|
8507
|
+
break; // Rest element must be last
|
|
8508
|
+
} else if (param.type === 'ObjectPattern') {
|
|
8509
|
+
// Destructuring parameter: function({a, b})
|
|
8510
|
+
this.bindObjectPattern(param, args[i], funcEnv);
|
|
8511
|
+
} else if (param.type === 'ArrayPattern') {
|
|
8512
|
+
// Array destructuring parameter: function([a, b])
|
|
8513
|
+
this.bindArrayPattern(param, args[i], funcEnv);
|
|
8514
|
+
} else {
|
|
8515
|
+
// Fallback for simple parameter names
|
|
8516
|
+
funcEnv.define(param.name, args[i]);
|
|
8517
|
+
}
|
|
8518
|
+
}
|
|
8519
|
+
|
|
8520
|
+
const result = this.evaluate(methodFunc.__body, funcEnv);
|
|
8521
|
+
|
|
8522
|
+
if (result instanceof ReturnValue) {
|
|
8523
|
+
// Mark that this was an explicit return for constructor handling
|
|
8524
|
+
return { __explicitReturn: true, value: result.value };
|
|
8525
|
+
}
|
|
8526
|
+
|
|
8527
|
+
// If the result is a ThrowSignal, throw the error
|
|
8528
|
+
if (result instanceof ThrowSignal) {
|
|
8529
|
+
throw result.value;
|
|
8530
|
+
}
|
|
8531
|
+
|
|
8532
|
+
// Return implicit result (for arrow function expressions)
|
|
8533
|
+
return result;
|
|
8534
|
+
}
|
|
8535
|
+
|
|
8536
|
+
evaluateMethodDefinition(node, env) {
|
|
8537
|
+
// This is handled by class creation
|
|
8538
|
+
return undefined;
|
|
8539
|
+
}
|
|
8540
|
+
|
|
8541
|
+
evaluateSpreadElement(node, env) {
|
|
8542
|
+
const arg = this.evaluate(node.argument, env);
|
|
8543
|
+
if (Array.isArray(arg)) {
|
|
8544
|
+
return { __spread: true, __values: arg };
|
|
8545
|
+
}
|
|
8546
|
+
if (typeof arg === 'object' && arg !== null) {
|
|
8547
|
+
return { __spread: true, __values: Object.entries(arg) };
|
|
8548
|
+
}
|
|
8549
|
+
throw new TypeError('Spread syntax requires an iterable');
|
|
8550
|
+
}
|
|
8551
|
+
|
|
8552
|
+
evaluateRestElement(node, env) {
|
|
8553
|
+
// Handled during parameter binding
|
|
8554
|
+
return undefined;
|
|
8555
|
+
}
|
|
8556
|
+
|
|
8557
|
+
evaluateObjectPattern(node, env) {
|
|
8558
|
+
// Handled during destructuring
|
|
8559
|
+
return undefined;
|
|
8560
|
+
}
|
|
8561
|
+
|
|
8562
|
+
evaluateArrayPattern(node, env) {
|
|
8563
|
+
// Handled during destructuring
|
|
8564
|
+
return undefined;
|
|
8565
|
+
}
|
|
8566
|
+
|
|
8567
|
+
evaluateAssignmentPattern(node, env) {
|
|
8568
|
+
// Handled during parameter binding with defaults
|
|
8569
|
+
return undefined;
|
|
8570
|
+
}
|
|
8571
|
+
|
|
8572
|
+
evaluateProperty(node, env) {
|
|
8573
|
+
// Already handled in evaluateObjectExpression
|
|
8574
|
+
return undefined;
|
|
8575
|
+
}
|
|
8576
|
+
}
|
|
8577
|
+
|
|
8578
|
+
// Built-in global objects and functions
|
|
8579
|
+
|
|
8580
|
+
function createGlobalEnvironment(env) {
|
|
8581
|
+
// Global objects (only those not already defined below)
|
|
8582
|
+
env.define('Date', Date);
|
|
8583
|
+
|
|
8584
|
+
// console object
|
|
8585
|
+
env.define('console', {
|
|
8586
|
+
log: (...args) => {
|
|
8587
|
+
console.log(...args);
|
|
8588
|
+
return undefined;
|
|
8589
|
+
},
|
|
8590
|
+
error: (...args) => {
|
|
8591
|
+
console.error(...args);
|
|
8592
|
+
return undefined;
|
|
8593
|
+
},
|
|
8594
|
+
warn: (...args) => {
|
|
8595
|
+
console.warn(...args);
|
|
8596
|
+
return undefined;
|
|
8597
|
+
},
|
|
8598
|
+
info: (...args) => {
|
|
8599
|
+
console.info(...args);
|
|
8600
|
+
return undefined;
|
|
8601
|
+
},
|
|
8602
|
+
dir: (...args) => {
|
|
8603
|
+
console.dir(...args);
|
|
8604
|
+
return undefined;
|
|
8605
|
+
}
|
|
8606
|
+
});
|
|
8607
|
+
|
|
8608
|
+
// Math object
|
|
8609
|
+
env.define('Math', {
|
|
8610
|
+
PI: Math.PI,
|
|
8611
|
+
E: Math.E,
|
|
8612
|
+
abs: Math.abs,
|
|
8613
|
+
acos: Math.acos,
|
|
8614
|
+
asin: Math.asin,
|
|
8615
|
+
atan: Math.atan,
|
|
8616
|
+
atan2: Math.atan2,
|
|
8617
|
+
ceil: Math.ceil,
|
|
8618
|
+
cos: Math.cos,
|
|
8619
|
+
exp: Math.exp,
|
|
8620
|
+
floor: Math.floor,
|
|
8621
|
+
log: Math.log,
|
|
8622
|
+
max: Math.max,
|
|
8623
|
+
min: Math.min,
|
|
8624
|
+
pow: Math.pow,
|
|
8625
|
+
random: Math.random,
|
|
8626
|
+
round: Math.round,
|
|
8627
|
+
sin: Math.sin,
|
|
8628
|
+
sqrt: Math.sqrt,
|
|
8629
|
+
tan: Math.tan,
|
|
8630
|
+
trunc: Math.trunc
|
|
8631
|
+
});
|
|
8632
|
+
|
|
8633
|
+
// Global values
|
|
8634
|
+
env.define('undefined', undefined);
|
|
8635
|
+
|
|
8636
|
+
// Global functions
|
|
8637
|
+
env.define('parseInt', parseInt);
|
|
8638
|
+
env.define('parseFloat', parseFloat);
|
|
8639
|
+
env.define('isNaN', isNaN);
|
|
8640
|
+
env.define('isFinite', isFinite);
|
|
8641
|
+
|
|
8642
|
+
// Array constructor
|
|
8643
|
+
env.define('Array', Array);
|
|
8644
|
+
|
|
8645
|
+
// Object constructor
|
|
8646
|
+
env.define('Object', Object);
|
|
8647
|
+
|
|
8648
|
+
// String constructor
|
|
8649
|
+
env.define('String', String);
|
|
8650
|
+
|
|
8651
|
+
// Number constructor
|
|
8652
|
+
env.define('Number', Number);
|
|
8653
|
+
|
|
8654
|
+
// Boolean constructor
|
|
8655
|
+
env.define('Boolean', Boolean);
|
|
8656
|
+
|
|
8657
|
+
// Function constructor
|
|
8658
|
+
env.define('Function', Function);
|
|
8659
|
+
|
|
8660
|
+
// RegExp constructor
|
|
8661
|
+
env.define('RegExp', RegExp);
|
|
8662
|
+
|
|
8663
|
+
// Symbol constructor
|
|
8664
|
+
env.define('Symbol', Symbol);
|
|
8665
|
+
|
|
8666
|
+
// Map and Set constructors
|
|
8667
|
+
env.define('Map', Map);
|
|
8668
|
+
env.define('Set', Set);
|
|
8669
|
+
env.define('WeakMap', WeakMap);
|
|
8670
|
+
env.define('WeakSet', WeakSet);
|
|
8671
|
+
|
|
8672
|
+
// JSON object
|
|
8673
|
+
env.define('JSON', {
|
|
8674
|
+
parse: JSON.parse,
|
|
8675
|
+
stringify: JSON.stringify
|
|
8676
|
+
});
|
|
8677
|
+
|
|
8678
|
+
// setTimeout, setInterval (basic implementations)
|
|
8679
|
+
env.define('setTimeout', setTimeout);
|
|
8680
|
+
env.define('setInterval', setInterval);
|
|
8681
|
+
env.define('clearTimeout', clearTimeout);
|
|
8682
|
+
env.define('clearInterval', clearInterval);
|
|
8683
|
+
|
|
8684
|
+
// Promise
|
|
8685
|
+
env.define('Promise', Promise);
|
|
8686
|
+
|
|
8687
|
+
// Error constructors
|
|
8688
|
+
env.define('Error', Error);
|
|
8689
|
+
env.define('TypeError', TypeError);
|
|
8690
|
+
env.define('ReferenceError', ReferenceError);
|
|
8691
|
+
env.define('SyntaxError', SyntaxError);
|
|
8692
|
+
env.define('RangeError', RangeError);
|
|
8693
|
+
|
|
8694
|
+
// Global console functions (shortcuts for console.log/warn/error)
|
|
8695
|
+
env.define('log', (...args) => {
|
|
8696
|
+
console.log(...args);
|
|
8697
|
+
return undefined;
|
|
8698
|
+
});
|
|
8699
|
+
env.define('warn', (...args) => {
|
|
8700
|
+
console.warn(...args);
|
|
8701
|
+
return undefined;
|
|
8702
|
+
});
|
|
8703
|
+
env.define('error', (...args) => {
|
|
8704
|
+
console.error(...args);
|
|
8705
|
+
return undefined;
|
|
8706
|
+
});
|
|
8707
|
+
|
|
8708
|
+
// Wang Standard Library - Array Operations
|
|
8709
|
+
env.define('sort_by', (array, keyOrFn) => {
|
|
8710
|
+
const arr = [...array];
|
|
8711
|
+
if (typeof keyOrFn === 'function') {
|
|
8712
|
+
return arr.sort((a, b) => {
|
|
8713
|
+
const aVal = keyOrFn(a);
|
|
8714
|
+
const bVal = keyOrFn(b);
|
|
8715
|
+
return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
8716
|
+
});
|
|
8717
|
+
} else {
|
|
8718
|
+
return arr.sort((a, b) => {
|
|
8719
|
+
const aVal = a[keyOrFn];
|
|
8720
|
+
const bVal = b[keyOrFn];
|
|
8721
|
+
return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
8722
|
+
});
|
|
8723
|
+
}
|
|
8724
|
+
});
|
|
8725
|
+
|
|
8726
|
+
env.define('reverse', (array) => {
|
|
8727
|
+
return [...array].reverse();
|
|
8728
|
+
});
|
|
8729
|
+
|
|
8730
|
+
env.define('unique', (array) => {
|
|
8731
|
+
return [...new Set(array)];
|
|
8732
|
+
});
|
|
8733
|
+
|
|
8734
|
+
env.define('unique_by', (array, key) => {
|
|
8735
|
+
const seen = new Set();
|
|
8736
|
+
return array.filter(item => {
|
|
8737
|
+
const val = item[key];
|
|
8738
|
+
if (seen.has(val)) return false;
|
|
8739
|
+
seen.add(val);
|
|
8740
|
+
return true;
|
|
8741
|
+
});
|
|
8742
|
+
});
|
|
8743
|
+
|
|
8744
|
+
env.define('group_by', (array, key) => {
|
|
8745
|
+
return array.reduce((groups, item) => {
|
|
8746
|
+
const groupKey = item[key];
|
|
8747
|
+
if (!groups[groupKey]) groups[groupKey] = [];
|
|
8748
|
+
groups[groupKey].push(item);
|
|
8749
|
+
return groups;
|
|
8750
|
+
}, {});
|
|
8751
|
+
});
|
|
8752
|
+
|
|
8753
|
+
env.define('chunk', (array, size) => {
|
|
8754
|
+
const chunks = [];
|
|
8755
|
+
for (let i = 0; i < array.length; i += size) {
|
|
8756
|
+
chunks.push(array.slice(i, i + size));
|
|
8757
|
+
}
|
|
8758
|
+
return chunks;
|
|
8759
|
+
});
|
|
8760
|
+
|
|
8761
|
+
env.define('flatten', (array, depth = 1) => {
|
|
8762
|
+
return array.flat(depth);
|
|
8763
|
+
});
|
|
8764
|
+
|
|
8765
|
+
env.define('first', (array, n) => {
|
|
8766
|
+
if (n === undefined) return array[0];
|
|
8767
|
+
return array.slice(0, n);
|
|
8768
|
+
});
|
|
8769
|
+
|
|
8770
|
+
env.define('last', (array, n) => {
|
|
8771
|
+
if (n === undefined) return array[array.length - 1];
|
|
8772
|
+
return array.slice(-n);
|
|
8773
|
+
});
|
|
8774
|
+
|
|
8775
|
+
env.define('take', (array, n) => {
|
|
8776
|
+
return array.slice(0, n);
|
|
8777
|
+
});
|
|
8778
|
+
|
|
8779
|
+
env.define('drop', (array, n) => {
|
|
8780
|
+
return array.slice(n);
|
|
8781
|
+
});
|
|
8782
|
+
|
|
8783
|
+
env.define('zip', (...arrays) => {
|
|
8784
|
+
const length = Math.min(...arrays.map(a => a.length));
|
|
8785
|
+
return Array.from({ length }, (_, i) => arrays.map(a => a[i]));
|
|
8786
|
+
});
|
|
8787
|
+
|
|
8788
|
+
env.define('partition', (array, predicate) => {
|
|
8789
|
+
const truthy = [];
|
|
8790
|
+
const falsy = [];
|
|
8791
|
+
array.forEach(item => {
|
|
8792
|
+
if (predicate(item)) truthy.push(item);
|
|
8793
|
+
else falsy.push(item);
|
|
8794
|
+
});
|
|
8795
|
+
return [truthy, falsy];
|
|
8796
|
+
});
|
|
8797
|
+
|
|
8798
|
+
env.define('filter', (array, predicate) => {
|
|
8799
|
+
return array.filter(predicate);
|
|
8800
|
+
});
|
|
8801
|
+
|
|
8802
|
+
env.define('map', (array, fn) => {
|
|
8803
|
+
return array.map(fn);
|
|
8804
|
+
});
|
|
8805
|
+
|
|
8806
|
+
env.define('find', (array, predicate) => {
|
|
8807
|
+
return array.find(predicate);
|
|
8808
|
+
});
|
|
8809
|
+
|
|
8810
|
+
env.define('find_index', (array, predicate) => {
|
|
8811
|
+
return array.findIndex(predicate);
|
|
8812
|
+
});
|
|
8813
|
+
|
|
8814
|
+
env.define('every', (array, predicate) => {
|
|
8815
|
+
return array.every(predicate);
|
|
8816
|
+
});
|
|
8817
|
+
|
|
8818
|
+
env.define('some', (array, predicate) => {
|
|
8819
|
+
return array.some(predicate);
|
|
8820
|
+
});
|
|
8821
|
+
|
|
8822
|
+
env.define('count', (array, predicate) => {
|
|
8823
|
+
if (!predicate) {
|
|
8824
|
+
return array.length;
|
|
8825
|
+
}
|
|
8826
|
+
return array.filter(predicate).length;
|
|
8827
|
+
});
|
|
8828
|
+
|
|
8829
|
+
// Wang Standard Library - Object Operations
|
|
8830
|
+
env.define('keys', (obj) => {
|
|
8831
|
+
return Object.keys(obj);
|
|
8832
|
+
});
|
|
8833
|
+
|
|
8834
|
+
env.define('values', (obj) => {
|
|
8835
|
+
return Object.values(obj);
|
|
8836
|
+
});
|
|
8837
|
+
|
|
8838
|
+
env.define('entries', (obj) => {
|
|
8839
|
+
return Object.entries(obj);
|
|
8840
|
+
});
|
|
8841
|
+
|
|
8842
|
+
env.define('pick', (obj, keys) => {
|
|
8843
|
+
const result = {};
|
|
8844
|
+
keys.forEach(key => {
|
|
8845
|
+
if (key in obj) result[key] = obj[key];
|
|
8846
|
+
});
|
|
8847
|
+
return result;
|
|
8848
|
+
});
|
|
8849
|
+
|
|
8850
|
+
env.define('omit', (obj, keys) => {
|
|
8851
|
+
const result = { ...obj };
|
|
8852
|
+
keys.forEach(key => delete result[key]);
|
|
8853
|
+
return result;
|
|
8854
|
+
});
|
|
8855
|
+
|
|
8856
|
+
env.define('merge', (...objects) => {
|
|
8857
|
+
return Object.assign({}, ...objects);
|
|
8858
|
+
});
|
|
8859
|
+
|
|
8860
|
+
env.define('get', (obj, path, defaultValue) => {
|
|
8861
|
+
const keys = path.split('.');
|
|
8862
|
+
let current = obj;
|
|
8863
|
+
for (const key of keys) {
|
|
8864
|
+
if (current == null) return defaultValue;
|
|
8865
|
+
current = current[key];
|
|
8866
|
+
}
|
|
8867
|
+
return current !== undefined ? current : defaultValue;
|
|
8868
|
+
});
|
|
8869
|
+
|
|
8870
|
+
env.define('set', (obj, path, value) => {
|
|
8871
|
+
const keys = path.split('.');
|
|
8872
|
+
const result = JSON.parse(JSON.stringify(obj)); // Deep clone
|
|
8873
|
+
let current = result;
|
|
8874
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
8875
|
+
const key = keys[i];
|
|
8876
|
+
if (!(key in current)) current[key] = {};
|
|
8877
|
+
current = current[key];
|
|
8878
|
+
}
|
|
8879
|
+
current[keys[keys.length - 1]] = value;
|
|
8880
|
+
return result;
|
|
8881
|
+
});
|
|
8882
|
+
|
|
8883
|
+
env.define('clone', (obj) => {
|
|
8884
|
+
return JSON.parse(JSON.stringify(obj));
|
|
8885
|
+
});
|
|
8886
|
+
|
|
8887
|
+
// Wang Standard Library - String Operations
|
|
8888
|
+
env.define('split', (str, separator) => {
|
|
8889
|
+
return str.split(separator);
|
|
8890
|
+
});
|
|
8891
|
+
|
|
8892
|
+
env.define('join', (array, separator) => {
|
|
8893
|
+
return array.join(separator);
|
|
8894
|
+
});
|
|
8895
|
+
|
|
8896
|
+
env.define('trim', (str) => {
|
|
8897
|
+
return str.trim();
|
|
8898
|
+
});
|
|
8899
|
+
|
|
8900
|
+
env.define('trim_start', (str) => {
|
|
8901
|
+
return str.trimStart();
|
|
8902
|
+
});
|
|
8903
|
+
|
|
8904
|
+
env.define('trim_end', (str) => {
|
|
8905
|
+
return str.trimEnd();
|
|
8906
|
+
});
|
|
8907
|
+
|
|
8908
|
+
env.define('upper', (str) => {
|
|
8909
|
+
return str.toUpperCase();
|
|
8910
|
+
});
|
|
8911
|
+
|
|
8912
|
+
env.define('toUpperCase', (str) => {
|
|
8913
|
+
return str.toUpperCase();
|
|
8914
|
+
});
|
|
8915
|
+
|
|
8916
|
+
env.define('lower', (str) => {
|
|
8917
|
+
return str.toLowerCase();
|
|
8918
|
+
});
|
|
8919
|
+
|
|
8920
|
+
env.define('toLowerCase', (str) => {
|
|
8921
|
+
return str.toLowerCase();
|
|
8922
|
+
});
|
|
8923
|
+
|
|
8924
|
+
env.define('capitalize', (str) => {
|
|
8925
|
+
if (!str) return str;
|
|
8926
|
+
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
8927
|
+
});
|
|
8928
|
+
|
|
8929
|
+
env.define('starts_with', (str, prefix) => {
|
|
8930
|
+
return str.startsWith(prefix);
|
|
8931
|
+
});
|
|
8932
|
+
|
|
8933
|
+
env.define('ends_with', (str, suffix) => {
|
|
8934
|
+
return str.endsWith(suffix);
|
|
8935
|
+
});
|
|
8936
|
+
|
|
8937
|
+
env.define('includes', (str, substring) => {
|
|
8938
|
+
return str.includes(substring);
|
|
8939
|
+
});
|
|
8940
|
+
|
|
8941
|
+
env.define('pad_start', (str, length, char = ' ') => {
|
|
8942
|
+
return str.padStart(length, char);
|
|
8943
|
+
});
|
|
8944
|
+
|
|
8945
|
+
env.define('pad_end', (str, length, char = ' ') => {
|
|
8946
|
+
return str.padEnd(length, char);
|
|
8947
|
+
});
|
|
8948
|
+
|
|
8949
|
+
env.define('truncate', (str, length) => {
|
|
8950
|
+
if (str.length <= length) return str;
|
|
8951
|
+
return str.slice(0, length - 3) + '...';
|
|
8952
|
+
});
|
|
8953
|
+
|
|
8954
|
+
env.define('replace_all', (str, search, replace) => {
|
|
8955
|
+
return str.replaceAll(search, replace);
|
|
8956
|
+
});
|
|
8957
|
+
|
|
8958
|
+
// Wang Standard Library - Type Checking
|
|
8959
|
+
env.define('is_string', (value) => {
|
|
8960
|
+
return typeof value === 'string';
|
|
8961
|
+
});
|
|
8962
|
+
|
|
8963
|
+
env.define('is_number', (value) => {
|
|
8964
|
+
return typeof value === 'number';
|
|
8965
|
+
});
|
|
8966
|
+
|
|
8967
|
+
env.define('is_boolean', (value) => {
|
|
8968
|
+
return typeof value === 'boolean';
|
|
8969
|
+
});
|
|
8970
|
+
|
|
8971
|
+
env.define('is_array', (value) => {
|
|
8972
|
+
return Array.isArray(value);
|
|
8973
|
+
});
|
|
8974
|
+
|
|
8975
|
+
env.define('is_object', (value) => {
|
|
8976
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
8977
|
+
});
|
|
8978
|
+
|
|
8979
|
+
env.define('is_function', (value) => {
|
|
8980
|
+
return typeof value === 'function';
|
|
8981
|
+
});
|
|
8982
|
+
|
|
8983
|
+
env.define('is_null', (value) => {
|
|
8984
|
+
return value === null;
|
|
8985
|
+
});
|
|
8986
|
+
|
|
8987
|
+
env.define('is_undefined', (value) => {
|
|
8988
|
+
return value === undefined;
|
|
8989
|
+
});
|
|
8990
|
+
|
|
8991
|
+
env.define('is_empty', (value) => {
|
|
8992
|
+
if (value == null) return true;
|
|
8993
|
+
if (typeof value === 'string' || Array.isArray(value)) return value.length === 0;
|
|
8994
|
+
if (typeof value === 'object') return Object.keys(value).length === 0;
|
|
8995
|
+
return false;
|
|
8996
|
+
});
|
|
8997
|
+
|
|
8998
|
+
// Wang Standard Library - Math Operations
|
|
8999
|
+
env.define('min', (array) => {
|
|
9000
|
+
return Math.min(...array);
|
|
9001
|
+
});
|
|
9002
|
+
|
|
9003
|
+
env.define('max', (array) => {
|
|
9004
|
+
return Math.max(...array);
|
|
9005
|
+
});
|
|
9006
|
+
|
|
9007
|
+
env.define('sum', (array) => {
|
|
9008
|
+
return array.reduce((a, b) => a + b, 0);
|
|
9009
|
+
});
|
|
9010
|
+
|
|
9011
|
+
env.define('avg', (array) => {
|
|
9012
|
+
return array.reduce((a, b) => a + b, 0) / array.length;
|
|
9013
|
+
});
|
|
9014
|
+
|
|
9015
|
+
env.define('median', (array) => {
|
|
9016
|
+
const sorted = [...array].sort((a, b) => a - b);
|
|
9017
|
+
const mid = Math.floor(sorted.length / 2);
|
|
9018
|
+
return sorted.length % 2 === 0
|
|
9019
|
+
? (sorted[mid - 1] + sorted[mid]) / 2
|
|
9020
|
+
: sorted[mid];
|
|
9021
|
+
});
|
|
9022
|
+
|
|
9023
|
+
env.define('round', (num, decimals = 0) => {
|
|
9024
|
+
const factor = Math.pow(10, decimals);
|
|
9025
|
+
return Math.round(num * factor) / factor;
|
|
9026
|
+
});
|
|
9027
|
+
|
|
9028
|
+
env.define('floor', (num) => {
|
|
9029
|
+
return Math.floor(num);
|
|
9030
|
+
});
|
|
9031
|
+
|
|
9032
|
+
env.define('ceil', (num) => {
|
|
9033
|
+
return Math.ceil(num);
|
|
9034
|
+
});
|
|
9035
|
+
|
|
9036
|
+
env.define('abs', (num) => {
|
|
9037
|
+
return Math.abs(num);
|
|
9038
|
+
});
|
|
9039
|
+
|
|
9040
|
+
env.define('clamp', (num, min, max) => {
|
|
9041
|
+
return Math.min(Math.max(num, min), max);
|
|
9042
|
+
});
|
|
9043
|
+
|
|
9044
|
+
env.define('range', (start, end, step) => {
|
|
9045
|
+
// Support range(n) -> [0, 1, ..., n-1]
|
|
9046
|
+
if (end === undefined) {
|
|
9047
|
+
end = start;
|
|
9048
|
+
start = 0;
|
|
9049
|
+
step = 1;
|
|
9050
|
+
}
|
|
9051
|
+
if (step === undefined) {
|
|
9052
|
+
step = start < end ? 1 : -1;
|
|
9053
|
+
}
|
|
9054
|
+
|
|
9055
|
+
const result = [];
|
|
9056
|
+
if (step > 0) {
|
|
9057
|
+
for (let i = start; i < end; i += step) {
|
|
9058
|
+
result.push(i);
|
|
9059
|
+
}
|
|
9060
|
+
} else {
|
|
9061
|
+
for (let i = start; i > end; i += step) {
|
|
9062
|
+
result.push(i);
|
|
9063
|
+
}
|
|
9064
|
+
}
|
|
9065
|
+
return result;
|
|
9066
|
+
});
|
|
9067
|
+
|
|
9068
|
+
// Wang Standard Library - Utility Functions
|
|
9069
|
+
env.define('uuid', () => {
|
|
9070
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
9071
|
+
const r = Math.random() * 16 | 0;
|
|
9072
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
9073
|
+
return v.toString(16);
|
|
9074
|
+
});
|
|
9075
|
+
});
|
|
9076
|
+
|
|
9077
|
+
env.define('to_json', (value) => {
|
|
9078
|
+
return JSON.stringify(value);
|
|
9079
|
+
});
|
|
9080
|
+
|
|
9081
|
+
env.define('from_json', (str) => {
|
|
9082
|
+
return JSON.parse(str);
|
|
9083
|
+
});
|
|
9084
|
+
|
|
9085
|
+
env.define('sleep', async (ms) => {
|
|
9086
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
9087
|
+
});
|
|
9088
|
+
|
|
9089
|
+
env.define('wait', async (ms) => {
|
|
9090
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
9091
|
+
});
|
|
9092
|
+
|
|
9093
|
+
return env;
|
|
9094
|
+
}
|
|
9095
|
+
|
|
9096
|
+
/**
|
|
9097
|
+
* WangInterpreter compatibility adapter for JSLike
|
|
9098
|
+
* Maps Wang's interpreter API to JSLike's API
|
|
9099
|
+
*/
|
|
9100
|
+
|
|
9101
|
+
|
|
9102
|
+
class WangInterpreter {
|
|
9103
|
+
constructor(options = {}) {
|
|
9104
|
+
this.options = options;
|
|
9105
|
+
this.moduleResolver = options.moduleResolver;
|
|
9106
|
+
this.functions = options.functions || {};
|
|
9107
|
+
// Create a persistent environment for setVariable support
|
|
9108
|
+
this.persistentEnv = null;
|
|
9109
|
+
}
|
|
9110
|
+
|
|
9111
|
+
setVariable(name, value) {
|
|
9112
|
+
// If persistent environment doesn't exist, create it
|
|
9113
|
+
if (!this.persistentEnv) {
|
|
9114
|
+
this.persistentEnv = createEnvironment();
|
|
9115
|
+
|
|
9116
|
+
// Add custom functions
|
|
9117
|
+
for (const [funcName, func] of Object.entries(this.functions)) {
|
|
9118
|
+
if (this.persistentEnv.has(funcName)) {
|
|
9119
|
+
this.persistentEnv.set(funcName, func);
|
|
9120
|
+
} else {
|
|
9121
|
+
this.persistentEnv.define(funcName, func);
|
|
9122
|
+
}
|
|
9123
|
+
}
|
|
9124
|
+
}
|
|
9125
|
+
|
|
9126
|
+
// Set the variable in persistent environment
|
|
9127
|
+
if (this.persistentEnv.has(name)) {
|
|
9128
|
+
this.persistentEnv.set(name, value);
|
|
9129
|
+
} else {
|
|
9130
|
+
this.persistentEnv.define(name, value);
|
|
9131
|
+
}
|
|
9132
|
+
}
|
|
9133
|
+
|
|
9134
|
+
// Alias for setVariable - specifically for binding functions
|
|
9135
|
+
bindFunction(name, func) {
|
|
9136
|
+
return this.setVariable(name, func);
|
|
9137
|
+
}
|
|
9138
|
+
|
|
9139
|
+
// Expose global context for accessing variables after execution
|
|
9140
|
+
get globalContext() {
|
|
9141
|
+
return {
|
|
9142
|
+
variables: this.persistentEnv?.vars || new Map()
|
|
9143
|
+
};
|
|
9144
|
+
}
|
|
9145
|
+
|
|
9146
|
+
createExecutionEnvironment() {
|
|
9147
|
+
// Create fresh environment for each execution
|
|
9148
|
+
const env = createEnvironment();
|
|
9149
|
+
|
|
9150
|
+
// Add custom functions to environment, overriding built-ins if needed
|
|
9151
|
+
for (const [name, func] of Object.entries(this.functions)) {
|
|
9152
|
+
// Use set() to override existing variables instead of define()
|
|
9153
|
+
if (env.has(name)) {
|
|
9154
|
+
env.set(name, func);
|
|
9155
|
+
} else {
|
|
9156
|
+
env.define(name, func);
|
|
9157
|
+
}
|
|
9158
|
+
}
|
|
9159
|
+
|
|
9160
|
+
return env;
|
|
9161
|
+
}
|
|
9162
|
+
|
|
9163
|
+
async execute(code, initialEnv = undefined, userOptions = {}) {
|
|
9164
|
+
// Use provided environment, persistent environment, or create fresh one
|
|
9165
|
+
const env = initialEnv || this.persistentEnv || this.createExecutionEnvironment();
|
|
9166
|
+
|
|
9167
|
+
// Setup console capture if withMetadata option is enabled
|
|
9168
|
+
const withMetadata = userOptions.withMetadata || false;
|
|
9169
|
+
const capturedLogs = [];
|
|
9170
|
+
|
|
9171
|
+
if (withMetadata) {
|
|
9172
|
+
// Override log/warn/error functions to capture calls
|
|
9173
|
+
const createCapture = (type) => {
|
|
9174
|
+
return (...args) => {
|
|
9175
|
+
capturedLogs.push({
|
|
9176
|
+
type,
|
|
9177
|
+
args,
|
|
9178
|
+
timestamp: Date.now()
|
|
9179
|
+
});
|
|
9180
|
+
// Still output to console
|
|
9181
|
+
console[type](...args);
|
|
9182
|
+
return undefined;
|
|
9183
|
+
};
|
|
9184
|
+
};
|
|
9185
|
+
|
|
9186
|
+
env.set('log', createCapture('log'));
|
|
9187
|
+
env.set('warn', createCapture('warn'));
|
|
9188
|
+
env.set('error', createCapture('error'));
|
|
9189
|
+
}
|
|
9190
|
+
|
|
9191
|
+
// Check if code contains top-level return statements
|
|
9192
|
+
// Need to detect `return` at start of line, but NOT inside function bodies
|
|
9193
|
+
// Simple heuristic: check if braces are balanced before the return statement
|
|
9194
|
+
const hasTopLevelReturn = this.hasTopLevelReturn(code);
|
|
9195
|
+
|
|
9196
|
+
// Prepare execution options
|
|
9197
|
+
const options = {
|
|
9198
|
+
moduleResolver: this.moduleResolver
|
|
9199
|
+
// sourceType will be auto-detected from code
|
|
9200
|
+
};
|
|
9201
|
+
|
|
9202
|
+
let result;
|
|
9203
|
+
if (hasTopLevelReturn) {
|
|
9204
|
+
// Wrap code in async IIFE to support top-level return statements
|
|
9205
|
+
const wrappedCode = `(async function() { ${code} })()`;
|
|
9206
|
+
try {
|
|
9207
|
+
result = await execute(wrappedCode, env, options);
|
|
9208
|
+
} catch (error) {
|
|
9209
|
+
throw error;
|
|
9210
|
+
}
|
|
9211
|
+
} else {
|
|
9212
|
+
// Execute directly - JSLike returns last evaluated expression
|
|
9213
|
+
try {
|
|
9214
|
+
result = await execute(code, env, options);
|
|
9215
|
+
} catch (error) {
|
|
9216
|
+
throw error;
|
|
9217
|
+
}
|
|
9218
|
+
}
|
|
9219
|
+
|
|
9220
|
+
// Return result with metadata if requested
|
|
9221
|
+
if (withMetadata) {
|
|
9222
|
+
return {
|
|
9223
|
+
result,
|
|
9224
|
+
metadata: {
|
|
9225
|
+
logs: capturedLogs
|
|
9226
|
+
}
|
|
9227
|
+
};
|
|
9228
|
+
}
|
|
9229
|
+
|
|
9230
|
+
return result;
|
|
9231
|
+
}
|
|
9232
|
+
|
|
9233
|
+
hasTopLevelReturn(code) {
|
|
9234
|
+
const lines = code.split('\n');
|
|
9235
|
+
let braceDepth = 0;
|
|
9236
|
+
|
|
9237
|
+
for (const line of lines) {
|
|
9238
|
+
// Check if this line has a return statement at the start (ignoring whitespace)
|
|
9239
|
+
const trimmed = line.trim();
|
|
9240
|
+
if (trimmed.startsWith('return ') || trimmed === 'return') {
|
|
9241
|
+
// If we're at brace depth 0, it's a top-level return
|
|
9242
|
+
if (braceDepth === 0) {
|
|
9243
|
+
return true;
|
|
9244
|
+
}
|
|
9245
|
+
}
|
|
9246
|
+
|
|
9247
|
+
// Count braces on this line (after checking for return)
|
|
9248
|
+
for (const char of line) {
|
|
9249
|
+
if (char === '{') braceDepth++;
|
|
9250
|
+
if (char === '}') braceDepth--;
|
|
9251
|
+
}
|
|
9252
|
+
}
|
|
9253
|
+
|
|
9254
|
+
return false;
|
|
9255
|
+
}
|
|
9256
|
+
|
|
9257
|
+
async executeModule(code) {
|
|
9258
|
+
// Same as execute for now - modules are handled by execute()
|
|
9259
|
+
return await this.execute(code);
|
|
9260
|
+
}
|
|
9261
|
+
}
|
|
9262
|
+
|
|
9263
|
+
class InMemoryModuleResolver {
|
|
9264
|
+
constructor() {
|
|
9265
|
+
this.modules = new Map();
|
|
9266
|
+
}
|
|
9267
|
+
|
|
9268
|
+
addModule(name, code, metadata) {
|
|
9269
|
+
this.modules.set(name, { code, metadata });
|
|
9270
|
+
}
|
|
9271
|
+
|
|
9272
|
+
async resolve(modulePath, fromPath) {
|
|
9273
|
+
const module = this.modules.get(modulePath);
|
|
9274
|
+
if (!module) {
|
|
9275
|
+
return null;
|
|
9276
|
+
}
|
|
9277
|
+
return {
|
|
9278
|
+
code: typeof module === 'string' ? module : module.code,
|
|
9279
|
+
path: modulePath,
|
|
9280
|
+
metadata: module.metadata
|
|
9281
|
+
};
|
|
9282
|
+
}
|
|
9283
|
+
|
|
9284
|
+
async exists(modulePath, fromPath) {
|
|
9285
|
+
return this.modules.has(modulePath);
|
|
9286
|
+
}
|
|
9287
|
+
|
|
9288
|
+
async list(prefix) {
|
|
9289
|
+
const paths = Array.from(this.modules.keys());
|
|
9290
|
+
if (prefix) {
|
|
9291
|
+
return paths.filter(p => p.startsWith(prefix));
|
|
9292
|
+
}
|
|
9293
|
+
return paths;
|
|
9294
|
+
}
|
|
9295
|
+
}
|
|
9296
|
+
|
|
9297
|
+
// Use bundled Acorn parser for zero runtime dependencies
|
|
9298
|
+
|
|
9299
|
+
// Helper to detect if code contains module syntax or top-level await
|
|
9300
|
+
function containsModuleSyntax(code) {
|
|
9301
|
+
// Trigger module mode for:
|
|
9302
|
+
// 1. import/export statements
|
|
9303
|
+
// 2. Top-level await (await not inside a function)
|
|
9304
|
+
if (/^\s*(import|export)\s+/m.test(code)) {
|
|
9305
|
+
return true;
|
|
9306
|
+
}
|
|
9307
|
+
|
|
9308
|
+
// Check for top-level await (simple heuristic: await at start of line or after statement)
|
|
9309
|
+
// This isn't perfect but handles common cases, including await in template literals
|
|
9310
|
+
// Also matches await after operators like || or && or = or inside parentheses
|
|
9311
|
+
if (/^await\s+/m.test(code) || /[;\n{(|&=]\s*await\s+/.test(code)) {
|
|
9312
|
+
return true;
|
|
9313
|
+
}
|
|
9314
|
+
|
|
9315
|
+
return false;
|
|
9316
|
+
}
|
|
9317
|
+
|
|
9318
|
+
|
|
9319
|
+
function parse(code, options = {}) {
|
|
9320
|
+
// Determine sourceType: use 'module' ONLY if explicitly requested or if code has imports/exports
|
|
9321
|
+
// Default to 'script' for better compatibility with labeled statements
|
|
9322
|
+
let sourceType = options.sourceType || 'script';
|
|
9323
|
+
if (!options.sourceType && containsModuleSyntax(code)) {
|
|
9324
|
+
sourceType = 'module';
|
|
9325
|
+
}
|
|
9326
|
+
|
|
9327
|
+
// Parse with Acorn
|
|
9328
|
+
try {
|
|
9329
|
+
return parse$1(code, {
|
|
9330
|
+
ecmaVersion: 2022, // Support ES2022 features (including top-level await)
|
|
9331
|
+
sourceType: sourceType,
|
|
9332
|
+
locations: true, // Track source locations for better error messages
|
|
9333
|
+
allowReturnOutsideFunction: true // Allow top-level return statements
|
|
9334
|
+
});
|
|
9335
|
+
} catch (error) {
|
|
9336
|
+
// Reformat error message for consistency
|
|
9337
|
+
throw new SyntaxError(
|
|
9338
|
+
`Parse error at line ${error.loc?.line || '?'}: ${error.message}`
|
|
9339
|
+
);
|
|
9340
|
+
}
|
|
9341
|
+
}
|
|
9342
|
+
|
|
9343
|
+
// Helper to detect if AST contains import/export declarations
|
|
9344
|
+
function containsModuleDeclarations(node) {
|
|
9345
|
+
if (!node || typeof node !== 'object') return false;
|
|
9346
|
+
|
|
9347
|
+
if (node.type === 'ImportDeclaration' ||
|
|
9348
|
+
node.type === 'ExportNamedDeclaration' ||
|
|
9349
|
+
node.type === 'ExportDefaultDeclaration' ||
|
|
9350
|
+
node.type === 'ExportAllDeclaration') {
|
|
9351
|
+
return true;
|
|
9352
|
+
}
|
|
9353
|
+
|
|
9354
|
+
// Check Program body for module declarations
|
|
9355
|
+
if (node.type === 'Program' && node.body) {
|
|
9356
|
+
for (const statement of node.body) {
|
|
9357
|
+
if (containsModuleDeclarations(statement)) return true;
|
|
9358
|
+
}
|
|
9359
|
+
}
|
|
9360
|
+
|
|
9361
|
+
return false;
|
|
9362
|
+
}
|
|
9363
|
+
|
|
9364
|
+
// Helper to detect if AST contains top-level await expressions
|
|
9365
|
+
function containsTopLevelAwait(node) {
|
|
9366
|
+
if (!node || typeof node !== 'object') return false;
|
|
9367
|
+
|
|
9368
|
+
if (node.type === 'AwaitExpression') return true;
|
|
9369
|
+
|
|
9370
|
+
// Don't recurse into function bodies (they handle their own await)
|
|
9371
|
+
if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
|
|
9372
|
+
return false;
|
|
9373
|
+
}
|
|
9374
|
+
|
|
9375
|
+
// Check all properties
|
|
9376
|
+
for (const key in node) {
|
|
9377
|
+
if (key === 'loc' || key === 'range') continue;
|
|
9378
|
+
const value = node[key];
|
|
9379
|
+
if (Array.isArray(value)) {
|
|
9380
|
+
for (const item of value) {
|
|
9381
|
+
if (containsTopLevelAwait(item)) return true;
|
|
9382
|
+
}
|
|
9383
|
+
} else if (typeof value === 'object') {
|
|
9384
|
+
if (containsTopLevelAwait(value)) return true;
|
|
9385
|
+
}
|
|
9386
|
+
}
|
|
9387
|
+
|
|
9388
|
+
return false;
|
|
9389
|
+
}
|
|
9390
|
+
|
|
9391
|
+
async function execute(code, env = null, options = {}) {
|
|
9392
|
+
// Parse the code
|
|
9393
|
+
const ast = parse(code, options);
|
|
9394
|
+
|
|
9395
|
+
// Create global environment if not provided
|
|
9396
|
+
if (!env) {
|
|
9397
|
+
env = createGlobalEnvironment(new Environment());
|
|
9398
|
+
}
|
|
9399
|
+
|
|
9400
|
+
// Create a child environment for user code to allow shadowing of built-ins
|
|
9401
|
+
// This prevents conflicts when user code declares variables with same names as stdlib functions
|
|
9402
|
+
const userEnv = env.extend();
|
|
9403
|
+
|
|
9404
|
+
// Create interpreter with module resolver and abort signal if provided
|
|
9405
|
+
const interpreter = new Interpreter(env, {
|
|
9406
|
+
moduleResolver: options.moduleResolver,
|
|
9407
|
+
abortSignal: options.abortSignal
|
|
9408
|
+
});
|
|
9409
|
+
|
|
9410
|
+
// Use async evaluation if:
|
|
9411
|
+
// 1. Explicitly requested module mode
|
|
9412
|
+
// 2. AST contains import/export declarations
|
|
9413
|
+
// 3. Code contains top-level await
|
|
9414
|
+
const needsAsync = options.sourceType === 'module' ||
|
|
9415
|
+
containsModuleDeclarations(ast) ||
|
|
9416
|
+
containsTopLevelAwait(ast);
|
|
9417
|
+
|
|
9418
|
+
if (needsAsync) {
|
|
9419
|
+
const result = await interpreter.evaluateAsync(ast, userEnv);
|
|
9420
|
+
return result instanceof ReturnValue ? result.value : result;
|
|
9421
|
+
} else {
|
|
9422
|
+
const result = interpreter.evaluate(ast, userEnv);
|
|
9423
|
+
return result instanceof ReturnValue ? result.value : result;
|
|
9424
|
+
}
|
|
9425
|
+
}
|
|
9426
|
+
|
|
9427
|
+
function createEnvironment() {
|
|
9428
|
+
return createGlobalEnvironment(new Environment());
|
|
6231
9429
|
}
|
|
6232
9430
|
|
|
6233
|
-
//
|
|
6234
|
-
|
|
9431
|
+
// Export utility functions for CLI tools
|
|
9432
|
+
const isTopLevelAwait = containsModuleSyntax;
|
|
6235
9433
|
|
|
6236
|
-
|
|
6237
|
-
|
|
9434
|
+
/**
|
|
9435
|
+
* Abstract base class for module resolution
|
|
9436
|
+
* Extend this class to implement custom module loading strategies
|
|
9437
|
+
*/
|
|
9438
|
+
class ModuleResolver {
|
|
9439
|
+
/**
|
|
9440
|
+
* Resolve a module and return its code
|
|
9441
|
+
* @param {string} modulePath - The module path to resolve
|
|
9442
|
+
* @param {string} [fromPath] - The path of the importing module
|
|
9443
|
+
* @returns {Promise<ModuleResolution>} The resolved module
|
|
9444
|
+
*/
|
|
9445
|
+
async resolve(modulePath, fromPath) {
|
|
9446
|
+
throw new Error('ModuleResolver.resolve() must be implemented by subclass');
|
|
9447
|
+
}
|
|
9448
|
+
|
|
9449
|
+
/**
|
|
9450
|
+
* Check if a module exists
|
|
9451
|
+
* @param {string} modulePath - The module path to check
|
|
9452
|
+
* @param {string} [fromPath] - The path of the importing module
|
|
9453
|
+
* @returns {Promise<boolean>} Whether the module exists
|
|
9454
|
+
*/
|
|
9455
|
+
async exists(modulePath, fromPath) {
|
|
9456
|
+
throw new Error('ModuleResolver.exists() must be implemented by subclass');
|
|
9457
|
+
}
|
|
9458
|
+
|
|
9459
|
+
/**
|
|
9460
|
+
* List available modules
|
|
9461
|
+
* @param {string} [prefix] - Optional prefix to filter modules
|
|
9462
|
+
* @returns {Promise<string[]>} List of module paths
|
|
9463
|
+
*/
|
|
9464
|
+
async list(prefix) {
|
|
9465
|
+
throw new Error('ModuleResolver.list() must be implemented by subclass');
|
|
9466
|
+
}
|
|
6238
9467
|
}
|
|
6239
9468
|
|
|
6240
|
-
|
|
9469
|
+
/**
|
|
9470
|
+
* @typedef {Object} ModuleResolution
|
|
9471
|
+
* @property {string} code - The module source code
|
|
9472
|
+
* @property {string} path - The resolved module path
|
|
9473
|
+
* @property {any} [metadata] - Optional metadata about the module
|
|
9474
|
+
*/
|
|
6241
9475
|
|
|
9476
|
+
export { Environment, InMemoryModuleResolver, Interpreter, ModuleResolver, WangInterpreter, createEnvironment, execute, isTopLevelAwait, parse };
|