starlight-cli 1.0.21 → 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
 
@@ -1718,10 +1755,18 @@ class Lexer {
1718
1755
  constructor(input) {
1719
1756
  this.input = input;
1720
1757
  this.pos = 0;
1721
- this.currentChar = input[this.pos];
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
  }
@@ -1730,152 +1775,149 @@ class Lexer {
1730
1775
  return this.pos + 1 < this.input.length ? this.input[this.pos + 1] : null;
1731
1776
  }
1732
1777
 
1778
+ error(msg) {
1779
+ throw new Error(`${msg} at line ${this.line}, column ${this.column}`);
1780
+ }
1781
+
1733
1782
  skipWhitespace() {
1734
1783
  while (this.currentChar && /\s/.test(this.currentChar)) this.advance();
1735
1784
  }
1736
1785
 
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;
1786
+ skipComment() {
1787
+ if (this.currentChar === '#') {
1788
+ if (this.peek() === '*') {
1789
+ this.advance(); this.advance(); // skip #*
1790
+ while (this.currentChar !== null) {
1791
+ if (this.currentChar === '*' && this.peek() === '#') {
1792
+ this.advance(); this.advance();
1793
+ return;
1794
+ }
1795
+ this.advance();
1796
+ }
1797
+ this.error("Unterminated multi-line comment (#* ... *#)");
1798
+ } else {
1799
+ while (this.currentChar && this.currentChar !== '\n') this.advance();
1800
+ }
1801
+ }
1760
1802
  }
1761
1803
 
1762
1804
  number() {
1805
+ const startLine = this.line;
1806
+ const startCol = this.column;
1763
1807
  let result = '';
1764
- while (this.currentChar && /[0-9.]/.test(this.currentChar)) {
1808
+ while (this.currentChar && /[0-9]/.test(this.currentChar)) {
1765
1809
  result += this.currentChar;
1766
1810
  this.advance();
1767
1811
  }
1768
- return { type: 'NUMBER', value: parseFloat(result) };
1812
+
1813
+ if (this.currentChar === '.' && /[0-9]/.test(this.peek())) {
1814
+ result += '.'; this.advance();
1815
+ while (this.currentChar && /[0-9]/.test(this.currentChar)) {
1816
+ result += this.currentChar;
1817
+ this.advance();
1818
+ }
1819
+ return { type: 'NUMBER', value: parseFloat(result), line: startLine, column: startCol };
1820
+ }
1821
+
1822
+ return { type: 'NUMBER', value: parseInt(result), line: startLine, column: startCol };
1769
1823
  }
1770
1824
 
1771
1825
  identifier() {
1826
+ const startLine = this.line;
1827
+ const startCol = this.column;
1772
1828
  let result = '';
1773
- while (this.currentChar && /[a-zA-Z0-9_]/.test(this.currentChar)) {
1829
+ while (this.currentChar && /[A-Za-z0-9_]/.test(this.currentChar)) {
1774
1830
  result += this.currentChar;
1775
1831
  this.advance();
1776
1832
  }
1777
- const type = this.matchKeyword(result) || 'IDENTIFIER';
1778
- return { type, value: result };
1833
+
1834
+ const keywords = [
1835
+ 'let', 'sldeploy', 'if', 'else', 'while', 'for',
1836
+ 'break', 'continue', 'func', 'return', 'true', 'false', 'null',
1837
+ 'ask', 'define', 'import', 'from', 'as'
1838
+ ];
1839
+
1840
+ if (keywords.includes(result)) {
1841
+ return { type: result.toUpperCase(), value: result, line: startLine, column: startCol };
1842
+ }
1843
+
1844
+ return { type: 'IDENTIFIER', value: result, line: startLine, column: startCol };
1779
1845
  }
1780
1846
 
1781
- string(quoteType) {
1782
- this.advance(); // skip opening quote
1847
+ string() {
1848
+ const startLine = this.line;
1849
+ const startCol = this.column;
1850
+ const quote = this.currentChar;
1851
+ this.advance();
1783
1852
  let result = '';
1784
- while (this.currentChar && this.currentChar !== quoteType) {
1853
+
1854
+ while (this.currentChar && this.currentChar !== quote) {
1785
1855
  if (this.currentChar === '\\') {
1786
1856
  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();
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;
1791
1864
  }
1792
1865
  } else {
1793
1866
  result += this.currentChar;
1794
- this.advance();
1795
1867
  }
1868
+ this.advance();
1796
1869
  }
1797
- this.advance(); // skip closing quote
1798
- return { type: 'STRING', value: result };
1799
- }
1800
1870
 
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
- }
1871
+ if (this.currentChar !== quote) {
1872
+ this.error('Unterminated string literal');
1820
1873
  }
1821
- return tokens;
1874
+
1875
+ this.advance();
1876
+ return { type: 'STRING', value: result, line: startLine, column: startCol };
1822
1877
  }
1823
1878
 
1824
1879
  getTokens() {
1825
1880
  const tokens = [];
1826
- while (this.currentChar !== null) {
1827
- this.skipWhitespace();
1828
1881
 
1829
- if (!this.currentChar) break;
1830
-
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
- }
1882
+ while (this.currentChar !== null) {
1883
+ if (/\s/.test(this.currentChar)) { this.skipWhitespace(); continue; }
1884
+ if (this.currentChar === '#') { this.skipComment(); continue; }
1885
+ if (/[0-9]/.test(this.currentChar)) { tokens.push(this.number()); continue; }
1886
+ if (/[A-Za-z_]/.test(this.currentChar)) { tokens.push(this.identifier()); continue; }
1887
+ if (this.currentChar === '"' || this.currentChar === "'") { tokens.push(this.string()); continue; }
1888
+
1889
+ const char = this.currentChar;
1890
+ const next = this.peek();
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; }
1906
+
1907
+ const singles = {
1908
+ '+': 'PLUS', '-': 'MINUS', '*': 'STAR', '/': 'SLASH', '%': 'MOD',
1909
+ '=': 'EQUAL', '<': 'LT', '>': 'GT', '!': 'NOT',
1910
+ '(': 'LPAREN', ')': 'RPAREN', '{': 'LBRACE', '}': 'RBRACE',
1911
+ ';': 'SEMICOLON', ',': 'COMMA', '[': 'LBRACKET', ']': 'RBRACKET',
1912
+ ':': 'COLON', '.': 'DOT'
1913
+ };
1914
+
1915
+ if (singles[char]) { tokens.push({ type: singles[char], line: startLine, column: startCol }); this.advance(); continue; }
1916
+
1917
+ this.error("Unexpected character: " + char);
1877
1918
  }
1878
- tokens.push({ type: 'EOF' });
1919
+
1920
+ tokens.push({ type: 'EOF', line: this.line, column: this.column });
1879
1921
  return tokens;
1880
1922
  }
1881
1923
  }
