starlight-cli 1.0.21 → 1.0.23

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 CHANGED
@@ -1347,7 +1347,9 @@ const Lexer = __nccwpck_require__(211);
1347
1347
  const Parser = __nccwpck_require__(222);
1348
1348
  const path = __nccwpck_require__(928);
1349
1349
 
1350
- class ReturnValue { constructor(value) { this.value = value; } }
1350
+ class ReturnValue {
1351
+ constructor(value) { this.value = value; }
1352
+ }
1351
1353
  class BreakSignal {}
1352
1354
  class ContinueSignal {}
1353
1355
 
@@ -1390,27 +1392,37 @@ class Evaluator {
1390
1392
 
1391
1393
  setupBuiltins() {
1392
1394
  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;
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;
1396
1398
  });
1397
1399
 
1398
1400
  this.global.define('print', arg => { console.log(arg); return null; });
1399
1401
  this.global.define('type', arg => {
1400
- if (Array.isArray(arg)) return 'array';
1401
- return typeof arg;
1402
+ if (Array.isArray(arg)) return 'array';
1403
+ return typeof arg;
1402
1404
  });
1403
1405
  this.global.define('keys', arg => arg && typeof arg === 'object' ? Object.keys(arg) : []);
1404
1406
  this.global.define('values', arg => arg && typeof arg === 'object' ? Object.values(arg) : []);
1405
1407
 
1406
- this.global.define('ask', prompt => readlineSync.question(prompt + ' '));
1407
- this.global.define('num', arg => {
1408
- const n = Number(arg);
1409
- if (Number.isNaN(n)) throw new Error('Cannot convert value to number');
1410
- return n;
1408
+ this.global.define('ask', prompt => {
1409
+ const readlineSync = __nccwpck_require__(552);
1410
+ return readlineSync.question(prompt + ' ');
1411
1411
  });
1412
- this.global.define('str', arg => String(arg));
1413
- }
1412
+ this.global.define('num', arg => {
1413
+ const n = Number(arg);
1414
+ if (Number.isNaN(n)) {
1415
+ throw new Error('Cannot convert value to number');
1416
+ }
1417
+ return n;
1418
+ });
1419
+
1420
+ this.global.define('str', arg => {
1421
+ return String(arg);
1422
+ });
1423
+
1424
+ }
1425
+
1414
1426
 
