jslike 1.3.0 → 1.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jslike",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Production-ready JavaScript interpreter with full ES6+ support using Acorn parser",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -35,9 +35,10 @@ export class Interpreter {
35
35
 
36
36
  // For block statements, evaluate each statement async
37
37
  if (node.type === 'BlockStatement') {
38
+ const blockEnv = new Environment(env);
38
39
  let result = undefined;
39
40
  for (const statement of node.body) {
40
- result = await this.evaluateAsync(statement, env);
41
+ result = await this.evaluateAsync(statement, blockEnv);
41
42
  if (result instanceof ReturnValue || result instanceof ThrowSignal ||
42
43
  result instanceof BreakSignal || result instanceof ContinueSignal) {
43
44
  return result;
@@ -254,8 +255,360 @@ export class Interpreter {
254
255
  return result;
255
256
  }
256
257
 
257
- // For everything else, delegate to sync evaluate
258
- return this.evaluate(node, env);
258
+ // For ForStatement with async body
259
+ if (node.type === 'ForStatement') {
260
+ const forEnv = new Environment(env);
261
+ if (node.init) {
262
+ await this.evaluateAsync(node.init, forEnv);
263
+ }
264
+ while (!node.test || await this.evaluateAsync(node.test, forEnv)) {
265
+ const result = await this.evaluateAsync(node.body, forEnv);
266
+ if (result instanceof BreakSignal) {
267
+ break;
268
+ }
269
+ if (result instanceof ContinueSignal) {
270
+ if (node.update) {
271
+ await this.evaluateAsync(node.update, forEnv);
272
+ }
273
+ continue;
274
+ }
275
+ if (result instanceof ReturnValue || result instanceof ThrowSignal) {
276
+ return result;
277
+ }
278
+ if (node.update) {
279
+ await this.evaluateAsync(node.update, forEnv);
280
+ }
281
+ }
282
+ return undefined;
283
+ }
284
+
285
+ // For ForOfStatement with async body
286
+ if (node.type === 'ForOfStatement') {
287
+ const forEnv = new Environment(env);
288
+ const iterable = await this.evaluateAsync(node.right, forEnv);
289
+ const declarator = node.left.declarations[0];
290
+ const isConst = node.left.kind === 'const';
291
+
292
+ for (const value of iterable) {
293
+ const iterEnv = forEnv.extend();
294
+ if (declarator.id.type === 'Identifier') {
295
+ iterEnv.define(declarator.id.name, value, isConst);
296
+ } else if (declarator.id.type === 'ArrayPattern') {
297
+ this.bindArrayPattern(declarator.id, value, iterEnv, isConst);
298
+ } else if (declarator.id.type === 'ObjectPattern') {
299
+ this.bindObjectPattern(declarator.id, value, iterEnv, isConst);
300
+ }
301
+ const result = await this.evaluateAsync(node.body, iterEnv);
302
+ if (result instanceof BreakSignal) {
303
+ break;
304
+ }
305
+ if (result instanceof ContinueSignal) {
306
+ continue;
307
+ }
308
+ if (result instanceof ReturnValue || result instanceof ThrowSignal) {
309
+ return result;
310
+ }
311
+ }
312
+ return undefined;
313
+ }
314
+
315
+ // For ForInStatement with async body
316
+ if (node.type === 'ForInStatement') {
317
+ const forEnv = new Environment(env);
318
+ const obj = await this.evaluateAsync(node.right, forEnv);
319
+ if (obj === null || obj === undefined) {
320
+ throw new TypeError(`Cannot use 'in' operator to iterate over ${obj}`);
321
+ }
322
+ const varName = node.left.declarations[0].id.name;
323
+ forEnv.define(varName, undefined);
324
+
325
+ for (const key in obj) {
326
+ forEnv.set(varName, key);
327
+ const result = await this.evaluateAsync(node.body, forEnv);
328
+ if (result instanceof BreakSignal) {
329
+ break;
330
+ }
331
+ if (result instanceof ContinueSignal) {
332
+ continue;
333
+ }
334
+ if (result instanceof ReturnValue || result instanceof ThrowSignal) {
335
+ return result;
336
+ }
337
+ }
338
+ return undefined;
339
+ }
340
+
341
+ // For WhileStatement with async body
342
+ if (node.type === 'WhileStatement') {
343
+ while (await this.evaluateAsync(node.test, env)) {
344
+ const result = await this.evaluateAsync(node.body, env);
345
+ if (result instanceof BreakSignal) {
346
+ break;
347
+ }
348
+ if (result instanceof ContinueSignal) {
349
+ continue;
350
+ }
351
+ if (result instanceof ReturnValue || result instanceof ThrowSignal) {
352
+ return result;
353
+ }
354
+ }
355
+ return undefined;
356
+ }
357
+
358
+ // For DoWhileStatement with async body
359
+ if (node.type === 'DoWhileStatement') {
360
+ do {
361
+ const result = await this.evaluateAsync(node.body, env);
362
+ if (result instanceof BreakSignal) {
363
+ break;
364
+ }
365
+ if (result instanceof ContinueSignal) {
366
+ continue;
367
+ }
368
+ if (result instanceof ReturnValue || result instanceof ThrowSignal) {
369
+ return result;
370
+ }
371
+ } while (await this.evaluateAsync(node.test, env));
372
+ return undefined;
373
+ }
374
+
375
+ // For IfStatement with async branches
376
+ if (node.type === 'IfStatement') {
377
+ const test = await this.evaluateAsync(node.test, env);
378
+ if (test) {
379
+ return await this.evaluateAsync(node.consequent, env);
380
+ } else if (node.alternate) {
381
+ return await this.evaluateAsync(node.alternate, env);
382
+ }
383
+ return undefined;
384
+ }
385
+
386
+ // For SwitchStatement with async cases
387
+ if (node.type === 'SwitchStatement') {
388
+ const discriminant = await this.evaluateAsync(node.discriminant, env);
389
+ let matched = false;
390
+
391
+ for (const switchCase of node.cases) {
392
+ if (!matched && switchCase.test) {
393
+ const testValue = await this.evaluateAsync(switchCase.test, env);
394
+ if (testValue === discriminant) {
395
+ matched = true;
396
+ }
397
+ } else if (!switchCase.test) {
398
+ matched = true;
399
+ }
400
+
401
+ if (matched) {
402
+ for (const statement of switchCase.consequent) {
403
+ const result = await this.evaluateAsync(statement, env);
404
+ if (result instanceof BreakSignal) {
405
+ return undefined;
406
+ }
407
+ if (result instanceof ReturnValue || result instanceof ThrowSignal) {
408
+ return result;
409
+ }
410
+ }
411
+ }
412
+ }
413
+ return undefined;
414
+ }
415
+
416
+ // For ConditionalExpression (ternary) with async operands
417
+ if (node.type === 'ConditionalExpression') {
418
+ const test = await this.evaluateAsync(node.test, env);
419
+ return test
420
+ ? await this.evaluateAsync(node.consequent, env)
421
+ : await this.evaluateAsync(node.alternate, env);
422
+ }
423
+
424
+ // For AssignmentExpression with async value
425
+ if (node.type === 'AssignmentExpression') {
426
+ const value = await this.evaluateAsync(node.right, env);
427
+
428
+ if (node.left.type === 'Identifier') {
429
+ const name = node.left.name;
430
+ if (node.operator === '=') {
431
+ if (env.has(name)) {
432
+ env.set(name, value);
433
+ } else {
434
+ env.define(name, value);
435
+ }
436
+ return value;
437
+ } else {
438
+ const current = env.get(name);
439
+ const newValue = this.applyCompoundAssignment(node.operator, current, value);
440
+ env.set(name, newValue);
441
+ return newValue;
442
+ }
443
+ } else if (node.left.type === 'MemberExpression') {
444
+ const obj = await this.evaluateAsync(node.left.object, env);
445
+ const prop = node.left.computed
446
+ ? await this.evaluateAsync(node.left.property, env)
447
+ : node.left.property.name;
448
+
449
+ if (node.operator === '=') {
450
+ obj[prop] = value;
451
+ return value;
452
+ } else {
453
+ const newValue = this.applyCompoundAssignment(node.operator, obj[prop], value);
454
+ obj[prop] = newValue;
455
+ return newValue;
456
+ }
457
+ }
458
+ throw new Error('Invalid assignment target');
459
+ }
460
+
461
+ // For UnaryExpression with async argument
462
+ if (node.type === 'UnaryExpression') {
463
+ if (node.operator === 'delete' && node.argument.type === 'MemberExpression') {
464
+ const obj = await this.evaluateAsync(node.argument.object, env);
465
+ const prop = node.argument.computed
466
+ ? await this.evaluateAsync(node.argument.property, env)
467
+ : node.argument.property.name;
468
+ return delete obj[prop];
469
+ }
470
+ const argument = await this.evaluateAsync(node.argument, env);
471
+ switch (node.operator) {
472
+ case '+': return +argument;
473
+ case '-': return -argument;
474
+ case '!': return !argument;
475
+ case '~': return ~argument;
476
+ case 'typeof':
477
+ if (argument && argument.__isFunction) {
478
+ return 'function';
479
+ }
480
+ return typeof argument;
481
+ case 'void': return undefined;
482
+ case 'delete': return true;
483
+ default:
484
+ throw new Error(`Unknown unary operator: ${node.operator}`);
485
+ }
486
+ }
487
+
488
+ // For UpdateExpression with async member access
489
+ if (node.type === 'UpdateExpression') {
490
+ if (node.argument.type === 'Identifier') {
491
+ const name = node.argument.name;
492
+ const current = env.get(name);
493
+ const numericCurrent = (current === null || current === undefined) ? 0 : Number(current);
494
+ const newValue = node.operator === '++' ? numericCurrent + 1 : numericCurrent - 1;
495
+ env.set(name, newValue);
496
+ return node.prefix ? newValue : numericCurrent;
497
+ } else if (node.argument.type === 'MemberExpression') {
498
+ const obj = await this.evaluateAsync(node.argument.object, env);
499
+ if (obj === null || obj === undefined) {
500
+ throw new TypeError(
501
+ `Cannot read properties of ${obj} (reading '${
502
+ node.argument.computed
503
+ ? await this.evaluateAsync(node.argument.property, env)
504
+ : node.argument.property.name
505
+ }')`
506
+ );
507
+ }
508
+ const prop = node.argument.computed
509
+ ? await this.evaluateAsync(node.argument.property, env)
510
+ : node.argument.property.name;
511
+ let current = obj[prop];
512
+ const numericCurrent = (current === null || current === undefined) ? 0 : Number(current);
513
+ const newValue = node.operator === '++' ? numericCurrent + 1 : numericCurrent - 1;
514
+ obj[prop] = newValue;
515
+ return node.prefix ? newValue : numericCurrent;
516
+ }
517
+ throw new Error('Invalid update expression target');
518
+ }
519
+
520
+ // For ArrayExpression with async elements
521
+ if (node.type === 'ArrayExpression') {
522
+ const result = [];
523
+ for (const elem of node.elements) {
524
+ if (!elem) {
525
+ result.push(undefined);
526
+ } else if (elem.type === 'SpreadElement') {
527
+ const spreadValue = await this.evaluateAsync(elem.argument, env);
528
+ if (Array.isArray(spreadValue)) {
529
+ result.push(...spreadValue);
530
+ } else if (typeof spreadValue[Symbol.iterator] === 'function') {
531
+ result.push(...spreadValue);
532
+ } else {
533
+ throw new TypeError('Spread syntax requires an iterable');
534
+ }
535
+ } else {
536
+ result.push(await this.evaluateAsync(elem, env));
537
+ }
538
+ }
539
+ return result;
540
+ }
541
+
542
+ // For ObjectExpression with async values
543
+ if (node.type === 'ObjectExpression') {
544
+ const obj = {};
545
+ for (const prop of node.properties) {
546
+ if (prop.type === 'SpreadElement') {
547
+ const spreadValue = await this.evaluateAsync(prop.argument, env);
548
+ if (typeof spreadValue === 'object' && spreadValue !== null) {
549
+ Object.assign(obj, spreadValue);
550
+ }
551
+ } else {
552
+ const key = prop.key.type === 'Identifier' && !prop.computed
553
+ ? prop.key.name
554
+ : await this.evaluateAsync(prop.key, env);
555
+ const value = prop.value ? await this.evaluateAsync(prop.value, env) : env.get(key);
556
+ if (prop.method && prop.value.type === 'FunctionExpression') {
557
+ obj[key] = (...args) => {
558
+ const funcValue = this.evaluate(prop.value, env);
559
+ return this.callUserFunction(funcValue, args, env);
560
+ };
561
+ } else {
562
+ obj[key] = value;
563
+ }
564
+ }
565
+ }
566
+ return obj;
567
+ }
568
+
569
+ // For SequenceExpression with async expressions
570
+ if (node.type === 'SequenceExpression') {
571
+ let result;
572
+ for (const expr of node.expressions) {
573
+ result = await this.evaluateAsync(expr, env);
574
+ }
575
+ return result;
576
+ }
577
+
578
+ // For ThrowStatement with async argument
579
+ if (node.type === 'ThrowStatement') {
580
+ return new ThrowSignal(await this.evaluateAsync(node.argument, env));
581
+ }
582
+
583
+ // For FunctionDeclaration - define in environment
584
+ if (node.type === 'FunctionDeclaration') {
585
+ return this.evaluateFunctionDeclaration(node, env);
586
+ }
587
+
588
+ // For FunctionExpression/ArrowFunctionExpression - create function
589
+ if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
590
+ return this.evaluateFunctionExpression(node, env);
591
+ }
592
+
593
+ // For ClassDeclaration
594
+ if (node.type === 'ClassDeclaration') {
595
+ return this.evaluateClassDeclaration(node, env);
596
+ }
597
+
598
+ // For ClassExpression
599
+ if (node.type === 'ClassExpression') {
600
+ return this.evaluateClassExpression(node, env);
601
+ }
602
+
603
+ // Only leaf nodes should fall through to sync evaluate
604
+ // These have no sub-expressions that could contain await
605
+ if (['Literal', 'Identifier', 'BreakStatement', 'ContinueStatement',
606
+ 'EmptyStatement', 'ThisExpression', 'Super'].includes(node.type)) {
607
+ return this.evaluate(node, env);
608
+ }
609
+
610
+ // Safety check - if we get here, we missed a node type
611
+ throw new Error(`Unhandled node type in evaluateAsync: ${node.type}`);
259
612
  }
260
613
 
261
614
  evaluate(node, env) {