@@ -1902,7 +1944,10 @@ class Parser {
1902
1944
 
1903
1945
  eat(type) {
1904
1946
  if (this.current.type === type) this.advance();
1905
- 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
+
1906
1951
  }
1907
1952
 
1908
1953
  peekType(offset = 1) {
@@ -1989,52 +2034,52 @@ class Parser {
1989
2034
  const body = this.block();
1990
2035
  return { type: 'WhileStatement', test, body };
1991
2036
  }
1992
-
1993
2037
  importStatement() {
1994
2038
  this.eat('IMPORT');
1995
2039
 
1996
2040
  let specifiers = [];
1997
2041
 
1998
2042
  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 });
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 });
2004
2048
  }
2005
2049
  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');
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');
2015
2062
  }
2016
- specifiers.push({ type: 'NamedImport', imported: importedName, local: localName });
2017
- if (this.current.type === 'COMMA') this.eat('COMMA');
2018
- }
2019
- this.eat('RBRACE');
2063
+ this.eat('RBRACE');
2020
2064
  }
2021
2065
  else if (this.current.type === 'IDENTIFIER') {
2022
- const localName = this.current.value;
2023
- this.eat('IDENTIFIER');
2024
- specifiers.push({ type: 'DefaultImport', local: localName });
2066
+ const localName = this.current.value;
2067
+ this.eat('IDENTIFIER');
2068
+ specifiers.push({ type: 'DefaultImport', local: localName });
2025
2069
  } else {
2026
- 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}`);
2027
2071
  }
2028
2072
 
2029
2073
  this.eat('FROM');
2030
2074
  const pathToken = this.current;
2031
- 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}`);
2032
2076
  this.eat('STRING');
