starlight-cli 1.0.46 → 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 +530 -173
- package/package.json +1 -1
- package/src/evaluator.js +185 -62
- package/src/lexer.js +33 -8
- package/src/parser.js +289 -92
- package/src/starlight.js +23 -11
package/dist/index.js
CHANGED
|
@@ -1352,6 +1352,29 @@ class ReturnValue {
|
|
|
1352
1352
|
}
|
|
1353
1353
|
class BreakSignal {}
|
|
1354
1354
|
class ContinueSignal {}
|
|
1355
|
+
class RuntimeError extends Error {
|
|
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
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
|
|
1355
1378
|
|
|
1356
1379
|
class Environment {
|
|
1357
1380
|
constructor(parent = null) {
|
|
@@ -1365,11 +1388,11 @@ class Environment {
|
|
|
1365
1388
|
return false;
|
|
1366
1389
|
}
|
|
1367
1390
|
|
|
1368
|
-
get(name) {
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
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
|
+
}
|
|
1373
1396
|
|
|
1374
1397
|
set(name, value) {
|
|
1375
1398
|
if (name in this.store) { this.store[name] = value; return value; }
|
|
@@ -1385,7 +1408,8 @@ class Environment {
|
|
|
1385
1408
|
}
|
|
1386
1409
|
|
|
1387
1410
|
class Evaluator {
|
|
1388
|
-
constructor() {
|
|
1411
|
+
constructor(source = '') {
|
|
1412
|
+
this.source = source;
|
|
1389
1413
|
this.global = new Environment();
|
|
1390
1414
|
this.setupBuiltins();
|
|
1391
1415
|
}
|
|
@@ -1472,11 +1496,11 @@ this.global.define('range', (...args) => {
|
|
|
1472
1496
|
end = Number(args[1]);
|
|
1473
1497
|
step = Number(args[2]);
|
|
1474
1498
|
} else {
|
|
1475
|
-
throw new
|
|
1499
|
+
throw new RuntimeError('range() expects 1 to 3 arguments', null, this.source);
|
|
1476
1500
|
}
|
|
1477
1501
|
|
|
1478
1502
|
if (step === 0) {
|
|
1479
|
-
throw new
|
|
1503
|
+
throw new RuntimeError('range() step cannot be 0', null, this.source);
|
|
1480
1504
|
}
|
|
1481
1505
|
|
|
1482
1506
|
const result = [];
|
|
@@ -1500,7 +1524,7 @@ this.global.define('range', (...args) => {
|
|
|
1500
1524
|
this.global.define('num', arg => {
|
|
1501
1525
|
const n = Number(arg);
|
|
1502
1526
|
if (Number.isNaN(n)) {
|
|
1503
|
-
throw new
|
|
1527
|
+
throw new RuntimeError('Cannot convert value to number', null, this.source);
|
|
1504
1528
|
}
|
|
1505
1529
|
return n;
|
|
1506
1530
|
});
|
|
@@ -1553,7 +1577,7 @@ async evaluate(node, env = this.global) {
|
|
|
1553
1577
|
case 'LogicalExpression': return await this.evalLogical(node, env);
|
|
1554
1578
|
case 'UnaryExpression': return await this.evalUnary(node, env);
|
|
1555
1579
|
case 'Literal': return node.value;
|
|
1556
|
-
case 'Identifier': return env.get(node.name);
|
|
1580
|
+
case 'Identifier': return env.get(node.name, node);
|
|
1557
1581
|
case 'IfStatement': return await this.evalIf(node, env);
|
|
1558
1582
|
case 'WhileStatement': return await this.evalWhile(node, env);
|
|
1559
1583
|
case 'ForStatement':
|
|
@@ -1576,14 +1600,8 @@ case 'DoTrackStatement':
|
|
|
1576
1600
|
case 'ArrayExpression':
|
|
1577
1601
|
return await Promise.all(node.elements.map(el => this.evaluate(el, env)));
|
|
1578
1602
|
case 'IndexExpression': return await this.evalIndex(node, env);
|
|
1579
|
-
case 'ObjectExpression':
|
|
1580
|
-
|
|
1581
|
-
for (const p of node.props) {
|
|
1582
|
-
const key = await this.evaluate(p.key, env);
|
|
1583
|
-
out[key] = await this.evaluate(p.value, env);
|
|
1584
|
-
}
|
|
1585
|
-
return out;
|
|
1586
|
-
}
|
|
1603
|
+
case 'ObjectExpression': return await this.evalObject(node, env);
|
|
1604
|
+
|
|
1587
1605
|
|
|
1588
1606
|
case 'MemberExpression': return await this.evalMember(node, env);
|
|
1589
1607
|
case 'UpdateExpression': return await this.evalUpdate(node, env);
|
|
@@ -1611,7 +1629,7 @@ case 'DoTrackStatement':
|
|
|
1611
1629
|
}
|
|
1612
1630
|
|
|
1613
1631
|
if (typeof callee !== 'function') {
|
|
1614
|
-
throw new
|
|
1632
|
+
throw new RuntimeError('NewExpression callee is not a function', node, this.source);
|
|
1615
1633
|
}
|
|
1616
1634
|
|
|
1617
1635
|
const args = [];
|
|
@@ -1620,31 +1638,51 @@ case 'DoTrackStatement':
|
|
|
1620
1638
|
}
|
|
1621
1639
|
|
|
1622
1640
|
default:
|
|
1623
|
-
|
|
1641
|
+
throw new RuntimeError(`Unknown node type in evaluator: ${node.type}`, node, this.source);
|
|
1642
|
+
|
|
1624
1643
|
}
|
|
1625
1644
|
}
|
|
1626
1645
|
|
|
1627
1646
|
async evalProgram(node, env) {
|
|
1628
1647
|
let result = null;
|
|
1629
1648
|
for (const stmt of node.body) {
|
|
1630
|
-
|
|
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
|
+
}
|
|
1631
1657
|
}
|
|
1632
1658
|
return result;
|
|
1633
1659
|
}
|
|
1634
1660
|
|
|
1661
|
+
|
|
1635
1662
|
async evalDoTrack(node, env) {
|
|
1636
1663
|
try {
|
|
1637
1664
|
return await this.evaluate(node.body, env);
|
|
1638
1665
|
} catch (err) {
|
|
1639
|
-
if (!node.handler)
|
|
1666
|
+
if (!node.handler) {
|
|
1667
|
+
// Wrap any raw error into RuntimeError with line info
|
|
1668
|
+
if (err instanceof RuntimeError) throw err;
|
|
1669
|
+
throw new RuntimeError(err.message || 'Error in doTrack body', node.body, this.source);
|
|
1670
|
+
}
|
|
1640
1671
|
|
|
1641
1672
|
const trackEnv = new Environment(env);
|
|
1642
1673
|
trackEnv.define('error', err);
|
|
1643
1674
|
|
|
1644
|
-
|
|
1675
|
+
try {
|
|
1676
|
+
return await this.evaluate(node.handler, trackEnv);
|
|
1677
|
+
} catch (handlerErr) {
|
|
1678
|
+
// Wrap handler errors as well
|
|
1679
|
+
if (handlerErr instanceof RuntimeError) throw handlerErr;
|
|
1680
|
+
throw new RuntimeError(handlerErr.message || 'Error in doTrack handler', node.handler, this.source);
|
|
1681
|
+
}
|
|
1645
1682
|
}
|
|
1646
1683
|
}
|
|
1647
1684
|
|
|
1685
|
+
|
|
1648
1686
|
async evalImport(node, env) {
|
|
1649
1687
|
const spec = node.path;
|
|
1650
1688
|
let lib;
|
|
@@ -1660,7 +1698,7 @@ async evalImport(node, env) {
|
|
|
1660
1698
|
: path.join(process.cwd(), spec.endsWith('.sl') ? spec : spec + '.sl');
|
|
1661
1699
|
|
|
1662
1700
|
if (!fs.existsSync(fullPath)) {
|
|
1663
|
-
throw new
|
|
1701
|
+
throw new RuntimeError(`Import not found: ${spec}`, node, this.source);
|
|
1664
1702
|
}
|
|
1665
1703
|
|
|
1666
1704
|
const code = fs.readFileSync(fullPath, 'utf-8');
|
|
@@ -1687,7 +1725,7 @@ async evalImport(node, env) {
|
|
|
1687
1725
|
}
|
|
1688
1726
|
if (imp.type === 'NamedImport') {
|
|
1689
1727
|
if (!(imp.imported in lib)) {
|
|
1690
|
-
throw new
|
|
1728
|
+
throw new RuntimeError(`Module '${spec}' has no export '${imp.imported}'`, node, this.source);
|
|
1691
1729
|
}
|
|
1692
1730
|
env.define(imp.local, lib[imp.imported]);
|
|
1693
1731
|
}
|
|
@@ -1703,18 +1741,32 @@ async evalBlock(node, env) {
|
|
|
1703
1741
|
result = await this.evaluate(stmt, env);
|
|
1704
1742
|
} catch (e) {
|
|
1705
1743
|
if (e instanceof ReturnValue || e instanceof BreakSignal || e instanceof ContinueSignal) throw e;
|
|
1706
|
-
|
|
1744
|
+
// Wrap any other error in RuntimeError with the current block node
|
|
1745
|
+
throw new RuntimeError(e.message || 'Error in block', stmt, this.source);
|
|
1707
1746
|
}
|
|
1708
1747
|
}
|
|
1709
1748
|
return result;
|
|
1710
1749
|
}
|
|
1711
1750
|
|
|
1751
|
+
|
|
1712
1752
|
async evalVarDeclaration(node, env) {
|
|
1753
|
+
if (!node.expr) {
|
|
1754
|
+
throw new RuntimeError('Variable declaration requires an initializer', node, this.source);
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1713
1757
|
const val = await this.evaluate(node.expr, env);
|
|
1714
1758
|
return env.define(node.id, val);
|
|
1715
1759
|
}
|
|
1716
1760
|
|
|
1761
|
+
|
|
1717
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
|
+
|
|
1718
1770
|
return {
|
|
1719
1771
|
params: node.params,
|
|
1720
1772
|
body: node.body,
|
|
@@ -1724,24 +1776,29 @@ evalArrowFunction(node, env) {
|
|
|
1724
1776
|
};
|
|
1725
1777
|
}
|
|
1726
1778
|
|
|
1779
|
+
|
|
1727
1780
|
async evalAssignment(node, env) {
|
|
1728
1781
|
const rightVal = await this.evaluate(node.right, env);
|
|
1729
1782
|
const left = node.left;
|
|
1730
1783
|
|
|
1731
1784
|
if (left.type === 'Identifier') return env.set(left.name, rightVal);
|
|
1732
1785
|
if (left.type === 'MemberExpression') {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
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
|
+
|
|
1799
|
+
|
|
1800
|
+
throw new RuntimeError('Invalid assignment target', node, this.source);
|
|
1743
1801
|
|
|
1744
|
-
throw new Error('Invalid assignment target');
|
|
1745
1802
|
}
|
|
1746
1803
|
|
|
1747
1804
|
async evalCompoundAssignment(node, env) {
|
|
@@ -1751,7 +1808,8 @@ async evalCompoundAssignment(node, env) {
|
|
|
1751
1808
|
if (left.type === 'Identifier') current = env.get(left.name);
|
|
1752
1809
|
else if (left.type === 'MemberExpression') current = await this.evalMember(left, env);
|
|
1753
1810
|
else if (left.type === 'IndexExpression') current = await this.evalIndex(left, env);
|
|
1754
|
-
else throw new
|
|
1811
|
+
else throw new RuntimeError('Invalid compound assignment target', node, this.source);
|
|
1812
|
+
|
|
1755
1813
|
|
|
1756
1814
|
const rhs = await this.evaluate(node.right, env);
|
|
1757
1815
|
let computed;
|
|
@@ -1761,7 +1819,8 @@ async evalCompoundAssignment(node, env) {
|
|
|
1761
1819
|
case 'STAREQ': computed = current * rhs; break;
|
|
1762
1820
|
case 'SLASHEQ': computed = current / rhs; break;
|
|
1763
1821
|
case 'MODEQ': computed = current % rhs; break;
|
|
1764
|
-
default: throw new
|
|
1822
|
+
default: throw new RuntimeError(`Unknown compound operator: ${node.operator}`, node, this.source);
|
|
1823
|
+
|
|
1765
1824
|
}
|
|
1766
1825
|
|
|
1767
1826
|
if (left.type === 'Identifier') env.set(left.name, computed);
|
|
@@ -1780,21 +1839,33 @@ async evalSldeploy(node, env) {
|
|
|
1780
1839
|
|
|
1781
1840
|
async evalAsk(node, env) {
|
|
1782
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
|
+
|
|
1783
1847
|
const input = readlineSync.question(prompt + ' ');
|
|
1784
1848
|
return input;
|
|
1785
1849
|
}
|
|
1786
1850
|
|
|
1851
|
+
|
|
1787
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
|
+
|
|
1788
1857
|
const val = node.expr ? await this.evaluate(node.expr, env) : null;
|
|
1789
|
-
return
|
|
1858
|
+
return env.define(node.id, val);
|
|
1859
|
+
|
|
1790
1860
|
}
|
|
1791
1861
|
|
|
1862
|
+
|
|
1792
1863
|
async evalBinary(node, env) {
|
|
1793
1864
|
const l = await this.evaluate(node.left, env);
|
|
1794
1865
|
const r = await this.evaluate(node.right, env);
|
|
1795
1866
|
|
|
1796
1867
|
if (node.operator === 'SLASH' && r === 0) {
|
|
1797
|
-
throw new
|
|
1868
|
+
throw new RuntimeError('Division by zero', node, this.source);
|
|
1798
1869
|
}
|
|
1799
1870
|
|
|
1800
1871
|
switch (node.operator) {
|
|
@@ -1809,7 +1880,8 @@ async evalBinary(node, env) {
|
|
|
1809
1880
|
case 'LTE': return l <= r;
|
|
1810
1881
|
case 'GT': return l > r;
|
|
1811
1882
|
case 'GTE': return l >= r;
|
|
1812
|
-
default: throw new
|
|
1883
|
+
default: throw new RuntimeError(`Unknown binary operator: ${node.operator}`, node, this.source);
|
|
1884
|
+
|
|
1813
1885
|
}
|
|
1814
1886
|
}
|
|
1815
1887
|
|
|
@@ -1817,7 +1889,8 @@ async evalLogical(node, env) {
|
|
|
1817
1889
|
const l = await this.evaluate(node.left, env);
|
|
1818
1890
|
if (node.operator === 'AND') return l && await this.evaluate(node.right, env);
|
|
1819
1891
|
if (node.operator === 'OR') return l || await this.evaluate(node.right, env);
|
|
1820
|
-
throw new
|
|
1892
|
+
throw new RuntimeError(`Unknown logical operator: ${node.operator}`, node, this.source);
|
|
1893
|
+
|
|
1821
1894
|
}
|
|
1822
1895
|
|
|
1823
1896
|
async evalUnary(node, env) {
|
|
@@ -1826,21 +1899,43 @@ async evalUnary(node, env) {
|
|
|
1826
1899
|
case 'NOT': return !val;
|
|
1827
1900
|
case 'MINUS': return -val;
|
|
1828
1901
|
case 'PLUS': return +val;
|
|
1829
|
-
default: throw new
|
|
1902
|
+
default: throw new RuntimeError(`Unknown unary operator: ${node.operator}`, node, this.source);
|
|
1903
|
+
|
|
1830
1904
|
}
|
|
1831
1905
|
}
|
|
1832
1906
|
|
|
1833
1907
|
async evalIf(node, env) {
|
|
1834
1908
|
const test = await this.evaluate(node.test, env);
|
|
1835
|
-
|
|
1836
|
-
if (
|
|
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
|
+
|
|
1837
1922
|
return null;
|
|
1838
1923
|
}
|
|
1839
1924
|
|
|
1925
|
+
|
|
1840
1926
|
async evalWhile(node, env) {
|
|
1841
|
-
while (
|
|
1842
|
-
|
|
1843
|
-
|
|
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) {
|
|
1844
1939
|
if (e instanceof BreakSignal) break;
|
|
1845
1940
|
if (e instanceof ContinueSignal) continue;
|
|
1846
1941
|
throw e;
|
|
@@ -1848,6 +1943,7 @@ async evalWhile(node, env) {
|
|
|
1848
1943
|
}
|
|
1849
1944
|
return null;
|
|
1850
1945
|
}
|
|
1946
|
+
|
|
1851
1947
|
async evalFor(node, env) {
|
|
1852
1948
|
// -------------------------------
|
|
1853
1949
|
// Python-style: for x in iterable (with optional 'let')
|
|
@@ -1856,7 +1952,7 @@ async evalFor(node, env) {
|
|
|
1856
1952
|
const iterable = await this.evaluate(node.iterable, env);
|
|
1857
1953
|
|
|
1858
1954
|
if (iterable == null || typeof iterable !== 'object') {
|
|
1859
|
-
throw new
|
|
1955
|
+
throw new RuntimeError('Cannot iterate over non-iterable', node, this.source);
|
|
1860
1956
|
}
|
|
1861
1957
|
|
|
1862
1958
|
const loopVar = node.variable; // STRING from parser
|
|
@@ -1921,9 +2017,26 @@ async evalFor(node, env) {
|
|
|
1921
2017
|
|
|
1922
2018
|
return null;
|
|
1923
2019
|
}
|
|
1924
|
-
|
|
1925
2020
|
evalFunctionDeclaration(node, env) {
|
|
1926
|
-
|
|
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
|
+
|
|
1927
2040
|
env.define(node.name, fn);
|
|
1928
2041
|
return null;
|
|
1929
2042
|
}
|
|
@@ -1933,10 +2046,10 @@ async evalCall(node, env) {
|
|
|
1933
2046
|
if (typeof calleeEvaluated === 'function') {
|
|
1934
2047
|
const args = [];
|
|
1935
2048
|
for (const a of node.arguments) args.push(await this.evaluate(a, env));
|
|
1936
|
-
return calleeEvaluated(...args);
|
|
2049
|
+
return await calleeEvaluated(...args);
|
|
1937
2050
|
}
|
|
1938
2051
|
if (!calleeEvaluated || typeof calleeEvaluated !== 'object' || !calleeEvaluated.body) {
|
|
1939
|
-
throw new
|
|
2052
|
+
throw new RuntimeError('Call to non-function', node, this.source);
|
|
1940
2053
|
}
|
|
1941
2054
|
|
|
1942
2055
|
const fn = calleeEvaluated;
|
|
@@ -1949,7 +2062,7 @@ async evalCall(node, env) {
|
|
|
1949
2062
|
|
|
1950
2063
|
try {
|
|
1951
2064
|
const result = await this.evaluate(fn.body, callEnv);
|
|
1952
|
-
return
|
|
2065
|
+
return result;
|
|
1953
2066
|
} catch (e) {
|
|
1954
2067
|
if (e instanceof ReturnValue) return e.value;
|
|
1955
2068
|
throw e;
|
|
@@ -1960,12 +2073,13 @@ async evalIndex(node, env) {
|
|
|
1960
2073
|
const obj = await this.evaluate(node.object, env);
|
|
1961
2074
|
const idx = await this.evaluate(node.indexer, env);
|
|
1962
2075
|
|
|
1963
|
-
if (obj == null) throw new
|
|
2076
|
+
if (obj == null) throw new RuntimeError('Indexing null or undefined', node, this.source);
|
|
2077
|
+
|
|
1964
2078
|
if (Array.isArray(obj) && (idx < 0 || idx >= obj.length)) {
|
|
1965
|
-
throw new
|
|
2079
|
+
throw new RuntimeError('Array index out of bounds', node, this.source);
|
|
1966
2080
|
}
|
|
1967
2081
|
if (typeof obj === 'object' && !(idx in obj)) {
|
|
1968
|
-
throw new
|
|
2082
|
+
throw new RuntimeError(`Property '${idx}' does not exist`, node, this.source);
|
|
1969
2083
|
}
|
|
1970
2084
|
|
|
1971
2085
|
return obj[idx];
|
|
@@ -1973,14 +2087,22 @@ async evalIndex(node, env) {
|
|
|
1973
2087
|
|
|
1974
2088
|
async evalObject(node, env) {
|
|
1975
2089
|
const out = {};
|
|
1976
|
-
for (const p of node.props)
|
|
2090
|
+
for (const p of node.props) {
|
|
2091
|
+
if (!p.key || !p.value) {
|
|
2092
|
+
throw new RuntimeError('Invalid object property', node, this.source);
|
|
2093
|
+
}
|
|
2094
|
+
const key = await this.evaluate(p.key, env);
|
|
2095
|
+
const value = await this.evaluate(p.value, env);
|
|
2096
|
+
out[key] = value;
|
|
2097
|
+
}
|
|
1977
2098
|
return out;
|
|
1978
2099
|
}
|
|
1979
2100
|
|
|
2101
|
+
|
|
1980
2102
|
async evalMember(node, env) {
|
|
1981
2103
|
const obj = await this.evaluate(node.object, env);
|
|
1982
|
-
|
|
1983
|
-
|
|
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);
|
|
1984
2106
|
return obj[node.property];
|
|
1985
2107
|
}
|
|
1986
2108
|
|
|
@@ -1990,7 +2112,8 @@ async evalUpdate(node, env) {
|
|
|
1990
2112
|
if (arg.type === 'Identifier') return env.get(arg.name);
|
|
1991
2113
|
if (arg.type === 'MemberExpression') return await this.evalMember(arg, env);
|
|
1992
2114
|
if (arg.type === 'IndexExpression') return await this.evalIndex(arg, env);
|
|
1993
|
-
throw new
|
|
2115
|
+
throw new RuntimeError('Invalid update target', node, this.source);
|
|
2116
|
+
|
|
1994
2117
|
};
|
|
1995
2118
|
const setValue = async (v) => {
|
|
1996
2119
|
if (arg.type === 'Identifier') env.set(arg.name, v);
|
|
@@ -2020,14 +2143,33 @@ module.exports = Evaluator;
|
|
|
2020
2143
|
/***/ 211:
|
|
2021
2144
|
/***/ ((module) => {
|
|
2022
2145
|
|
|
2146
|
+
class LexerError extends Error {
|
|
2147
|
+
constructor(message, line, column, source = '') {
|
|
2148
|
+
let output = `SyntaxError: ${message}\n`;
|
|
2149
|
+
output += ` at line ${line}, column ${column}\n`;
|
|
2150
|
+
|
|
2151
|
+
if (source && line != null) {
|
|
2152
|
+
const lines = source.split('\n');
|
|
2153
|
+
const srcLine = lines[line - 1] || '';
|
|
2154
|
+
output += ` ${srcLine}\n`;
|
|
2155
|
+
output += ` ${' '.repeat(column - 1)}^\n`;
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
super(output);
|
|
2159
|
+
this.name = 'SyntaxError';
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2023
2163
|
class Lexer {
|
|
2024
2164
|
constructor(input) {
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2165
|
+
this.input = input;
|
|
2166
|
+
this.source = input;
|
|
2167
|
+
this.pos = 0;
|
|
2168
|
+
this.currentChar = input[0] || null;
|
|
2169
|
+
this.line = 1;
|
|
2170
|
+
this.column = 1;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2031
2173
|
|
|
2032
2174
|
advance() {
|
|
2033
2175
|
if (this.currentChar === '\n') {
|
|
@@ -2045,8 +2187,14 @@ class Lexer {
|
|
|
2045
2187
|
}
|
|
2046
2188
|
|
|
2047
2189
|
error(msg) {
|
|
2048
|
-
|
|
2049
|
-
|
|
2190
|
+
throw new LexerError(
|
|
2191
|
+
msg,
|
|
2192
|
+
this.line,
|
|
2193
|
+
this.column,
|
|
2194
|
+
this.source
|
|
2195
|
+
);
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2050
2198
|
|
|
2051
2199
|
skipWhitespace() {
|
|
2052
2200
|
while (this.currentChar && /\s/.test(this.currentChar)) this.advance();
|
|
@@ -2214,25 +2362,54 @@ module.exports = Lexer;
|
|
|
2214
2362
|
/***/ 222:
|
|
2215
2363
|
/***/ ((module) => {
|
|
2216
2364
|
|
|
2217
|
-
class
|
|
2218
|
-
constructor(
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2365
|
+
class ParseError extends Error {
|
|
2366
|
+
constructor(message, token, source) {
|
|
2367
|
+
const line = token?.line ?? '?';
|
|
2368
|
+
const column = token?.column ?? '?';
|
|
2369
|
+
|
|
2370
|
+
let output = `SyntaxError: ${message}\n`;
|
|
2371
|
+
|
|
2372
|
+
if (source && token?.line != null) {
|
|
2373
|
+
const lines = source.split('\n');
|
|
2374
|
+
const srcLine = lines[token.line - 1] || '';
|
|
2375
|
+
output += ` at line ${line}, column ${column}\n`;
|
|
2376
|
+
output += ` ${srcLine}\n`;
|
|
2377
|
+
output += ` ${' '.repeat(column - 1)}^\n`;
|
|
2378
|
+
} else {
|
|
2379
|
+
output += ` at line ${line}, column ${column}\n`;
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
super(output);
|
|
2383
|
+
this.name = 'SyntaxError';
|
|
2222
2384
|
}
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
class Parser {
|
|
2388
|
+
constructor(tokens, source = '') {
|
|
2389
|
+
this.tokens = tokens;
|
|
2390
|
+
this.source = source;
|
|
2391
|
+
this.pos = 0;
|
|
2392
|
+
this.current = this.tokens[this.pos];
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2223
2395
|
|
|
2224
2396
|
advance() {
|
|
2225
2397
|
this.pos++;
|
|
2226
2398
|
this.current = this.pos < this.tokens.length ? this.tokens[this.pos] : { type: 'EOF' };
|
|
2227
2399
|
}
|
|
2228
2400
|
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2401
|
+
eat(type) {
|
|
2402
|
+
if (this.current.type === type) {
|
|
2403
|
+
this.advance();
|
|
2404
|
+
} else {
|
|
2405
|
+
throw new ParseError(
|
|
2406
|
+
`Expected '${type}' but got '${this.current.type}'`,
|
|
2407
|
+
this.current,
|
|
2408
|
+
this.source
|
|
2409
|
+
);
|
|
2235
2410
|
}
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2236
2413
|
|
|
2237
2414
|
peekType(offset = 1) {
|
|
2238
2415
|
return (this.tokens[this.pos + offset] || { type: 'EOF' }).type;
|
|
@@ -2270,10 +2447,11 @@ class Parser {
|
|
|
2270
2447
|
return this.expressionStatement();
|
|
2271
2448
|
}
|
|
2272
2449
|
}
|
|
2273
|
-
|
|
2274
|
-
|
|
2450
|
+
varDeclaration() {
|
|
2451
|
+
const t = this.current; // LET token
|
|
2275
2452
|
this.eat('LET');
|
|
2276
|
-
const
|
|
2453
|
+
const idToken = this.current; // identifier token
|
|
2454
|
+
const id = idToken.value;
|
|
2277
2455
|
this.eat('IDENTIFIER');
|
|
2278
2456
|
|
|
2279
2457
|
let expr = null;
|
|
@@ -2285,16 +2463,30 @@ class Parser {
|
|
|
2285
2463
|
// semicolon optional
|
|
2286
2464
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
2287
2465
|
|
|
2288
|
-
return {
|
|
2466
|
+
return {
|
|
2467
|
+
type: 'VarDeclaration',
|
|
2468
|
+
id,
|
|
2469
|
+
expr,
|
|
2470
|
+
line: t.line,
|
|
2471
|
+
column: t.column
|
|
2472
|
+
};
|
|
2289
2473
|
}
|
|
2290
2474
|
|
|
2291
2475
|
sldeployStatement() {
|
|
2476
|
+
const t = this.current; // SLDEPLOY token
|
|
2292
2477
|
this.eat('SLDEPLOY');
|
|
2293
2478
|
const expr = this.expression();
|
|
2294
2479
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
2295
|
-
return {
|
|
2480
|
+
return {
|
|
2481
|
+
type: 'SldeployStatement',
|
|
2482
|
+
expr,
|
|
2483
|
+
line: t.line,
|
|
2484
|
+
column: t.column
|
|
2485
|
+
};
|
|
2296
2486
|
}
|
|
2487
|
+
|
|
2297
2488
|
doTrackStatement() {
|
|
2489
|
+
const t = this.current; // DO token
|
|
2298
2490
|
this.eat('DO');
|
|
2299
2491
|
|
|
2300
2492
|
const body = this.block();
|
|
@@ -2308,13 +2500,17 @@ doTrackStatement() {
|
|
|
2308
2500
|
return {
|
|
2309
2501
|
type: 'DoTrackStatement',
|
|
2310
2502
|
body,
|
|
2311
|
-
handler
|
|
2503
|
+
handler,
|
|
2504
|
+
line: t.line,
|
|
2505
|
+
column: t.column
|
|
2312
2506
|
};
|
|
2313
2507
|
}
|
|
2314
2508
|
|
|
2315
2509
|
defineStatement() {
|
|
2510
|
+
const t = this.current; // DEFINE token
|
|
2316
2511
|
this.eat('DEFINE');
|
|
2317
|
-
const
|
|
2512
|
+
const idToken = this.current; // identifier token
|
|
2513
|
+
const id = idToken.value;
|
|
2318
2514
|
this.eat('IDENTIFIER');
|
|
2319
2515
|
|
|
2320
2516
|
let expr = null;
|
|
@@ -2325,10 +2521,17 @@ defineStatement() {
|
|
|
2325
2521
|
|
|
2326
2522
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
2327
2523
|
|
|
2328
|
-
return {
|
|
2524
|
+
return {
|
|
2525
|
+
type: 'DefineStatement',
|
|
2526
|
+
id,
|
|
2527
|
+
expr,
|
|
2528
|
+
line: t.line,
|
|
2529
|
+
column: t.column
|
|
2530
|
+
};
|
|
2329
2531
|
}
|
|
2330
2532
|
|
|
2331
2533
|
asyncFuncDeclaration() {
|
|
2534
|
+
const t = this.current; // first token of function (identifier)
|
|
2332
2535
|
const name = this.current.value;
|
|
2333
2536
|
this.eat('IDENTIFIER');
|
|
2334
2537
|
|
|
@@ -2354,10 +2557,19 @@ asyncFuncDeclaration() {
|
|
|
2354
2557
|
}
|
|
2355
2558
|
|
|
2356
2559
|
const body = this.block();
|
|
2357
|
-
return {
|
|
2560
|
+
return {
|
|
2561
|
+
type: 'FunctionDeclaration',
|
|
2562
|
+
name,
|
|
2563
|
+
params,
|
|
2564
|
+
body,
|
|
2565
|
+
async: true,
|
|
2566
|
+
line: t.line,
|
|
2567
|
+
column: t.column
|
|
2568
|
+
};
|
|
2358
2569
|
}
|
|
2359
2570
|
|
|
2360
2571
|
ifStatement() {
|
|
2572
|
+
const t = this.current; // IF token
|
|
2361
2573
|
this.eat('IF');
|
|
2362
2574
|
|
|
2363
2575
|
let test;
|
|
@@ -2366,7 +2578,7 @@ ifStatement() {
|
|
|
2366
2578
|
test = this.expression();
|
|
2367
2579
|
this.eat('RPAREN');
|
|
2368
2580
|
} else {
|
|
2369
|
-
//
|
|
2581
|
+
// Python style: no parentheses
|
|
2370
2582
|
test = this.expression();
|
|
2371
2583
|
}
|
|
2372
2584
|
|
|
@@ -2379,10 +2591,18 @@ ifStatement() {
|
|
|
2379
2591
|
else alternate = this.block();
|
|
2380
2592
|
}
|
|
2381
2593
|
|
|
2382
|
-
return {
|
|
2594
|
+
return {
|
|
2595
|
+
type: 'IfStatement',
|
|
2596
|
+
test,
|
|
2597
|
+
consequent,
|
|
2598
|
+
alternate,
|
|
2599
|
+
line: t.line,
|
|
2600
|
+
column: t.column
|
|
2601
|
+
};
|
|
2383
2602
|
}
|
|
2384
2603
|
|
|
2385
2604
|
whileStatement() {
|
|
2605
|
+
const t = this.current; // WHILE token
|
|
2386
2606
|
this.eat('WHILE');
|
|
2387
2607
|
|
|
2388
2608
|
let test;
|
|
@@ -2395,10 +2615,17 @@ whileStatement() {
|
|
|
2395
2615
|
}
|
|
2396
2616
|
|
|
2397
2617
|
const body = this.block();
|
|
2398
|
-
return {
|
|
2618
|
+
return {
|
|
2619
|
+
type: 'WhileStatement',
|
|
2620
|
+
test,
|
|
2621
|
+
body,
|
|
2622
|
+
line: t.line,
|
|
2623
|
+
column: t.column
|
|
2624
|
+
};
|
|
2399
2625
|
}
|
|
2400
2626
|
|
|
2401
2627
|
importStatement() {
|
|
2628
|
+
const t = this.current; // IMPORT token
|
|
2402
2629
|
this.eat('IMPORT');
|
|
2403
2630
|
|
|
2404
2631
|
let specifiers = [];
|
|
@@ -2407,11 +2634,13 @@ importStatement() {
|
|
|
2407
2634
|
this.eat('AS');
|
|
2408
2635
|
const name = this.current.value;
|
|
2409
2636
|
this.eat('IDENTIFIER');
|
|
2410
|
-
specifiers.push({ type: 'NamespaceImport', local: name });
|
|
2637
|
+
specifiers.push({ type: 'NamespaceImport', local: name, line: t.line, column: t.column });
|
|
2411
2638
|
} else if (this.current.type === 'LBRACE') {
|
|
2412
2639
|
this.eat('LBRACE');
|
|
2413
2640
|
while (this.current.type !== 'RBRACE') {
|
|
2414
2641
|
const importedName = this.current.value;
|
|
2642
|
+
const importedLine = this.current.line;
|
|
2643
|
+
const importedColumn = this.current.column;
|
|
2415
2644
|
this.eat('IDENTIFIER');
|
|
2416
2645
|
|
|
2417
2646
|
let localName = importedName;
|
|
@@ -2421,14 +2650,16 @@ importStatement() {
|
|
|
2421
2650
|
this.eat('IDENTIFIER');
|
|
2422
2651
|
}
|
|
2423
2652
|
|
|
2424
|
-
specifiers.push({ type: 'NamedImport', imported: importedName, local: localName });
|
|
2653
|
+
specifiers.push({ type: 'NamedImport', imported: importedName, local: localName, line: importedLine, column: importedColumn });
|
|
2425
2654
|
if (this.current.type === 'COMMA') this.eat('COMMA');
|
|
2426
2655
|
}
|
|
2427
2656
|
this.eat('RBRACE');
|
|
2428
2657
|
} else if (this.current.type === 'IDENTIFIER') {
|
|
2429
2658
|
const localName = this.current.value;
|
|
2659
|
+
const localLine = this.current.line;
|
|
2660
|
+
const localColumn = this.current.column;
|
|
2430
2661
|
this.eat('IDENTIFIER');
|
|
2431
|
-
specifiers.push({ type: 'DefaultImport', local: localName });
|
|
2662
|
+
specifiers.push({ type: 'DefaultImport', local: localName, line: localLine, column: localColumn });
|
|
2432
2663
|
} else {
|
|
2433
2664
|
throw new Error(`Unexpected token in import at line ${this.current.line}, column ${this.current.column}`);
|
|
2434
2665
|
}
|
|
@@ -2440,9 +2671,17 @@ importStatement() {
|
|
|
2440
2671
|
|
|
2441
2672
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
2442
2673
|
|
|
2443
|
-
return {
|
|
2674
|
+
return {
|
|
2675
|
+
type: 'ImportStatement',
|
|
2676
|
+
path: pathToken.value,
|
|
2677
|
+
specifiers,
|
|
2678
|
+
line: t.line,
|
|
2679
|
+
column: t.column
|
|
2680
|
+
};
|
|
2444
2681
|
}
|
|
2682
|
+
|
|
2445
2683
|
forStatement() {
|
|
2684
|
+
const t = this.current; // FOR token
|
|
2446
2685
|
this.eat('FOR');
|
|
2447
2686
|
|
|
2448
2687
|
// --- Python-style: for variable in iterable (supports optional 'let') ---
|
|
@@ -2450,17 +2689,22 @@ forStatement() {
|
|
|
2450
2689
|
(this.current.type === 'IDENTIFIER' && this.peekType() === 'IN')) {
|
|
2451
2690
|
|
|
2452
2691
|
let variable;
|
|
2692
|
+
let variableLine, variableColumn;
|
|
2453
2693
|
let iterable;
|
|
2454
2694
|
let letKeyword = false;
|
|
2455
2695
|
|
|
2456
2696
|
if (this.current.type === 'LET') {
|
|
2457
2697
|
letKeyword = true;
|
|
2458
2698
|
this.eat('LET');
|
|
2699
|
+
variableLine = this.current.line;
|
|
2700
|
+
variableColumn = this.current.column;
|
|
2459
2701
|
variable = this.current.value;
|
|
2460
2702
|
this.eat('IDENTIFIER');
|
|
2461
2703
|
this.eat('IN');
|
|
2462
2704
|
iterable = this.expression();
|
|
2463
2705
|
} else {
|
|
2706
|
+
variableLine = this.current.line;
|
|
2707
|
+
variableColumn = this.current.column;
|
|
2464
2708
|
variable = this.current.value;
|
|
2465
2709
|
this.eat('IDENTIFIER');
|
|
2466
2710
|
this.eat('IN');
|
|
@@ -2468,7 +2712,17 @@ forStatement() {
|
|
|
2468
2712
|
}
|
|
2469
2713
|
|
|
2470
2714
|
const body = this.block();
|
|
2471
|
-
return {
|
|
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
|
+
};
|
|
2472
2726
|
}
|
|
2473
2727
|
|
|
2474
2728
|
// --- C-style: for(init; test; update) ---
|
|
@@ -2503,37 +2757,51 @@ forStatement() {
|
|
|
2503
2757
|
}
|
|
2504
2758
|
|
|
2505
2759
|
const body = this.block();
|
|
2506
|
-
return {
|
|
2760
|
+
return {
|
|
2761
|
+
type: 'ForStatement',
|
|
2762
|
+
init,
|
|
2763
|
+
test,
|
|
2764
|
+
update,
|
|
2765
|
+
body,
|
|
2766
|
+
line: t.line,
|
|
2767
|
+
column: t.column
|
|
2768
|
+
};
|
|
2507
2769
|
}
|
|
2508
2770
|
|
|
2509
2771
|
breakStatement() {
|
|
2772
|
+
const t = this.current; // BREAK token
|
|
2510
2773
|
this.eat('BREAK');
|
|
2511
2774
|
// Python-style: no semicolon needed, ignore if present
|
|
2512
2775
|
if (this.current.type === 'SEMICOLON') this.advance();
|
|
2513
|
-
return { type: 'BreakStatement' };
|
|
2776
|
+
return { type: 'BreakStatement', line: t.line, column: t.column };
|
|
2514
2777
|
}
|
|
2515
2778
|
|
|
2516
2779
|
continueStatement() {
|
|
2780
|
+
const t = this.current; // CONTINUE token
|
|
2517
2781
|
this.eat('CONTINUE');
|
|
2518
2782
|
// Python-style: no semicolon needed, ignore if present
|
|
2519
2783
|
if (this.current.type === 'SEMICOLON') this.advance();
|
|
2520
|
-
return { type: 'ContinueStatement' };
|
|
2784
|
+
return { type: 'ContinueStatement', line: t.line, column: t.column };
|
|
2521
2785
|
}
|
|
2522
2786
|
|
|
2523
2787
|
funcDeclaration() {
|
|
2788
|
+
const t = this.current; // FUNC token
|
|
2524
2789
|
this.eat('FUNC');
|
|
2525
|
-
const
|
|
2790
|
+
const nameToken = this.current;
|
|
2791
|
+
const name = nameToken.value;
|
|
2526
2792
|
this.eat('IDENTIFIER');
|
|
2527
2793
|
|
|
2528
2794
|
let params = [];
|
|
2529
2795
|
if (this.current.type === 'LPAREN') {
|
|
2530
2796
|
this.eat('LPAREN');
|
|
2531
2797
|
if (this.current.type !== 'RPAREN') {
|
|
2532
|
-
|
|
2798
|
+
const paramToken = this.current;
|
|
2799
|
+
params.push({ name: paramToken.value, line: paramToken.line, column: paramToken.column });
|
|
2533
2800
|
this.eat('IDENTIFIER');
|
|
2534
2801
|
while (this.current.type === 'COMMA') {
|
|
2535
2802
|
this.eat('COMMA');
|
|
2536
|
-
|
|
2803
|
+
const paramToken2 = this.current;
|
|
2804
|
+
params.push({ name: paramToken2.value, line: paramToken2.line, column: paramToken2.column });
|
|
2537
2805
|
this.eat('IDENTIFIER');
|
|
2538
2806
|
}
|
|
2539
2807
|
}
|
|
@@ -2541,16 +2809,18 @@ funcDeclaration() {
|
|
|
2541
2809
|
} else {
|
|
2542
2810
|
// Python-style: single param without parentheses
|
|
2543
2811
|
if (this.current.type === 'IDENTIFIER') {
|
|
2544
|
-
|
|
2812
|
+
const paramToken = this.current;
|
|
2813
|
+
params.push({ name: paramToken.value, line: paramToken.line, column: paramToken.column });
|
|
2545
2814
|
this.eat('IDENTIFIER');
|
|
2546
2815
|
}
|
|
2547
2816
|
}
|
|
2548
2817
|
|
|
2549
2818
|
const body = this.block();
|
|
2550
|
-
return { type: 'FunctionDeclaration', name, params, body };
|
|
2819
|
+
return { type: 'FunctionDeclaration', name, params, body, line: t.line, column: t.column };
|
|
2551
2820
|
}
|
|
2552
2821
|
|
|
2553
2822
|
returnStatement() {
|
|
2823
|
+
const t = this.current; // RETURN token
|
|
2554
2824
|
this.eat('RETURN');
|
|
2555
2825
|
|
|
2556
2826
|
let argument = null;
|
|
@@ -2561,24 +2831,26 @@ returnStatement() {
|
|
|
2561
2831
|
// semicolon optional
|
|
2562
2832
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
2563
2833
|
|
|
2564
|
-
return { type: 'ReturnStatement', argument };
|
|
2834
|
+
return { type: 'ReturnStatement', argument, line: t.line, column: t.column };
|
|
2565
2835
|
}
|
|
2566
2836
|
|
|
2567
2837
|
block() {
|
|
2838
|
+
const t = this.current; // LBRACE token
|
|
2568
2839
|
this.eat('LBRACE');
|
|
2569
2840
|
const body = [];
|
|
2570
2841
|
while (this.current.type !== 'RBRACE') {
|
|
2571
2842
|
body.push(this.statement());
|
|
2572
2843
|
}
|
|
2573
2844
|
this.eat('RBRACE');
|
|
2574
|
-
return { type: 'BlockStatement', body };
|
|
2845
|
+
return { type: 'BlockStatement', body, line: t.line, column: t.column };
|
|
2575
2846
|
}
|
|
2576
2847
|
|
|
2577
2848
|
expressionStatement() {
|
|
2849
|
+
const exprToken = this.current; // first token of the expression
|
|
2578
2850
|
const expr = this.expression();
|
|
2579
2851
|
// semicolon optional
|
|
2580
2852
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
2581
|
-
return { type: 'ExpressionStatement', expression: expr };
|
|
2853
|
+
return { type: 'ExpressionStatement', expression: expr, line: exprToken.line, column: exprToken.column };
|
|
2582
2854
|
}
|
|
2583
2855
|
|
|
2584
2856
|
expression() {
|
|
@@ -2589,17 +2861,19 @@ assignment() {
|
|
|
2589
2861
|
const node = this.logicalOr();
|
|
2590
2862
|
const compoundOps = ['PLUSEQ', 'MINUSEQ', 'STAREQ', 'SLASHEQ', 'MODEQ'];
|
|
2591
2863
|
|
|
2592
|
-
|
|
2593
|
-
|
|
2864
|
+
const t = this.current;
|
|
2865
|
+
|
|
2866
|
+
if (compoundOps.includes(t.type)) {
|
|
2867
|
+
const op = t.type;
|
|
2594
2868
|
this.eat(op);
|
|
2595
2869
|
const right = this.assignment();
|
|
2596
|
-
return { type: 'CompoundAssignment', operator: op, left: node, right };
|
|
2870
|
+
return { type: 'CompoundAssignment', operator: op, left: node, right, line: t.line, column: t.column };
|
|
2597
2871
|
}
|
|
2598
2872
|
|
|
2599
|
-
if (
|
|
2873
|
+
if (t.type === 'EQUAL') {
|
|
2600
2874
|
this.eat('EQUAL');
|
|
2601
2875
|
const right = this.assignment();
|
|
2602
|
-
return { type: 'AssignmentExpression', left: node, right };
|
|
2876
|
+
return { type: 'AssignmentExpression', left: node, right, line: t.line, column: t.column };
|
|
2603
2877
|
}
|
|
2604
2878
|
|
|
2605
2879
|
return node;
|
|
@@ -2608,9 +2882,10 @@ assignment() {
|
|
|
2608
2882
|
logicalOr() {
|
|
2609
2883
|
let node = this.logicalAnd();
|
|
2610
2884
|
while (this.current.type === 'OR') {
|
|
2611
|
-
const
|
|
2885
|
+
const t = this.current;
|
|
2886
|
+
const op = t.type;
|
|
2612
2887
|
this.eat(op);
|
|
2613
|
-
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 };
|
|
2614
2889
|
}
|
|
2615
2890
|
return node;
|
|
2616
2891
|
}
|
|
@@ -2618,18 +2893,21 @@ logicalOr() {
|
|
|
2618
2893
|
logicalAnd() {
|
|
2619
2894
|
let node = this.equality();
|
|
2620
2895
|
while (this.current.type === 'AND') {
|
|
2621
|
-
const
|
|
2896
|
+
const t = this.current;
|
|
2897
|
+
const op = t.type;
|
|
2622
2898
|
this.eat(op);
|
|
2623
|
-
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 };
|
|
2624
2900
|
}
|
|
2625
2901
|
return node;
|
|
2626
2902
|
}
|
|
2903
|
+
|
|
2627
2904
|
equality() {
|
|
2628
2905
|
let node = this.comparison();
|
|
2629
2906
|
while (['EQEQ', 'NOTEQ'].includes(this.current.type)) {
|
|
2630
|
-
const
|
|
2907
|
+
const t = this.current;
|
|
2908
|
+
const op = t.type;
|
|
2631
2909
|
this.eat(op);
|
|
2632
|
-
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 };
|
|
2633
2911
|
}
|
|
2634
2912
|
return node;
|
|
2635
2913
|
}
|
|
@@ -2637,9 +2915,10 @@ equality() {
|
|
|
2637
2915
|
comparison() {
|
|
2638
2916
|
let node = this.term();
|
|
2639
2917
|
while (['LT', 'LTE', 'GT', 'GTE'].includes(this.current.type)) {
|
|
2640
|
-
const
|
|
2918
|
+
const t = this.current;
|
|
2919
|
+
const op = t.type;
|
|
2641
2920
|
this.eat(op);
|
|
2642
|
-
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 };
|
|
2643
2922
|
}
|
|
2644
2923
|
return node;
|
|
2645
2924
|
}
|
|
@@ -2647,9 +2926,10 @@ comparison() {
|
|
|
2647
2926
|
term() {
|
|
2648
2927
|
let node = this.factor();
|
|
2649
2928
|
while (['PLUS', 'MINUS'].includes(this.current.type)) {
|
|
2650
|
-
const
|
|
2929
|
+
const t = this.current;
|
|
2930
|
+
const op = t.type;
|
|
2651
2931
|
this.eat(op);
|
|
2652
|
-
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 };
|
|
2653
2933
|
}
|
|
2654
2934
|
return node;
|
|
2655
2935
|
}
|
|
@@ -2657,26 +2937,43 @@ term() {
|
|
|
2657
2937
|
factor() {
|
|
2658
2938
|
let node = this.unary();
|
|
2659
2939
|
while (['STAR', 'SLASH', 'MOD'].includes(this.current.type)) {
|
|
2660
|
-
const
|
|
2940
|
+
const t = this.current;
|
|
2941
|
+
const op = t.type;
|
|
2661
2942
|
this.eat(op);
|
|
2662
|
-
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 };
|
|
2663
2944
|
}
|
|
2664
2945
|
return node;
|
|
2665
2946
|
}
|
|
2666
2947
|
|
|
2948
|
+
|
|
2667
2949
|
unary() {
|
|
2668
|
-
|
|
2669
|
-
|
|
2950
|
+
const t = this.current;
|
|
2951
|
+
|
|
2952
|
+
if (['NOT', 'MINUS', 'PLUS'].includes(t.type)) {
|
|
2953
|
+
const op = t.type;
|
|
2670
2954
|
this.eat(op);
|
|
2671
|
-
return {
|
|
2955
|
+
return {
|
|
2956
|
+
type: 'UnaryExpression',
|
|
2957
|
+
operator: op,
|
|
2958
|
+
argument: this.unary(),
|
|
2959
|
+
line: t.line,
|
|
2960
|
+
column: t.column
|
|
2961
|
+
};
|
|
2672
2962
|
}
|
|
2673
2963
|
|
|
2674
2964
|
// Python-like: ignore ++ and -- if not used
|
|
2675
|
-
if (
|
|
2676
|
-
const op =
|
|
2965
|
+
if (t.type === 'PLUSPLUS' || t.type === 'MINUSMINUS') {
|
|
2966
|
+
const op = t.type;
|
|
2677
2967
|
this.eat(op);
|
|
2678
2968
|
const argument = this.unary();
|
|
2679
|
-
return {
|
|
2969
|
+
return {
|
|
2970
|
+
type: 'UpdateExpression',
|
|
2971
|
+
operator: op,
|
|
2972
|
+
argument,
|
|
2973
|
+
prefix: true,
|
|
2974
|
+
line: t.line,
|
|
2975
|
+
column: t.column
|
|
2976
|
+
};
|
|
2680
2977
|
}
|
|
2681
2978
|
|
|
2682
2979
|
return this.postfix();
|
|
@@ -2685,15 +2982,21 @@ unary() {
|
|
|
2685
2982
|
postfix() {
|
|
2686
2983
|
let node = this.primary();
|
|
2687
2984
|
while (true) {
|
|
2688
|
-
|
|
2985
|
+
const t = this.current;
|
|
2986
|
+
|
|
2987
|
+
if (t.type === 'LBRACKET') {
|
|
2988
|
+
const startLine = t.line;
|
|
2989
|
+
const startCol = t.column;
|
|
2689
2990
|
this.eat('LBRACKET');
|
|
2690
2991
|
const index = this.expression();
|
|
2691
2992
|
if (this.current.type === 'RBRACKET') this.eat('RBRACKET');
|
|
2692
|
-
node = { type: 'IndexExpression', object: node, indexer: index };
|
|
2993
|
+
node = { type: 'IndexExpression', object: node, indexer: index, line: startLine, column: startCol };
|
|
2693
2994
|
continue;
|
|
2694
2995
|
}
|
|
2695
2996
|
|
|
2696
|
-
if (
|
|
2997
|
+
if (t.type === 'LPAREN') {
|
|
2998
|
+
const startLine = t.line;
|
|
2999
|
+
const startCol = t.column;
|
|
2697
3000
|
this.eat('LPAREN');
|
|
2698
3001
|
const args = [];
|
|
2699
3002
|
while (this.current.type !== 'RPAREN' && this.current.type !== 'EOF') {
|
|
@@ -2701,30 +3004,30 @@ postfix() {
|
|
|
2701
3004
|
if (this.current.type === 'COMMA') this.eat('COMMA'); // optional comma
|
|
2702
3005
|
}
|
|
2703
3006
|
if (this.current.type === 'RPAREN') this.eat('RPAREN');
|
|
2704
|
-
node = { type: 'CallExpression', callee: node, arguments: args };
|
|
3007
|
+
node = { type: 'CallExpression', callee: node, arguments: args, line: startLine, column: startCol };
|
|
2705
3008
|
continue;
|
|
2706
3009
|
}
|
|
2707
3010
|
|
|
2708
|
-
if (
|
|
3011
|
+
if (t.type === 'DOT') {
|
|
3012
|
+
const startLine = t.line;
|
|
3013
|
+
const startCol = t.column;
|
|
2709
3014
|
this.eat('DOT');
|
|
2710
3015
|
const property = this.current.value || '';
|
|
2711
3016
|
if (this.current.type === 'IDENTIFIER') this.eat('IDENTIFIER');
|
|
2712
|
-
node = { type: 'MemberExpression', object: node, property };
|
|
3017
|
+
node = { type: 'MemberExpression', object: node, property, line: startLine, column: startCol };
|
|
2713
3018
|
continue;
|
|
2714
3019
|
}
|
|
2715
3020
|
|
|
2716
|
-
if (
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
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 };
|
|
2720
3024
|
continue;
|
|
2721
3025
|
}
|
|
2722
3026
|
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
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 };
|
|
2726
3029
|
this.eat('IDENTIFIER');
|
|
2727
|
-
node = { type: 'CallExpression', callee: node, arguments: [argNode] };
|
|
3030
|
+
node = { type: 'CallExpression', callee: node, arguments: [argNode], line: node.line, column: node.column };
|
|
2728
3031
|
continue;
|
|
2729
3032
|
}
|
|
2730
3033
|
|
|
@@ -2734,12 +3037,18 @@ postfix() {
|
|
|
2734
3037
|
}
|
|
2735
3038
|
|
|
2736
3039
|
arrowFunction(params) {
|
|
2737
|
-
|
|
3040
|
+
const t = this.current;
|
|
3041
|
+
if (t.type === 'ARROW') this.eat('ARROW');
|
|
2738
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
|
+
|
|
2739
3046
|
return {
|
|
2740
3047
|
type: 'ArrowFunctionExpression',
|
|
2741
3048
|
params,
|
|
2742
|
-
body
|
|
3049
|
+
body,
|
|
3050
|
+
line: startLine,
|
|
3051
|
+
column: startCol
|
|
2743
3052
|
};
|
|
2744
3053
|
}
|
|
2745
3054
|
|
|
@@ -2747,16 +3056,35 @@ arrowFunction(params) {
|
|
|
2747
3056
|
primary() {
|
|
2748
3057
|
const t = this.current;
|
|
2749
3058
|
|
|
2750
|
-
if (t.type === 'NUMBER') {
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
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
|
+
}
|
|
2755
3083
|
|
|
2756
3084
|
if (t.type === 'AWAIT') {
|
|
2757
3085
|
this.eat('AWAIT');
|
|
2758
3086
|
const argument = this.expression();
|
|
2759
|
-
return { type: 'AwaitExpression', argument };
|
|
3087
|
+
return { type: 'AwaitExpression', argument, line: t.line, column: t.column };
|
|
2760
3088
|
}
|
|
2761
3089
|
|
|
2762
3090
|
if (t.type === 'NEW') {
|
|
@@ -2772,7 +3100,7 @@ arrowFunction(params) {
|
|
|
2772
3100
|
}
|
|
2773
3101
|
}
|
|
2774
3102
|
this.eat('RPAREN');
|
|
2775
|
-
return { type: 'NewExpression', callee, arguments: args };
|
|
3103
|
+
return { type: 'NewExpression', callee, arguments: args, line: t.line, column: t.column };
|
|
2776
3104
|
}
|
|
2777
3105
|
|
|
2778
3106
|
if (t.type === 'ASK') {
|
|
@@ -2787,7 +3115,13 @@ arrowFunction(params) {
|
|
|
2787
3115
|
}
|
|
2788
3116
|
}
|
|
2789
3117
|
this.eat('RPAREN');
|
|
2790
|
-
return {
|
|
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
|
+
};
|
|
2791
3125
|
}
|
|
2792
3126
|
|
|
2793
3127
|
if (t.type === 'IDENTIFIER') {
|
|
@@ -2795,13 +3129,15 @@ arrowFunction(params) {
|
|
|
2795
3129
|
this.eat('IDENTIFIER');
|
|
2796
3130
|
|
|
2797
3131
|
if (this.current.type === 'ARROW') {
|
|
2798
|
-
return this.arrowFunction([name]);
|
|
3132
|
+
return this.arrowFunction([{ type: 'Identifier', name, line: t.line, column: t.column }]);
|
|
2799
3133
|
}
|
|
2800
3134
|
|
|
2801
|
-
return { type: 'Identifier', name };
|
|
3135
|
+
return { type: 'Identifier', name, line: t.line, column: t.column };
|
|
2802
3136
|
}
|
|
2803
3137
|
|
|
2804
3138
|
if (t.type === 'LPAREN') {
|
|
3139
|
+
const startLine = t.line;
|
|
3140
|
+
const startCol = t.column;
|
|
2805
3141
|
this.eat('LPAREN');
|
|
2806
3142
|
const elements = [];
|
|
2807
3143
|
|
|
@@ -2817,10 +3153,12 @@ arrowFunction(params) {
|
|
|
2817
3153
|
|
|
2818
3154
|
if (this.current.type === 'ARROW') return this.arrowFunction(elements);
|
|
2819
3155
|
|
|
2820
|
-
return elements.length === 1 ? elements[0] : { type: 'ArrayExpression', elements };
|
|
3156
|
+
return elements.length === 1 ? elements[0] : { type: 'ArrayExpression', elements, line: startLine, column: startCol };
|
|
2821
3157
|
}
|
|
2822
3158
|
|
|
2823
3159
|
if (t.type === 'LBRACKET') {
|
|
3160
|
+
const startLine = t.line;
|
|
3161
|
+
const startCol = t.column;
|
|
2824
3162
|
this.eat('LBRACKET');
|
|
2825
3163
|
const elements = [];
|
|
2826
3164
|
if (this.current.type !== 'RBRACKET') {
|
|
@@ -2831,10 +3169,12 @@ arrowFunction(params) {
|
|
|
2831
3169
|
}
|
|
2832
3170
|
}
|
|
2833
3171
|
this.eat('RBRACKET');
|
|
2834
|
-
return { type: 'ArrayExpression', elements };
|
|
3172
|
+
return { type: 'ArrayExpression', elements, line: startLine, column: startCol };
|
|
2835
3173
|
}
|
|
2836
3174
|
|
|
2837
3175
|
if (t.type === 'LBRACE') {
|
|
3176
|
+
const startLine = t.line;
|
|
3177
|
+
const startCol = t.column;
|
|
2838
3178
|
this.eat('LBRACE');
|
|
2839
3179
|
const props = [];
|
|
2840
3180
|
while (this.current.type !== 'RBRACE') {
|
|
@@ -2845,12 +3185,17 @@ arrowFunction(params) {
|
|
|
2845
3185
|
if (this.current.type === 'COMMA') this.eat('COMMA'); // optional trailing comma
|
|
2846
3186
|
}
|
|
2847
3187
|
this.eat('RBRACE');
|
|
2848
|
-
return { type: 'ObjectExpression', props };
|
|
3188
|
+
return { type: 'ObjectExpression', props, line: startLine, column: startCol };
|
|
2849
3189
|
}
|
|
2850
3190
|
|
|
2851
|
-
throw new
|
|
3191
|
+
throw new ParseError(
|
|
3192
|
+
`Unexpected token '${t.type}'`,
|
|
3193
|
+
t,
|
|
3194
|
+
this.source
|
|
3195
|
+
);
|
|
2852
3196
|
}
|
|
2853
3197
|
|
|
3198
|
+
|
|
2854
3199
|
}
|
|
2855
3200
|
|
|
2856
3201
|
module.exports = Parser;
|
|
@@ -2955,7 +3300,7 @@ const Lexer = __nccwpck_require__(211);
|
|
|
2955
3300
|
const Parser = __nccwpck_require__(222);
|
|
2956
3301
|
const Evaluator = __nccwpck_require__(112);
|
|
2957
3302
|
|
|
2958
|
-
const VERSION = '1.0.
|
|
3303
|
+
const VERSION = '1.0.48';
|
|
2959
3304
|
|
|
2960
3305
|
const COLOR = {
|
|
2961
3306
|
reset: '\x1b[0m',
|
|
@@ -2966,7 +3311,8 @@ const COLOR = {
|
|
|
2966
3311
|
cyan: '\x1b[36m',
|
|
2967
3312
|
gray: '\x1b[90m',
|
|
2968
3313
|
green: '\x1b[32m',
|
|
2969
|
-
magenta: '\x1b[35m'
|
|
3314
|
+
magenta: '\x1b[35m',
|
|
3315
|
+
white: '\x1b[37m'
|
|
2970
3316
|
};
|
|
2971
3317
|
|
|
2972
3318
|
function waitAndExit(code = 0) {
|
|
@@ -2977,7 +3323,7 @@ function waitAndExit(code = 0) {
|
|
|
2977
3323
|
}
|
|
2978
3324
|
|
|
2979
3325
|
function fatal(msg) {
|
|
2980
|
-
console.error(COLOR.
|
|
3326
|
+
console.error(COLOR.white + msg + COLOR.reset);
|
|
2981
3327
|
waitAndExit(1);
|
|
2982
3328
|
}
|
|
2983
3329
|
|
|
@@ -3118,24 +3464,35 @@ function savePrompt(lines) {
|
|
|
3118
3464
|
waitAndExit(0);
|
|
3119
3465
|
}
|
|
3120
3466
|
|
|
3121
|
-
function runFile(filePath, isTemp = false, callback) {
|
|
3467
|
+
async function runFile(filePath, isTemp = false, callback) {
|
|
3122
3468
|
const code = fs.readFileSync(filePath, 'utf8');
|
|
3123
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
|
+
|
|
3124
3476
|
try {
|
|
3125
|
-
|
|
3126
|
-
const parser = new Parser(lexer.getTokens());
|
|
3127
|
-
const ast = parser.parse();
|
|
3128
|
-
new Evaluator().evaluate(ast);
|
|
3477
|
+
await evaluator.evaluate(ast);
|
|
3129
3478
|
} catch (e) {
|
|
3130
|
-
|
|
3479
|
+
if (e.name === 'RuntimeError' || e.name === 'SyntaxError') {
|
|
3480
|
+
console.error(COLOR.white + e.message + COLOR.reset);
|
|
3481
|
+
} else {
|
|
3482
|
+
console.error(COLOR.white + `Unexpected Error: ${e.message || e}` + COLOR.reset);
|
|
3483
|
+
}
|
|
3131
3484
|
return waitAndExit(1);
|
|
3132
3485
|
}
|
|
3133
3486
|
|
|
3134
|
-
|
|
3135
3487
|
if (callback) callback();
|
|
3136
|
-
|
|
3488
|
+
|
|
3489
|
+
if (isTemp) {
|
|
3490
|
+
try { fs.unlinkSync(filePath); } catch {}
|
|
3491
|
+
}
|
|
3137
3492
|
}
|
|
3138
3493
|
|
|
3494
|
+
|
|
3495
|
+
|
|
3139
3496
|
if (!args[0].startsWith('--')) {
|
|
3140
3497
|
runFile(path.resolve(args[0]));
|
|
3141
3498
|
}
|