1415
1427
  evaluate(node, env = this.global) {
1416
1428
  switch (node.type) {
@@ -1427,7 +1439,6 @@ class Evaluator {
1427
1439
  case 'LogicalExpression': return this.evalLogical(node, env);
1428
1440
  case 'UnaryExpression': return this.evalUnary(node, env);
1429
1441
  case 'Literal': return node.value;
1430
- case 'TemplateLiteral': return this.evalTemplateLiteral(node, env);
1431
1442
  case 'Identifier': return env.get(node.name);
1432
1443
  case 'IfStatement': return this.evalIf(node, env);
1433
1444
  case 'WhileStatement': return this.evalWhile(node, env);
@@ -1453,65 +1464,72 @@ class Evaluator {
1453
1464
 
1454
1465
  evalProgram(node, env) {
1455
1466
  let result = null;
1456
- for (const stmt of node.body) result = this.evaluate(stmt, env);
1467
+ for (const stmt of node.body) {
1468
+ result = this.evaluate(stmt, env);
1469
+ }
1457
1470
  return result;
1458
1471
  }
1472
+ evalImport(node, env) {
1473
+ const spec = node.path;
1474
+ let lib;
1459
1475
 
1460
- evalTemplateLiteral(node, env) {
1461
- // node.parts is an array from parser: Literal or expression nodes
1462
- let result = '';
1463
- for (const part of node.parts) {
1464
- if (part.type === 'Literal') {
1465
- result += part.value;
1466
- } else {
1467
- const val = this.evaluate(part, env);
1468
- result += val != null ? val.toString() : '';
1476
+ try {
1477
+ const resolved = require.resolve(spec, {
1478
+ paths: [process.cwd()]
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
+
1486
+ if (!fs.existsSync(fullPath)) {
1487
+ throw new Error(`Import not found: ${spec}`);
1469
1488
  }
1470
- }
1471
- return result;
1472
- }
1473
1489
 
1490
+ const code = fs.readFileSync(fullPath, 'utf-8');
1491
+ const tokens = new Lexer(code).getTokens();
1492
+ const ast = new Parser(tokens).parse();
1474
1493
 
1475
- evalImport(node, env) {
1476
- const spec = node.path;
1477
- let lib;
1478
- try {
1479
- const resolved = require.resolve(spec, { paths: [process.cwd()] });
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');
1494
+ const moduleEnv = new Environment(env);
1495
+ this.evaluate(ast, moduleEnv);
1485
1496
 
1486
- if (!fs.existsSync(fullPath)) throw new Error(`Import not found: ${spec}`);
1497
+ lib = {};
1498
+ for (const key of Object.keys(moduleEnv.store)) {
1499
+ lib[key] = moduleEnv.store[key];
1500
+ }
1487
1501
 
1488
- const code = fs.readFileSync(fullPath, 'utf-8');
1489
- const tokens = new Lexer(code).getTokens();
1490
- const ast = new Parser(tokens).parse();
1491
- const moduleEnv = new Environment(env);
1492
- this.evaluate(ast, moduleEnv);
1502
+ lib.default = lib;
1503
+ }
1493
1504
 
1494
- lib = {};
1495
- for (const key of Object.keys(moduleEnv.store)) lib[key] = moduleEnv.store[key];
1496
- lib.default = lib;
1505
+ for (const imp of node.specifiers) {
1506
+ if (imp.type === 'DefaultImport') {
1507
+ env.define(imp.local, lib.default ?? lib);
1497
1508
  }
1498
-
1499
- for (const imp of node.specifiers) {
1500
- if (imp.type === 'DefaultImport') env.define(imp.local, lib.default ?? lib);
1501
- if (imp.type === 'NamespaceImport') env.define(imp.local, lib);
1502
- if (imp.type === 'NamedImport') {
1503
- if (!(imp.imported in lib)) throw new Error(`Module '${spec}' has no export '${imp.imported}'`);
1504
- env.define(imp.local, lib[imp.imported]);
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}'`);
1505
1515
  }
1516
+ env.define(imp.local, lib[imp.imported]);
1506
1517
  }
1507
- return null;
1508
1518
  }
1509
1519
 
1520
+ return null;
1521
+ }
1522
+
1523
+
1510
1524
  evalBlock(node, env) {
1511
1525
  let result = null;
1512
1526
  for (const stmt of node.body) {
1513
- try { result = this.evaluate(stmt, env); }
1514
- catch (e) { if (e instanceof ReturnValue || e instanceof BreakSignal || e instanceof ContinueSignal) throw e; else throw e; }
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
+ }
1515
1533
  }
1516
1534
  return result;
1517
1535
  }
@@ -1524,15 +1542,27 @@ class Evaluator {
1524
1542
  evalAssignment(node, env) {
1525
1543
  const rightVal = this.evaluate(node.right, env);
1526
1544
  const left = node.left;
1545
+
1527
1546
  if (left.type === 'Identifier') return env.set(left.name, rightVal);
1528
- if (left.type === 'MemberExpression') { const obj = this.evaluate(left.object, env); obj[left.property] = rightVal; return rightVal; }
1529
- if (left.type === 'IndexExpression') { const obj = this.evaluate(left.object, env); const idx = this.evaluate(left.indexer, env); obj[idx] = rightVal; return rightVal; }
1547
+ if (left.type === 'MemberExpression') {
1548
+ const obj = this.evaluate(left.object, env);
1549
+ obj[left.property] = rightVal;
1550
+ return rightVal;
1551
+ }
1552
+ if (left.type === 'IndexExpression') {
1553
+ const obj = this.evaluate(left.object, env);
1554
+ const idx = this.evaluate(left.indexer, env);
1555
+ obj[idx] = rightVal;
1556
+ return rightVal;
1557
+ }
1558
+
1530
1559
  throw new Error('Invalid assignment target');
1531
1560
  }
1532
1561
 
1533
1562
  evalCompoundAssignment(node, env) {
1534
1563
  const left = node.left;
1535
1564
  let current;
1565
+
1536
1566
  if (left.type === 'Identifier') current = env.get(left.name);
1537
1567
  else if (left.type === 'MemberExpression') current = this.evalMember(left, env);
1538
1568
  else if (left.type === 'IndexExpression') current = this.evalIndex(left, env);
@@ -1550,7 +1580,9 @@ class Evaluator {
1550
1580
  }
1551
1581
 
1552
1582
  if (left.type === 'Identifier') env.set(left.name, computed);
1583
+ else if (left.type === 'MemberExpression') this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
1553
1584
  else this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
1585
+
1554
1586
  return computed;
1555
1587
  }
1556
1588
 
@@ -1562,7 +1594,8 @@ class Evaluator {
1562
1594
 
1563
1595
  evalAsk(node, env) {
1564
1596
  const prompt = this.evaluate(node.prompt, env);
1565
- return readlineSync.question(prompt + ' ');
1597
+ const input = readlineSync.question(prompt + ' ');
1598
+ return input;
1566
1599
  }
1567
1600
 
1568
1601
  evalDefine(node, env) {
@@ -1573,25 +1606,27 @@ class Evaluator {
1573
1606
  evalBinary(node, env) {
1574
1607
  const l = this.evaluate(node.left, env);
1575
1608
  const r = this.evaluate(node.right, env);
1609
+
1610
+ if (node.operator === 'SLASH' && r === 0) {
1611
+ throw new Error('Division by zero');
1612
+ }
1613
+
1576
1614
  switch (node.operator) {
1577
- case 'PLUS': return l + r;
1578
- case 'MINUS': return l - r;
1579
- case 'STAR': return l * r;
1580
- case 'SLASH': return l / r;
1581
- case 'MOD': return l % r;
1582
- case 'EQEQ': return l == r;
1583
- case 'NOTEQ': return l != r;
1584
- case 'STRICT_EQ': return l === r;
1585
- case 'STRICT_NOTEQ': return l !== r;
1586
- case 'LT': return l < r;
1587
- case 'LTE': return l <= r;
1588
- case 'GT': return l > r;
1589
- case 'GTE': return l >= r;
1590
- case 'LSHIFT': return l << r;
1591
- case 'RSHIFT': return l >> r;
1592
- default: throw new Error(`Unknown binary operator ${node.operator}`);
1615
+ case 'PLUS': return l + r;
1616
+ case 'MINUS': return l - r;
1617
+ case 'STAR': return l * r;
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}`);
1593
1627
  }
1594
- }
1628
+ }
1629
+
1595
1630
 
1596
1631
  evalLogical(node, env) {
1597
1632
  const l = this.evaluate(node.left, env);
@@ -1656,7 +1691,9 @@ class Evaluator {
1656
1691
  const args = node.arguments.map(a => this.evaluate(a, env));
1657
1692
  return calleeEvaluated(...args);
1658
1693
  }
1659
- if (!calleeEvaluated || typeof calleeEvaluated !== 'object' || !calleeEvaluated.body) throw new Error('Call to non-function');
1694
+ if (!calleeEvaluated || typeof calleeEvaluated !== 'object' || !calleeEvaluated.body) {
1695
+ throw new Error('Call to non-function');
1696
+ }
1660
1697
  const fn = calleeEvaluated;
1661
1698
  const callEnv = new Environment(fn.env);
1662
1699
  fn.params.forEach((p, i) => {
@@ -1670,9 +1707,18 @@ class Evaluator {
1670
1707
  evalIndex(node, env) {
1671
1708
  const obj = this.evaluate(node.object, env);
1672
1709
  const idx = this.evaluate(node.indexer, env);
1673
- if (obj == null) throw new Error('Indexing null/undefined');
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
+ }
1718
+
1674
1719
  return obj[idx];
1675
- }
1720
+ }
1721
+
1676
1722
 
1677
1723
  evalObject(node, env) {
1678
1724
  const out = {};
@@ -1682,9 +1728,11 @@ class Evaluator {
1682
1728
 
1683
1729
  evalMember(node, env) {
1684
1730
  const obj = this.evaluate(node.object, env);
1685
- if (obj == null) throw new Error('Member access of null/undefined');
1731
+ if (obj == null) throw new Error('Member access of null or undefined');
1732
+ if (!(node.property in obj)) throw new Error(`Property '${node.property}' does not exist`);
1686
1733
  return obj[node.property];
1687
- }
1734
+ }
1735
+
1688
1736
 
1689
1737
  evalUpdate(node, env) {
1690
1738
  const arg = node.argument;
@@ -1696,8 +1744,15 @@ class Evaluator {
1696
1744
  };
1697
1745
  const setValue = (v) => {
1698
1746
  if (arg.type === 'Identifier') env.set(arg.name, v);
1699
- else if (arg.type === 'MemberExpression') { const obj = this.evaluate(arg.object, env); obj[arg.property] = v; }
1700
- else if (arg.type === 'IndexExpression') { const obj = this.evaluate(arg.object, env); const idx = this.evaluate(arg.indexer, env); obj[idx] = 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
+ }
1701
1756
  };
1702
1757
  const current = getCurrent();
1703
1758
  const newVal = (node.operator === 'PLUSPLUS') ? current + 1 : current - 1;
@@ -1706,8 +1761,7 @@ class Evaluator {
1706
1761
  }
1707
1762
  }
1708
1763
 
1709
- module.exports = Evaluator;
1710
-
1764
+ module.exports = Evaluator;
1711
1765
 
1712
1766
  /***/ }),
1713
1767
 
@@ -1718,10 +1772,18 @@ class Lexer {
1718
1772
  constructor(input) {
1719
1773
  this.input = input;
1720
1774
  this.pos = 0;
1721
- this.currentChar = input[this.pos];
1775
+ this.currentChar = input[0] || null;
1776
+ this.line = 1; // current line
1777
+ this.column = 1; // current column
1722
1778
  }
1723
1779
 
1724
1780
  advance() {
1781
+ if (this.currentChar === '\n') {
1782
+ this.line++;
1783
+ this.column = 0;
1784
+ } else {
1785
+ this.column++;
1786
+ }
1725
1787
  this.pos++;
1726
1788
  this.currentChar = this.pos < this.input.length ? this.input[this.pos] : null;
1727
1789
  }
@@ -1730,152 +1792,149 @@ class Lexer {
1730
1792
  return this.pos + 1 < this.input.length ? this.input[this.pos + 1] : null;
1731
1793
  }
1732
1794
 
1795
+ error(msg) {
1796
+ throw new Error(`${msg} at line ${this.line}, column ${this.column}`);
1797
+ }
1798
+
1733
1799
  skipWhitespace() {
1734
1800
  while (this.currentChar && /\s/.test(this.currentChar)) this.advance();
1735
1801
  }
1736
1802
 
1737
- matchKeyword(id) {
1738
- const keywords = {
1739
- 'let': 'LET',
1740
- 'define': 'DEFINE',
1741
- 'if': 'IF',
1742
- 'else': 'ELSE',
1743
- 'while': 'WHILE',
1744
- 'for': 'FOR',
1745
- 'break': 'BREAK',
1746
- 'continue': 'CONTINUE',
1747
- 'func': 'FUNC',
1748
- 'return': 'RETURN',
1749
- 'import': 'IMPORT',
1750
- 'from': 'FROM',
1751
- 'as': 'AS',
1752
- 'true': 'TRUE',
1753
- 'false': 'FALSE',
1754
- 'null': 'NULL',
1755
- 'undefined': 'UNDEFINED',
1756
- 'ask': 'ASK',
1757
- 'sldeploy': 'SLDEPLOY'
1758
- };
1759
- return keywords[id] || null;
1803
+ skipComment() {
1804
+ if (this.currentChar === '#') {
1805
+ if (this.peek() === '*') {
1806
+ this.advance(); this.advance(); // skip #*
1807
+ while (this.currentChar !== null) {
1808
+ if (this.currentChar === '*' && this.peek() === '#') {
1809
+ this.advance(); this.advance();
1810
+ return;
1811
+ }
1812
+ this.advance();
1813
+ }
1814
+ this.error("Unterminated multi-line comment (#* ... *#)");
1815
+ } else {
1816
+ while (this.currentChar && this.currentChar !== '\n') this.advance();
1817
+ }
1818
+ }
1760
1819
  }
1761
1820
 
1762
1821
  number() {
1822
+ const startLine = this.line;
1823
+ const startCol = this.column;
1763
1824
  let result = '';
1764
- while (this.currentChar && /[0-9.]/.test(this.currentChar)) {
1825
+ while (this.currentChar && /[0-9]/.test(this.currentChar)) {
1765
1826
  result += this.currentChar;
1766
1827
  this.advance();
1767
1828
  }
1768
- return { type: 'NUMBER', value: parseFloat(result) };
1829
+
1830
+ if (this.currentChar === '.' && /[0-9]/.test(this.peek())) {
1831
+ result += '.'; this.advance();
1832
+ while (this.currentChar && /[0-9]/.test(this.currentChar)) {
1833
+ result += this.currentChar;
1834
+ this.advance();
1835
+ }
1836
+ return { type: 'NUMBER', value: parseFloat(result), line: startLine, column: startCol };
1837
+ }
1838
+
1839
+ return { type: 'NUMBER', value: parseInt(result), line: startLine, column: startCol };
1769
1840
  }
1770
1841
 
1771
1842
  identifier() {
1843
+ const startLine = this.line;
1844
+ const startCol = this.column;
1772
1845
  let result = '';
1773
- while (this.currentChar && /[a-zA-Z0-9_]/.test(this.currentChar)) {
1846
+ while (this.currentChar && /[A-Za-z0-9_]/.test(this.currentChar)) {
1774
1847
  result += this.currentChar;
1775
1848
  this.advance();
1776
1849
  }
1777
- const type = this.matchKeyword(result) || 'IDENTIFIER';
1778
- return { type, value: result };
1850
+
1851
+ const keywords = [
1852
+ 'let', 'sldeploy', 'if', 'else', 'while', 'for',
1853
+ 'break', 'continue', 'func', 'return', 'true', 'false', 'null',
1854
+ 'ask', 'define', 'import', 'from', 'as'
1855
+ ];
1856
+
1857
+ if (keywords.includes(result)) {
1858
+ return { type: result.toUpperCase(), value: result, line: startLine, column: startCol };
1859
+ }
1860
+
1861
+ return { type: 'IDENTIFIER', value: result, line: startLine, column: startCol };
1779
1862
  }
1780
1863
 
1781
- string(quoteType) {
1782
- this.advance(); // skip opening quote
1864
+ string() {
1865
+ const startLine = this.line;
1866
+ const startCol = this.column;
1867
+ const quote = this.currentChar;
1868
+ this.advance();
1783
1869
  let result = '';
1784
- while (this.currentChar && this.currentChar !== quoteType) {
1870
+
1871
+ while (this.currentChar && this.currentChar !== quote) {
1785
1872
  if (this.currentChar === '\\') {
1786
1873
  this.advance();
1787
- if (this.currentChar) {
1788
- const escapeChars = { n: '\n', r: '\r', t: '\t', '\\': '\\', '"': '"', "'": "'" };
1789
- result += escapeChars[this.currentChar] || this.currentChar;
1790
- this.advance();
1874
+ switch (this.currentChar) {
1875
+ case 'n': result += '\n'; break;
1876
+ case 't': result += '\t'; break;
1877
+ case '"': result += '"'; break;
1878
+ case "'": result += "'"; break;
1879
+ case '\\': result += '\\'; break;
1880
+ default: result += this.currentChar;
1791
1881
  }
1792
1882
  } else {
1793
1883
  result += this.currentChar;
1794
- this.advance();
1795
1884
  }
1885
+ this.advance();
1796
1886
  }
1797
- this.advance(); // skip closing quote
1798
- return { type: 'STRING', value: result };
1799
- }
1800
1887
 
1801
- templateLiteral() {
1802
- let result = '';
1803
- const tokens = [];
1804
- this.advance(); // skip initial backtick
1805
- while (this.currentChar !== null) {
1806
- if (this.currentChar === '$' && this.peek() === '{') {
1807
- if (result) tokens.push({ type: 'TEMPLATE_STRING', value: result });
1808
- result = '';
1809
- tokens.push({ type: 'DOLLAR_LBRACE' });
1810
- this.advance();
1811
- this.advance(); // skip "${"
1812
- } else if (this.currentChar === '`') {
1813
- if (result) tokens.push({ type: 'TEMPLATE_STRING', value: result });
1814
- this.advance();
1815
- break;
1816
- } else {
1817
- result += this.currentChar;
1818
- this.advance();
1819
- }
1888
+ if (this.currentChar !== quote) {
1889
+ this.error('Unterminated string literal');
1820
1890
  }
1821
- return tokens;
1891
+
1892
+ this.advance();
1893
+ return { type: 'STRING', value: result, line: startLine, column: startCol };
1822
1894
  }
1823
1895
 
1824
1896
  getTokens() {
1825
1897
  const tokens = [];
1826
- while (this.currentChar !== null) {
1827
- this.skipWhitespace();
1828
-
1829
- if (!this.currentChar) break;
1830
1898
 
1831
- if (/[0-9]/.test(this.currentChar)) tokens.push(this.number());
1832
- else if (/[a-zA-Z_]/.test(this.currentChar)) tokens.push(this.identifier());
1833
- else if (this.currentChar === '"' || this.currentChar === "'") tokens.push(this.string(this.currentChar));
1834
- else if (this.currentChar === '`') {
1835
- const parts = this.templateLiteral();
1836
- tokens.push(...parts);
1837
- }
1838
- else {
1839
- const char = this.currentChar;
1840
- switch (char) {
1841
- case '+':
1842
- if (this.peek() === '+') { tokens.push({ type: 'PLUSPLUS' }); this.advance(); }
1843
- else if (this.peek() === '=') { tokens.push({ type: 'PLUSEQ' }); this.advance(); }
1844
- else tokens.push({ type: 'PLUS' });
1845
- break;
1846
- case '-':
1847
- if (this.peek() === '-') { tokens.push({ type: 'MINUSMINUS' }); this.advance(); }
1848
- else if (this.peek() === '=') { tokens.push({ type: 'MINUSEQ' }); this.advance(); }
1849
- else tokens.push({ type: 'MINUS' });
1850
- break;
1851
- case '*': tokens.push(this.peek() === '=' ? (this.advance(), { type: 'STAREQ' }) : { type: 'STAR' }); break;
1852
- case '/': tokens.push(this.peek() === '=' ? (this.advance(), { type: 'SLASHEQ' }) : { type: 'SLASH' }); break;
1853
- case '%': tokens.push(this.peek() === '=' ? (this.advance(), { type: 'MODEQ' }) : { type: 'MOD' }); break;
1854
- case '=': tokens.push(this.peek() === '=' ? (this.advance(), this.peek() === '=' ? (this.advance(), { type: 'STRICT_EQ' }) : { type: 'EQEQ' }) : { type: 'EQUAL' }); break;
1855
- case '!': tokens.push(this.peek() === '=' ? (this.advance(), this.peek() === '=' ? (this.advance(), { type: 'STRICT_NOTEQ' }) : { type: 'NOTEQ' }) : { type: 'NOT' }); break;
1856
- case '<': tokens.push(this.peek() === '<' ? (this.advance(), { type: 'LSHIFT' }) : this.peek() === '=' ? (this.advance(), { type: 'LTE' }) : { type: 'LT' }); break;
1857
- case '>': tokens.push(this.peek() === '>' ? (this.advance(), { type: 'RSHIFT' }) : this.peek() === '=' ? (this.advance(), { type: 'GTE' }) : { type: 'GT' }); break;
1858
- case '&': tokens.push(this.peek() === '&' ? (this.advance(), { type: 'AND' }) : { type: 'AMP' }); break;
1859
- case '|': tokens.push(this.peek() === '|' ? (this.advance(), { type: 'OR' }) : { type: 'PIPE' }); break;
1860
- case '(': tokens.push({ type: 'LPAREN' }); break;
1861
- case ')': tokens.push({ type: 'RPAREN' }); break;
1862
- case '{': tokens.push({ type: 'LBRACE' }); break;
1863
- case '}': tokens.push({ type: 'RBRACE' }); break;
1864
- case '[': tokens.push({ type: 'LBRACKET' }); break;
1865
- case ']': tokens.push({ type: 'RBRACKET' }); break;
1866
- case ',': tokens.push({ type: 'COMMA' }); break;
1867
- case ';': tokens.push({ type: 'SEMICOLON' }); break;
1868
- case '.': tokens.push({ type: 'DOT' }); break;
1869
- case ':': tokens.push({ type: 'COLON' }); break;
1870
- case '*': tokens.push({ type: 'STAR' }); break;
1871
- case '$': tokens.push({ type: 'DOLLAR' }); break;
1872
- case '?': tokens.push({ type: 'QUESTION' }); break;
1873
- default: throw new Error(`Unexpected character: ${char} at position ${this.pos}`);
1874
- }
1875
- this.advance();
1876
- }
1899
+ while (this.currentChar !== null) {
1900
+ if (/\s/.test(this.currentChar)) { this.skipWhitespace(); continue; }
1901
+ if (this.currentChar === '#') { this.skipComment(); continue; }
1902
+ if (/[0-9]/.test(this.currentChar)) { tokens.push(this.number()); continue; }
1903
+ if (/[A-Za-z_]/.test(this.currentChar)) { tokens.push(this.identifier()); continue; }
1904
+ if (this.currentChar === '"' || this.currentChar === "'") { tokens.push(this.string()); continue; }
1905
+
1906
+ const char = this.currentChar;
1907
+ const next = this.peek();
1908
+ const startLine = this.line;
1909
+ const startCol = this.column;
1910
+
1911
+ if (char === '=' && next === '=') { tokens.push({ type: 'EQEQ', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1912
+ if (char === '=' && next === '>') { tokens.push({ type: 'ARROW', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1913
+ if (char === '!' && next === '=') { tokens.push({ type: 'NOTEQ', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1914
+ if (char === '<' && next === '=') { tokens.push({ type: 'LTE', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1915
+ if (char === '>' && next === '=') { tokens.push({ type: 'GTE', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1916
+ if (char === '&' && next === '&') { tokens.push({ type: 'AND', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1917
+ if (char === '|' && next === '|') { tokens.push({ type: 'OR', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1918
+ if (char === '+' && next === '+') { tokens.push({ type: 'PLUSPLUS', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1919
+ if (char === '-' && next === '-') { tokens.push({ type: 'MINUSMINUS', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1920
+
1921
+ const map = { '+': 'PLUSEQ', '-': 'MINUSEQ', '*': 'STAREQ', '/': 'SLASHEQ', '%': 'MODEQ' };
1922
+ if (next === '=' && map[char]) { tokens.push({ type: map[char], line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1923
+
1924
+ const singles = {
1925
+ '+': 'PLUS', '-': 'MINUS', '*': 'STAR', '/': 'SLASH', '%': 'MOD',
1926
+ '=': 'EQUAL', '<': 'LT', '>': 'GT', '!': 'NOT',
1927
+ '(': 'LPAREN', ')': 'RPAREN', '{': 'LBRACE', '}': 'RBRACE',
1928
+ ';': 'SEMICOLON', ',': 'COMMA', '[': 'LBRACKET', ']': 'RBRACKET',
1929
+ ':': 'COLON', '.': 'DOT'
1930
+ };
1931
+
1932
+ if (singles[char]) { tokens.push({ type: singles[char], line: startLine, column: startCol }); this.advance(); continue; }
1933
+
1934
+ this.error("Unexpected character: " + char);
1877
1935
  }
1878
- tokens.push({ type: 'EOF' });
1936
+
1937
+ tokens.push({ type: 'EOF', line: this.line, column: this.column });
1879
1938
  return tokens;
1880
1939
  }
1881
1940
  }
@@ -1902,7 +1961,10 @@ class Parser {
1902
1961
 
1903
1962
  eat(type) {
1904
1963
  if (this.current.type === type) this.advance();
1905
- else throw new Error(`Expected ${type}, got ${this.current.type}`);
1964
+ else throw new Error(
1965
+ `Expected ${type}, got ${this.current.type} at line ${this.current.line}, column ${this.current.column}`
1966
+ );
1967
+
1906
1968
  }
1907
1969
 
1908
1970
  peekType(offset = 1) {
@@ -1989,52 +2051,52 @@ class Parser {
1989
2051
  const body = this.block();
1990
2052
  return { type: 'WhileStatement', test, body };
1991
2053
  }
1992
-
1993
2054
  importStatement() {
1994
2055
  this.eat('IMPORT');
1995
2056
 
1996
2057
  let specifiers = [];
1997
2058
 
1998
2059
  if (this.current.type === 'STAR') {
1999
- this.eat('STAR');
2000
- this.eat('AS');
2001
- const name = this.current.value;
2002
- this.eat('IDENTIFIER');
2003
- specifiers.push({ type: 'NamespaceImport', local: name });
2060
+ this.eat('STAR');
2061
+ this.eat('AS');
2062
+ const name = this.current.value;
2063
+ this.eat('IDENTIFIER');
2064
+ specifiers.push({ type: 'NamespaceImport', local: name });
2004
2065
  }
2005
2066
  else if (this.current.type === 'LBRACE') {
2006
- this.eat('LBRACE');
2007
- while (this.current.type !== 'RBRACE') {
2008
- const importedName = this.current.value;
2009
- this.eat('IDENTIFIER');
2010
- let localName = importedName;
2011
- if (this.current.type === 'AS') {
2012
- this.eat('AS');
2013
- localName = this.current.value;
2014
- this.eat('IDENTIFIER');
2067
+ this.eat('LBRACE');
2068
+ while (this.current.type !== 'RBRACE') {
2069
+ const importedName = this.current.value;
2070
+ this.eat('IDENTIFIER');
2071
+ let localName = importedName;
2072
+ if (this.current.type === 'AS') {
2073
+ this.eat('AS');
2074
+ localName = this.current.value;
2075
+ this.eat('IDENTIFIER');
2076
+ }
2077
+ specifiers.push({ type: 'NamedImport', imported: importedName, local: localName });
2078
+ if (this.current.type === 'COMMA') this.eat('COMMA');
2015
2079
  }
2016
- specifiers.push({ type: 'NamedImport', imported: importedName, local: localName });
2017
- if (this.current.type === 'COMMA') this.eat('COMMA');
2018
- }
2019
- this.eat('RBRACE');
2080
+ this.eat('RBRACE');
2020
2081
  }
2021
2082
  else if (this.current.type === 'IDENTIFIER') {
2022
- const localName = this.current.value;
2023
- this.eat('IDENTIFIER');
2024
- specifiers.push({ type: 'DefaultImport', local: localName });
2083
+ const localName = this.current.value;
2084
+ this.eat('IDENTIFIER');
2085
+ specifiers.push({ type: 'DefaultImport', local: localName });
2025
2086
  } else {
2026
- throw new Error('Unexpected token in import statement');
2087
+ throw new Error(`Unexpected token in import statement at line ${this.current.line}, column ${this.current.column}`);
2027
2088
  }
2028
2089
 
2029
2090
  this.eat('FROM');
2030
2091
  const pathToken = this.current;
2031
- if (pathToken.type !== 'STRING') throw new Error('Expected string literal after from in import');
2092
+ if (pathToken.type !== 'STRING') throw new Error(`Expected string literal after from in import at line ${this.current.line}, column ${this.current.column}`);
2032
2093
  this.eat('STRING');
2033
2094
 
2034
2095
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2035
2096
 
2036
2097
  return { type: 'ImportStatement', path: pathToken.value, specifiers };
2037
- }
2098
+ }
2099
+
2038
2100
 
2039
2101
  forStatement() {
2040
2102
  this.eat('FOR');
@@ -2114,23 +2176,6 @@ class Parser {
2114
2176
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2115
2177
  return { type: 'ExpressionStatement', expression: expr };
2116
2178
  }
2117
- parseTemplateLiteral() {
2118
- const parts = [];
2119
- while (true) {
2120
- if (this.current.type === 'TEMPLATE_STRING') {
2121
- parts.push({ type: 'Literal', value: this.current.value });
2122
- this.eat('TEMPLATE_STRING');
2123
- } else if (this.current.type === 'DOLLAR_LBRACE') {
2124
- this.eat('DOLLAR_LBRACE');
2125
- const expr = this.expression();
2126
- parts.push(expr);
2127
- this.eat('RBRACE');
2128
- } else {
2129
- break;
2130
- }
2131
- }
2132
- return { type: 'TemplateLiteral', parts };
2133
- }
2134
2179
 
2135
2180
  expression() {
2136
2181
  return this.assignment();
@@ -2177,7 +2222,7 @@ class Parser {
2177
2222
 
2178
2223
  equality() {
2179
2224
  let node = this.comparison();
2180
- while (['EQEQ', 'NOTEQ', 'STRICT_EQ', 'STRICT_NOTEQ'].includes(this.current.type)) {
2225
+ while (['EQEQ', 'NOTEQ'].includes(this.current.type)) {
2181
2226
  const op = this.current.type;
2182
2227
  this.eat(op);
2183
2228
  node = { type: 'BinaryExpression', operator: op, left: node, right: this.comparison() };
@@ -2187,7 +2232,7 @@ class Parser {
2187
2232
 
2188
2233
  comparison() {
2189
2234
  let node = this.term();
2190
- while (['LT', 'LTE', 'GT', 'GTE', 'LSHIFT', 'RSHIFT'].includes(this.current.type)) {
2235
+ while (['LT', 'LTE', 'GT', 'GTE'].includes(this.current.type)) {
2191
2236
  const op = this.current.type;
2192
2237
  this.eat(op);
2193
2238
  node = { type: 'BinaryExpression', operator: op, left: node, right: this.term() };
@@ -2256,7 +2301,7 @@ class Parser {
2256
2301
  }
2257
2302
  if (this.current.type === 'DOT') {
2258
2303
  this.eat('DOT');
2259
- if (this.current.type !== 'IDENTIFIER') throw new Error('Expected property name after dot');
2304
+ if (this.current.type !== 'IDENTIFIER') throw new Error(`Expected property name after dot at line ${this.current.line}, column ${this.current.column}`);
2260
2305
  const property = this.current.value;
2261
2306
  this.eat('IDENTIFIER');
2262
2307
  node = { type: 'MemberExpression', object: node, property };
@@ -2278,15 +2323,9 @@ class Parser {
2278
2323
 
2279
2324
  if (t.type === 'NUMBER') { this.eat('NUMBER'); return { type: 'Literal', value: t.value }; }
2280
2325
  if (t.type === 'STRING') { this.eat('STRING'); return { type: 'Literal', value: t.value }; }
2281
- if (t.type === 'TEMPLATE_STRING') {
2282
- return this.parseTemplateLiteral();
2283
- }
2284
-
2285
-
2286
2326
  if (t.type === 'TRUE') { this.eat('TRUE'); return { type: 'Literal', value: true }; }
2287
2327
  if (t.type === 'FALSE') { this.eat('FALSE'); return { type: 'Literal', value: false }; }
2288
2328
  if (t.type === 'NULL') { this.eat('NULL'); return { type: 'Literal', value: null }; }
2289
- if (t.type === 'UNDEFINED') { this.eat('UNDEFINED'); return { type: 'Literal', value: undefined }; }
2290
2329
 
2291
2330
  if (t.type === 'ASK') {
2292
2331
  this.eat('ASK');
@@ -2337,7 +2376,7 @@ class Parser {
2337
2376
  let key;
2338
2377
  if (this.current.type === 'STRING') { key = this.current.value; this.eat('STRING'); }
2339
2378
  else if (this.current.type === 'IDENTIFIER') { key = this.current.value; this.eat('IDENTIFIER'); }
2340
- else throw new Error('Invalid object key');
2379
+ else throw new Error(`Invalid object key at line ${this.current.line}, column ${this.current.column}`);
2341
2380
  this.eat('COLON');
2342
2381
  const value = this.expression();
2343
2382
  props.push({ key, value });
@@ -2347,12 +2386,11 @@ class Parser {
2347
2386
  return { type: 'ObjectExpression', props };
2348
2387
  }
2349
2388
 
2350
- throw new Error(`Unexpected token in primary: ${t.type}`);
2389
+ throw new Error(`Unexpected token in primary: ${t.type} at line ${this.current.line}, column ${this.current.column}`);
2351
2390
  }
2352
2391
  }
2353
2392
 
2354
- module.exports = Parser;
2355
-
2393
+ module.exports = Parser;
2356
2394
 
2357
2395
  /***/ }),
2358
2396
 
@@ -2454,7 +2492,7 @@ const Lexer = __nccwpck_require__(211);
2454
2492
  const Parser = __nccwpck_require__(222);
2455
2493
  const Evaluator = __nccwpck_require__(112);
2456
2494
 
2457
- const VERSION = '1.0.21';
2495
+ const VERSION = '1.0.23';
2458
2496
 
2459
2497
  const COLOR = {
2460
2498
  reset: '\x1b[0m',