starlight-cli 1.0.47 → 1.0.49

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