starlight-cli 1.0.47 → 1.0.48

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
@@ -1353,14 +1353,29 @@ class ReturnValue {
1353
1353
  class BreakSignal {}
1354
1354
  class ContinueSignal {}
1355
1355
  class RuntimeError extends Error {
1356
- constructor(message, node) {
1357
- const line = node?.line ?? '?';
1358
- const column = node?.column ?? '?';
1359
- super(`RuntimeError: ${message} at line ${line}, column ${column}`);
1360
- this.name = 'RuntimeError';
1361
- }
1356
+ constructor(message, node, source) {
1357
+ const line = node?.line ?? '?';
1358
+ const column = node?.column ?? '?';
1359
+
1360
+ let output = ` ${message} at line ${line}, column ${column}\n`;
1361
+
1362
+ if (source && node?.line != null) {
1363
+ const lines = source.split('\n');
1364
+ const srcLine = lines[node.line - 1] || '';
1365
+ output += ` ${srcLine}\n`;
1366
+ output += ` ${' '.repeat(column - 1)}^\n`;
1367
+ }
1368
+
1369
+ super(output);
1370
+
1371
+ this.name = 'RuntimeError';
1372
+ this.line = line;
1373
+ this.column = column;
1374
+ }
1362
1375
  }
1363
1376
 
1377
+
1378
+
1364
1379
  class Environment {
1365
1380
  constructor(parent = null) {
1366
1381
  this.store = Object.create(null);
@@ -1373,11 +1388,11 @@ class Environment {
1373
1388
  return false;
1374
1389
  }
1375
1390
 
1376
- get(name) {
1377
- if (name in this.store) return this.store[name];
1378
- if (this.parent) return this.parent.get(name);
1379
- throw new RuntimeError(`Undefined variable: ${name}`);
1380
- }
1391
+ get(name, node) {
1392
+ if (name in this.store) return this.store[name];
1393
+ if (this.parent) return this.parent.get(name, node);
1394
+ throw new RuntimeError(`Undefined variable: ${name}`, node);
1395
+ }
1381
1396
 
1382
1397
  set(name, value) {
1383
1398
  if (name in this.store) { this.store[name] = value; return value; }
@@ -1393,7 +1408,8 @@ class Environment {
1393
1408
  }
1394
1409
 
1395
1410
  class Evaluator {
1396
- constructor() {
1411
+ constructor(source = '') {
1412
+ this.source = source;
1397
1413
  this.global = new Environment();
1398
1414
  this.setupBuiltins();
1399
1415
  }
@@ -1480,11 +1496,11 @@ this.global.define('range', (...args) => {
1480
1496
  end = Number(args[1]);
1481
1497
  step = Number(args[2]);
1482
1498
  } else {
1483
- throw new RuntimeError('range() expects 1 to 3 arguments');
1499
+ throw new RuntimeError('range() expects 1 to 3 arguments', null, this.source);
1484
1500
  }
1485
1501
 
1486
1502
  if (step === 0) {
1487
- throw new RuntimeError('range() step cannot be 0');
1503
+ throw new RuntimeError('range() step cannot be 0', null, this.source);
1488
1504
  }
1489
1505
 
1490
1506
  const result = [];
@@ -1508,7 +1524,7 @@ this.global.define('range', (...args) => {
1508
1524
  this.global.define('num', arg => {
1509
1525
  const n = Number(arg);
1510
1526
  if (Number.isNaN(n)) {
1511
- throw new RuntimeError('Cannot convert value to number');
1527
+ throw new RuntimeError('Cannot convert value to number', null, this.source);
1512
1528
  }
1513
1529
  return n;
1514
1530
  });
@@ -1561,7 +1577,7 @@ async evaluate(node, env = this.global) {
1561
1577
  case 'LogicalExpression': return await this.evalLogical(node, env);
1562
1578
  case 'UnaryExpression': return await this.evalUnary(node, env);
1563
1579
  case 'Literal': return node.value;
1564
- case 'Identifier': return env.get(node.name);
1580
+ case 'Identifier': return env.get(node.name, node);
1565
1581
  case 'IfStatement': return await this.evalIf(node, env);
1566
1582
  case 'WhileStatement': return await this.evalWhile(node, env);
1567
1583
  case 'ForStatement':
@@ -1584,14 +1600,8 @@ case 'DoTrackStatement':
1584
1600
  case 'ArrayExpression':
1585
1601
  return await Promise.all(node.elements.map(el => this.evaluate(el, env)));
1586
1602
  case 'IndexExpression': return await this.evalIndex(node, env);
1587
- case 'ObjectExpression': {
1588
- const out = {};
1589
- for (const p of node.props) {
1590
- const key = await this.evaluate(p.key, env);
1591
- out[key] = await this.evaluate(p.value, env);
1592
- }
1593
- return out;
1594
- }
1603
+ case 'ObjectExpression': return await this.evalObject(node, env);
1604
+
1595
1605
 
1596
1606
  case 'MemberExpression': return await this.evalMember(node, env);
1597
1607
  case 'UpdateExpression': return await this.evalUpdate(node, env);
@@ -1619,7 +1629,7 @@ case 'DoTrackStatement':
1619
1629
  }
1620
1630
 
1621
1631
  if (typeof callee !== 'function') {
1622
- throw new RuntimeError('NewExpression callee is not a function', node);
1632
+ throw new RuntimeError('NewExpression callee is not a function', node, this.source);
1623
1633
  }
1624
1634
 
1625
1635
  const args = [];
@@ -1628,7 +1638,7 @@ case 'DoTrackStatement':
1628
1638
  }
1629
1639
 
1630
1640
  default:
1631
- throw new RuntimeError(`Unknown node type in evaluator: ${node.type}`, node);
1641
+ throw new RuntimeError(`Unknown node type in evaluator: ${node.type}`, node, this.source);
1632
1642
 
1633
1643
  }
1634
1644
  }
@@ -1636,11 +1646,19 @@ case 'DoTrackStatement':
1636
1646
  async evalProgram(node, env) {
1637
1647
  let result = null;
1638
1648
  for (const stmt of node.body) {
1639
- result = await this.evaluate(stmt, env);
1649
+ try {
1650
+ result = await this.evaluate(stmt, env);
1651
+ } catch (e) {
1652
+ if (e instanceof RuntimeError || e instanceof BreakSignal || e instanceof ContinueSignal || e instanceof ReturnValue) {
1653
+ throw e;
1654
+ }
1655
+ throw new RuntimeError(e.message || 'Error in program', stmt, this.source);
1656
+ }
1640
1657
  }
1641
1658
  return result;
1642
1659
  }
1643
1660
 
1661
+
1644
1662
  async evalDoTrack(node, env) {
1645
1663
  try {
1646
1664
  return await this.evaluate(node.body, env);
@@ -1648,7 +1666,7 @@ async evalDoTrack(node, env) {
1648
1666
  if (!node.handler) {
1649
1667
  // Wrap any raw error into RuntimeError with line info
1650
1668
  if (err instanceof RuntimeError) throw err;
1651
- throw new RuntimeError(err.message || 'Error in doTrack body', node.body);
1669
+ throw new RuntimeError(err.message || 'Error in doTrack body', node.body, this.source);
1652
1670
  }
1653
1671
 
1654
1672
  const trackEnv = new Environment(env);
@@ -1659,7 +1677,7 @@ async evalDoTrack(node, env) {
1659
1677
  } catch (handlerErr) {
1660
1678
  // Wrap handler errors as well
1661
1679
  if (handlerErr instanceof RuntimeError) throw handlerErr;
1662
- throw new RuntimeError(handlerErr.message || 'Error in doTrack handler', node.handler);
1680
+ throw new RuntimeError(handlerErr.message || 'Error in doTrack handler', node.handler, this.source);
1663
1681
  }
1664
1682
  }
1665
1683
  }
@@ -1680,7 +1698,7 @@ async evalImport(node, env) {
1680
1698
  : path.join(process.cwd(), spec.endsWith('.sl') ? spec : spec + '.sl');
1681
1699
 
1682
1700
  if (!fs.existsSync(fullPath)) {
1683
- throw new RuntimeError(`Import not found: ${spec}`, node);
1701
+ throw new RuntimeError(`Import not found: ${spec}`, node, this.source);
1684
1702
  }
1685
1703
 
1686
1704
  const code = fs.readFileSync(fullPath, 'utf-8');
@@ -1707,7 +1725,7 @@ async evalImport(node, env) {
1707
1725
  }
1708
1726
  if (imp.type === 'NamedImport') {
1709
1727
  if (!(imp.imported in lib)) {
1710
- throw new RuntimeError(`Module '${spec}' has no export '${imp.imported}'`, node);
1728
+ throw new RuntimeError(`Module '${spec}' has no export '${imp.imported}'`, node, this.source);
1711
1729
  }
1712
1730
  env.define(imp.local, lib[imp.imported]);
1713
1731
  }
@@ -1724,7 +1742,7 @@ async evalBlock(node, env) {
1724
1742
  } catch (e) {
1725
1743
  if (e instanceof ReturnValue || e instanceof BreakSignal || e instanceof ContinueSignal) throw e;
1726
1744
  // Wrap any other error in RuntimeError with the current block node
1727
- throw new RuntimeError(e.message || 'Error in block', stmt);
1745
+ throw new RuntimeError(e.message || 'Error in block', stmt, this.source);
1728
1746
  }
1729
1747
  }
1730
1748
  return result;
@@ -1732,11 +1750,23 @@ async evalBlock(node, env) {
1732
1750
 
1733
1751
 
1734
1752
  async evalVarDeclaration(node, env) {
1753
+ if (!node.expr) {
1754
+ throw new RuntimeError('Variable declaration requires an initializer', node, this.source);
1755
+ }
1756
+
1735
1757
  const val = await this.evaluate(node.expr, env);
1736
1758
  return env.define(node.id, val);
1737
1759
  }
1738
1760
 
1761
+
1739
1762
  evalArrowFunction(node, env) {
1763
+ if (!node.body) {
1764
+ throw new RuntimeError('Arrow function missing body', node, this.source);
1765
+ }
1766
+ if (!Array.isArray(node.params)) {
1767
+ throw new RuntimeError('Invalid arrow function parameters', node, this.source);
1768
+ }
1769
+
1740
1770
  return {
1741
1771
  params: node.params,
1742
1772
  body: node.body,
@@ -1746,24 +1776,28 @@ evalArrowFunction(node, env) {
1746
1776
  };
1747
1777
  }
1748
1778
 
1779
+
1749
1780
  async evalAssignment(node, env) {
1750
1781
  const rightVal = await this.evaluate(node.right, env);
1751
1782
  const left = node.left;
1752
1783
 
1753
1784
  if (left.type === 'Identifier') return env.set(left.name, rightVal);
1754
1785
  if (left.type === 'MemberExpression') {
1755
- const obj = await this.evaluate(left.object, env);
1756
- obj[left.property] = rightVal;
1757
- return rightVal;
1758
- }
1759
- if (left.type === 'IndexExpression') {
1760
- const obj = await this.evaluate(left.object, env);
1761
- const idx = await this.evaluate(left.indexer, env);
1762
- obj[idx] = rightVal;
1763
- return rightVal;
1764
- }
1786
+ const obj = await this.evaluate(left.object, env);
1787
+ if (obj == null) throw new RuntimeError('Cannot assign to null or undefined', node, this.source);
1788
+ obj[left.property] = rightVal;
1789
+ return rightVal;
1790
+ }
1791
+ if (left.type === 'IndexExpression') {
1792
+ const obj = await this.evaluate(left.object, env);
1793
+ const idx = await this.evaluate(left.indexer, env);
1794
+ if (obj == null) throw new RuntimeError('Cannot assign to null or undefined', node, this.source);
1795
+ obj[idx] = rightVal;
1796
+ return rightVal;
1797
+ }
1798
+
1765
1799
 
1766
- throw new RuntimeError('Invalid assignment target', node);
1800
+ throw new RuntimeError('Invalid assignment target', node, this.source);
1767
1801
 
1768
1802
  }
1769
1803
 
@@ -1774,7 +1808,7 @@ async evalCompoundAssignment(node, env) {
1774
1808
  if (left.type === 'Identifier') current = env.get(left.name);
1775
1809
  else if (left.type === 'MemberExpression') current = await this.evalMember(left, env);
1776
1810
  else if (left.type === 'IndexExpression') current = await this.evalIndex(left, env);
1777
- else throw new RuntimeError('Invalid compound assignment target', node);
1811
+ else throw new RuntimeError('Invalid compound assignment target', node, this.source);
1778
1812
 
1779
1813
 
1780
1814
  const rhs = await this.evaluate(node.right, env);
@@ -1785,7 +1819,7 @@ async evalCompoundAssignment(node, env) {
1785
1819
  case 'STAREQ': computed = current * rhs; break;
1786
1820
  case 'SLASHEQ': computed = current / rhs; break;
1787
1821
  case 'MODEQ': computed = current % rhs; break;
1788
- default: throw new RuntimeError(`Unknown compound operator: ${node.operator}`, node);
1822
+ default: throw new RuntimeError(`Unknown compound operator: ${node.operator}`, node, this.source);
1789
1823
 
1790
1824
  }
1791
1825
 
@@ -1805,21 +1839,33 @@ async evalSldeploy(node, env) {
1805
1839
 
1806
1840
  async evalAsk(node, env) {
1807
1841
  const prompt = await this.evaluate(node.prompt, env);
1842
+
1843
+ if (typeof prompt !== 'string') {
1844
+ throw new RuntimeError('ask() prompt must be a string', node, this.source);
1845
+ }
1846
+
1808
1847
  const input = readlineSync.question(prompt + ' ');
1809
1848
  return input;
1810
1849
  }
1811
1850
 
1851
+
1812
1852
  async evalDefine(node, env) {
1853
+ if (!node.id || typeof node.id !== 'string') {
1854
+ throw new RuntimeError('Invalid identifier in define statement', node, this.source);
1855
+ }
1856
+
1813
1857
  const val = node.expr ? await this.evaluate(node.expr, env) : null;
1814
- return this.global.define(node.id, val);
1858
+ return env.define(node.id, val);
1859
+
1815
1860
  }
1816
1861
 
1862
+
1817
1863
  async evalBinary(node, env) {
1818
1864
  const l = await this.evaluate(node.left, env);
1819
1865
  const r = await this.evaluate(node.right, env);
1820
1866
 
1821
1867
  if (node.operator === 'SLASH' && r === 0) {
1822
- throw new RuntimeError('Division by zero', node);
1868
+ throw new RuntimeError('Division by zero', node, this.source);
1823
1869
  }
1824
1870
 
1825
1871
  switch (node.operator) {
@@ -1834,7 +1880,7 @@ async evalBinary(node, env) {
1834
1880
  case 'LTE': return l <= r;
1835
1881
  case 'GT': return l > r;
1836
1882
  case 'GTE': return l >= r;
1837
- default: throw new RuntimeError(`Unknown binary operator: ${node.operator}`, node);
1883
+ default: throw new RuntimeError(`Unknown binary operator: ${node.operator}`, node, this.source);
1838
1884
 
1839
1885
  }
1840
1886
  }
@@ -1843,7 +1889,7 @@ async evalLogical(node, env) {
1843
1889
  const l = await this.evaluate(node.left, env);
1844
1890
  if (node.operator === 'AND') return l && await this.evaluate(node.right, env);
1845
1891
  if (node.operator === 'OR') return l || await this.evaluate(node.right, env);
1846
- throw new RuntimeError(`Unknown logical operator: ${node.operator}`, node);
1892
+ throw new RuntimeError(`Unknown logical operator: ${node.operator}`, node, this.source);
1847
1893
 
1848
1894
  }
1849
1895
 
@@ -1853,22 +1899,43 @@ async evalUnary(node, env) {
1853
1899
  case 'NOT': return !val;
1854
1900
  case 'MINUS': return -val;
1855
1901
  case 'PLUS': return +val;
1856
- default: throw new RuntimeError(`Unknown unary operator: ${node.operator}`, node);
1902
+ default: throw new RuntimeError(`Unknown unary operator: ${node.operator}`, node, this.source);
1857
1903
 
1858
1904
  }
1859
1905
  }
1860
1906
 
1861
1907
  async evalIf(node, env) {
1862
1908
  const test = await this.evaluate(node.test, env);
1863
- if (test) return await this.evaluate(node.consequent, env);
1864
- if (node.alternate) return await this.evaluate(node.alternate, env);
1909
+
1910
+ if (typeof test !== 'boolean') {
1911
+ throw new RuntimeError('If condition must evaluate to a boolean', node.test, this.source);
1912
+ }
1913
+
1914
+ if (test) {
1915
+ return await this.evaluate(node.consequent, env);
1916
+ }
1917
+
1918
+ if (node.alternate) {
1919
+ return await this.evaluate(node.alternate, env);
1920
+ }
1921
+
1865
1922
  return null;
1866
1923
  }
1867
1924
 
1925
+
1868
1926
  async evalWhile(node, env) {
1869
- while (await this.evaluate(node.test, env)) {
1870
- try { await this.evaluate(node.body, env); }
1871
- catch (e) {
1927
+ while (true) {
1928
+ const test = await this.evaluate(node.test, env);
1929
+
1930
+ if (typeof test !== 'boolean') {
1931
+ throw new RuntimeError('While condition must evaluate to a boolean', node.test, this.source);
1932
+ }
1933
+
1934
+ if (!test) break;
1935
+
1936
+ try {
1937
+ await this.evaluate(node.body, env);
1938
+ } catch (e) {
1872
1939
  if (e instanceof BreakSignal) break;
1873
1940
  if (e instanceof ContinueSignal) continue;
1874
1941
  throw e;
@@ -1876,6 +1943,7 @@ async evalWhile(node, env) {
1876
1943
  }
1877
1944
  return null;
1878
1945
  }
1946
+
1879
1947
  async evalFor(node, env) {
1880
1948
  // -------------------------------
1881
1949
  // Python-style: for x in iterable (with optional 'let')
@@ -1884,7 +1952,7 @@ async evalFor(node, env) {
1884
1952
  const iterable = await this.evaluate(node.iterable, env);
1885
1953
 
1886
1954
  if (iterable == null || typeof iterable !== 'object') {
1887
- throw new RuntimeError('Cannot iterate over non-iterable', node);
1955
+ throw new RuntimeError('Cannot iterate over non-iterable', node, this.source);
1888
1956
  }
1889
1957
 
1890
1958
  const loopVar = node.variable; // STRING from parser
@@ -1949,9 +2017,26 @@ async evalFor(node, env) {
1949
2017
 
1950
2018
  return null;
1951
2019
  }
1952
-
1953
2020
  evalFunctionDeclaration(node, env) {
1954
- const fn = { params: node.params, body: node.body, env, async: node.async || false };
2021
+ if (!node.name || typeof node.name !== 'string') {
2022
+ throw new RuntimeError('Function declaration requires a valid name', node, this.source);
2023
+ }
2024
+
2025
+ if (!Array.isArray(node.params)) {
2026
+ throw new RuntimeError(`Invalid parameter list in function '${node.name}'`, node, this.source);
2027
+ }
2028
+
2029
+ if (!node.body) {
2030
+ throw new RuntimeError(`Function '${node.name}' has no body`, node, this.source);
2031
+ }
2032
+
2033
+ const fn = {
2034
+ params: node.params,
2035
+ body: node.body,
2036
+ env,
2037
+ async: node.async || false
2038
+ };
2039
+
1955
2040
  env.define(node.name, fn);
1956
2041
  return null;
1957
2042
  }
@@ -1961,10 +2046,10 @@ async evalCall(node, env) {
1961
2046
  if (typeof calleeEvaluated === 'function') {
1962
2047
  const args = [];
1963
2048
  for (const a of node.arguments) args.push(await this.evaluate(a, env));
1964
- return calleeEvaluated(...args);
2049
+ return await calleeEvaluated(...args);
1965
2050
  }
1966
2051
  if (!calleeEvaluated || typeof calleeEvaluated !== 'object' || !calleeEvaluated.body) {
1967
- throw new RuntimeError('Call to non-function', node);
2052
+ throw new RuntimeError('Call to non-function', node, this.source);
1968
2053
  }
1969
2054
 
1970
2055
  const fn = calleeEvaluated;
@@ -1977,7 +2062,7 @@ async evalCall(node, env) {
1977
2062
 
1978
2063
  try {
1979
2064
  const result = await this.evaluate(fn.body, callEnv);
1980
- return fn.arrow ? result : result;
2065
+ return result;
1981
2066
  } catch (e) {
1982
2067
  if (e instanceof ReturnValue) return e.value;
1983
2068
  throw e;
@@ -1988,13 +2073,13 @@ async evalIndex(node, env) {
1988
2073
  const obj = await this.evaluate(node.object, env);
1989
2074
  const idx = await this.evaluate(node.indexer, env);
1990
2075
 
1991
- if (obj == null) throw new RuntimeError('Indexing null or undefined', node);
2076
+ if (obj == null) throw new RuntimeError('Indexing null or undefined', node, this.source);
1992
2077
 
1993
2078
  if (Array.isArray(obj) && (idx < 0 || idx >= obj.length)) {
1994
- throw new RuntimeError('Array index out of bounds', node);
2079
+ throw new RuntimeError('Array index out of bounds', node, this.source);
1995
2080
  }
1996
2081
  if (typeof obj === 'object' && !(idx in obj)) {
1997
- throw new RuntimeError(`Property '${idx}' does not exist`, node);
2082
+ throw new RuntimeError(`Property '${idx}' does not exist`, node, this.source);
1998
2083
  }
1999
2084
 
2000
2085
  return obj[idx];
@@ -2004,7 +2089,7 @@ async evalObject(node, env) {
2004
2089
  const out = {};
2005
2090
  for (const p of node.props) {
2006
2091
  if (!p.key || !p.value) {
2007
- throw new RuntimeError('Invalid object property', node);
2092
+ throw new RuntimeError('Invalid object property', node, this.source);
2008
2093
  }
2009
2094
  const key = await this.evaluate(p.key, env);
2010
2095
  const value = await this.evaluate(p.value, env);
@@ -2016,8 +2101,8 @@ async evalObject(node, env) {
2016
2101
 
2017
2102
  async evalMember(node, env) {
2018
2103
  const obj = await this.evaluate(node.object, env);
2019
- if (obj == null) throw new RuntimeError('Member access of null or undefined', node);
2020
- if (!(node.property in obj)) throw new RuntimeError(`Property '${node.property}' does not exist`, node);
2104
+ if (obj == null) throw new RuntimeError('Member access of null or undefined', node, this.source);
2105
+ if (!(node.property in obj)) throw new RuntimeError(`Property '${node.property}' does not exist`, node, this.source);
2021
2106
  return obj[node.property];
2022
2107
  }
2023
2108
 
@@ -2027,7 +2112,7 @@ async evalUpdate(node, env) {
2027
2112
  if (arg.type === 'Identifier') return env.get(arg.name);
2028
2113
  if (arg.type === 'MemberExpression') return await this.evalMember(arg, env);
2029
2114
  if (arg.type === 'IndexExpression') return await this.evalIndex(arg, env);
2030
- throw new RuntimeError('Invalid update target', node);
2115
+ throw new RuntimeError('Invalid update target', node, this.source);
2031
2116
 
2032
2117
  };
2033
2118
  const setValue = async (v) => {
@@ -2362,10 +2447,11 @@ class Parser {
2362
2447
  return this.expressionStatement();
2363
2448
  }
2364
2449
  }
2365
-
2366
- varDeclaration() {
2450
+ varDeclaration() {
2451
+ const t = this.current; // LET token
2367
2452
  this.eat('LET');
2368
- const id = this.current.value;
2453
+ const idToken = this.current; // identifier token
2454
+ const id = idToken.value;
2369
2455
  this.eat('IDENTIFIER');
2370
2456
 
2371
2457
  let expr = null;
@@ -2377,16 +2463,30 @@ class Parser {
2377
2463
  // semicolon optional
2378
2464
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2379
2465
 
2380
- return { type: 'VarDeclaration', id, expr };
2466
+ return {
2467
+ type: 'VarDeclaration',
2468
+ id,
2469
+ expr,
2470
+ line: t.line,
2471
+ column: t.column
2472
+ };
2381
2473
  }
2382
2474
 
2383
2475
  sldeployStatement() {
2476
+ const t = this.current; // SLDEPLOY token
2384
2477
  this.eat('SLDEPLOY');
2385
2478
  const expr = this.expression();
2386
2479
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2387
- return { type: 'SldeployStatement', expr };
2480
+ return {
2481
+ type: 'SldeployStatement',
2482
+ expr,
2483
+ line: t.line,
2484
+ column: t.column
2485
+ };
2388
2486
  }
2487
+
2389
2488
  doTrackStatement() {
2489
+ const t = this.current; // DO token
2390
2490
  this.eat('DO');
2391
2491
 
2392
2492
  const body = this.block();
@@ -2400,13 +2500,17 @@ doTrackStatement() {
2400
2500
  return {
2401
2501
  type: 'DoTrackStatement',
2402
2502
  body,
2403
- handler
2503
+ handler,
2504
+ line: t.line,
2505
+ column: t.column
2404
2506
  };
2405
2507
  }
2406
2508
 
2407
2509
  defineStatement() {
2510
+ const t = this.current; // DEFINE token
2408
2511
  this.eat('DEFINE');
2409
- const id = this.current.value;
2512
+ const idToken = this.current; // identifier token
2513
+ const id = idToken.value;
2410
2514
  this.eat('IDENTIFIER');
2411
2515
 
2412
2516
  let expr = null;
@@ -2417,10 +2521,17 @@ defineStatement() {
2417
2521
 
2418
2522
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2419
2523
 
2420
- return { type: 'DefineStatement', id, expr };
2524
+ return {
2525
+ type: 'DefineStatement',
2526
+ id,
2527
+ expr,
2528
+ line: t.line,
2529
+ column: t.column
2530
+ };
2421
2531
  }
2422
2532
 
2423
2533
  asyncFuncDeclaration() {
2534
+ const t = this.current; // first token of function (identifier)
2424
2535
  const name = this.current.value;
2425
2536
  this.eat('IDENTIFIER');
2426
2537
 
@@ -2446,10 +2557,19 @@ asyncFuncDeclaration() {
2446
2557
  }
2447
2558
 
2448
2559
  const body = this.block();
2449
- return { type: 'FunctionDeclaration', name, params, body, async: true };
2560
+ return {
2561
+ type: 'FunctionDeclaration',
2562
+ name,
2563
+ params,
2564
+ body,
2565
+ async: true,
2566
+ line: t.line,
2567
+ column: t.column
2568
+ };
2450
2569
  }
2451
2570
 
2452
2571
  ifStatement() {
2572
+ const t = this.current; // IF token
2453
2573
  this.eat('IF');
2454
2574
 
2455
2575
  let test;
@@ -2458,7 +2578,7 @@ ifStatement() {
2458
2578
  test = this.expression();
2459
2579
  this.eat('RPAREN');
2460
2580
  } else {
2461
- // python style: no parentheses
2581
+ // Python style: no parentheses
2462
2582
  test = this.expression();
2463
2583
  }
2464
2584
 
@@ -2471,10 +2591,18 @@ ifStatement() {
2471
2591
  else alternate = this.block();
2472
2592
  }
2473
2593
 
2474
- return { type: 'IfStatement', test, consequent, alternate };
2594
+ return {
2595
+ type: 'IfStatement',
2596
+ test,
2597
+ consequent,
2598
+ alternate,
2599
+ line: t.line,
2600
+ column: t.column
2601
+ };
2475
2602
  }
2476
2603
 
2477
2604
  whileStatement() {
2605
+ const t = this.current; // WHILE token
2478
2606
  this.eat('WHILE');
2479
2607
 
2480
2608
  let test;
@@ -2487,10 +2615,17 @@ whileStatement() {
2487
2615
  }
2488
2616
 
2489
2617
  const body = this.block();
2490
- return { type: 'WhileStatement', test, body };
2618
+ return {
2619
+ type: 'WhileStatement',
2620
+ test,
2621
+ body,
2622
+ line: t.line,
2623
+ column: t.column
2624
+ };
2491
2625
  }
2492
2626
 
2493
2627
  importStatement() {
2628
+ const t = this.current; // IMPORT token
2494
2629
  this.eat('IMPORT');
2495
2630
 
2496
2631
  let specifiers = [];
@@ -2499,11 +2634,13 @@ importStatement() {
2499
2634
  this.eat('AS');
2500
2635
  const name = this.current.value;
2501
2636
  this.eat('IDENTIFIER');
2502
- specifiers.push({ type: 'NamespaceImport', local: name });
2637
+ specifiers.push({ type: 'NamespaceImport', local: name, line: t.line, column: t.column });
2503
2638
  } else if (this.current.type === 'LBRACE') {
2504
2639
  this.eat('LBRACE');
2505
2640
  while (this.current.type !== 'RBRACE') {
2506
2641
  const importedName = this.current.value;
2642
+ const importedLine = this.current.line;
2643
+ const importedColumn = this.current.column;
2507
2644
  this.eat('IDENTIFIER');
2508
2645
 
2509
2646
  let localName = importedName;
@@ -2513,14 +2650,16 @@ importStatement() {
2513
2650
  this.eat('IDENTIFIER');
2514
2651
  }
2515
2652
 
2516
- specifiers.push({ type: 'NamedImport', imported: importedName, local: localName });
2653
+ specifiers.push({ type: 'NamedImport', imported: importedName, local: localName, line: importedLine, column: importedColumn });
2517
2654
  if (this.current.type === 'COMMA') this.eat('COMMA');
2518
2655
  }
2519
2656
  this.eat('RBRACE');
2520
2657
  } else if (this.current.type === 'IDENTIFIER') {
2521
2658
  const localName = this.current.value;
2659
+ const localLine = this.current.line;
2660
+ const localColumn = this.current.column;
2522
2661
  this.eat('IDENTIFIER');
2523
- specifiers.push({ type: 'DefaultImport', local: localName });
2662
+ specifiers.push({ type: 'DefaultImport', local: localName, line: localLine, column: localColumn });
2524
2663
  } else {
2525
2664
  throw new Error(`Unexpected token in import at line ${this.current.line}, column ${this.current.column}`);
2526
2665
  }
@@ -2532,9 +2671,17 @@ importStatement() {
2532
2671
 
2533
2672
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2534
2673
 
2535
- return { type: 'ImportStatement', path: pathToken.value, specifiers };
2674
+ return {
2675
+ type: 'ImportStatement',
2676
+ path: pathToken.value,
2677
+ specifiers,
2678
+ line: t.line,
2679
+ column: t.column
2680
+ };
2536
2681
  }
2682
+
2537
2683
  forStatement() {
2684
+ const t = this.current; // FOR token
2538
2685
  this.eat('FOR');
2539
2686
 
2540
2687
  // --- Python-style: for variable in iterable (supports optional 'let') ---
@@ -2542,17 +2689,22 @@ forStatement() {
2542
2689
  (this.current.type === 'IDENTIFIER' && this.peekType() === 'IN')) {
2543
2690
 
2544
2691
  let variable;
2692
+ let variableLine, variableColumn;
2545
2693
  let iterable;
2546
2694
  let letKeyword = false;
2547
2695
 
2548
2696
  if (this.current.type === 'LET') {
2549
2697
  letKeyword = true;
2550
2698
  this.eat('LET');
2699
+ variableLine = this.current.line;
2700
+ variableColumn = this.current.column;
2551
2701
  variable = this.current.value;
2552
2702
  this.eat('IDENTIFIER');
2553
2703
  this.eat('IN');
2554
2704
  iterable = this.expression();
2555
2705
  } else {
2706
+ variableLine = this.current.line;
2707
+ variableColumn = this.current.column;
2556
2708
  variable = this.current.value;
2557
2709
  this.eat('IDENTIFIER');
2558
2710
  this.eat('IN');
@@ -2560,7 +2712,17 @@ forStatement() {
2560
2712
  }
2561
2713
 
2562
2714
  const body = this.block();
2563
- return { type: 'ForInStatement', variable, iterable, letKeyword, body };
2715
+ return {
2716
+ type: 'ForInStatement',
2717
+ variable,
2718
+ variableLine,
2719
+ variableColumn,
2720
+ iterable,
2721
+ letKeyword,
2722
+ body,
2723
+ line: t.line,
2724
+ column: t.column
2725
+ };
2564
2726
  }
2565
2727
 
2566
2728
  // --- C-style: for(init; test; update) ---
@@ -2595,37 +2757,51 @@ forStatement() {
2595
2757
  }
2596
2758
 
2597
2759
  const body = this.block();
2598
- return { type: 'ForStatement', init, test, update, body };
2760
+ return {
2761
+ type: 'ForStatement',
2762
+ init,
2763
+ test,
2764
+ update,
2765
+ body,
2766
+ line: t.line,
2767
+ column: t.column
2768
+ };
2599
2769
  }
2600
2770
 
2601
2771
  breakStatement() {
2772
+ const t = this.current; // BREAK token
2602
2773
  this.eat('BREAK');
2603
2774
  // Python-style: no semicolon needed, ignore if present
2604
2775
  if (this.current.type === 'SEMICOLON') this.advance();
2605
- return { type: 'BreakStatement' };
2776
+ return { type: 'BreakStatement', line: t.line, column: t.column };
2606
2777
  }
2607
2778
 
2608
2779
  continueStatement() {
2780
+ const t = this.current; // CONTINUE token
2609
2781
  this.eat('CONTINUE');
2610
2782
  // Python-style: no semicolon needed, ignore if present
2611
2783
  if (this.current.type === 'SEMICOLON') this.advance();
2612
- return { type: 'ContinueStatement' };
2784
+ return { type: 'ContinueStatement', line: t.line, column: t.column };
2613
2785
  }
2614
2786
 
2615
2787
  funcDeclaration() {
2788
+ const t = this.current; // FUNC token
2616
2789
  this.eat('FUNC');
2617
- const name = this.current.value;
2790
+ const nameToken = this.current;
2791
+ const name = nameToken.value;
2618
2792
  this.eat('IDENTIFIER');
2619
2793
 
2620
2794
  let params = [];
2621
2795
  if (this.current.type === 'LPAREN') {
2622
2796
  this.eat('LPAREN');
2623
2797
  if (this.current.type !== 'RPAREN') {
2624
- params.push(this.current.value);
2798
+ const paramToken = this.current;
2799
+ params.push({ name: paramToken.value, line: paramToken.line, column: paramToken.column });
2625
2800
  this.eat('IDENTIFIER');
2626
2801
  while (this.current.type === 'COMMA') {
2627
2802
  this.eat('COMMA');
2628
- params.push(this.current.value);
2803
+ const paramToken2 = this.current;
2804
+ params.push({ name: paramToken2.value, line: paramToken2.line, column: paramToken2.column });
2629
2805
  this.eat('IDENTIFIER');
2630
2806
  }
2631
2807
  }
@@ -2633,16 +2809,18 @@ funcDeclaration() {
2633
2809
  } else {
2634
2810
  // Python-style: single param without parentheses
2635
2811
  if (this.current.type === 'IDENTIFIER') {
2636
- params.push(this.current.value);
2812
+ const paramToken = this.current;
2813
+ params.push({ name: paramToken.value, line: paramToken.line, column: paramToken.column });
2637
2814
  this.eat('IDENTIFIER');
2638
2815
  }
2639
2816
  }
2640
2817
 
2641
2818
  const body = this.block();
2642
- return { type: 'FunctionDeclaration', name, params, body };
2819
+ return { type: 'FunctionDeclaration', name, params, body, line: t.line, column: t.column };
2643
2820
  }
2644
2821
 
2645
2822
  returnStatement() {
2823
+ const t = this.current; // RETURN token
2646
2824
  this.eat('RETURN');
2647
2825
 
2648
2826
  let argument = null;
@@ -2653,24 +2831,26 @@ returnStatement() {
2653
2831
  // semicolon optional
2654
2832
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2655
2833
 
2656
- return { type: 'ReturnStatement', argument };
2834
+ return { type: 'ReturnStatement', argument, line: t.line, column: t.column };
2657
2835
  }
2658
2836
 
2659
2837
  block() {
2838
+ const t = this.current; // LBRACE token
2660
2839
  this.eat('LBRACE');
2661
2840
  const body = [];
2662
2841
  while (this.current.type !== 'RBRACE') {
2663
2842
  body.push(this.statement());
2664
2843
  }
2665
2844
  this.eat('RBRACE');
2666
- return { type: 'BlockStatement', body };
2845
+ return { type: 'BlockStatement', body, line: t.line, column: t.column };
2667
2846
  }
2668
2847
 
2669
2848
  expressionStatement() {
2849
+ const exprToken = this.current; // first token of the expression
2670
2850
  const expr = this.expression();
2671
2851
  // semicolon optional
2672
2852
  if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
2673
- return { type: 'ExpressionStatement', expression: expr };
2853
+ return { type: 'ExpressionStatement', expression: expr, line: exprToken.line, column: exprToken.column };
2674
2854
  }
2675
2855
 
2676
2856
  expression() {
@@ -2681,17 +2861,19 @@ assignment() {
2681
2861
  const node = this.logicalOr();
2682
2862
  const compoundOps = ['PLUSEQ', 'MINUSEQ', 'STAREQ', 'SLASHEQ', 'MODEQ'];
2683
2863
 
2684
- if (compoundOps.includes(this.current.type)) {
2685
- const op = this.current.type;
2864
+ const t = this.current;
2865
+
2866
+ if (compoundOps.includes(t.type)) {
2867
+ const op = t.type;
2686
2868
  this.eat(op);
2687
2869
  const right = this.assignment();
2688
- return { type: 'CompoundAssignment', operator: op, left: node, right };
2870
+ return { type: 'CompoundAssignment', operator: op, left: node, right, line: t.line, column: t.column };
2689
2871
  }
2690
2872
 
2691
- if (this.current.type === 'EQUAL') {
2873
+ if (t.type === 'EQUAL') {
2692
2874
  this.eat('EQUAL');
2693
2875
  const right = this.assignment();
2694
- return { type: 'AssignmentExpression', left: node, right };
2876
+ return { type: 'AssignmentExpression', left: node, right, line: t.line, column: t.column };
2695
2877
  }
2696
2878
 
2697
2879
  return node;
@@ -2700,9 +2882,10 @@ assignment() {
2700
2882
  logicalOr() {
2701
2883
  let node = this.logicalAnd();
2702
2884
  while (this.current.type === 'OR') {
2703
- const op = this.current.type;
2885
+ const t = this.current;
2886
+ const op = t.type;
2704
2887
  this.eat(op);
2705
- node = { type: 'LogicalExpression', operator: op, left: node, right: this.logicalAnd() };
2888
+ node = { type: 'LogicalExpression', operator: op, left: node, right: this.logicalAnd(), line: t.line, column: t.column };
2706
2889
  }
2707
2890
  return node;
2708
2891
  }
@@ -2710,18 +2893,21 @@ logicalOr() {
2710
2893
  logicalAnd() {
2711
2894
  let node = this.equality();
2712
2895
  while (this.current.type === 'AND') {
2713
- const op = this.current.type;
2896
+ const t = this.current;
2897
+ const op = t.type;
2714
2898
  this.eat(op);
2715
- node = { type: 'LogicalExpression', operator: op, left: node, right: this.equality() };
2899
+ node = { type: 'LogicalExpression', operator: op, left: node, right: this.equality(), line: t.line, column: t.column };
2716
2900
  }
2717
2901
  return node;
2718
2902
  }
2903
+
2719
2904
  equality() {
2720
2905
  let node = this.comparison();
2721
2906
  while (['EQEQ', 'NOTEQ'].includes(this.current.type)) {
2722
- const op = this.current.type;
2907
+ const t = this.current;
2908
+ const op = t.type;
2723
2909
  this.eat(op);
2724
- node = { type: 'BinaryExpression', operator: op, left: node, right: this.comparison() };
2910
+ node = { type: 'BinaryExpression', operator: op, left: node, right: this.comparison(), line: t.line, column: t.column };
2725
2911
  }
2726
2912
  return node;
2727
2913
  }
@@ -2729,9 +2915,10 @@ equality() {
2729
2915
  comparison() {
2730
2916
  let node = this.term();
2731
2917
  while (['LT', 'LTE', 'GT', 'GTE'].includes(this.current.type)) {
2732
- const op = this.current.type;
2918
+ const t = this.current;
2919
+ const op = t.type;
2733
2920
  this.eat(op);
2734
- node = { type: 'BinaryExpression', operator: op, left: node, right: this.term() };
2921
+ node = { type: 'BinaryExpression', operator: op, left: node, right: this.term(), line: t.line, column: t.column };
2735
2922
  }
2736
2923
  return node;
2737
2924
  }
@@ -2739,9 +2926,10 @@ comparison() {
2739
2926
  term() {
2740
2927
  let node = this.factor();
2741
2928
  while (['PLUS', 'MINUS'].includes(this.current.type)) {
2742
- const op = this.current.type;
2929
+ const t = this.current;
2930
+ const op = t.type;
2743
2931
  this.eat(op);
2744
- node = { type: 'BinaryExpression', operator: op, left: node, right: this.factor() };
2932
+ node = { type: 'BinaryExpression', operator: op, left: node, right: this.factor(), line: t.line, column: t.column };
2745
2933
  }
2746
2934
  return node;
2747
2935
  }
@@ -2749,26 +2937,43 @@ term() {
2749
2937
  factor() {
2750
2938
  let node = this.unary();
2751
2939
  while (['STAR', 'SLASH', 'MOD'].includes(this.current.type)) {
2752
- const op = this.current.type;
2940
+ const t = this.current;
2941
+ const op = t.type;
2753
2942
  this.eat(op);
2754
- node = { type: 'BinaryExpression', operator: op, left: node, right: this.unary() };
2943
+ node = { type: 'BinaryExpression', operator: op, left: node, right: this.unary(), line: t.line, column: t.column };
2755
2944
  }
2756
2945
  return node;
2757
2946
  }
2758
2947
 
2948
+
2759
2949
  unary() {
2760
- if (['NOT', 'MINUS', 'PLUS'].includes(this.current.type)) {
2761
- const op = this.current.type;
2950
+ const t = this.current;
2951
+
2952
+ if (['NOT', 'MINUS', 'PLUS'].includes(t.type)) {
2953
+ const op = t.type;
2762
2954
  this.eat(op);
2763
- return { type: 'UnaryExpression', operator: op, argument: this.unary() };
2955
+ return {
2956
+ type: 'UnaryExpression',
2957
+ operator: op,
2958
+ argument: this.unary(),
2959
+ line: t.line,
2960
+ column: t.column
2961
+ };
2764
2962
  }
2765
2963
 
2766
2964
  // Python-like: ignore ++ and -- if not used
2767
- if (this.current.type === 'PLUSPLUS' || this.current.type === 'MINUSMINUS') {
2768
- const op = this.current.type;
2965
+ if (t.type === 'PLUSPLUS' || t.type === 'MINUSMINUS') {
2966
+ const op = t.type;
2769
2967
  this.eat(op);
2770
2968
  const argument = this.unary();
2771
- return { type: 'UpdateExpression', operator: op, argument, prefix: true };
2969
+ return {
2970
+ type: 'UpdateExpression',
2971
+ operator: op,
2972
+ argument,
2973
+ prefix: true,
2974
+ line: t.line,
2975
+ column: t.column
2976
+ };
2772
2977
  }
2773
2978
 
2774
2979
  return this.postfix();
@@ -2777,15 +2982,21 @@ unary() {
2777
2982
  postfix() {
2778
2983
  let node = this.primary();
2779
2984
  while (true) {
2780
- if (this.current.type === 'LBRACKET') {
2985
+ const t = this.current;
2986
+
2987
+ if (t.type === 'LBRACKET') {
2988
+ const startLine = t.line;
2989
+ const startCol = t.column;
2781
2990
  this.eat('LBRACKET');
2782
2991
  const index = this.expression();
2783
2992
  if (this.current.type === 'RBRACKET') this.eat('RBRACKET');
2784
- node = { type: 'IndexExpression', object: node, indexer: index };
2993
+ node = { type: 'IndexExpression', object: node, indexer: index, line: startLine, column: startCol };
2785
2994
  continue;
2786
2995
  }
2787
2996
 
2788
- if (this.current.type === 'LPAREN') {
2997
+ if (t.type === 'LPAREN') {
2998
+ const startLine = t.line;
2999
+ const startCol = t.column;
2789
3000
  this.eat('LPAREN');
2790
3001
  const args = [];
2791
3002
  while (this.current.type !== 'RPAREN' && this.current.type !== 'EOF') {
@@ -2793,30 +3004,30 @@ postfix() {
2793
3004
  if (this.current.type === 'COMMA') this.eat('COMMA'); // optional comma
2794
3005
  }
2795
3006
  if (this.current.type === 'RPAREN') this.eat('RPAREN');
2796
- node = { type: 'CallExpression', callee: node, arguments: args };
3007
+ node = { type: 'CallExpression', callee: node, arguments: args, line: startLine, column: startCol };
2797
3008
  continue;
2798
3009
  }
2799
3010
 
2800
- if (this.current.type === 'DOT') {
3011
+ if (t.type === 'DOT') {
3012
+ const startLine = t.line;
3013
+ const startCol = t.column;
2801
3014
  this.eat('DOT');
2802
3015
  const property = this.current.value || '';
2803
3016
  if (this.current.type === 'IDENTIFIER') this.eat('IDENTIFIER');
2804
- node = { type: 'MemberExpression', object: node, property };
3017
+ node = { type: 'MemberExpression', object: node, property, line: startLine, column: startCol };
2805
3018
  continue;
2806
3019
  }
2807
3020
 
2808
- if (this.current.type === 'PLUSPLUS' || this.current.type === 'MINUSMINUS') {
2809
- const op = this.current.type;
2810
- this.eat(op);
2811
- node = { type: 'UpdateExpression', operator: op, argument: node, prefix: false };
3021
+ if (t.type === 'PLUSPLUS' || t.type === 'MINUSMINUS') {
3022
+ this.eat(t.type);
3023
+ node = { type: 'UpdateExpression', operator: t.type, argument: node, prefix: false, line: t.line, column: t.column };
2812
3024
  continue;
2813
3025
  }
2814
3026
 
2815
- // Python-style: implicit function calls (if identifier followed by identifier)
2816
- if (node.type === 'Identifier' && this.current.type === 'IDENTIFIER') {
2817
- const argNode = { type: 'Identifier', name: this.current.value };
3027
+ if (node.type === 'Identifier' && t.type === 'IDENTIFIER') {
3028
+ const argNode = { type: 'Identifier', name: t.value, line: t.line, column: t.column };
2818
3029
  this.eat('IDENTIFIER');
2819
- node = { type: 'CallExpression', callee: node, arguments: [argNode] };
3030
+ node = { type: 'CallExpression', callee: node, arguments: [argNode], line: node.line, column: node.column };
2820
3031
  continue;
2821
3032
  }
2822
3033
 
@@ -2826,12 +3037,18 @@ postfix() {
2826
3037
  }
2827
3038
 
2828
3039
  arrowFunction(params) {
2829
- if (this.current.type === 'ARROW') this.eat('ARROW');
3040
+ const t = this.current;
3041
+ if (t.type === 'ARROW') this.eat('ARROW');
2830
3042
  const body = this.expression();
3043
+ const startLine = params.length > 0 ? params[0].line : t.line;
3044
+ const startCol = params.length > 0 ? params[0].column : t.column;
3045
+
2831
3046
  return {
2832
3047
  type: 'ArrowFunctionExpression',
2833
3048
  params,
2834
- body
3049
+ body,
3050
+ line: startLine,
3051
+ column: startCol
2835
3052
  };
2836
3053
  }
2837
3054
 
@@ -2839,16 +3056,35 @@ arrowFunction(params) {
2839
3056
  primary() {
2840
3057
  const t = this.current;
2841
3058
 
2842
- if (t.type === 'NUMBER') { this.eat('NUMBER'); return { type: 'Literal', value: t.value }; }
2843
- if (t.type === 'STRING') { this.eat('STRING'); return { type: 'Literal', value: t.value }; }
2844
- if (t.type === 'TRUE') { this.eat('TRUE'); return { type: 'Literal', value: true }; }
2845
- if (t.type === 'FALSE') { this.eat('FALSE'); return { type: 'Literal', value: false }; }
2846
- if (t.type === 'NULL') { this.eat('NULL'); return { type: 'Literal', value: null }; }
3059
+ if (t.type === 'NUMBER') {
3060
+ this.eat('NUMBER');
3061
+ return { type: 'Literal', value: t.value, line: t.line, column: t.column };
3062
+ }
3063
+
3064
+ if (t.type === 'STRING') {
3065
+ this.eat('STRING');
3066
+ return { type: 'Literal', value: t.value, line: t.line, column: t.column };
3067
+ }
3068
+
3069
+ if (t.type === 'TRUE') {
3070
+ this.eat('TRUE');
3071
+ return { type: 'Literal', value: true, line: t.line, column: t.column };
3072
+ }
3073
+
3074
+ if (t.type === 'FALSE') {
3075
+ this.eat('FALSE');
3076
+ return { type: 'Literal', value: false, line: t.line, column: t.column };
3077
+ }
3078
+
3079
+ if (t.type === 'NULL') {
3080
+ this.eat('NULL');
3081
+ return { type: 'Literal', value: null, line: t.line, column: t.column };
3082
+ }
2847
3083
 
2848
3084
  if (t.type === 'AWAIT') {
2849
3085
  this.eat('AWAIT');
2850
3086
  const argument = this.expression();
2851
- return { type: 'AwaitExpression', argument };
3087
+ return { type: 'AwaitExpression', argument, line: t.line, column: t.column };
2852
3088
  }
2853
3089
 
2854
3090
  if (t.type === 'NEW') {
@@ -2864,7 +3100,7 @@ arrowFunction(params) {
2864
3100
  }
2865
3101
  }
2866
3102
  this.eat('RPAREN');
2867
- return { type: 'NewExpression', callee, arguments: args };
3103
+ return { type: 'NewExpression', callee, arguments: args, line: t.line, column: t.column };
2868
3104
  }
2869
3105
 
2870
3106
  if (t.type === 'ASK') {
@@ -2879,7 +3115,13 @@ arrowFunction(params) {
2879
3115
  }
2880
3116
  }
2881
3117
  this.eat('RPAREN');
2882
- return { type: 'CallExpression', callee: { type: 'Identifier', name: 'ask' }, arguments: args };
3118
+ return {
3119
+ type: 'CallExpression',
3120
+ callee: { type: 'Identifier', name: 'ask', line: t.line, column: t.column },
3121
+ arguments: args,
3122
+ line: t.line,
3123
+ column: t.column
3124
+ };
2883
3125
  }
2884
3126
 
2885
3127
  if (t.type === 'IDENTIFIER') {
@@ -2887,13 +3129,15 @@ arrowFunction(params) {
2887
3129
  this.eat('IDENTIFIER');
2888
3130
 
2889
3131
  if (this.current.type === 'ARROW') {
2890
- return this.arrowFunction([name]);
3132
+ return this.arrowFunction([{ type: 'Identifier', name, line: t.line, column: t.column }]);
2891
3133
  }
2892
3134
 
2893
- return { type: 'Identifier', name };
3135
+ return { type: 'Identifier', name, line: t.line, column: t.column };
2894
3136
  }
2895
3137
 
2896
3138
  if (t.type === 'LPAREN') {
3139
+ const startLine = t.line;
3140
+ const startCol = t.column;
2897
3141
  this.eat('LPAREN');
2898
3142
  const elements = [];
2899
3143
 
@@ -2909,10 +3153,12 @@ arrowFunction(params) {
2909
3153
 
2910
3154
  if (this.current.type === 'ARROW') return this.arrowFunction(elements);
2911
3155
 
2912
- return elements.length === 1 ? elements[0] : { type: 'ArrayExpression', elements };
3156
+ return elements.length === 1 ? elements[0] : { type: 'ArrayExpression', elements, line: startLine, column: startCol };
2913
3157
  }
2914
3158
 
2915
3159
  if (t.type === 'LBRACKET') {
3160
+ const startLine = t.line;
3161
+ const startCol = t.column;
2916
3162
  this.eat('LBRACKET');
2917
3163
  const elements = [];
2918
3164
  if (this.current.type !== 'RBRACKET') {
@@ -2923,10 +3169,12 @@ arrowFunction(params) {
2923
3169
  }
2924
3170
  }
2925
3171
  this.eat('RBRACKET');
2926
- return { type: 'ArrayExpression', elements };
3172
+ return { type: 'ArrayExpression', elements, line: startLine, column: startCol };
2927
3173
  }
2928
3174
 
2929
3175
  if (t.type === 'LBRACE') {
3176
+ const startLine = t.line;
3177
+ const startCol = t.column;
2930
3178
  this.eat('LBRACE');
2931
3179
  const props = [];
2932
3180
  while (this.current.type !== 'RBRACE') {
@@ -2937,17 +3185,17 @@ arrowFunction(params) {
2937
3185
  if (this.current.type === 'COMMA') this.eat('COMMA'); // optional trailing comma
2938
3186
  }
2939
3187
  this.eat('RBRACE');
2940
- return { type: 'ObjectExpression', props };
3188
+ return { type: 'ObjectExpression', props, line: startLine, column: startCol };
2941
3189
  }
2942
3190
 
2943
3191
  throw new ParseError(
2944
- `Unexpected token '${t.type}'`,
2945
- t,
2946
- this.source
2947
- );
2948
-
3192
+ `Unexpected token '${t.type}'`,
3193
+ t,
3194
+ this.source
3195
+ );
2949
3196
  }
2950
3197
 
3198
+
2951
3199
  }
2952
3200
 
2953
3201
  module.exports = Parser;
@@ -3052,7 +3300,7 @@ const Lexer = __nccwpck_require__(211);
3052
3300
  const Parser = __nccwpck_require__(222);
3053
3301
  const Evaluator = __nccwpck_require__(112);
3054
3302
 
3055
- const VERSION = '1.0.47';
3303
+ const VERSION = '1.0.48';
3056
3304
 
3057
3305
  const COLOR = {
3058
3306
  reset: '\x1b[0m',
@@ -3063,7 +3311,8 @@ const COLOR = {
3063
3311
  cyan: '\x1b[36m',
3064
3312
  gray: '\x1b[90m',
3065
3313
  green: '\x1b[32m',
3066
- magenta: '\x1b[35m'
3314
+ magenta: '\x1b[35m',
3315
+ white: '\x1b[37m'
3067
3316
  };
3068
3317
 
3069
3318
  function waitAndExit(code = 0) {
@@ -3074,7 +3323,7 @@ function waitAndExit(code = 0) {
3074
3323
  }
3075
3324
 
3076
3325
  function fatal(msg) {
3077
- console.error(COLOR.red + msg + COLOR.reset);
3326
+ console.error(COLOR.white + msg + COLOR.reset);
3078
3327
  waitAndExit(1);
3079
3328
  }
3080
3329
 
@@ -3215,33 +3464,35 @@ function savePrompt(lines) {
3215
3464
  waitAndExit(0);
3216
3465
  }
3217
3466
 
3218
- function runFile(filePath, isTemp = false, callback) {
3467
+ async function runFile(filePath, isTemp = false, callback) {
3219
3468
  const code = fs.readFileSync(filePath, 'utf8');
3220
3469
 
3470
+ const lexer = new Lexer(code);
3471
+ const tokens = lexer.getTokens();
3472
+ const parser = new Parser(tokens, code);
3473
+ const ast = parser.parse();
3474
+ const evaluator = new Evaluator(code);
3475
+
3221
3476
  try {
3222
- const lexer = new Lexer(code);
3223
- const tokens = lexer.getTokens();
3224
- const parser = new Parser(tokens);
3225
- const ast = parser.parse();
3226
- new Evaluator().evaluate(ast);
3477
+ await evaluator.evaluate(ast);
3227
3478
  } catch (e) {
3228
- if (e.name === 'RuntimeError') {
3229
- console.error(COLOR.red + e.message + COLOR.reset);
3230
- } else if (e instanceof SyntaxError) {
3231
- console.error(COLOR.red + `SyntaxError: ${e.message}` + COLOR.reset);
3479
+ if (e.name === 'RuntimeError' || e.name === 'SyntaxError') {
3480
+ console.error(COLOR.white + e.message + COLOR.reset);
3232
3481
  } else {
3233
- console.error(COLOR.red + `Error: ${e.message || e}` + COLOR.reset);
3482
+ console.error(COLOR.white + `Unexpected Error: ${e.message || e}` + COLOR.reset);
3234
3483
  }
3235
3484
  return waitAndExit(1);
3236
3485
  }
3237
3486
 
3238
3487
  if (callback) callback();
3488
+
3239
3489
  if (isTemp) {
3240
3490
  try { fs.unlinkSync(filePath); } catch {}
3241
3491
  }
3242
3492
  }
3243
3493
 
3244
3494
 
3495
+
3245
3496
  if (!args[0].startsWith('--')) {
3246
3497
  runFile(path.resolve(args[0]));
3247
3498
  }