starlight-cli 1.0.20 → 1.0.22

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) {
@@ -1579,16 +1612,12 @@ class Evaluator {
1579
1612
  case 'STAR': return l * r;
1580
1613
  case 'SLASH': return l / r;
1581
1614
  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;
1615
+ case 'EQEQ': return l === r;
1616
+ case 'NOTEQ': return l !== r;
1586
1617
  case 'LT': return l < r;
1587
1618
  case 'LTE': return l <= r;
1588
1619
  case 'GT': return l > r;
1589
1620
  case 'GTE': return l >= r;
1590
- case 'LSHIFT': return l << r;
1591
- case 'RSHIFT': return l >> r;
1592
1621
  default: throw new Error(`Unknown binary operator ${node.operator}`);
1593
1622
  }
1594
1623
  }
@@ -1656,7 +1685,9 @@ class Evaluator {
1656
1685
  const args = node.arguments.map(a => this.evaluate(a, env));
1657
1686
  return calleeEvaluated(...args);
1658
1687
  }
1659
- if (!calleeEvaluated || typeof calleeEvaluated !== 'object' || !calleeEvaluated.body) throw new Error('Call to non-function');
1688
+ if (!calleeEvaluated || typeof calleeEvaluated !== 'object' || !calleeEvaluated.body) {
1689
+ throw new Error('Call to non-function');
1690
+ }
1660
1691
  const fn = calleeEvaluated;
1661
1692
  const callEnv = new Environment(fn.env);
1662
1693
  fn.params.forEach((p, i) => {
@@ -1696,8 +1727,15 @@ class Evaluator {
1696
1727
  };
1697
1728
  const setValue = (v) => {
1698
1729
  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; }
1730
+ else if (arg.type === 'MemberExpression') {
1731
+ const obj = this.evaluate(arg.object, env);
1732
+ obj[arg.property] = v;
1733
+ }
1734
+ else if (arg.type === 'IndexExpression') {
1735
+ const obj = this.evaluate(arg.object, env);
1736
+ const idx = this.evaluate(arg.indexer, env);
1737
+ obj[idx] = v;
1738
+ }
1701
1739
  };
1702
1740
  const current = getCurrent();
1703
1741
  const newVal = (node.operator === 'PLUSPLUS') ? current + 1 : current - 1;
@@ -1706,8 +1744,7 @@ class Evaluator {
1706
1744
  }
1707
1745
  }
1708
1746
 
1709
- module.exports = Evaluator;
1710
-
1747
+ module.exports = Evaluator;
1711
1748
 
1712
1749
  /***/ }),
1713
1750
 
@@ -1719,19 +1756,27 @@ class Lexer {
1719
1756
  this.input = input;
1720
1757
  this.pos = 0;
1721
1758
  this.currentChar = input[0] || null;
1759
+ this.line = 1; // current line
1760
+ this.column = 1; // current column
1722
1761
  }
1723
1762
 
1724
1763
  advance() {
1764
+ if (this.currentChar === '\n') {
1765
+ this.line++;
1766
+ this.column = 0;
1767
+ } else {
1768
+ this.column++;
1769
+ }
1725
1770
  this.pos++;
1726
1771
  this.currentChar = this.pos < this.input.length ? this.input[this.pos] : null;
1727
1772
  }
1728
1773
 
