starlight-cli 1.0.23 → 1.0.25
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/index.js +389 -357
- package/package.json +1 -1
- package/src/evaluator.js +362 -390
- package/src/lexer.js +13 -1
- package/src/parser.js +47 -8
- package/src/starlight.js +1 -1
package/dist/index.js
CHANGED
|
@@ -1347,421 +1347,394 @@ const Lexer = __nccwpck_require__(211);
|
|
|
1347
1347
|
const Parser = __nccwpck_require__(222);
|
|
1348
1348
|
const path = __nccwpck_require__(928);
|
|
1349
1349
|
|
|
1350
|
-
class ReturnValue {
|
|
1351
|
-
constructor(value) { this.value = value; }
|
|
1352
|
-
}
|
|
1350
|
+
class ReturnValue { constructor(value) { this.value = value; } }
|
|
1353
1351
|
class BreakSignal {}
|
|
1354
1352
|
class ContinueSignal {}
|
|
1355
1353
|
|
|
1356
1354
|
class Environment {
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1355
|
+
constructor(parent = null) {
|
|
1356
|
+
this.store = Object.create(null);
|
|
1357
|
+
this.parent = parent;
|
|
1358
|
+
}
|
|
1361
1359
|
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1360
|
+
has(name) {
|
|
1361
|
+
if (name in this.store) return true;
|
|
1362
|
+
if (this.parent) return this.parent.has(name);
|
|
1363
|
+
return false;
|
|
1364
|
+
}
|
|
1367
1365
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1366
|
+
get(name) {
|
|
1367
|
+
if (name in this.store) return this.store[name];
|
|
1368
|
+
if (this.parent) return this.parent.get(name);
|
|
1369
|
+
throw new Error(`Undefined variable: ${name}`);
|
|
1370
|
+
}
|
|
1373
1371
|
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1372
|
+
set(name, value) {
|
|
1373
|
+
if (name in this.store) { this.store[name] = value; return value; }
|
|
1374
|
+
if (this.parent && this.parent.has(name)) { return this.parent.set(name, value); }
|
|
1375
|
+
this.store[name] = value;
|
|
1376
|
+
return value;
|
|
1377
|
+
}
|
|
1380
1378
|
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1379
|
+
define(name, value) {
|
|
1380
|
+
this.store[name] = value;
|
|
1381
|
+
return value;
|
|
1382
|
+
}
|
|
1385
1383
|
}
|
|
1386
1384
|
|
|
1387
1385
|
class Evaluator {
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
setupBuiltins() {
|
|
1394
|
-
this.global.define('len', arg => {
|
|
1395
|
-
if (Array.isArray(arg) || typeof arg === 'string') return arg.length;
|
|
1396
|
-
if (arg && typeof arg === 'object') return Object.keys(arg).length;
|
|
1397
|
-
return 0;
|
|
1398
|
-
});
|
|
1399
|
-
|
|
1400
|
-
this.global.define('print', arg => { console.log(arg); return null; });
|
|
1401
|
-
this.global.define('type', arg => {
|
|
1402
|
-
if (Array.isArray(arg)) return 'array';
|
|
1403
|
-
return typeof arg;
|
|
1404
|
-
});
|
|
1405
|
-
this.global.define('keys', arg => arg && typeof arg === 'object' ? Object.keys(arg) : []);
|
|
1406
|
-
this.global.define('values', arg => arg && typeof arg === 'object' ? Object.values(arg) : []);
|
|
1386
|
+
constructor() {
|
|
1387
|
+
this.global = new Environment();
|
|
1388
|
+
this.setupBuiltins();
|
|
1389
|
+
}
|
|
1407
1390
|
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1391
|
+
setupBuiltins() {
|
|
1392
|
+
this.global.define('len', arg => {
|
|
1393
|
+
if (Array.isArray(arg) || typeof arg === 'string') return arg.length;
|
|
1394
|
+
if (arg && typeof arg === 'object') return Object.keys(arg).length;
|
|
1395
|
+
return 0;
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
this.global.define('print', arg => { console.log(arg); return null; });
|
|
1399
|
+
this.global.define('type', arg => Array.isArray(arg) ? 'array' : typeof arg);
|
|
1400
|
+
this.global.define('keys', arg => arg && typeof arg === 'object' ? Object.keys(arg) : []);
|
|
1401
|
+
this.global.define('values', arg => arg && typeof arg === 'object' ? Object.values(arg) : []);
|
|
1402
|
+
|
|
1403
|
+
this.global.define('ask', prompt => readlineSync.question(prompt + ' '));
|
|
1404
|
+
this.global.define('num', arg => {
|
|
1405
|
+
const n = Number(arg);
|
|
1406
|
+
if (Number.isNaN(n)) throw new Error('Cannot convert value to number');
|
|
1407
|
+
return n;
|
|
1408
|
+
});
|
|
1409
|
+
this.global.define('str', arg => String(arg));
|
|
1410
|
+
|
|
1411
|
+
// Async fetch built-in for API requests
|
|
1412
|
+
this.global.define('fetch', async (url, options) => {
|
|
1413
|
+
const fetch = __nccwpck_require__(266); // Make sure node-fetch is installed
|
|
1414
|
+
const res = await fetch(url, options);
|
|
1415
|
+
return res;
|
|
1416
|
+
});
|
|
1416
1417
|
}
|
|
1417
|
-
return n;
|
|
1418
|
-
});
|
|
1419
1418
|
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1419
|
+
async evaluate(node, env = this.global) {
|
|
1420
|
+
switch (node.type) {
|
|
1421
|
+
case 'Program': return await this.evalProgram(node, env);
|
|
1422
|
+
case 'BlockStatement': return await this.evalBlock(node, new Environment(env));
|
|
1423
|
+
case 'VarDeclaration': return await this.evalVarDeclaration(node, env);
|
|
1424
|
+
case 'AssignmentExpression': return await this.evalAssignment(node, env);
|
|
1425
|
+
case 'CompoundAssignment': return await this.evalCompoundAssignment(node, env);
|
|
1426
|
+
case 'SldeployStatement': return await this.evalSldeploy(node, env);
|
|
1427
|
+
case 'AskStatement': return await this.evalAsk(node, env);
|
|
1428
|
+
case 'DefineStatement': return await this.evalDefine(node, env);
|
|
1429
|
+
case 'ExpressionStatement': return await this.evaluate(node.expression, env);
|
|
1430
|
+
case 'BinaryExpression': return await this.evalBinary(node, env);
|
|
1431
|
+
case 'LogicalExpression': return await this.evalLogical(node, env);
|
|
1432
|
+
case 'UnaryExpression': return await this.evalUnary(node, env);
|
|
1433
|
+
case 'Literal': return node.value;
|
|
1434
|
+
case 'Identifier': return env.get(node.name);
|
|
1435
|
+
case 'IfStatement': return await this.evalIf(node, env);
|
|
1436
|
+
case 'WhileStatement': return await this.evalWhile(node, env);
|
|
1437
|
+
case 'ForStatement': return await this.evalFor(node, env);
|
|
1438
|
+
case 'BreakStatement': throw new BreakSignal();
|
|
1439
|
+
case 'ContinueStatement': throw new ContinueSignal();
|
|
1440
|
+
case 'ImportStatement': return await this.evalImport(node, env);
|
|
1441
|
+
case 'FunctionDeclaration': return await this.evalFunctionDeclaration(node, env);
|
|
1442
|
+
case 'CallExpression': return await this.evalCall(node, env);
|
|
1443
|
+
case 'ArrowFunctionExpression': return await this.evalArrowFunction(node, env);
|
|
1444
|
+
case 'ReturnStatement': {
|
|
1445
|
+
const val = node.argument ? await this.evaluate(node.argument, env) : null;
|
|
1446
|
+
throw new ReturnValue(val);
|
|
1447
|
+
}
|
|
1448
|
+
case 'ArrayExpression': return await Promise.all(node.elements.map(el => this.evaluate(el, env)));
|
|
1449
|
+
case 'IndexExpression': return await this.evalIndex(node, env);
|
|
1450
|
+
case 'ObjectExpression': return await this.evalObject(node, env);
|
|
1451
|
+
case 'MemberExpression': return await this.evalMember(node, env);
|
|
1452
|
+
case 'UpdateExpression': return await this.evalUpdate(node, env);
|
|
1453
|
+
default:
|
|
1454
|
+
throw new Error(`Unknown node type in evaluator: ${node.type}`);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1423
1457
|
|
|
1424
|
-
|
|
1458
|
+
async evalProgram(node, env) {
|
|
1459
|
+
let result = null;
|
|
1460
|
+
for (const stmt of node.body) result = await this.evaluate(stmt, env);
|
|
1461
|
+
return result;
|
|
1462
|
+
}
|
|
1425
1463
|
|
|
1464
|
+
async evalBlock(node, env) {
|
|
1465
|
+
let result = null;
|
|
1466
|
+
for (const stmt of node.body) {
|
|
1467
|
+
try { result = await this.evaluate(stmt, env); }
|
|
1468
|
+
catch (e) {
|
|
1469
|
+
if (e instanceof ReturnValue || e instanceof BreakSignal || e instanceof ContinueSignal) throw e;
|
|
1470
|
+
throw e;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
return result;
|
|
1474
|
+
}
|
|
1426
1475
|
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
case 'BlockStatement': return this.evalBlock(node, new Environment(env));
|
|
1431
|
-
case 'VarDeclaration': return this.evalVarDeclaration(node, env);
|
|
1432
|
-
case 'AssignmentExpression': return this.evalAssignment(node, env);
|
|
1433
|
-
case 'CompoundAssignment': return this.evalCompoundAssignment(node, env);
|
|
1434
|
-
case 'SldeployStatement': return this.evalSldeploy(node, env);
|
|
1435
|
-
case 'AskStatement': return this.evalAsk(node, env);
|
|
1436
|
-
case 'DefineStatement': return this.evalDefine(node, env);
|
|
1437
|
-
case 'ExpressionStatement': return this.evaluate(node.expression, env);
|
|
1438
|
-
case 'BinaryExpression': return this.evalBinary(node, env);
|
|
1439
|
-
case 'LogicalExpression': return this.evalLogical(node, env);
|
|
1440
|
-
case 'UnaryExpression': return this.evalUnary(node, env);
|
|
1441
|
-
case 'Literal': return node.value;
|
|
1442
|
-
case 'Identifier': return env.get(node.name);
|
|
1443
|
-
case 'IfStatement': return this.evalIf(node, env);
|
|
1444
|
-
case 'WhileStatement': return this.evalWhile(node, env);
|
|
1445
|
-
case 'ForStatement': return this.evalFor(node, env);
|
|
1446
|
-
case 'BreakStatement': throw new BreakSignal();
|
|
1447
|
-
case 'ContinueStatement': throw new ContinueSignal();
|
|
1448
|
-
case 'ImportStatement': return this.evalImport(node, env);
|
|
1449
|
-
case 'FunctionDeclaration': return this.evalFunctionDeclaration(node, env);
|
|
1450
|
-
case 'CallExpression': return this.evalCall(node, env);
|
|
1451
|
-
case 'ReturnStatement': {
|
|
1452
|
-
const val = node.argument ? this.evaluate(node.argument, env) : null;
|
|
1453
|
-
throw new ReturnValue(val);
|
|
1454
|
-
}
|
|
1455
|
-
case 'ArrayExpression': return node.elements.map(el => this.evaluate(el, env));
|
|
1456
|
-
case 'IndexExpression': return this.evalIndex(node, env);
|
|
1457
|
-
case 'ObjectExpression': return this.evalObject(node, env);
|
|
1458
|
-
case 'MemberExpression': return this.evalMember(node, env);
|
|
1459
|
-
case 'UpdateExpression': return this.evalUpdate(node, env);
|
|
1460
|
-
default:
|
|
1461
|
-
throw new Error(`Unknown node type in evaluator: ${node.type}`);
|
|
1476
|
+
async evalVarDeclaration(node, env) {
|
|
1477
|
+
const val = await this.evaluate(node.expr, env);
|
|
1478
|
+
return env.define(node.id, val);
|
|
1462
1479
|
}
|
|
1463
|
-
}
|
|
1464
1480
|
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
for (const stmt of node.body) {
|
|
1468
|
-
result = this.evaluate(stmt, env);
|
|
1481
|
+
async evalArrowFunction(node, env) {
|
|
1482
|
+
return { params: node.params, body: node.body, env, arrow: true };
|
|
1469
1483
|
}
|
|
1470
|
-
return result;
|
|
1471
|
-
}
|
|
1472
|
-
evalImport(node, env) {
|
|
1473
|
-
const spec = node.path;
|
|
1474
|
-
let lib;
|
|
1475
1484
|
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
});
|
|
1480
|
-
lib = require(resolved);
|
|
1481
|
-
} catch (e) {
|
|
1482
|
-
const fullPath = path.isAbsolute(spec)
|
|
1483
|
-
? spec
|
|
1484
|
-
: path.join(process.cwd(), spec.endsWith('.sl') ? spec : spec + '.sl');
|
|
1485
|
+
async evalAssignment(node, env) {
|
|
1486
|
+
const rightVal = await this.evaluate(node.right, env);
|
|
1487
|
+
const left = node.left;
|
|
1485
1488
|
|
|
1486
|
-
|
|
1487
|
-
|
|
1489
|
+
if (left.type === 'Identifier') return env.set(left.name, rightVal);
|
|
1490
|
+
if (left.type === 'MemberExpression') {
|
|
1491
|
+
const obj = await this.evalMemberObj(left, env);
|
|
1492
|
+
obj[left.property] = rightVal;
|
|
1493
|
+
return rightVal;
|
|
1494
|
+
}
|
|
1495
|
+
if (left.type === 'IndexExpression') {
|
|
1496
|
+
const obj = await this.evalIndexObj(left, env);
|
|
1497
|
+
const idx = await this.evaluate(left.indexer, env);
|
|
1498
|
+
obj[idx] = rightVal;
|
|
1499
|
+
return rightVal;
|
|
1500
|
+
}
|
|
1501
|
+
throw new Error('Invalid assignment target');
|
|
1488
1502
|
}
|
|
1489
1503
|
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1504
|
+
async evalCompoundAssignment(node, env) {
|
|
1505
|
+
const left = node.left;
|
|
1506
|
+
let current;
|
|
1507
|
+
if (left.type === 'Identifier') current = env.get(left.name);
|
|
1508
|
+
else if (left.type === 'MemberExpression') current = await this.evalMember(left, env);
|
|
1509
|
+
else if (left.type === 'IndexExpression') current = await this.evalIndex(left, env);
|
|
1510
|
+
else throw new Error('Invalid compound assignment target');
|
|
1511
|
+
|
|
1512
|
+
const rhs = await this.evaluate(node.right, env);
|
|
1513
|
+
let computed;
|
|
1514
|
+
switch (node.operator) {
|
|
1515
|
+
case 'PLUSEQ': computed = current + rhs; break;
|
|
1516
|
+
case 'MINUSEQ': computed = current - rhs; break;
|
|
1517
|
+
case 'STAREQ': computed = current * rhs; break;
|
|
1518
|
+
case 'SLASHEQ': computed = current / rhs; break;
|
|
1519
|
+
case 'MODEQ': computed = current % rhs; break;
|
|
1520
|
+
default: throw new Error('Unknown compound operator');
|
|
1521
|
+
}
|
|
1493
1522
|
|
|
1494
|
-
|
|
1495
|
-
|
|
1523
|
+
if (left.type === 'Identifier') env.set(left.name, computed);
|
|
1524
|
+
else await this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
|
|
1496
1525
|
|
|
1497
|
-
|
|
1498
|
-
for (const key of Object.keys(moduleEnv.store)) {
|
|
1499
|
-
lib[key] = moduleEnv.store[key];
|
|
1526
|
+
return computed;
|
|
1500
1527
|
}
|
|
1501
1528
|
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
if (imp.type === 'DefaultImport') {
|
|
1507
|
-
env.define(imp.local, lib.default ?? lib);
|
|
1529
|
+
async evalSldeploy(node, env) {
|
|
1530
|
+
const val = await this.evaluate(node.expr, env);
|
|
1531
|
+
console.log(val);
|
|
1532
|
+
return val;
|
|
1508
1533
|
}
|
|
1509
|
-
if (imp.type === 'NamespaceImport') {
|
|
1510
|
-
env.define(imp.local, lib);
|
|
1511
|
-
}
|
|
1512
|
-
if (imp.type === 'NamedImport') {
|
|
1513
|
-
if (!(imp.imported in lib)) {
|
|
1514
|
-
throw new Error(`Module '${spec}' has no export '${imp.imported}'`);
|
|
1515
|
-
}
|
|
1516
|
-
env.define(imp.local, lib[imp.imported]);
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
1534
|
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
evalBlock(node, env) {
|
|
1525
|
-
let result = null;
|
|
1526
|
-
for (const stmt of node.body) {
|
|
1527
|
-
try {
|
|
1528
|
-
result = this.evaluate(stmt, env);
|
|
1529
|
-
} catch (e) {
|
|
1530
|
-
if (e instanceof ReturnValue || e instanceof BreakSignal || e instanceof ContinueSignal) throw e;
|
|
1531
|
-
throw e;
|
|
1532
|
-
}
|
|
1535
|
+
async evalAsk(node, env) {
|
|
1536
|
+
const prompt = await this.evaluate(node.prompt, env);
|
|
1537
|
+
return readlineSync.question(prompt + ' ');
|
|
1533
1538
|
}
|
|
1534
|
-
return result;
|
|
1535
|
-
}
|
|
1536
1539
|
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1540
|
+
async evalDefine(node, env) {
|
|
1541
|
+
const val = node.expr ? await this.evaluate(node.expr, env) : null;
|
|
1542
|
+
return this.global.define(node.id, val);
|
|
1543
|
+
}
|
|
1541
1544
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
+
async evalBinary(node, env) {
|
|
1546
|
+
const l = await this.evaluate(node.left, env);
|
|
1547
|
+
const r = await this.evaluate(node.right, env);
|
|
1548
|
+
|
|
1549
|
+
if (node.operator === 'SLASH' && r === 0) throw new Error('Division by zero');
|
|
1550
|
+
|
|
1551
|
+
switch (node.operator) {
|
|
1552
|
+
case 'PLUS': return l + r;
|
|
1553
|
+
case 'MINUS': return l - r;
|
|
1554
|
+
case 'STAR': return l * r;
|
|
1555
|
+
case 'SLASH': return l / r;
|
|
1556
|
+
case 'MOD': return l % r;
|
|
1557
|
+
case 'EQEQ': return l === r;
|
|
1558
|
+
case 'NOTEQ': return l !== r;
|
|
1559
|
+
case 'LT': return l < r;
|
|
1560
|
+
case 'LTE': return l <= r;
|
|
1561
|
+
case 'GT': return l > r;
|
|
1562
|
+
case 'GTE': return l >= r;
|
|
1563
|
+
default: throw new Error(`Unknown binary operator ${node.operator}`);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1545
1566
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1567
|
+
async evalLogical(node, env) {
|
|
1568
|
+
const l = await this.evaluate(node.left, env);
|
|
1569
|
+
if (node.operator === 'AND') return l && await this.evaluate(node.right, env);
|
|
1570
|
+
if (node.operator === 'OR') return l || await this.evaluate(node.right, env);
|
|
1571
|
+
throw new Error(`Unknown logical operator ${node.operator}`);
|
|
1551
1572
|
}
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1573
|
+
|
|
1574
|
+
async evalUnary(node, env) {
|
|
1575
|
+
const val = await this.evaluate(node.argument, env);
|
|
1576
|
+
switch (node.operator) {
|
|
1577
|
+
case 'NOT': return !val;
|
|
1578
|
+
case 'MINUS': return -val;
|
|
1579
|
+
case 'PLUS': return +val;
|
|
1580
|
+
default: throw new Error(`Unknown unary operator ${node.operator}`);
|
|
1581
|
+
}
|
|
1557
1582
|
}
|
|
1558
1583
|
|
|
1559
|
-
|
|
1560
|
-
|
|
1584
|
+
async evalIf(node, env) {
|
|
1585
|
+
const test = await this.evaluate(node.test, env);
|
|
1586
|
+
if (test) return await this.evaluate(node.consequent, env);
|
|
1587
|
+
if (node.alternate) return await this.evaluate(node.alternate, env);
|
|
1588
|
+
return null;
|
|
1589
|
+
}
|
|
1561
1590
|
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1591
|
+
async evalWhile(node, env) {
|
|
1592
|
+
while (await this.evaluate(node.test, env)) {
|
|
1593
|
+
try { await this.evaluate(node.body, env); }
|
|
1594
|
+
catch (e) { if (e instanceof BreakSignal) break; if (e instanceof ContinueSignal) continue; throw e; }
|
|
1595
|
+
}
|
|
1596
|
+
return null;
|
|
1597
|
+
}
|
|
1565
1598
|
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1599
|
+
async evalFor(node, env) {
|
|
1600
|
+
const local = new Environment(env);
|
|
1601
|
+
if (node.init) await this.evaluate(node.init, local);
|
|
1602
|
+
while (!node.test || await this.evaluate(node.test, local)) {
|
|
1603
|
+
try { await this.evaluate(node.body, local); }
|
|
1604
|
+
catch (e) {
|
|
1605
|
+
if (e instanceof BreakSignal) break;
|
|
1606
|
+
if (e instanceof ContinueSignal) { if (node.update) await this.evaluate(node.update, local); continue; }
|
|
1607
|
+
throw e;
|
|
1608
|
+
}
|
|
1609
|
+
if (node.update) await this.evaluate(node.update, local);
|
|
1610
|
+
}
|
|
1611
|
+
return null;
|
|
1612
|
+
}
|
|
1570
1613
|
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
case 'MINUSEQ': computed = current - rhs; break;
|
|
1576
|
-
case 'STAREQ': computed = current * rhs; break;
|
|
1577
|
-
case 'SLASHEQ': computed = current / rhs; break;
|
|
1578
|
-
case 'MODEQ': computed = current % rhs; break;
|
|
1579
|
-
default: throw new Error('Unknown compound operator');
|
|
1614
|
+
async evalFunctionDeclaration(node, env) {
|
|
1615
|
+
const fn = { params: node.params, body: node.body, env };
|
|
1616
|
+
env.define(node.name, fn);
|
|
1617
|
+
return null;
|
|
1580
1618
|
}
|
|
1581
1619
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
else this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
|
|
1620
|
+
async evalCall(node, env) {
|
|
1621
|
+
const calleeEvaluated = await this.evaluate(node.callee, env);
|
|
1585
1622
|
|
|
1586
|
-
|
|
1587
|
-
|
|
1623
|
+
if (typeof calleeEvaluated === 'function') {
|
|
1624
|
+
const args = [];
|
|
1625
|
+
for (const a of node.arguments) args.push(await this.evaluate(a, env));
|
|
1626
|
+
return await calleeEvaluated(...args);
|
|
1627
|
+
}
|
|
1588
1628
|
|
|
1589
|
-
|
|
1590
|
-
const val = this.evaluate(node.expr, env);
|
|
1591
|
-
console.log(val);
|
|
1592
|
-
return val;
|
|
1593
|
-
}
|
|
1629
|
+
if (!calleeEvaluated || typeof calleeEvaluated !== 'object' || !calleeEvaluated.body) throw new Error('Call to non-function');
|
|
1594
1630
|
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1631
|
+
const fn = calleeEvaluated;
|
|
1632
|
+
const callEnv = new Environment(fn.env);
|
|
1633
|
+
for (let i = 0; i < fn.params.length; i++) {
|
|
1634
|
+
const argVal = node.arguments[i] ? await this.evaluate(node.arguments[i], env) : null;
|
|
1635
|
+
callEnv.define(fn.params[i], argVal);
|
|
1636
|
+
}
|
|
1600
1637
|
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1638
|
+
try {
|
|
1639
|
+
const result = await this.evaluate(fn.body, callEnv);
|
|
1640
|
+
return fn.arrow ? result : result;
|
|
1641
|
+
} catch (e) { if (e instanceof ReturnValue) return e.value; throw e; }
|
|
1642
|
+
}
|
|
1605
1643
|
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1644
|
+
async evalIndex(node, env) {
|
|
1645
|
+
const obj = await this.evaluate(node.object, env);
|
|
1646
|
+
const idx = await this.evaluate(node.indexer, env);
|
|
1609
1647
|
|
|
1610
|
-
|
|
1611
|
-
throw new Error('
|
|
1648
|
+
if (obj == null) throw new Error('Indexing null or undefined');
|
|
1649
|
+
if (Array.isArray(obj) && (idx < 0 || idx >= obj.length)) throw new Error('Array index out of bounds');
|
|
1650
|
+
if (typeof obj === 'object' && !(idx in obj)) throw new Error(`Property '${idx}' does not exist`);
|
|
1651
|
+
return obj[idx];
|
|
1612
1652
|
}
|
|
1613
1653
|
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
case 'SLASH': return l / r;
|
|
1619
|
-
case 'MOD': return l % r;
|
|
1620
|
-
case 'EQEQ': return l === r;
|
|
1621
|
-
case 'NOTEQ': return l !== r;
|
|
1622
|
-
case 'LT': return l < r;
|
|
1623
|
-
case 'LTE': return l <= r;
|
|
1624
|
-
case 'GT': return l > r;
|
|
1625
|
-
case 'GTE': return l >= r;
|
|
1626
|
-
default: throw new Error(`Unknown binary operator ${node.operator}`);
|
|
1654
|
+
async evalObject(node, env) {
|
|
1655
|
+
const out = {};
|
|
1656
|
+
for (const p of node.props) out[p.key] = await this.evaluate(p.value, env);
|
|
1657
|
+
return out;
|
|
1627
1658
|
}
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
1659
|
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
evalUnary(node, env) {
|
|
1639
|
-
const val = this.evaluate(node.argument, env);
|
|
1640
|
-
switch (node.operator) {
|
|
1641
|
-
case 'NOT': return !val;
|
|
1642
|
-
case 'MINUS': return -val;
|
|
1643
|
-
case 'PLUS': return +val;
|
|
1644
|
-
default: throw new Error(`Unknown unary operator ${node.operator}`);
|
|
1660
|
+
async evalMember(node, env) {
|
|
1661
|
+
const obj = await this.evaluate(node.object, env);
|
|
1662
|
+
if (obj == null) throw new Error('Member access of null or undefined');
|
|
1663
|
+
if (!(node.property in obj)) throw new Error(`Property '${node.property}' does not exist`);
|
|
1664
|
+
return obj[node.property];
|
|
1645
1665
|
}
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
evalIf(node, env) {
|
|
1649
|
-
const test = this.evaluate(node.test, env);
|
|
1650
|
-
if (test) return this.evaluate(node.consequent, env);
|
|
1651
|
-
if (node.alternate) return this.evaluate(node.alternate, env);
|
|
1652
|
-
return null;
|
|
1653
|
-
}
|
|
1654
1666
|
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
if (e instanceof BreakSignal) break;
|
|
1660
|
-
if (e instanceof ContinueSignal) continue;
|
|
1661
|
-
throw e;
|
|
1662
|
-
}
|
|
1667
|
+
async evalMemberObj(node, env) {
|
|
1668
|
+
const obj = await this.evaluate(node.object, env);
|
|
1669
|
+
if (obj == null) throw new Error('Member access of null or undefined');
|
|
1670
|
+
return obj;
|
|
1663
1671
|
}
|
|
1664
|
-
return null;
|
|
1665
|
-
}
|
|
1666
1672
|
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
try { this.evaluate(node.body, local); }
|
|
1672
|
-
catch (e) {
|
|
1673
|
-
if (e instanceof BreakSignal) break;
|
|
1674
|
-
if (e instanceof ContinueSignal) { if (node.update) this.evaluate(node.update, local); continue; }
|
|
1675
|
-
throw e;
|
|
1676
|
-
}
|
|
1677
|
-
if (node.update) this.evaluate(node.update, local);
|
|
1673
|
+
async evalIndexObj(node, env) {
|
|
1674
|
+
const obj = await this.evaluate(node.object, env);
|
|
1675
|
+
if (obj == null) throw new Error('Indexing null or undefined');
|
|
1676
|
+
return obj;
|
|
1678
1677
|
}
|
|
1679
|
-
return null;
|
|
1680
|
-
}
|
|
1681
|
-
|
|
1682
|
-
evalFunctionDeclaration(node, env) {
|
|
1683
|
-
const fn = { params: node.params, body: node.body, env };
|
|
1684
|
-
env.define(node.name, fn);
|
|
1685
|
-
return null;
|
|
1686
|
-
}
|
|
1687
1678
|
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1679
|
+
async evalUpdate(node, env) {
|
|
1680
|
+
const arg = node.argument;
|
|
1681
|
+
const getCurrent = async () => {
|
|
1682
|
+
if (arg.type === 'Identifier') return env.get(arg.name);
|
|
1683
|
+
if (arg.type === 'MemberExpression') return await this.evalMember(arg, env);
|
|
1684
|
+
if (arg.type === 'IndexExpression') return await this.evalIndex(arg, env);
|
|
1685
|
+
throw new Error('Invalid update target');
|
|
1686
|
+
};
|
|
1687
|
+
const setValue = async (v) => {
|
|
1688
|
+
if (arg.type === 'Identifier') env.set(arg.name, v);
|
|
1689
|
+
else if (arg.type === 'MemberExpression') { const obj = await this.evalMemberObj(arg, env); obj[arg.property] = v; }
|
|
1690
|
+
else if (arg.type === 'IndexExpression') { const obj = await this.evalIndexObj(arg, env); const idx = await this.evaluate(arg.indexer, env); obj[idx] = v; }
|
|
1691
|
+
};
|
|
1692
|
+
|
|
1693
|
+
const current = await getCurrent();
|
|
1694
|
+
const newVal = (node.operator === 'PLUSPLUS') ? current + 1 : current - 1;
|
|
1695
|
+
|
|
1696
|
+
if (node.prefix) { await setValue(newVal); return newVal; }
|
|
1697
|
+
else { await setValue(newVal); return current; }
|
|
1696
1698
|
}
|
|
1697
|
-
const fn = calleeEvaluated;
|
|
1698
|
-
const callEnv = new Environment(fn.env);
|
|
1699
|
-
fn.params.forEach((p, i) => {
|
|
1700
|
-
const argVal = node.arguments[i] ? this.evaluate(node.arguments[i], env) : null;
|
|
1701
|
-
callEnv.define(p, argVal);
|
|
1702
|
-
});
|
|
1703
|
-
try { return this.evaluate(fn.body, callEnv); }
|
|
1704
|
-
catch (e) { if (e instanceof ReturnValue) return e.value; throw e; }
|
|
1705
|
-
}
|
|
1706
1699
|
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
if (obj == null) throw new Error('Indexing null or undefined');
|
|
1712
|
-
if (Array.isArray(obj) && (idx < 0 || idx >= obj.length)) {
|
|
1713
|
-
throw new Error('Array index out of bounds');
|
|
1714
|
-
}
|
|
1715
|
-
if (typeof obj === 'object' && !(idx in obj)) {
|
|
1716
|
-
throw new Error(`Property '${idx}' does not exist`);
|
|
1717
|
-
}
|
|
1700
|
+
async evalImport(node, env) {
|
|
1701
|
+
const spec = node.path;
|
|
1702
|
+
let lib;
|
|
1718
1703
|
|
|
1719
|
-
|
|
1720
|
-
}
|
|
1704
|
+
try {
|
|
1705
|
+
const resolved = require.resolve(spec, { paths: [process.cwd()] });
|
|
1706
|
+
lib = require(resolved);
|
|
1707
|
+
} catch (e) {
|
|
1708
|
+
const fullPath = path.isAbsolute(spec) ? spec : path.join(process.cwd(), spec.endsWith('.sl') ? spec : spec + '.sl');
|
|
1709
|
+
if (!fs.existsSync(fullPath)) throw new Error(`Import not found: ${spec}`);
|
|
1721
1710
|
|
|
1711
|
+
const code = fs.readFileSync(fullPath, 'utf-8');
|
|
1712
|
+
const tokens = new Lexer(code).getTokens();
|
|
1713
|
+
const ast = new Parser(tokens).parse();
|
|
1722
1714
|
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
for (const p of node.props) out[p.key] = this.evaluate(p.value, env);
|
|
1726
|
-
return out;
|
|
1727
|
-
}
|
|
1715
|
+
const moduleEnv = new Environment(env);
|
|
1716
|
+
await this.evaluate(ast, moduleEnv);
|
|
1728
1717
|
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
return obj[node.property];
|
|
1734
|
-
}
|
|
1718
|
+
lib = {};
|
|
1719
|
+
for (const key of Object.keys(moduleEnv.store)) lib[key] = moduleEnv.store[key];
|
|
1720
|
+
lib.default = lib;
|
|
1721
|
+
}
|
|
1735
1722
|
|
|
1723
|
+
for (const imp of node.specifiers) {
|
|
1724
|
+
if (imp.type === 'DefaultImport') env.define(imp.local, lib.default ?? lib);
|
|
1725
|
+
if (imp.type === 'NamespaceImport') env.define(imp.local, lib);
|
|
1726
|
+
if (imp.type === 'NamedImport') {
|
|
1727
|
+
if (!(imp.imported in lib)) throw new Error(`Module '${spec}' has no export '${imp.imported}'`);
|
|
1728
|
+
env.define(imp.local, lib[imp.imported]);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1736
1731
|
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
const getCurrent = () => {
|
|
1740
|
-
if (arg.type === 'Identifier') return env.get(arg.name);
|
|
1741
|
-
if (arg.type === 'MemberExpression') return this.evalMember(arg, env);
|
|
1742
|
-
if (arg.type === 'IndexExpression') return this.evalIndex(arg, env);
|
|
1743
|
-
throw new Error('Invalid update target');
|
|
1744
|
-
};
|
|
1745
|
-
const setValue = (v) => {
|
|
1746
|
-
if (arg.type === 'Identifier') env.set(arg.name, v);
|
|
1747
|
-
else if (arg.type === 'MemberExpression') {
|
|
1748
|
-
const obj = this.evaluate(arg.object, env);
|
|
1749
|
-
obj[arg.property] = v;
|
|
1750
|
-
}
|
|
1751
|
-
else if (arg.type === 'IndexExpression') {
|
|
1752
|
-
const obj = this.evaluate(arg.object, env);
|
|
1753
|
-
const idx = this.evaluate(arg.indexer, env);
|
|
1754
|
-
obj[idx] = v;
|
|
1755
|
-
}
|
|
1756
|
-
};
|
|
1757
|
-
const current = getCurrent();
|
|
1758
|
-
const newVal = (node.operator === 'PLUSPLUS') ? current + 1 : current - 1;
|
|
1759
|
-
if (node.prefix) { setValue(newVal); return newVal; }
|
|
1760
|
-
else { setValue(newVal); return current; }
|
|
1761
|
-
}
|
|
1732
|
+
return null;
|
|
1733
|
+
}
|
|
1762
1734
|
}
|
|
1763
1735
|
|
|
1764
|
-
module.exports = Evaluator;
|
|
1736
|
+
module.exports = Evaluator;
|
|
1737
|
+
|
|
1765
1738
|
|
|
1766
1739
|
/***/ }),
|
|
1767
1740
|
|
|
@@ -1909,7 +1882,19 @@ class Lexer {
|
|
|
1909
1882
|
const startCol = this.column;
|
|
1910
1883
|
|
|
1911
1884
|
if (char === '=' && next === '=') { tokens.push({ type: 'EQEQ', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
|
|
1912
|
-
|
|
1885
|
+
|
|
1886
|
+
if (char === '=' && next === '>') {
|
|
1887
|
+
tokens.push({ type: 'ARROW', line: startLine, column: startCol });
|
|
1888
|
+
this.advance(); this.advance();
|
|
1889
|
+
continue;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
if (char === '-' && next === '>') {
|
|
1893
|
+
tokens.push({ type: 'ARROW', line: startLine, column: startCol });
|
|
1894
|
+
this.advance(); this.advance();
|
|
1895
|
+
continue;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1913
1898
|
if (char === '!' && next === '=') { tokens.push({ type: 'NOTEQ', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
|
|
1914
1899
|
if (char === '<' && next === '=') { tokens.push({ type: 'LTE', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
|
|
1915
1900
|
if (char === '>' && next === '=') { tokens.push({ type: 'GTE', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
|
|
@@ -2317,6 +2302,15 @@ class Parser {
|
|
|
2317
2302
|
}
|
|
2318
2303
|
return node;
|
|
2319
2304
|
}
|
|
2305
|
+
arrowFunction(params) {
|
|
2306
|
+
this.eat('ARROW');
|
|
2307
|
+
const body = this.expression();
|
|
2308
|
+
return {
|
|
2309
|
+
type: 'ArrowFunctionExpression',
|
|
2310
|
+
params,
|
|
2311
|
+
body
|
|
2312
|
+
};
|
|
2313
|
+
}
|
|
2320
2314
|
|
|
2321
2315
|
primary() {
|
|
2322
2316
|
const t = this.current;
|
|
@@ -2343,17 +2337,47 @@ class Parser {
|
|
|
2343
2337
|
}
|
|
2344
2338
|
|
|
2345
2339
|
if (t.type === 'IDENTIFIER') {
|
|
2346
|
-
|
|
2340
|
+
const name = t.value;
|
|
2341
|
+
this.eat('IDENTIFIER');
|
|
2342
|
+
|
|
2343
|
+
if (this.current.type === 'ARROW') {
|
|
2344
|
+
return this.arrowFunction([name]);
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
return { type: 'Identifier', name };
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
|
|
2351
|
+
if (t.type === 'LPAREN') {
|
|
2352
|
+
this.eat('LPAREN');
|
|
2353
|
+
|
|
2354
|
+
const params = [];
|
|
2355
|
+
|
|
2356
|
+
if (this.current.type !== 'RPAREN') {
|
|
2357
|
+
params.push(this.current.value);
|
|
2358
|
+
this.eat('IDENTIFIER');
|
|
2359
|
+
while (this.current.type === 'COMMA') {
|
|
2360
|
+
this.eat('COMMA');
|
|
2361
|
+
params.push(this.current.value);
|
|
2347
2362
|
this.eat('IDENTIFIER');
|
|
2348
|
-
return { type: 'Identifier', name };
|
|
2349
2363
|
}
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
this.eat('RPAREN');
|
|
2367
|
+
|
|
2368
|
+
if (this.current.type === 'ARROW') {
|
|
2369
|
+
return this.arrowFunction(params);
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
if (params.length === 1) {
|
|
2373
|
+
return { type: 'Identifier', name: params[0] };
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
throw new Error(
|
|
2377
|
+
`Invalid grouped expression at line ${this.current.line}, column ${this.current.column}`
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2350
2380
|
|
|
2351
|
-
if (t.type === 'LPAREN') {
|
|
2352
|
-
this.eat('LPAREN');
|
|
2353
|
-
const expr = this.expression();
|
|
2354
|
-
this.eat('RPAREN');
|
|
2355
|
-
return expr;
|
|
2356
|
-
}
|
|
2357
2381
|
|
|
2358
2382
|
if (t.type === 'LBRACKET') {
|
|
2359
2383
|
this.eat('LBRACKET');
|
|
@@ -2392,6 +2416,14 @@ class Parser {
|
|
|
2392
2416
|
|
|
2393
2417
|
module.exports = Parser;
|
|
2394
2418
|
|
|
2419
|
+
/***/ }),
|
|
2420
|
+
|
|
2421
|
+
/***/ 266:
|
|
2422
|
+
/***/ ((module) => {
|
|
2423
|
+
|
|
2424
|
+
module.exports = eval("require")("node-fetch");
|
|
2425
|
+
|
|
2426
|
+
|
|
2395
2427
|
/***/ }),
|
|
2396
2428
|
|
|
2397
2429
|
/***/ 317:
|
|
@@ -2492,7 +2524,7 @@ const Lexer = __nccwpck_require__(211);
|
|
|
2492
2524
|
const Parser = __nccwpck_require__(222);
|
|
2493
2525
|
const Evaluator = __nccwpck_require__(112);
|
|
2494
2526
|
|
|
2495
|
-
const VERSION = '1.0.
|
|
2527
|
+
const VERSION = '1.0.25';
|
|
2496
2528
|
|
|
2497
2529
|
const COLOR = {
|
|
2498
2530
|
reset: '\x1b[0m',
|