jslike 1.6.3 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/interpreter/interpreter.js +128 -4
- package/dist/index.cjs +91 -3
- package/dist/index.d.cts +128 -4
- package/dist/index.d.ts +128 -4
- package/dist/index.js +91 -3
- package/package.json +1 -1
|
@@ -219,6 +219,56 @@ export class Interpreter {
|
|
|
219
219
|
return result;
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
if (node.type === 'TaggedTemplateExpression') {
|
|
223
|
+
// 1. Evaluate tag function async (may contain awaits)
|
|
224
|
+
let thisContext = undefined;
|
|
225
|
+
let tagFunction;
|
|
226
|
+
|
|
227
|
+
if (node.tag.type === 'MemberExpression') {
|
|
228
|
+
thisContext = await this.evaluateAsync(node.tag.object, env);
|
|
229
|
+
const prop = node.tag.computed
|
|
230
|
+
? await this.evaluateAsync(node.tag.property, env)
|
|
231
|
+
: node.tag.property.name;
|
|
232
|
+
tagFunction = thisContext[prop];
|
|
233
|
+
} else {
|
|
234
|
+
tagFunction = await this.evaluateAsync(node.tag, env);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 2. Build strings array (synchronous - no awaits in quasis)
|
|
238
|
+
const strings = [];
|
|
239
|
+
const rawStrings = [];
|
|
240
|
+
for (const quasi of node.quasi.quasis) {
|
|
241
|
+
strings.push(quasi.value.cooked || quasi.value.raw);
|
|
242
|
+
rawStrings.push(quasi.value.raw);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
Object.defineProperty(strings, 'raw', {
|
|
246
|
+
value: Object.freeze(rawStrings),
|
|
247
|
+
writable: false,
|
|
248
|
+
enumerable: false,
|
|
249
|
+
configurable: false
|
|
250
|
+
});
|
|
251
|
+
Object.freeze(strings);
|
|
252
|
+
|
|
253
|
+
// 3. Evaluate expressions async (may contain awaits)
|
|
254
|
+
const values = [];
|
|
255
|
+
for (const expr of node.quasi.expressions) {
|
|
256
|
+
values.push(await this.evaluateAsync(expr, env));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 4. Call tag function (may be async)
|
|
260
|
+
if (typeof tagFunction === 'function') {
|
|
261
|
+
if (thisContext !== undefined) {
|
|
262
|
+
return await tagFunction.call(thisContext, strings, ...values);
|
|
263
|
+
}
|
|
264
|
+
return await tagFunction(strings, ...values);
|
|
265
|
+
} else if (tagFunction && tagFunction.__isFunction) {
|
|
266
|
+
return await this.callUserFunction(tagFunction, [strings, ...values], env, thisContext);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
throw new TypeError('Tag must be a function');
|
|
270
|
+
}
|
|
271
|
+
|
|
222
272
|
// For logical expressions with async operands (await support)
|
|
223
273
|
if (node.type === 'LogicalExpression') {
|
|
224
274
|
const left = await this.evaluateAsync(node.left, env);
|
|
@@ -822,6 +872,9 @@ export class Interpreter {
|
|
|
822
872
|
case 'TemplateLiteral':
|
|
823
873
|
return this.evaluateTemplateLiteral(node, env);
|
|
824
874
|
|
|
875
|
+
case 'TaggedTemplateExpression':
|
|
876
|
+
return this.evaluateTaggedTemplateExpression(node, env);
|
|
877
|
+
|
|
825
878
|
case 'ClassDeclaration':
|
|
826
879
|
return this.evaluateClassDeclaration(node, env);
|
|
827
880
|
|
|
@@ -1255,8 +1308,14 @@ export class Interpreter {
|
|
|
1255
1308
|
// Get function name for call stack tracking
|
|
1256
1309
|
const funcName = metadata.name || func.name || 'anonymous';
|
|
1257
1310
|
|
|
1258
|
-
// Bind 'this'
|
|
1259
|
-
if (
|
|
1311
|
+
// Bind 'this': for arrow functions use captured this (lexical), for regular functions use call-site this
|
|
1312
|
+
if (metadata.isArrow) {
|
|
1313
|
+
// Arrow functions use lexically captured 'this', ignoring call-site 'this'
|
|
1314
|
+
if (metadata.capturedThis !== undefined) {
|
|
1315
|
+
funcEnv.define('this', metadata.capturedThis);
|
|
1316
|
+
}
|
|
1317
|
+
} else if (thisContext !== undefined) {
|
|
1318
|
+
// Regular functions use 'this' from the call site
|
|
1260
1319
|
funcEnv.define('this', thisContext);
|
|
1261
1320
|
}
|
|
1262
1321
|
|
|
@@ -1437,13 +1496,28 @@ export class Interpreter {
|
|
|
1437
1496
|
}
|
|
1438
1497
|
|
|
1439
1498
|
evaluateFunctionExpression(node, env) {
|
|
1499
|
+
const isArrow = node.type === 'ArrowFunctionExpression';
|
|
1500
|
+
|
|
1501
|
+
// For arrow functions, capture 'this' lexically from the enclosing scope
|
|
1502
|
+
let capturedThis;
|
|
1503
|
+
if (isArrow) {
|
|
1504
|
+
try {
|
|
1505
|
+
capturedThis = env.get('this');
|
|
1506
|
+
} catch (e) {
|
|
1507
|
+
// 'this' not defined in enclosing scope, leave undefined
|
|
1508
|
+
capturedThis = undefined;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1440
1512
|
const funcMetadata = {
|
|
1441
1513
|
__isFunction: true,
|
|
1442
1514
|
params: node.params,
|
|
1443
1515
|
body: node.body,
|
|
1444
1516
|
closure: env,
|
|
1445
|
-
expression:
|
|
1446
|
-
async: node.async || false
|
|
1517
|
+
expression: isArrow && node.expression,
|
|
1518
|
+
async: node.async || false,
|
|
1519
|
+
isArrow: isArrow,
|
|
1520
|
+
capturedThis: isArrow ? capturedThis : undefined
|
|
1447
1521
|
};
|
|
1448
1522
|
|
|
1449
1523
|
// Wrap in actual JavaScript function so it can be called by native code
|
|
@@ -2066,6 +2140,56 @@ export class Interpreter {
|
|
|
2066
2140
|
return result;
|
|
2067
2141
|
}
|
|
2068
2142
|
|
|
2143
|
+
evaluateTaggedTemplateExpression(node, env) {
|
|
2144
|
+
// 1. Evaluate the tag function, preserving 'this' context for member expressions
|
|
2145
|
+
let thisContext = undefined;
|
|
2146
|
+
let tagFunction;
|
|
2147
|
+
|
|
2148
|
+
if (node.tag.type === 'MemberExpression') {
|
|
2149
|
+
// For method calls like obj.tag`...`, set this to obj
|
|
2150
|
+
thisContext = this.evaluate(node.tag.object, env);
|
|
2151
|
+
const prop = node.tag.computed
|
|
2152
|
+
? this.evaluate(node.tag.property, env)
|
|
2153
|
+
: node.tag.property.name;
|
|
2154
|
+
tagFunction = thisContext[prop];
|
|
2155
|
+
} else {
|
|
2156
|
+
tagFunction = this.evaluate(node.tag, env);
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
// 2. Build the strings array from quasis (cooked values)
|
|
2160
|
+
const strings = [];
|
|
2161
|
+
const rawStrings = [];
|
|
2162
|
+
for (const quasi of node.quasi.quasis) {
|
|
2163
|
+
strings.push(quasi.value.cooked || quasi.value.raw);
|
|
2164
|
+
rawStrings.push(quasi.value.raw);
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
// 3. Add the raw property (frozen per ES6 spec)
|
|
2168
|
+
Object.defineProperty(strings, 'raw', {
|
|
2169
|
+
value: Object.freeze(rawStrings),
|
|
2170
|
+
writable: false,
|
|
2171
|
+
enumerable: false,
|
|
2172
|
+
configurable: false
|
|
2173
|
+
});
|
|
2174
|
+
Object.freeze(strings);
|
|
2175
|
+
|
|
2176
|
+
// 4. Evaluate the embedded expressions
|
|
2177
|
+
const values = node.quasi.expressions.map(expr => this.evaluate(expr, env));
|
|
2178
|
+
|
|
2179
|
+
// 5. Call the tag function with proper this context
|
|
2180
|
+
if (typeof tagFunction === 'function') {
|
|
2181
|
+
if (thisContext !== undefined) {
|
|
2182
|
+
return tagFunction.call(thisContext, strings, ...values);
|
|
2183
|
+
}
|
|
2184
|
+
return tagFunction(strings, ...values);
|
|
2185
|
+
} else if (tagFunction && tagFunction.__isFunction) {
|
|
2186
|
+
// User-defined function
|
|
2187
|
+
return this.callUserFunction(tagFunction, [strings, ...values], env, thisContext);
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
throw new TypeError('Tag must be a function');
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2069
2193
|
evaluateClassDeclaration(node, env) {
|
|
2070
2194
|
const className = node.id.name;
|
|
2071
2195
|
const classFunc = this.createClass(node, env);
|
package/dist/index.cjs
CHANGED
|
@@ -6632,6 +6632,43 @@ var Interpreter = class _Interpreter {
|
|
|
6632
6632
|
}
|
|
6633
6633
|
return result;
|
|
6634
6634
|
}
|
|
6635
|
+
if (node.type === "TaggedTemplateExpression") {
|
|
6636
|
+
let thisContext = void 0;
|
|
6637
|
+
let tagFunction;
|
|
6638
|
+
if (node.tag.type === "MemberExpression") {
|
|
6639
|
+
thisContext = await this.evaluateAsync(node.tag.object, env);
|
|
6640
|
+
const prop = node.tag.computed ? await this.evaluateAsync(node.tag.property, env) : node.tag.property.name;
|
|
6641
|
+
tagFunction = thisContext[prop];
|
|
6642
|
+
} else {
|
|
6643
|
+
tagFunction = await this.evaluateAsync(node.tag, env);
|
|
6644
|
+
}
|
|
6645
|
+
const strings = [];
|
|
6646
|
+
const rawStrings = [];
|
|
6647
|
+
for (const quasi of node.quasi.quasis) {
|
|
6648
|
+
strings.push(quasi.value.cooked || quasi.value.raw);
|
|
6649
|
+
rawStrings.push(quasi.value.raw);
|
|
6650
|
+
}
|
|
6651
|
+
Object.defineProperty(strings, "raw", {
|
|
6652
|
+
value: Object.freeze(rawStrings),
|
|
6653
|
+
writable: false,
|
|
6654
|
+
enumerable: false,
|
|
6655
|
+
configurable: false
|
|
6656
|
+
});
|
|
6657
|
+
Object.freeze(strings);
|
|
6658
|
+
const values = [];
|
|
6659
|
+
for (const expr of node.quasi.expressions) {
|
|
6660
|
+
values.push(await this.evaluateAsync(expr, env));
|
|
6661
|
+
}
|
|
6662
|
+
if (typeof tagFunction === "function") {
|
|
6663
|
+
if (thisContext !== void 0) {
|
|
6664
|
+
return await tagFunction.call(thisContext, strings, ...values);
|
|
6665
|
+
}
|
|
6666
|
+
return await tagFunction(strings, ...values);
|
|
6667
|
+
} else if (tagFunction && tagFunction.__isFunction) {
|
|
6668
|
+
return await this.callUserFunction(tagFunction, [strings, ...values], env, thisContext);
|
|
6669
|
+
}
|
|
6670
|
+
throw new TypeError("Tag must be a function");
|
|
6671
|
+
}
|
|
6635
6672
|
if (node.type === "LogicalExpression") {
|
|
6636
6673
|
const left = await this.evaluateAsync(node.left, env);
|
|
6637
6674
|
if (node.operator === "&&") {
|
|
@@ -7115,6 +7152,8 @@ var Interpreter = class _Interpreter {
|
|
|
7115
7152
|
// ES6+ Features
|
|
7116
7153
|
case "TemplateLiteral":
|
|
7117
7154
|
return this.evaluateTemplateLiteral(node, env);
|
|
7155
|
+
case "TaggedTemplateExpression":
|
|
7156
|
+
return this.evaluateTaggedTemplateExpression(node, env);
|
|
7118
7157
|
case "ClassDeclaration":
|
|
7119
7158
|
return this.evaluateClassDeclaration(node, env);
|
|
7120
7159
|
case "ClassExpression":
|
|
@@ -7465,7 +7504,11 @@ var Interpreter = class _Interpreter {
|
|
|
7465
7504
|
const metadata = func.__metadata || func;
|
|
7466
7505
|
const funcEnv = new Environment(metadata.closure);
|
|
7467
7506
|
const funcName = metadata.name || func.name || "anonymous";
|
|
7468
|
-
if (
|
|
7507
|
+
if (metadata.isArrow) {
|
|
7508
|
+
if (metadata.capturedThis !== void 0) {
|
|
7509
|
+
funcEnv.define("this", metadata.capturedThis);
|
|
7510
|
+
}
|
|
7511
|
+
} else if (thisContext !== void 0) {
|
|
7469
7512
|
funcEnv.define("this", thisContext);
|
|
7470
7513
|
}
|
|
7471
7514
|
for (let i = 0; i < metadata.params.length; i++) {
|
|
@@ -7600,13 +7643,24 @@ var Interpreter = class _Interpreter {
|
|
|
7600
7643
|
return obj;
|
|
7601
7644
|
}
|
|
7602
7645
|
evaluateFunctionExpression(node, env) {
|
|
7646
|
+
const isArrow = node.type === "ArrowFunctionExpression";
|
|
7647
|
+
let capturedThis;
|
|
7648
|
+
if (isArrow) {
|
|
7649
|
+
try {
|
|
7650
|
+
capturedThis = env.get("this");
|
|
7651
|
+
} catch (e) {
|
|
7652
|
+
capturedThis = void 0;
|
|
7653
|
+
}
|
|
7654
|
+
}
|
|
7603
7655
|
const funcMetadata = {
|
|
7604
7656
|
__isFunction: true,
|
|
7605
7657
|
params: node.params,
|
|
7606
7658
|
body: node.body,
|
|
7607
7659
|
closure: env,
|
|
7608
|
-
expression:
|
|
7609
|
-
async: node.async || false
|
|
7660
|
+
expression: isArrow && node.expression,
|
|
7661
|
+
async: node.async || false,
|
|
7662
|
+
isArrow,
|
|
7663
|
+
capturedThis: isArrow ? capturedThis : void 0
|
|
7610
7664
|
};
|
|
7611
7665
|
const interpreter = this;
|
|
7612
7666
|
const wrappedFunc = funcMetadata.async ? async function(...args) {
|
|
@@ -8058,6 +8112,40 @@ var Interpreter = class _Interpreter {
|
|
|
8058
8112
|
}
|
|
8059
8113
|
return result;
|
|
8060
8114
|
}
|
|
8115
|
+
evaluateTaggedTemplateExpression(node, env) {
|
|
8116
|
+
let thisContext = void 0;
|
|
8117
|
+
let tagFunction;
|
|
8118
|
+
if (node.tag.type === "MemberExpression") {
|
|
8119
|
+
thisContext = this.evaluate(node.tag.object, env);
|
|
8120
|
+
const prop = node.tag.computed ? this.evaluate(node.tag.property, env) : node.tag.property.name;
|
|
8121
|
+
tagFunction = thisContext[prop];
|
|
8122
|
+
} else {
|
|
8123
|
+
tagFunction = this.evaluate(node.tag, env);
|
|
8124
|
+
}
|
|
8125
|
+
const strings = [];
|
|
8126
|
+
const rawStrings = [];
|
|
8127
|
+
for (const quasi of node.quasi.quasis) {
|
|
8128
|
+
strings.push(quasi.value.cooked || quasi.value.raw);
|
|
8129
|
+
rawStrings.push(quasi.value.raw);
|
|
8130
|
+
}
|
|
8131
|
+
Object.defineProperty(strings, "raw", {
|
|
8132
|
+
value: Object.freeze(rawStrings),
|
|
8133
|
+
writable: false,
|
|
8134
|
+
enumerable: false,
|
|
8135
|
+
configurable: false
|
|
8136
|
+
});
|
|
8137
|
+
Object.freeze(strings);
|
|
8138
|
+
const values = node.quasi.expressions.map((expr) => this.evaluate(expr, env));
|
|
8139
|
+
if (typeof tagFunction === "function") {
|
|
8140
|
+
if (thisContext !== void 0) {
|
|
8141
|
+
return tagFunction.call(thisContext, strings, ...values);
|
|
8142
|
+
}
|
|
8143
|
+
return tagFunction(strings, ...values);
|
|
8144
|
+
} else if (tagFunction && tagFunction.__isFunction) {
|
|
8145
|
+
return this.callUserFunction(tagFunction, [strings, ...values], env, thisContext);
|
|
8146
|
+
}
|
|
8147
|
+
throw new TypeError("Tag must be a function");
|
|
8148
|
+
}
|
|
8061
8149
|
evaluateClassDeclaration(node, env) {
|
|
8062
8150
|
const className = node.id.name;
|
|
8063
8151
|
const classFunc = this.createClass(node, env);
|
package/dist/index.d.cts
CHANGED
|
@@ -7384,6 +7384,56 @@ class Interpreter {
|
|
|
7384
7384
|
return result;
|
|
7385
7385
|
}
|
|
7386
7386
|
|
|
7387
|
+
if (node.type === 'TaggedTemplateExpression') {
|
|
7388
|
+
// 1. Evaluate tag function async (may contain awaits)
|
|
7389
|
+
let thisContext = undefined;
|
|
7390
|
+
let tagFunction;
|
|
7391
|
+
|
|
7392
|
+
if (node.tag.type === 'MemberExpression') {
|
|
7393
|
+
thisContext = await this.evaluateAsync(node.tag.object, env);
|
|
7394
|
+
const prop = node.tag.computed
|
|
7395
|
+
? await this.evaluateAsync(node.tag.property, env)
|
|
7396
|
+
: node.tag.property.name;
|
|
7397
|
+
tagFunction = thisContext[prop];
|
|
7398
|
+
} else {
|
|
7399
|
+
tagFunction = await this.evaluateAsync(node.tag, env);
|
|
7400
|
+
}
|
|
7401
|
+
|
|
7402
|
+
// 2. Build strings array (synchronous - no awaits in quasis)
|
|
7403
|
+
const strings = [];
|
|
7404
|
+
const rawStrings = [];
|
|
7405
|
+
for (const quasi of node.quasi.quasis) {
|
|
7406
|
+
strings.push(quasi.value.cooked || quasi.value.raw);
|
|
7407
|
+
rawStrings.push(quasi.value.raw);
|
|
7408
|
+
}
|
|
7409
|
+
|
|
7410
|
+
Object.defineProperty(strings, 'raw', {
|
|
7411
|
+
value: Object.freeze(rawStrings),
|
|
7412
|
+
writable: false,
|
|
7413
|
+
enumerable: false,
|
|
7414
|
+
configurable: false
|
|
7415
|
+
});
|
|
7416
|
+
Object.freeze(strings);
|
|
7417
|
+
|
|
7418
|
+
// 3. Evaluate expressions async (may contain awaits)
|
|
7419
|
+
const values = [];
|
|
7420
|
+
for (const expr of node.quasi.expressions) {
|
|
7421
|
+
values.push(await this.evaluateAsync(expr, env));
|
|
7422
|
+
}
|
|
7423
|
+
|
|
7424
|
+
// 4. Call tag function (may be async)
|
|
7425
|
+
if (typeof tagFunction === 'function') {
|
|
7426
|
+
if (thisContext !== undefined) {
|
|
7427
|
+
return await tagFunction.call(thisContext, strings, ...values);
|
|
7428
|
+
}
|
|
7429
|
+
return await tagFunction(strings, ...values);
|
|
7430
|
+
} else if (tagFunction && tagFunction.__isFunction) {
|
|
7431
|
+
return await this.callUserFunction(tagFunction, [strings, ...values], env, thisContext);
|
|
7432
|
+
}
|
|
7433
|
+
|
|
7434
|
+
throw new TypeError('Tag must be a function');
|
|
7435
|
+
}
|
|
7436
|
+
|
|
7387
7437
|
// For logical expressions with async operands (await support)
|
|
7388
7438
|
if (node.type === 'LogicalExpression') {
|
|
7389
7439
|
const left = await this.evaluateAsync(node.left, env);
|
|
@@ -7987,6 +8037,9 @@ class Interpreter {
|
|
|
7987
8037
|
case 'TemplateLiteral':
|
|
7988
8038
|
return this.evaluateTemplateLiteral(node, env);
|
|
7989
8039
|
|
|
8040
|
+
case 'TaggedTemplateExpression':
|
|
8041
|
+
return this.evaluateTaggedTemplateExpression(node, env);
|
|
8042
|
+
|
|
7990
8043
|
case 'ClassDeclaration':
|
|
7991
8044
|
return this.evaluateClassDeclaration(node, env);
|
|
7992
8045
|
|
|
@@ -8420,8 +8473,14 @@ class Interpreter {
|
|
|
8420
8473
|
// Get function name for call stack tracking
|
|
8421
8474
|
const funcName = metadata.name || func.name || 'anonymous';
|
|
8422
8475
|
|
|
8423
|
-
// Bind 'this'
|
|
8424
|
-
if (
|
|
8476
|
+
// Bind 'this': for arrow functions use captured this (lexical), for regular functions use call-site this
|
|
8477
|
+
if (metadata.isArrow) {
|
|
8478
|
+
// Arrow functions use lexically captured 'this', ignoring call-site 'this'
|
|
8479
|
+
if (metadata.capturedThis !== undefined) {
|
|
8480
|
+
funcEnv.define('this', metadata.capturedThis);
|
|
8481
|
+
}
|
|
8482
|
+
} else if (thisContext !== undefined) {
|
|
8483
|
+
// Regular functions use 'this' from the call site
|
|
8425
8484
|
funcEnv.define('this', thisContext);
|
|
8426
8485
|
}
|
|
8427
8486
|
|
|
@@ -8602,13 +8661,28 @@ class Interpreter {
|
|
|
8602
8661
|
}
|
|
8603
8662
|
|
|
8604
8663
|
evaluateFunctionExpression(node, env) {
|
|
8664
|
+
const isArrow = node.type === 'ArrowFunctionExpression';
|
|
8665
|
+
|
|
8666
|
+
// For arrow functions, capture 'this' lexically from the enclosing scope
|
|
8667
|
+
let capturedThis;
|
|
8668
|
+
if (isArrow) {
|
|
8669
|
+
try {
|
|
8670
|
+
capturedThis = env.get('this');
|
|
8671
|
+
} catch (e) {
|
|
8672
|
+
// 'this' not defined in enclosing scope, leave undefined
|
|
8673
|
+
capturedThis = undefined;
|
|
8674
|
+
}
|
|
8675
|
+
}
|
|
8676
|
+
|
|
8605
8677
|
const funcMetadata = {
|
|
8606
8678
|
__isFunction: true,
|
|
8607
8679
|
params: node.params,
|
|
8608
8680
|
body: node.body,
|
|
8609
8681
|
closure: env,
|
|
8610
|
-
expression:
|
|
8611
|
-
async: node.async || false
|
|
8682
|
+
expression: isArrow && node.expression,
|
|
8683
|
+
async: node.async || false,
|
|
8684
|
+
isArrow: isArrow,
|
|
8685
|
+
capturedThis: isArrow ? capturedThis : undefined
|
|
8612
8686
|
};
|
|
8613
8687
|
|
|
8614
8688
|
// Wrap in actual JavaScript function so it can be called by native code
|
|
@@ -9231,6 +9305,56 @@ class Interpreter {
|
|
|
9231
9305
|
return result;
|
|
9232
9306
|
}
|
|
9233
9307
|
|
|
9308
|
+
evaluateTaggedTemplateExpression(node, env) {
|
|
9309
|
+
// 1. Evaluate the tag function, preserving 'this' context for member expressions
|
|
9310
|
+
let thisContext = undefined;
|
|
9311
|
+
let tagFunction;
|
|
9312
|
+
|
|
9313
|
+
if (node.tag.type === 'MemberExpression') {
|
|
9314
|
+
// For method calls like obj.tag`...`, set this to obj
|
|
9315
|
+
thisContext = this.evaluate(node.tag.object, env);
|
|
9316
|
+
const prop = node.tag.computed
|
|
9317
|
+
? this.evaluate(node.tag.property, env)
|
|
9318
|
+
: node.tag.property.name;
|
|
9319
|
+
tagFunction = thisContext[prop];
|
|
9320
|
+
} else {
|
|
9321
|
+
tagFunction = this.evaluate(node.tag, env);
|
|
9322
|
+
}
|
|
9323
|
+
|
|
9324
|
+
// 2. Build the strings array from quasis (cooked values)
|
|
9325
|
+
const strings = [];
|
|
9326
|
+
const rawStrings = [];
|
|
9327
|
+
for (const quasi of node.quasi.quasis) {
|
|
9328
|
+
strings.push(quasi.value.cooked || quasi.value.raw);
|
|
9329
|
+
rawStrings.push(quasi.value.raw);
|
|
9330
|
+
}
|
|
9331
|
+
|
|
9332
|
+
// 3. Add the raw property (frozen per ES6 spec)
|
|
9333
|
+
Object.defineProperty(strings, 'raw', {
|
|
9334
|
+
value: Object.freeze(rawStrings),
|
|
9335
|
+
writable: false,
|
|
9336
|
+
enumerable: false,
|
|
9337
|
+
configurable: false
|
|
9338
|
+
});
|
|
9339
|
+
Object.freeze(strings);
|
|
9340
|
+
|
|
9341
|
+
// 4. Evaluate the embedded expressions
|
|
9342
|
+
const values = node.quasi.expressions.map(expr => this.evaluate(expr, env));
|
|
9343
|
+
|
|
9344
|
+
// 5. Call the tag function with proper this context
|
|
9345
|
+
if (typeof tagFunction === 'function') {
|
|
9346
|
+
if (thisContext !== undefined) {
|
|
9347
|
+
return tagFunction.call(thisContext, strings, ...values);
|
|
9348
|
+
}
|
|
9349
|
+
return tagFunction(strings, ...values);
|
|
9350
|
+
} else if (tagFunction && tagFunction.__isFunction) {
|
|
9351
|
+
// User-defined function
|
|
9352
|
+
return this.callUserFunction(tagFunction, [strings, ...values], env, thisContext);
|
|
9353
|
+
}
|
|
9354
|
+
|
|
9355
|
+
throw new TypeError('Tag must be a function');
|
|
9356
|
+
}
|
|
9357
|
+
|
|
9234
9358
|
evaluateClassDeclaration(node, env) {
|
|
9235
9359
|
const className = node.id.name;
|
|
9236
9360
|
const classFunc = this.createClass(node, env);
|
package/dist/index.d.ts
CHANGED
|
@@ -7384,6 +7384,56 @@ class Interpreter {
|
|
|
7384
7384
|
return result;
|
|
7385
7385
|
}
|
|
7386
7386
|
|
|
7387
|
+
if (node.type === 'TaggedTemplateExpression') {
|
|
7388
|
+
// 1. Evaluate tag function async (may contain awaits)
|
|
7389
|
+
let thisContext = undefined;
|
|
7390
|
+
let tagFunction;
|
|
7391
|
+
|
|
7392
|
+
if (node.tag.type === 'MemberExpression') {
|
|
7393
|
+
thisContext = await this.evaluateAsync(node.tag.object, env);
|
|
7394
|
+
const prop = node.tag.computed
|
|
7395
|
+
? await this.evaluateAsync(node.tag.property, env)
|
|
7396
|
+
: node.tag.property.name;
|
|
7397
|
+
tagFunction = thisContext[prop];
|
|
7398
|
+
} else {
|
|
7399
|
+
tagFunction = await this.evaluateAsync(node.tag, env);
|
|
7400
|
+
}
|
|
7401
|
+
|
|
7402
|
+
// 2. Build strings array (synchronous - no awaits in quasis)
|
|
7403
|
+
const strings = [];
|
|
7404
|
+
const rawStrings = [];
|
|
7405
|
+
for (const quasi of node.quasi.quasis) {
|
|
7406
|
+
strings.push(quasi.value.cooked || quasi.value.raw);
|
|
7407
|
+
rawStrings.push(quasi.value.raw);
|
|
7408
|
+
}
|
|
7409
|
+
|
|
7410
|
+
Object.defineProperty(strings, 'raw', {
|
|
7411
|
+
value: Object.freeze(rawStrings),
|
|
7412
|
+
writable: false,
|
|
7413
|
+
enumerable: false,
|
|
7414
|
+
configurable: false
|
|
7415
|
+
});
|
|
7416
|
+
Object.freeze(strings);
|
|
7417
|
+
|
|
7418
|
+
// 3. Evaluate expressions async (may contain awaits)
|
|
7419
|
+
const values = [];
|
|
7420
|
+
for (const expr of node.quasi.expressions) {
|
|
7421
|
+
values.push(await this.evaluateAsync(expr, env));
|
|
7422
|
+
}
|
|
7423
|
+
|
|
7424
|
+
// 4. Call tag function (may be async)
|
|
7425
|
+
if (typeof tagFunction === 'function') {
|
|
7426
|
+
if (thisContext !== undefined) {
|
|
7427
|
+
return await tagFunction.call(thisContext, strings, ...values);
|
|
7428
|
+
}
|
|
7429
|
+
return await tagFunction(strings, ...values);
|
|
7430
|
+
} else if (tagFunction && tagFunction.__isFunction) {
|
|
7431
|
+
return await this.callUserFunction(tagFunction, [strings, ...values], env, thisContext);
|
|
7432
|
+
}
|
|
7433
|
+
|
|
7434
|
+
throw new TypeError('Tag must be a function');
|
|
7435
|
+
}
|
|
7436
|
+
|
|
7387
7437
|
// For logical expressions with async operands (await support)
|
|
7388
7438
|
if (node.type === 'LogicalExpression') {
|
|
7389
7439
|
const left = await this.evaluateAsync(node.left, env);
|
|
@@ -7987,6 +8037,9 @@ class Interpreter {
|
|
|
7987
8037
|
case 'TemplateLiteral':
|
|
7988
8038
|
return this.evaluateTemplateLiteral(node, env);
|
|
7989
8039
|
|
|
8040
|
+
case 'TaggedTemplateExpression':
|
|
8041
|
+
return this.evaluateTaggedTemplateExpression(node, env);
|
|
8042
|
+
|
|
7990
8043
|
case 'ClassDeclaration':
|
|
7991
8044
|
return this.evaluateClassDeclaration(node, env);
|
|
7992
8045
|
|
|
@@ -8420,8 +8473,14 @@ class Interpreter {
|
|
|
8420
8473
|
// Get function name for call stack tracking
|
|
8421
8474
|
const funcName = metadata.name || func.name || 'anonymous';
|
|
8422
8475
|
|
|
8423
|
-
// Bind 'this'
|
|
8424
|
-
if (
|
|
8476
|
+
// Bind 'this': for arrow functions use captured this (lexical), for regular functions use call-site this
|
|
8477
|
+
if (metadata.isArrow) {
|
|
8478
|
+
// Arrow functions use lexically captured 'this', ignoring call-site 'this'
|
|
8479
|
+
if (metadata.capturedThis !== undefined) {
|
|
8480
|
+
funcEnv.define('this', metadata.capturedThis);
|
|
8481
|
+
}
|
|
8482
|
+
} else if (thisContext !== undefined) {
|
|
8483
|
+
// Regular functions use 'this' from the call site
|
|
8425
8484
|
funcEnv.define('this', thisContext);
|
|
8426
8485
|
}
|
|
8427
8486
|
|
|
@@ -8602,13 +8661,28 @@ class Interpreter {
|
|
|
8602
8661
|
}
|
|
8603
8662
|
|
|
8604
8663
|
evaluateFunctionExpression(node, env) {
|
|
8664
|
+
const isArrow = node.type === 'ArrowFunctionExpression';
|
|
8665
|
+
|
|
8666
|
+
// For arrow functions, capture 'this' lexically from the enclosing scope
|
|
8667
|
+
let capturedThis;
|
|
8668
|
+
if (isArrow) {
|
|
8669
|
+
try {
|
|
8670
|
+
capturedThis = env.get('this');
|
|
8671
|
+
} catch (e) {
|
|
8672
|
+
// 'this' not defined in enclosing scope, leave undefined
|
|
8673
|
+
capturedThis = undefined;
|
|
8674
|
+
}
|
|
8675
|
+
}
|
|
8676
|
+
|
|
8605
8677
|
const funcMetadata = {
|
|
8606
8678
|
__isFunction: true,
|
|
8607
8679
|
params: node.params,
|
|
8608
8680
|
body: node.body,
|
|
8609
8681
|
closure: env,
|
|
8610
|
-
expression:
|
|
8611
|
-
async: node.async || false
|
|
8682
|
+
expression: isArrow && node.expression,
|
|
8683
|
+
async: node.async || false,
|
|
8684
|
+
isArrow: isArrow,
|
|
8685
|
+
capturedThis: isArrow ? capturedThis : undefined
|
|
8612
8686
|
};
|
|
8613
8687
|
|
|
8614
8688
|
// Wrap in actual JavaScript function so it can be called by native code
|
|
@@ -9231,6 +9305,56 @@ class Interpreter {
|
|
|
9231
9305
|
return result;
|
|
9232
9306
|
}
|
|
9233
9307
|
|
|
9308
|
+
evaluateTaggedTemplateExpression(node, env) {
|
|
9309
|
+
// 1. Evaluate the tag function, preserving 'this' context for member expressions
|
|
9310
|
+
let thisContext = undefined;
|
|
9311
|
+
let tagFunction;
|
|
9312
|
+
|
|
9313
|
+
if (node.tag.type === 'MemberExpression') {
|
|
9314
|
+
// For method calls like obj.tag`...`, set this to obj
|
|
9315
|
+
thisContext = this.evaluate(node.tag.object, env);
|
|
9316
|
+
const prop = node.tag.computed
|
|
9317
|
+
? this.evaluate(node.tag.property, env)
|
|
9318
|
+
: node.tag.property.name;
|
|
9319
|
+
tagFunction = thisContext[prop];
|
|
9320
|
+
} else {
|
|
9321
|
+
tagFunction = this.evaluate(node.tag, env);
|
|
9322
|
+
}
|
|
9323
|
+
|
|
9324
|
+
// 2. Build the strings array from quasis (cooked values)
|
|
9325
|
+
const strings = [];
|
|
9326
|
+
const rawStrings = [];
|
|
9327
|
+
for (const quasi of node.quasi.quasis) {
|
|
9328
|
+
strings.push(quasi.value.cooked || quasi.value.raw);
|
|
9329
|
+
rawStrings.push(quasi.value.raw);
|
|
9330
|
+
}
|
|
9331
|
+
|
|
9332
|
+
// 3. Add the raw property (frozen per ES6 spec)
|
|
9333
|
+
Object.defineProperty(strings, 'raw', {
|
|
9334
|
+
value: Object.freeze(rawStrings),
|
|
9335
|
+
writable: false,
|
|
9336
|
+
enumerable: false,
|
|
9337
|
+
configurable: false
|
|
9338
|
+
});
|
|
9339
|
+
Object.freeze(strings);
|
|
9340
|
+
|
|
9341
|
+
// 4. Evaluate the embedded expressions
|
|
9342
|
+
const values = node.quasi.expressions.map(expr => this.evaluate(expr, env));
|
|
9343
|
+
|
|
9344
|
+
// 5. Call the tag function with proper this context
|
|
9345
|
+
if (typeof tagFunction === 'function') {
|
|
9346
|
+
if (thisContext !== undefined) {
|
|
9347
|
+
return tagFunction.call(thisContext, strings, ...values);
|
|
9348
|
+
}
|
|
9349
|
+
return tagFunction(strings, ...values);
|
|
9350
|
+
} else if (tagFunction && tagFunction.__isFunction) {
|
|
9351
|
+
// User-defined function
|
|
9352
|
+
return this.callUserFunction(tagFunction, [strings, ...values], env, thisContext);
|
|
9353
|
+
}
|
|
9354
|
+
|
|
9355
|
+
throw new TypeError('Tag must be a function');
|
|
9356
|
+
}
|
|
9357
|
+
|
|
9234
9358
|
evaluateClassDeclaration(node, env) {
|
|
9235
9359
|
const className = node.id.name;
|
|
9236
9360
|
const classFunc = this.createClass(node, env);
|
package/dist/index.js
CHANGED
|
@@ -6598,6 +6598,43 @@ var Interpreter = class _Interpreter {
|
|
|
6598
6598
|
}
|
|
6599
6599
|
return result;
|
|
6600
6600
|
}
|
|
6601
|
+
if (node.type === "TaggedTemplateExpression") {
|
|
6602
|
+
let thisContext = void 0;
|
|
6603
|
+
let tagFunction;
|
|
6604
|
+
if (node.tag.type === "MemberExpression") {
|
|
6605
|
+
thisContext = await this.evaluateAsync(node.tag.object, env);
|
|
6606
|
+
const prop = node.tag.computed ? await this.evaluateAsync(node.tag.property, env) : node.tag.property.name;
|
|
6607
|
+
tagFunction = thisContext[prop];
|
|
6608
|
+
} else {
|
|
6609
|
+
tagFunction = await this.evaluateAsync(node.tag, env);
|
|
6610
|
+
}
|
|
6611
|
+
const strings = [];
|
|
6612
|
+
const rawStrings = [];
|
|
6613
|
+
for (const quasi of node.quasi.quasis) {
|
|
6614
|
+
strings.push(quasi.value.cooked || quasi.value.raw);
|
|
6615
|
+
rawStrings.push(quasi.value.raw);
|
|
6616
|
+
}
|
|
6617
|
+
Object.defineProperty(strings, "raw", {
|
|
6618
|
+
value: Object.freeze(rawStrings),
|
|
6619
|
+
writable: false,
|
|
6620
|
+
enumerable: false,
|
|
6621
|
+
configurable: false
|
|
6622
|
+
});
|
|
6623
|
+
Object.freeze(strings);
|
|
6624
|
+
const values = [];
|
|
6625
|
+
for (const expr of node.quasi.expressions) {
|
|
6626
|
+
values.push(await this.evaluateAsync(expr, env));
|
|
6627
|
+
}
|
|
6628
|
+
if (typeof tagFunction === "function") {
|
|
6629
|
+
if (thisContext !== void 0) {
|
|
6630
|
+
return await tagFunction.call(thisContext, strings, ...values);
|
|
6631
|
+
}
|
|
6632
|
+
return await tagFunction(strings, ...values);
|
|
6633
|
+
} else if (tagFunction && tagFunction.__isFunction) {
|
|
6634
|
+
return await this.callUserFunction(tagFunction, [strings, ...values], env, thisContext);
|
|
6635
|
+
}
|
|
6636
|
+
throw new TypeError("Tag must be a function");
|
|
6637
|
+
}
|
|
6601
6638
|
if (node.type === "LogicalExpression") {
|
|
6602
6639
|
const left = await this.evaluateAsync(node.left, env);
|
|
6603
6640
|
if (node.operator === "&&") {
|
|
@@ -7081,6 +7118,8 @@ var Interpreter = class _Interpreter {
|
|
|
7081
7118
|
// ES6+ Features
|
|
7082
7119
|
case "TemplateLiteral":
|
|
7083
7120
|
return this.evaluateTemplateLiteral(node, env);
|
|
7121
|
+
case "TaggedTemplateExpression":
|
|
7122
|
+
return this.evaluateTaggedTemplateExpression(node, env);
|
|
7084
7123
|
case "ClassDeclaration":
|
|
7085
7124
|
return this.evaluateClassDeclaration(node, env);
|
|
7086
7125
|
case "ClassExpression":
|
|
@@ -7431,7 +7470,11 @@ var Interpreter = class _Interpreter {
|
|
|
7431
7470
|
const metadata = func.__metadata || func;
|
|
7432
7471
|
const funcEnv = new Environment(metadata.closure);
|
|
7433
7472
|
const funcName = metadata.name || func.name || "anonymous";
|
|
7434
|
-
if (
|
|
7473
|
+
if (metadata.isArrow) {
|
|
7474
|
+
if (metadata.capturedThis !== void 0) {
|
|
7475
|
+
funcEnv.define("this", metadata.capturedThis);
|
|
7476
|
+
}
|
|
7477
|
+
} else if (thisContext !== void 0) {
|
|
7435
7478
|
funcEnv.define("this", thisContext);
|
|
7436
7479
|
}
|
|
7437
7480
|
for (let i = 0; i < metadata.params.length; i++) {
|
|
@@ -7566,13 +7609,24 @@ var Interpreter = class _Interpreter {
|
|
|
7566
7609
|
return obj;
|
|
7567
7610
|
}
|
|
7568
7611
|
evaluateFunctionExpression(node, env) {
|
|
7612
|
+
const isArrow = node.type === "ArrowFunctionExpression";
|
|
7613
|
+
let capturedThis;
|
|
7614
|
+
if (isArrow) {
|
|
7615
|
+
try {
|
|
7616
|
+
capturedThis = env.get("this");
|
|
7617
|
+
} catch (e) {
|
|
7618
|
+
capturedThis = void 0;
|
|
7619
|
+
}
|
|
7620
|
+
}
|
|
7569
7621
|
const funcMetadata = {
|
|
7570
7622
|
__isFunction: true,
|
|
7571
7623
|
params: node.params,
|
|
7572
7624
|
body: node.body,
|
|
7573
7625
|
closure: env,
|
|
7574
|
-
expression:
|
|
7575
|
-
async: node.async || false
|
|
7626
|
+
expression: isArrow && node.expression,
|
|
7627
|
+
async: node.async || false,
|
|
7628
|
+
isArrow,
|
|
7629
|
+
capturedThis: isArrow ? capturedThis : void 0
|
|
7576
7630
|
};
|
|
7577
7631
|
const interpreter = this;
|
|
7578
7632
|
const wrappedFunc = funcMetadata.async ? async function(...args) {
|
|
@@ -8024,6 +8078,40 @@ var Interpreter = class _Interpreter {
|
|
|
8024
8078
|
}
|
|
8025
8079
|
return result;
|
|
8026
8080
|
}
|
|
8081
|
+
evaluateTaggedTemplateExpression(node, env) {
|
|
8082
|
+
let thisContext = void 0;
|
|
8083
|
+
let tagFunction;
|
|
8084
|
+
if (node.tag.type === "MemberExpression") {
|
|
8085
|
+
thisContext = this.evaluate(node.tag.object, env);
|
|
8086
|
+
const prop = node.tag.computed ? this.evaluate(node.tag.property, env) : node.tag.property.name;
|
|
8087
|
+
tagFunction = thisContext[prop];
|
|
8088
|
+
} else {
|
|
8089
|
+
tagFunction = this.evaluate(node.tag, env);
|
|
8090
|
+
}
|
|
8091
|
+
const strings = [];
|
|
8092
|
+
const rawStrings = [];
|
|
8093
|
+
for (const quasi of node.quasi.quasis) {
|
|
8094
|
+
strings.push(quasi.value.cooked || quasi.value.raw);
|
|
8095
|
+
rawStrings.push(quasi.value.raw);
|
|
8096
|
+
}
|
|
8097
|
+
Object.defineProperty(strings, "raw", {
|
|
8098
|
+
value: Object.freeze(rawStrings),
|
|
8099
|
+
writable: false,
|
|
8100
|
+
enumerable: false,
|
|
8101
|
+
configurable: false
|
|
8102
|
+
});
|
|
8103
|
+
Object.freeze(strings);
|
|
8104
|
+
const values = node.quasi.expressions.map((expr) => this.evaluate(expr, env));
|
|
8105
|
+
if (typeof tagFunction === "function") {
|
|
8106
|
+
if (thisContext !== void 0) {
|
|
8107
|
+
return tagFunction.call(thisContext, strings, ...values);
|
|
8108
|
+
}
|
|
8109
|
+
return tagFunction(strings, ...values);
|
|
8110
|
+
} else if (tagFunction && tagFunction.__isFunction) {
|
|
8111
|
+
return this.callUserFunction(tagFunction, [strings, ...values], env, thisContext);
|
|
8112
|
+
}
|
|
8113
|
+
throw new TypeError("Tag must be a function");
|
|
8114
|
+
}
|
|
8027
8115
|
evaluateClassDeclaration(node, env) {
|
|
8028
8116
|
const className = node.id.name;
|
|
8029
8117
|
const classFunc = this.createClass(node, env);
|