1729
- peek(n = 1) {
1730
- return this.pos + n < this.input.length ? this.input[this.pos + n] : null;
1774
+ peek() {
1775
+ return this.pos + 1 < this.input.length ? this.input[this.pos + 1] : null;
1731
1776
  }
1732
1777
 
1733
1778
  error(msg) {
1734
- throw new Error(`LEXER ERROR: ${msg} at position ${this.pos}`);
1779
+ throw new Error(`${msg} at line ${this.line}, column ${this.column}`);
1735
1780
  }
1736
1781
 
1737
1782
  skipWhitespace() {
@@ -1741,7 +1786,7 @@ class Lexer {
1741
1786
  skipComment() {
1742
1787
  if (this.currentChar === '#') {
1743
1788
  if (this.peek() === '*') {
1744
- this.advance(); this.advance(); // #*
1789
+ this.advance(); this.advance(); // skip #*
1745
1790
  while (this.currentChar !== null) {
1746
1791
  if (this.currentChar === '*' && this.peek() === '#') {
1747
1792
  this.advance(); this.advance();
@@ -1749,7 +1794,7 @@ class Lexer {
1749
1794
  }
1750
1795
  this.advance();
1751
1796
  }
1752
- this.error('Unterminated multi-line comment (#* ... *#)');
1797
+ this.error("Unterminated multi-line comment (#* ... *#)");
1753
1798
  } else {
1754
1799
  while (this.currentChar && this.currentChar !== '\n') this.advance();
1755
1800
  }
@@ -1757,6 +1802,8 @@ class Lexer {
1757
1802
  }
1758
1803
 
1759
1804
  number() {
1805
+ const startLine = this.line;
1806
+ const startCol = this.column;
1760
1807
  let result = '';
1761
1808
  while (this.currentChar && /[0-9]/.test(this.currentChar)) {
1762
1809
  result += this.currentChar;
@@ -1764,32 +1811,20 @@ class Lexer {
1764
1811
  }
1765
1812
 
1766
1813
  if (this.currentChar === '.' && /[0-9]/.test(this.peek())) {
1767
- result += '.';
1768
- this.advance();
1814
+ result += '.'; this.advance();
1769
1815
  while (this.currentChar && /[0-9]/.test(this.currentChar)) {
1770
1816
  result += this.currentChar;
1771
1817
  this.advance();
1772
1818
  }
1819
+ return { type: 'NUMBER', value: parseFloat(result), line: startLine, column: startCol };
1773
1820
  }
1774
1821
 
1775
- if (this.currentChar && /[eE]/.test(this.currentChar)) {
1776
- result += this.currentChar;
1777
- this.advance();
1778
- if (this.currentChar === '+' || this.currentChar === '-') {
1779
- result += this.currentChar;
1780
- this.advance();
1781
- }
1782
- if (!/[0-9]/.test(this.currentChar)) this.error('Invalid exponent in number');
1783
- while (this.currentChar && /[0-9]/.test(this.currentChar)) {
1784
- result += this.currentChar;
1785
- this.advance();
1786
- }
1787
- }
1788
-
1789
- return { type: 'NUMBER', value: parseFloat(result) };
1822
+ return { type: 'NUMBER', value: parseInt(result), line: startLine, column: startCol };
1790
1823
  }
1791
1824
 
1792
1825
  identifier() {
1826
+ const startLine = this.line;
1827
+ const startCol = this.column;
1793
1828
  let result = '';
1794
1829
  while (this.currentChar && /[A-Za-z0-9_]/.test(this.currentChar)) {
1795
1830
  result += this.currentChar;
@@ -1798,19 +1833,20 @@ class Lexer {
1798
1833
 
1799
1834
  const keywords = [
1800
1835
  'let', 'sldeploy', 'if', 'else', 'while', 'for',
1801
- 'break', 'continue', 'func', 'return',
1802
- 'true', 'false', 'null', 'undefined',
1836
+ 'break', 'continue', 'func', 'return', 'true', 'false', 'null',
1803
1837
  'ask', 'define', 'import', 'from', 'as'
1804
1838
  ];
1805
1839
 
1806
1840
  if (keywords.includes(result)) {
1807
- return { type: result.toUpperCase(), value: result };
1841
+ return { type: result.toUpperCase(), value: result, line: startLine, column: startCol };
1808
1842
  }
1809
1843
 
1810
- return { type: 'IDENTIFIER', value: result };
1844
+ return { type: 'IDENTIFIER', value: result, line: startLine, column: startCol };
1811
1845
  }
1812
1846
 
1813
1847
  string() {
1848
+ const startLine = this.line;
1849
+ const startCol = this.column;
1814
1850
  const quote = this.currentChar;
1815
1851
  this.advance();
1816
1852
  let result = '';
@@ -1818,58 +1854,26 @@ class Lexer {
1818
1854
  while (this.currentChar && this.currentChar !== quote) {
1819
1855
  if (this.currentChar === '\\') {
1820
1856
  this.advance();
1821
- const map = { n: '\n', t: '\t', '"': '"', "'": "'", '\\': '\\' };
1822
- result += map[this.currentChar] ?? this.currentChar;
1857
+ switch (this.currentChar) {
1858
+ case 'n': result += '\n'; break;
1859
+ case 't': result += '\t'; break;
1860
+ case '"': result += '"'; break;
1861
+ case "'": result += "'"; break;
1862
+ case '\\': result += '\\'; break;
1863
+ default: result += this.currentChar;
1864
+ }
1823
1865
  } else {
1824
1866
  result += this.currentChar;
1825
1867
  }
1826
1868
  this.advance();
1827
1869
  }
1828
1870
 
1829
- if (this.currentChar !== quote) this.error('Unterminated string literal');
1830
-
1831
- this.advance();
1832
- return { type: 'STRING', value: result };
1833
- }
1834
-
1835
-
1836
- templateString(tokens) {
1837
- this.advance(); // skip opening `
1838
-
1839
- let buffer = '';
1840
-
1841
- while (this.currentChar !== null) {
1842
- if (this.currentChar === '`') {
1843
- if (buffer.length > 0) {
1844
- tokens.push({ type: 'TEMPLATE_STRING', value: buffer });
1845
- }
1846
- this.advance();
1847
- return;
1848
- }
1849
-
1850
- if (this.currentChar === '$' && this.peek() === '{') {
1851
- if (buffer.length > 0) {
1852
- tokens.push({ type: 'TEMPLATE_STRING', value: buffer });
1853
- buffer = '';
1854
- }
1855
- this.advance(); // $
1856
- this.advance(); // {
1857
- tokens.push({ type: 'DOLLAR_LBRACE' });
1858
- return;
1859
- }
1860
-
1861
- if (this.currentChar === '\\') {
1862
- this.advance();
1863
- buffer += this.currentChar;
1864
- this.advance();
1865
- continue;
1866
- }
1867
-
1868
- buffer += this.currentChar;
1869
- this.advance();
1871
+ if (this.currentChar !== quote) {
1872
+ this.error('Unterminated string literal');
1870
1873
  }
1871
1874
 
1872
- this.error('Unterminated template string');
1875
+ this.advance();
1876
+ return { type: 'STRING', value: result, line: startLine, column: startCol };
1873
1877
  }
1874
1878
 
1875
1879
  getTokens() {
@@ -1882,56 +1886,38 @@ class Lexer {
1882
1886
  if (/[A-Za-z_]/.test(this.currentChar)) { tokens.push(this.identifier()); continue; }
1883
1887
  if (this.currentChar === '"' || this.currentChar === "'") { tokens.push(this.string()); continue; }
1884
1888
 
1885
- if (this.currentChar === '`') {
1886
- this.templateString(tokens);
1887
- continue;
1888
- }
1889
-
1890
1889
  const char = this.currentChar;
1891
1890
  const next = this.peek();
1892
- const next2 = this.peek(2);
1893
-
1894
- if (char === '=' && next === '=' && next2 === '=') { tokens.push({ type: 'STRICT_EQ' }); this.advance(); this.advance(); this.advance(); continue; }
1895
- if (char === '!' && next === '=' && next2 === '=') { tokens.push({ type: 'STRICT_NOTEQ' }); this.advance(); this.advance(); this.advance(); continue; }
1896
- if (char === '=' && next === '=') { tokens.push({ type: 'EQEQ' }); this.advance(); this.advance(); continue; }
1897
- if (char === '=' && next === '>') { tokens.push({ type: 'ARROW' }); this.advance(); this.advance(); continue; }
1898
- if (char === '!' && next === '=') { tokens.push({ type: 'NOTEQ' }); this.advance(); this.advance(); continue; }
1899
- if (char === '<' && next === '=') { tokens.push({ type: 'LTE' }); this.advance(); this.advance(); continue; }
1900
- if (char === '>' && next === '=') { tokens.push({ type: 'GTE' }); this.advance(); this.advance(); continue; }
1901
- if (char === '<' && next === '<') { tokens.push({ type: 'LSHIFT' }); this.advance(); this.advance(); continue; }
1902
- if (char === '>' && next === '>') { tokens.push({ type: 'RSHIFT' }); this.advance(); this.advance(); continue; }
1903
- if (char === '&' && next === '&') { tokens.push({ type: 'AND' }); this.advance(); this.advance(); continue; }
1904
- if (char === '|' && next === '|') { tokens.push({ type: 'OR' }); this.advance(); this.advance(); continue; }
1905
- if (char === '+' && next === '+') { tokens.push({ type: 'PLUSPLUS' }); this.advance(); this.advance(); continue; }
1906
- if (char === '-' && next === '-') { tokens.push({ type: 'MINUSMINUS' }); this.advance(); this.advance(); continue; }
1907
-
1908
- const compound = { '+': 'PLUSEQ', '-': 'MINUSEQ', '*': 'STAREQ', '/': 'SLASHEQ', '%': 'MODEQ' };
1909
- if (next === '=' && compound[char]) {
1910
- tokens.push({ type: compound[char] });
1911
- this.advance(); this.advance();
1912
- continue;
1913
- }
1891
+ const startLine = this.line;
1892
+ const startCol = this.column;
1893
+
1894
+ if (char === '=' && next === '=') { tokens.push({ type: 'EQEQ', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1895
+ if (char === '=' && next === '>') { tokens.push({ type: 'ARROW', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1896
+ if (char === '!' && next === '=') { tokens.push({ type: 'NOTEQ', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1897
+ if (char === '<' && next === '=') { tokens.push({ type: 'LTE', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1898
+ if (char === '>' && next === '=') { tokens.push({ type: 'GTE', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1899
+ if (char === '&' && next === '&') { tokens.push({ type: 'AND', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1900
+ if (char === '|' && next === '|') { tokens.push({ type: 'OR', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1901
+ if (char === '+' && next === '+') { tokens.push({ type: 'PLUSPLUS', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1902
+ if (char === '-' && next === '-') { tokens.push({ type: 'MINUSMINUS', line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1903
+
1904
+ const map = { '+': 'PLUSEQ', '-': 'MINUSEQ', '*': 'STAREQ', '/': 'SLASHEQ', '%': 'MODEQ' };
1905
+ if (next === '=' && map[char]) { tokens.push({ type: map[char], line: startLine, column: startCol }); this.advance(); this.advance(); continue; }
1914
1906
 
1915
1907
  const singles = {
1916
1908
  '+': 'PLUS', '-': 'MINUS', '*': 'STAR', '/': 'SLASH', '%': 'MOD',
1917
1909
  '=': 'EQUAL', '<': 'LT', '>': 'GT', '!': 'NOT',
1918
- '(': 'LPAREN', ')': 'RPAREN',
1919
- '{': 'LBRACE', '}': 'RBRACE',
1920
- '[': 'LBRACKET', ']': 'RBRACKET',
1921
- ';': 'SEMICOLON', ',': 'COMMA',
1910
+ '(': 'LPAREN', ')': 'RPAREN', '{': 'LBRACE', '}': 'RBRACE',
1911
+ ';': 'SEMICOLON', ',': 'COMMA', '[': 'LBRACKET', ']': 'RBRACKET',
1922
1912
  ':': 'COLON', '.': 'DOT'
1923
1913
  };
1924
1914
 
1925
- if (singles[char]) {
1926
- tokens.push({ type: singles[char] });
1927
- this.advance();
1928
- continue;
1929
- }
1915
+ if (singles[char]) { tokens.push({ type: singles[char], line: startLine, column: startCol }); this.advance(); continue; }
1930
1916
 
1931
- this.error(`Unexpected character: ${char}`);
1917
+ this.error("Unexpected character: " + char);
1932
1918
  }
1933
1919
 
1934
- tokens.push({ type: 'EOF' });
1920
+ tokens.push({ type: 'EOF', line: this.line, column: this.column });
1935
1921
  return tokens;
1936
1922
  }
1937
1923
  }
@@ -1958,7 +1944,10 @@ class Parser {
1958
1944
 
1959
1945
  eat(type) {
1960
1946
  if (this.current.type === type) this.advance();
1961
- else throw new Error(`Expected ${type}, got ${this.current.type}`);
1947
+ else throw new Error(
1948
+ `Expected ${type}, got ${this.current.type} at line ${this.current.line}, column ${this.current.column}`
1949
+ );
1950
+
1962
1951
  }
1963
1952
 
1964
1953
  peekType(offset = 1) {
@@ -2045,52 +2034,52 @@ class Parser {
2045
2034
  const body = this.block();
2046
2035
  return { type: 'WhileStatement', test, body };
2047
2036
  }
2048
-
2049
2037
  importStatement() {
2050
2038
  this.eat('IMPORT');
2051
2039
 
2052
2040
  let specifiers = [];
2053
2041
 
2054
2042
  if (this.current.type === 'STAR') {
2055
- this.eat('STAR');
2056
- this.eat('AS');
2057
- const name = this.current.value;
2058
- this.eat('IDENTIFIER');
2059
- specifiers.push({ type: 'NamespaceImport', local: name });
2043
+ this.eat('STAR');
2044
+ this.eat('AS');
2045
+ const name = this.current.value;
2046
+ this.eat('IDENTIFIER');
2047
+ specifiers.push({ type: 'NamespaceImport', local: name });
2060
2048
  }
2061
2049
  else if (this.current.type === 'LBRACE') {
2062
- this.eat('LBRACE');
2063
- while (this.current.type !== 'RBRACE') {
2064
- const importedName = this.current.value;
2065
- this.eat('IDENTIFIER');
2066
- let localName = importedName;
2067
- if (this.current.type === 'AS') {
2068
- this.eat('AS');
2069
- localName = this.current.value;
2070
- this.eat('IDENTIFIER');
2050
+ this.eat('LBRACE');
2051
+ while (this.current.type !== 'RBRACE') {
2052
+ const importedName = this.current.value;
2053
+ this.eat('IDENTIFIER');
2054
+ let localName = importedName;
2055
+ if (this.current.type === 'AS') {
2056
+ this.eat('AS');
2057
+ localName = this.current.value;
2058
+ this.eat('IDENTIFIER');
2059
+ }
2060
+ specifiers.push({ type: 'NamedImport', imported: importedName, local: localName });
2061
+ if (this.current.type === 'COMMA') this.eat('COMMA');
2071
2062
  }
2072
- specifiers.push({ type: 'NamedImport', imported: importedName, local: localName });
2073
- if (this.current.type === 'COMMA') this.eat('COMMA');
2074
- }
2075
- this.eat('RBRACE');
2063
+ this.eat('RBRACE');
2076
2064
  }
2077
2065
  else if (this.current.type === 'IDENTIFIER') {
2078
- const localName = this.current.value;
2079
- this.eat('IDENTIFIER');
2080
- specifiers.push({ type: 'DefaultImport', local: localName });
2066
+ const localName = this.current.value;
2067
+ this.eat('IDENTIFIER');
2068
+ specifiers.push({ type: 'DefaultImport', local: localName });
2081
2069
  } else {
2082
- throw new Error('Unexpected token in import statement');
2070
+ throw new Error(`Unexpected token in import statement at line ${this.current.line}, column ${this.current.column}`);
2083
2071
  }
2084
2072
 
2085
2073
  this.eat('FROM');
2086
2074
  const pathToken = this.current;
2087
- if (pathToken.type !== 'STRING') throw new Error('Expected string literal after from in import');
2075
+ if (pathToken.type !== 'STRING') throw new Error(`Expected string literal after from in import at line ${this.current.line}, column ${this.current.column}`);
2088
2076
  this.eat('STRING');
2089
2077
 
2090
2078
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2091
2079
 
2092
2080
  return { type: 'ImportStatement', path: pathToken.value, specifiers };
2093
- }
2081
+ }
2082
+
2094
2083
 
2095
2084
  forStatement() {
2096
2085
  this.eat('FOR');
@@ -2170,23 +2159,6 @@ class Parser {
2170
2159
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2171
2160
  return { type: 'ExpressionStatement', expression: expr };
2172
2161
  }
2173
- parseTemplateLiteral() {
2174
- const parts = [];
2175
- while (true) {
2176
- if (this.current.type === 'TEMPLATE_STRING') {
2177
- parts.push({ type: 'Literal', value: this.current.value });
2178
- this.eat('TEMPLATE_STRING');
2179
- } else if (this.current.type === 'DOLLAR_LBRACE') {
2180
- this.eat('DOLLAR_LBRACE');
2181
- const expr = this.expression();
2182
- parts.push(expr);
2183
- this.eat('RBRACE');
2184
- } else {
2185
- break;
2186
- }
2187
- }
2188
- return { type: 'TemplateLiteral', parts };
2189
- }
2190
2162
 
2191
2163
  expression() {
2192
2164
  return this.assignment();
@@ -2233,7 +2205,7 @@ class Parser {
2233
2205
 
2234
2206
  equality() {
2235
2207
  let node = this.comparison();
2236
- while (['EQEQ', 'NOTEQ', 'STRICT_EQ', 'STRICT_NOTEQ'].includes(this.current.type)) {
2208
+ while (['EQEQ', 'NOTEQ'].includes(this.current.type)) {
2237
2209
  const op = this.current.type;
2238
2210
  this.eat(op);
2239
2211
  node = { type: 'BinaryExpression', operator: op, left: node, right: this.comparison() };
@@ -2243,7 +2215,7 @@ class Parser {
2243
2215
 
2244
2216
  comparison() {
2245
2217
  let node = this.term();
2246
- while (['LT', 'LTE', 'GT', 'GTE', 'LSHIFT', 'RSHIFT'].includes(this.current.type)) {
2218
+ while (['LT', 'LTE', 'GT', 'GTE'].includes(this.current.type)) {
2247
2219
  const op = this.current.type;
2248
2220
  this.eat(op);
2249
2221
  node = { type: 'BinaryExpression', operator: op, left: node, right: this.term() };
@@ -2312,7 +2284,7 @@ class Parser {
2312
2284
  }
2313
2285
  if (this.current.type === 'DOT') {
2314
2286
  this.eat('DOT');
2315
- if (this.current.type !== 'IDENTIFIER') throw new Error('Expected property name after dot');
2287
+ if (this.current.type !== 'IDENTIFIER') throw new Error(`Expected property name after dot at line ${this.current.line}, column ${this.current.column}`);
2316
2288
  const property = this.current.value;
2317
2289
  this.eat('IDENTIFIER');
2318
2290
  node = { type: 'MemberExpression', object: node, property };
@@ -2334,15 +2306,9 @@ class Parser {
2334
2306
 
2335
2307
  if (t.type === 'NUMBER') { this.eat('NUMBER'); return { type: 'Literal', value: t.value }; }
2336
2308
  if (t.type === 'STRING') { this.eat('STRING'); return { type: 'Literal', value: t.value }; }
2337
- if (t.type === 'TEMPLATE_STRING') {
2338
- return this.parseTemplateLiteral();
2339
- }
2340
-
2341
-
2342
2309
  if (t.type === 'TRUE') { this.eat('TRUE'); return { type: 'Literal', value: true }; }
2343
2310
  if (t.type === 'FALSE') { this.eat('FALSE'); return { type: 'Literal', value: false }; }
2344
2311
  if (t.type === 'NULL') { this.eat('NULL'); return { type: 'Literal', value: null }; }
2345
- if (t.type === 'UNDEFINED') { this.eat('UNDEFINED'); return { type: 'Literal', value: undefined }; }
2346
2312
 
2347
2313
  if (t.type === 'ASK') {
2348
2314
  this.eat('ASK');
@@ -2393,7 +2359,7 @@ class Parser {
2393
2359
  let key;
2394
2360
  if (this.current.type === 'STRING') { key = this.current.value; this.eat('STRING'); }
2395
2361
  else if (this.current.type === 'IDENTIFIER') { key = this.current.value; this.eat('IDENTIFIER'); }
2396
- else throw new Error('Invalid object key');
2362
+ else throw new Error(`Invalid object key at line ${this.current.line}, column ${this.current.column}`);
2397
2363
  this.eat('COLON');
2398
2364
  const value = this.expression();
2399
2365
  props.push({ key, value });
@@ -2403,12 +2369,11 @@ class Parser {
2403
2369
  return { type: 'ObjectExpression', props };
2404
2370
  }
2405
2371
 
2406
- throw new Error(`Unexpected token in primary: ${t.type}`);
2372
+ throw new Error(`Unexpected token in primary: ${t.type} at line ${this.current.line}, column ${this.current.column}`);
2407
2373
  }
2408
2374
  }
2409
2375
 
2410
- module.exports = Parser;
2411
-
2376
+ module.exports = Parser;
2412
2377
 
2413
2378
  /***/ }),
2414
2379
 
@@ -2510,7 +2475,7 @@ const Lexer = __nccwpck_require__(211);
2510
2475
  const Parser = __nccwpck_require__(222);
2511
2476
  const Evaluator = __nccwpck_require__(112);
2512
2477
 
2513
- const VERSION = '1.0.20';
2478
+ const VERSION = '1.0.22';
2514
2479
 
2515
2480
  const COLOR = {
2516
2481
  reset: '\x1b[0m',