2033
2077
 
2034
2078
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2035
2079
 
2036
2080
  return { type: 'ImportStatement', path: pathToken.value, specifiers };
2037
- }
2081
+ }
2082
+
2038
2083
 
2039
2084
  forStatement() {
2040
2085
  this.eat('FOR');
@@ -2114,23 +2159,6 @@ class Parser {
2114
2159
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2115
2160
  return { type: 'ExpressionStatement', expression: expr };
2116
2161
  }
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
2162
 
2135
2163
  expression() {
2136
2164
  return this.assignment();
@@ -2177,7 +2205,7 @@ class Parser {
2177
2205
 
2178
2206
  equality() {
2179
2207
  let node = this.comparison();
2180
- while (['EQEQ', 'NOTEQ', 'STRICT_EQ', 'STRICT_NOTEQ'].includes(this.current.type)) {
2208
+ while (['EQEQ', 'NOTEQ'].includes(this.current.type)) {
2181
2209
  const op = this.current.type;
2182
2210
  this.eat(op);
2183
2211
  node = { type: 'BinaryExpression', operator: op, left: node, right: this.comparison() };
@@ -2187,7 +2215,7 @@ class Parser {
2187
2215
 
2188
2216
  comparison() {
2189
2217
  let node = this.term();
2190
- while (['LT', 'LTE', 'GT', 'GTE', 'LSHIFT', 'RSHIFT'].includes(this.current.type)) {
2218
+ while (['LT', 'LTE', 'GT', 'GTE'].includes(this.current.type)) {
2191
2219
  const op = this.current.type;
2192
2220
  this.eat(op);
2193
2221
  node = { type: 'BinaryExpression', operator: op, left: node, right: this.term() };
@@ -2256,7 +2284,7 @@ class Parser {
2256
2284
  }
2257
2285
  if (this.current.type === 'DOT') {
2258
2286
  this.eat('DOT');
2259
- 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}`);
2260
2288
  const property = this.current.value;
2261
2289
  this.eat('IDENTIFIER');
2262
2290
  node = { type: 'MemberExpression', object: node, property };
@@ -2278,15 +2306,9 @@ class Parser {
2278
2306
 
2279
2307
  if (t.type === 'NUMBER') { this.eat('NUMBER'); return { type: 'Literal', value: t.value }; }
2280
2308
  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
2309
  if (t.type === 'TRUE') { this.eat('TRUE'); return { type: 'Literal', value: true }; }
2287
2310
  if (t.type === 'FALSE') { this.eat('FALSE'); return { type: 'Literal', value: false }; }
2288
2311
  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
2312
 
2291
2313
  if (t.type === 'ASK') {
2292
2314
  this.eat('ASK');
@@ -2337,7 +2359,7 @@ class Parser {
2337
2359
  let key;
2338
2360
  if (this.current.type === 'STRING') { key = this.current.value; this.eat('STRING'); }
2339
2361
  else if (this.current.type === 'IDENTIFIER') { key = this.current.value; this.eat('IDENTIFIER'); }
2340
- else throw new Error('Invalid object key');
2362
+ else throw new Error(`Invalid object key at line ${this.current.line}, column ${this.current.column}`);
2341
2363
  this.eat('COLON');
2342
2364
  const value = this.expression();
2343
2365
  props.push({ key, value });
@@ -2347,12 +2369,11 @@ class Parser {
2347
2369
  return { type: 'ObjectExpression', props };
2348
2370
  }
2349
2371
 
2350
- 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}`);
2351
2373
  }
2352
2374
  }
2353
2375
 
2354
- module.exports = Parser;
2355
-
2376
+ module.exports = Parser;
2356
2377
 
2357
2378
  /***/ }),
2358
2379
 
@@ -2454,7 +2475,7 @@ const Lexer = __nccwpck_require__(211);
2454
2475
  const Parser = __nccwpck_require__(222);
2455
2476
  const Evaluator = __nccwpck_require__(112);
2456
2477
 
2457
- const VERSION = '1.0.21';
2478
+ const VERSION = '1.0.22';
2458
2479
 
2459
2480
  const COLOR = {
2460
2481
  reset: '\x1b[0m',