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.
@@ -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' if provided (for method calls)
1259
- if (thisContext !== undefined) {
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: node.type === 'ArrowFunctionExpression' && node.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 (thisContext !== void 0) {
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: node.type === "ArrowFunctionExpression" && node.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' if provided (for method calls)
8424
- if (thisContext !== undefined) {
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: node.type === 'ArrowFunctionExpression' && node.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' if provided (for method calls)
8424
- if (thisContext !== undefined) {
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: node.type === 'ArrowFunctionExpression' && node.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 (thisContext !== void 0) {
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: node.type === "ArrowFunctionExpression" && node.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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jslike",
3
- "version": "1.6.3",
3
+ "version": "1.7.1",
4
4
  "description": "Production-ready JavaScript interpreter with full ES6+ support using Acorn parser",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",