starlight-cli 1.1.2 → 1.1.4
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/README.md +121 -122
- package/dist/index.js +170 -70
- package/package.json +1 -1
- package/src/evaluator.js +97 -34
- package/src/lexer.js +1 -1
- package/src/parser.js +71 -34
- package/src/starlight.js +1 -1
package/dist/index.js
CHANGED
|
@@ -1392,7 +1392,6 @@ class Environment {
|
|
|
1392
1392
|
if (name in this.store) return this.store[name];
|
|
1393
1393
|
if (this.parent) return this.parent.get(name, node, source);
|
|
1394
1394
|
|
|
1395
|
-
// Only suggest for top-level variable/function names
|
|
1396
1395
|
let suggestion = null;
|
|
1397
1396
|
if (node && source) {
|
|
1398
1397
|
suggestion = this.suggest?.(name, this) || null;
|
|
@@ -1427,7 +1426,6 @@ class Evaluator {
|
|
|
1427
1426
|
this.setupBuiltins();
|
|
1428
1427
|
}
|
|
1429
1428
|
suggest(name, env) {
|
|
1430
|
-
// Collect all variable/function names from the environment chain
|
|
1431
1429
|
const names = new Set();
|
|
1432
1430
|
let current = env;
|
|
1433
1431
|
while (current) {
|
|
@@ -1441,11 +1439,10 @@ suggest(name, env) {
|
|
|
1441
1439
|
let bestScore = Infinity;
|
|
1442
1440
|
|
|
1443
1441
|
for (const item of names) {
|
|
1444
|
-
// simple edit distance approximation
|
|
1445
1442
|
const dist = Math.abs(item.length - name.length) +
|
|
1446
1443
|
[...name].filter((c, i) => c !== item[i]).length;
|
|
1447
1444
|
|
|
1448
|
-
if (dist < bestScore && dist <= 2) {
|
|
1445
|
+
if (dist < bestScore && dist <= 2) {
|
|
1449
1446
|
bestScore = dist;
|
|
1450
1447
|
best = item;
|
|
1451
1448
|
}
|
|
@@ -1457,39 +1454,31 @@ suggest(name, env) {
|
|
|
1457
1454
|
formatValue(value, seen = new Set()) {
|
|
1458
1455
|
const color = __nccwpck_require__(55);
|
|
1459
1456
|
|
|
1460
|
-
// Handle circular references
|
|
1461
1457
|
if (typeof value === 'object' && value !== null) {
|
|
1462
1458
|
if (seen.has(value)) return color.red('[Circular]');
|
|
1463
1459
|
seen.add(value);
|
|
1464
1460
|
}
|
|
1465
1461
|
|
|
1466
|
-
// Python-style null / undefined
|
|
1467
1462
|
if (value === null) return color.yellow('None');
|
|
1468
1463
|
if (value === undefined) return color.yellow('undefined');
|
|
1469
1464
|
|
|
1470
1465
|
const t = typeof value;
|
|
1471
1466
|
|
|
1472
|
-
// Strings (no quotes)
|
|
1473
1467
|
if (t === 'string') return color.cyan(value);
|
|
1474
1468
|
|
|
1475
|
-
// Numbers
|
|
1476
1469
|
if (t === 'number') return color.green(String(value));
|
|
1477
1470
|
|
|
1478
|
-
// Booleans
|
|
1479
1471
|
if (t === 'boolean') return color.yellow(value ? 'true' : 'false');
|
|
1480
1472
|
|
|
1481
|
-
// Native JS functions
|
|
1482
1473
|
if (t === 'function') {
|
|
1483
1474
|
return color.magenta(value.name ? `<function ${value.name}>` : '<function>');
|
|
1484
1475
|
}
|
|
1485
1476
|
|
|
1486
|
-
// Arrays
|
|
1487
1477
|
if (Array.isArray(value)) {
|
|
1488
1478
|
const items = value.map(v => this.formatValue(v, seen));
|
|
1489
1479
|
return color.white('[ ' + items.join(', ') + ' ]');
|
|
1490
1480
|
}
|
|
1491
1481
|
|
|
1492
|
-
// Objects (including user-defined functions)
|
|
1493
1482
|
if (t === 'object') {
|
|
1494
1483
|
if (value.params && value.body) {
|
|
1495
1484
|
return color.magenta(value.name ? `<function ${value.name}>` : '<function>');
|
|
@@ -1501,7 +1490,6 @@ formatValue(value, seen = new Set()) {
|
|
|
1501
1490
|
return color.magenta('{ ') + entries.join(color.magenta(', ')) + color.magenta(' }');
|
|
1502
1491
|
}
|
|
1503
1492
|
|
|
1504
|
-
// Fallback
|
|
1505
1493
|
try {
|
|
1506
1494
|
return String(value);
|
|
1507
1495
|
} catch {
|
|
@@ -1581,7 +1569,6 @@ this.global.define('fetch', async (url, options = {}) => {
|
|
|
1581
1569
|
};
|
|
1582
1570
|
});
|
|
1583
1571
|
|
|
1584
|
-
// GET request
|
|
1585
1572
|
this.global.define('get', async (url) => {
|
|
1586
1573
|
const res = await fetch(url);
|
|
1587
1574
|
return await res.json();
|
|
@@ -1628,6 +1615,11 @@ case 'ForInStatement':
|
|
|
1628
1615
|
return await this.evalFor(node, env);
|
|
1629
1616
|
case 'DoTrackStatement':
|
|
1630
1617
|
return await this.evalDoTrack(node, env);
|
|
1618
|
+
case 'StartStatement':
|
|
1619
|
+
return await this.evalStartStatement(node, env);
|
|
1620
|
+
|
|
1621
|
+
case 'RaceClause':
|
|
1622
|
+
return await this.evalRaceClause(node, env);
|
|
1631
1623
|
|
|
1632
1624
|
case 'BreakStatement': throw new BreakSignal();
|
|
1633
1625
|
case 'ContinueStatement': throw new ContinueSignal();
|
|
@@ -1656,14 +1648,14 @@ case 'DoTrackStatement':
|
|
|
1656
1648
|
const callee = await this.evaluate(node.callee, env);
|
|
1657
1649
|
|
|
1658
1650
|
if (typeof callee === 'object' && callee.body) {
|
|
1659
|
-
const evaluator = this;
|
|
1651
|
+
const evaluator = this;
|
|
1660
1652
|
const Constructor = function(...args) {
|
|
1661
1653
|
const newEnv = new Environment(callee.env);
|
|
1662
1654
|
newEnv.define('this', this);
|
|
1663
1655
|
for (let i = 0; i < callee.params.length; i++) {
|
|
1664
1656
|
newEnv.define(callee.params[i], args[i]);
|
|
1665
1657
|
}
|
|
1666
|
-
return evaluator.evaluate(callee.body, newEnv);
|
|
1658
|
+
return evaluator.evaluate(callee.body, newEnv);
|
|
1667
1659
|
};
|
|
1668
1660
|
|
|
1669
1661
|
const args = [];
|
|
@@ -1701,13 +1693,78 @@ async evalProgram(node, env) {
|
|
|
1701
1693
|
return result;
|
|
1702
1694
|
}
|
|
1703
1695
|
|
|
1696
|
+
async evalStartStatement(node, env) {
|
|
1697
|
+
try {
|
|
1698
|
+
const value = await this.evaluate(node.discriminant, env);
|
|
1699
|
+
let executing = false;
|
|
1700
|
+
for (const c of node.cases) {
|
|
1701
|
+
try {
|
|
1702
|
+
if (!executing) {
|
|
1703
|
+
const testValue = await this.evaluate(c.test, env);
|
|
1704
|
+
if (testValue === value) executing = true;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
if (executing) {
|
|
1708
|
+
await this.evaluate(c.consequent, new Environment(env));
|
|
1709
|
+
}
|
|
1710
|
+
} catch (caseErr) {
|
|
1711
|
+
if (caseErr instanceof BreakSignal) break;
|
|
1712
|
+
if (caseErr instanceof RuntimeError ||
|
|
1713
|
+
caseErr instanceof ReturnValue ||
|
|
1714
|
+
caseErr instanceof ContinueSignal) {
|
|
1715
|
+
throw caseErr; // propagate signals
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
throw new RuntimeError(
|
|
1719
|
+
caseErr.message || 'Error evaluating case in start statement',
|
|
1720
|
+
c,
|
|
1721
|
+
this.source
|
|
1722
|
+
);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
return null;
|
|
1727
|
+
} catch (err) {
|
|
1728
|
+
if (err instanceof RuntimeError ||
|
|
1729
|
+
err instanceof ReturnValue ||
|
|
1730
|
+
err instanceof BreakSignal ||
|
|
1731
|
+
err instanceof ContinueSignal) {
|
|
1732
|
+
throw err;
|
|
1733
|
+
}
|
|
1734
|
+
throw new RuntimeError(
|
|
1735
|
+
err.message || 'Error evaluating start statement',
|
|
1736
|
+
node,
|
|
1737
|
+
this.source
|
|
1738
|
+
);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
|
|
1743
|
+
async evalRaceClause(node, env) {
|
|
1744
|
+
try {
|
|
1745
|
+
const testValue = await this.evaluate(node.test, env);
|
|
1746
|
+
const result = await this.evaluate(node.consequent, new Environment(env));
|
|
1747
|
+
return { testValue, result };
|
|
1748
|
+
} catch (err) {
|
|
1749
|
+
if (err instanceof RuntimeError ||
|
|
1750
|
+
err instanceof ReturnValue ||
|
|
1751
|
+
err instanceof BreakSignal ||
|
|
1752
|
+
err instanceof ContinueSignal) {
|
|
1753
|
+
throw err;
|
|
1754
|
+
}
|
|
1755
|
+
throw new RuntimeError(
|
|
1756
|
+
err.message || 'Error evaluating race clause',
|
|
1757
|
+
node,
|
|
1758
|
+
this.source
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1704
1762
|
|
|
1705
1763
|
async evalDoTrack(node, env) {
|
|
1706
1764
|
try {
|
|
1707
1765
|
return await this.evaluate(node.body, env);
|
|
1708
1766
|
} catch (err) {
|
|
1709
1767
|
if (!node.handler) {
|
|
1710
|
-
// Wrap any raw error into RuntimeError with line info
|
|
1711
1768
|
if (err instanceof RuntimeError) throw err;
|
|
1712
1769
|
throw new RuntimeError(err.message || 'Error in doTrack body', node.body, this.source);
|
|
1713
1770
|
}
|
|
@@ -1718,7 +1775,6 @@ async evalDoTrack(node, env) {
|
|
|
1718
1775
|
try {
|
|
1719
1776
|
return await this.evaluate(node.handler, trackEnv);
|
|
1720
1777
|
} catch (handlerErr) {
|
|
1721
|
-
// Wrap handler errors as well
|
|
1722
1778
|
if (handlerErr instanceof RuntimeError) throw handlerErr;
|
|
1723
1779
|
throw new RuntimeError(handlerErr.message || 'Error in doTrack handler', node.handler, this.source);
|
|
1724
1780
|
}
|
|
@@ -1818,16 +1874,33 @@ evalArrowFunction(node, env) {
|
|
|
1818
1874
|
if (!node.body) {
|
|
1819
1875
|
throw new RuntimeError('Arrow function missing body', node, this.source);
|
|
1820
1876
|
}
|
|
1877
|
+
|
|
1821
1878
|
if (!Array.isArray(node.params)) {
|
|
1822
1879
|
throw new RuntimeError('Invalid arrow function parameters', node, this.source);
|
|
1823
1880
|
}
|
|
1824
1881
|
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1882
|
+
const evaluator = this;
|
|
1883
|
+
|
|
1884
|
+
return async function (...args) {
|
|
1885
|
+
const localEnv = new Environment(env);
|
|
1886
|
+
|
|
1887
|
+
node.params.forEach((p, i) => {
|
|
1888
|
+
localEnv.define(p.name, args[i]);
|
|
1889
|
+
});
|
|
1890
|
+
|
|
1891
|
+
try {
|
|
1892
|
+
if (node.isBlock) {
|
|
1893
|
+
const result = await evaluator.evaluate(node.body, localEnv);
|
|
1894
|
+
return result ?? null;
|
|
1895
|
+
} else {
|
|
1896
|
+
return await evaluator.evaluate(node.body, localEnv);
|
|
1897
|
+
}
|
|
1898
|
+
} catch (err) {
|
|
1899
|
+
if (err instanceof ReturnValue) {
|
|
1900
|
+
return err.value;
|
|
1901
|
+
}
|
|
1902
|
+
throw err;
|
|
1903
|
+
}
|
|
1831
1904
|
};
|
|
1832
1905
|
}
|
|
1833
1906
|
|
|
@@ -2002,7 +2075,6 @@ async evalWhile(node, env) {
|
|
|
2002
2075
|
return null;
|
|
2003
2076
|
}
|
|
2004
2077
|
async evalFor(node, env) {
|
|
2005
|
-
// Python-style: for x in iterable (with optional 'let')
|
|
2006
2078
|
if (node.type === 'ForInStatement') {
|
|
2007
2079
|
const iterable = await this.evaluate(node.iterable, env);
|
|
2008
2080
|
|
|
@@ -2012,8 +2084,6 @@ async evalFor(node, env) {
|
|
|
2012
2084
|
|
|
2013
2085
|
const loopVar = node.variable; // string name of the loop variable
|
|
2014
2086
|
const createLoopEnv = () => node.letKeyword ? new Environment(env) : env;
|
|
2015
|
-
|
|
2016
|
-
// Arrays: iterate over elements
|
|
2017
2087
|
if (Array.isArray(iterable)) {
|
|
2018
2088
|
for (const value of iterable) {
|
|
2019
2089
|
const loopEnv = createLoopEnv();
|
|
@@ -2028,7 +2098,6 @@ async evalFor(node, env) {
|
|
|
2028
2098
|
}
|
|
2029
2099
|
}
|
|
2030
2100
|
}
|
|
2031
|
-
// Objects: iterate over keys
|
|
2032
2101
|
else {
|
|
2033
2102
|
for (const key of Object.keys(iterable)) {
|
|
2034
2103
|
const loopEnv = createLoopEnv();
|
|
@@ -2047,7 +2116,6 @@ async evalFor(node, env) {
|
|
|
2047
2116
|
return null;
|
|
2048
2117
|
}
|
|
2049
2118
|
|
|
2050
|
-
// C-style for loop (classic JS style)
|
|
2051
2119
|
const local = new Environment(env);
|
|
2052
2120
|
|
|
2053
2121
|
if (node.init) await this.evaluate(node.init, local);
|
|
@@ -2133,19 +2201,14 @@ async evalIndex(node, env) {
|
|
|
2133
2201
|
const idx = await this.evaluate(node.indexer, env);
|
|
2134
2202
|
|
|
2135
2203
|
if (obj == null) throw new RuntimeError('Cannot index null or undefined', node, this.source);
|
|
2136
|
-
|
|
2137
|
-
// Array access: return undefined if out of bounds
|
|
2138
2204
|
if (Array.isArray(obj)) {
|
|
2139
2205
|
if (idx < 0 || idx >= obj.length) return undefined;
|
|
2140
2206
|
return obj[idx];
|
|
2141
2207
|
}
|
|
2142
|
-
|
|
2143
|
-
// Object access: return undefined if property missing
|
|
2144
2208
|
if (typeof obj === 'object') {
|
|
2145
2209
|
return obj[idx]; // undefined if missing
|
|
2146
2210
|
}
|
|
2147
2211
|
|
|
2148
|
-
// Fallback for non-object non-array
|
|
2149
2212
|
return undefined;
|
|
2150
2213
|
}
|
|
2151
2214
|
async evalObject(node, env) {
|
|
@@ -2239,7 +2302,7 @@ class Lexer {
|
|
|
2239
2302
|
'break', 'continue', 'func', 'return',
|
|
2240
2303
|
'true', 'false', 'null',
|
|
2241
2304
|
'ask', 'define', 'import', 'from', 'as',
|
|
2242
|
-
'async', 'await', 'new', 'in', 'do', 'track'
|
|
2305
|
+
'async', 'await', 'new', 'in', 'do', 'track', 'start', 'race'
|
|
2243
2306
|
];
|
|
2244
2307
|
}
|
|
2245
2308
|
|
|
@@ -2502,6 +2565,7 @@ class Parser {
|
|
|
2502
2565
|
case 'WHILE': return this.whileStatement();
|
|
2503
2566
|
case 'FOR': return this.forStatement();
|
|
2504
2567
|
case 'DO': return this.doTrackStatement();
|
|
2568
|
+
case 'START': return this.startStatement();
|
|
2505
2569
|
case 'BREAK': return this.breakStatement();
|
|
2506
2570
|
case 'CONTINUE': return this.continueStatement();
|
|
2507
2571
|
case 'FUNC': return this.funcDeclaration();
|
|
@@ -2513,9 +2577,9 @@ class Parser {
|
|
|
2513
2577
|
}
|
|
2514
2578
|
}
|
|
2515
2579
|
varDeclaration() {
|
|
2516
|
-
const t = this.current;
|
|
2580
|
+
const t = this.current;
|
|
2517
2581
|
this.eat('LET');
|
|
2518
|
-
const idToken = this.current;
|
|
2582
|
+
const idToken = this.current;
|
|
2519
2583
|
const id = idToken.value;
|
|
2520
2584
|
this.eat('IDENTIFIER');
|
|
2521
2585
|
|
|
@@ -2525,7 +2589,6 @@ varDeclaration() {
|
|
|
2525
2589
|
expr = this.expression();
|
|
2526
2590
|
}
|
|
2527
2591
|
|
|
2528
|
-
// semicolon optional
|
|
2529
2592
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
2530
2593
|
|
|
2531
2594
|
return {
|
|
@@ -2536,9 +2599,49 @@ varDeclaration() {
|
|
|
2536
2599
|
column: t.column
|
|
2537
2600
|
};
|
|
2538
2601
|
}
|
|
2602
|
+
startStatement() {
|
|
2603
|
+
const t = this.current;
|
|
2604
|
+
this.eat('START');
|
|
2605
|
+
|
|
2606
|
+
const discriminant = this.expression();
|
|
2607
|
+
|
|
2608
|
+
this.eat('LBRACE');
|
|
2609
|
+
|
|
2610
|
+
const cases = [];
|
|
2611
|
+
|
|
2612
|
+
while (this.current.type === 'RACE') {
|
|
2613
|
+
cases.push(this.raceClause());
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
this.eat('RBRACE');
|
|
2617
|
+
|
|
2618
|
+
return {
|
|
2619
|
+
type: 'StartStatement',
|
|
2620
|
+
discriminant,
|
|
2621
|
+
cases,
|
|
2622
|
+
line: t.line,
|
|
2623
|
+
column: t.column
|
|
2624
|
+
};
|
|
2625
|
+
}
|
|
2626
|
+
raceClause() {
|
|
2627
|
+
const t = this.current;
|
|
2628
|
+
this.eat('RACE');
|
|
2629
|
+
|
|
2630
|
+
const test = this.expression();
|
|
2631
|
+
|
|
2632
|
+
const consequent = this.block();
|
|
2633
|
+
|
|
2634
|
+
return {
|
|
2635
|
+
type: 'RaceClause',
|
|
2636
|
+
test,
|
|
2637
|
+
consequent,
|
|
2638
|
+
line: t.line,
|
|
2639
|
+
column: t.column
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2539
2642
|
|
|
2540
2643
|
sldeployStatement() {
|
|
2541
|
-
const t = this.current;
|
|
2644
|
+
const t = this.current;
|
|
2542
2645
|
this.eat('SLDEPLOY');
|
|
2543
2646
|
const expr = this.expression();
|
|
2544
2647
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
@@ -2551,7 +2654,7 @@ sldeployStatement() {
|
|
|
2551
2654
|
}
|
|
2552
2655
|
|
|
2553
2656
|
doTrackStatement() {
|
|
2554
|
-
const t = this.current;
|
|
2657
|
+
const t = this.current;
|
|
2555
2658
|
this.eat('DO');
|
|
2556
2659
|
|
|
2557
2660
|
const body = this.block();
|
|
@@ -2572,9 +2675,9 @@ doTrackStatement() {
|
|
|
2572
2675
|
}
|
|
2573
2676
|
|
|
2574
2677
|
defineStatement() {
|
|
2575
|
-
const t = this.current;
|
|
2678
|
+
const t = this.current;
|
|
2576
2679
|
this.eat('DEFINE');
|
|
2577
|
-
const idToken = this.current;
|
|
2680
|
+
const idToken = this.current;
|
|
2578
2681
|
const id = idToken.value;
|
|
2579
2682
|
this.eat('IDENTIFIER');
|
|
2580
2683
|
|
|
@@ -2596,7 +2699,7 @@ defineStatement() {
|
|
|
2596
2699
|
}
|
|
2597
2700
|
|
|
2598
2701
|
asyncFuncDeclaration() {
|
|
2599
|
-
const t = this.current;
|
|
2702
|
+
const t = this.current;
|
|
2600
2703
|
const name = this.current.value;
|
|
2601
2704
|
this.eat('IDENTIFIER');
|
|
2602
2705
|
|
|
@@ -2614,7 +2717,6 @@ asyncFuncDeclaration() {
|
|
|
2614
2717
|
}
|
|
2615
2718
|
this.eat('RPAREN');
|
|
2616
2719
|
} else {
|
|
2617
|
-
// no parentheses: single param as Python style
|
|
2618
2720
|
if (this.current.type === 'IDENTIFIER') {
|
|
2619
2721
|
params.push(this.current.value);
|
|
2620
2722
|
this.eat('IDENTIFIER');
|
|
@@ -2634,7 +2736,7 @@ asyncFuncDeclaration() {
|
|
|
2634
2736
|
}
|
|
2635
2737
|
|
|
2636
2738
|
ifStatement() {
|
|
2637
|
-
const t = this.current;
|
|
2739
|
+
const t = this.current;
|
|
2638
2740
|
this.eat('IF');
|
|
2639
2741
|
|
|
2640
2742
|
let test;
|
|
@@ -2643,7 +2745,6 @@ ifStatement() {
|
|
|
2643
2745
|
test = this.expression();
|
|
2644
2746
|
this.eat('RPAREN');
|
|
2645
2747
|
} else {
|
|
2646
|
-
// Python style: no parentheses
|
|
2647
2748
|
test = this.expression();
|
|
2648
2749
|
}
|
|
2649
2750
|
|
|
@@ -2667,7 +2768,7 @@ ifStatement() {
|
|
|
2667
2768
|
}
|
|
2668
2769
|
|
|
2669
2770
|
whileStatement() {
|
|
2670
|
-
const t = this.current;
|
|
2771
|
+
const t = this.current;
|
|
2671
2772
|
this.eat('WHILE');
|
|
2672
2773
|
|
|
2673
2774
|
let test;
|
|
@@ -2690,7 +2791,7 @@ whileStatement() {
|
|
|
2690
2791
|
}
|
|
2691
2792
|
|
|
2692
2793
|
importStatement() {
|
|
2693
|
-
const t = this.current;
|
|
2794
|
+
const t = this.current;
|
|
2694
2795
|
this.eat('IMPORT');
|
|
2695
2796
|
|
|
2696
2797
|
let specifiers = [];
|
|
@@ -2749,7 +2850,6 @@ forStatement() {
|
|
|
2749
2850
|
const t = this.current; // FOR token
|
|
2750
2851
|
this.eat('FOR');
|
|
2751
2852
|
|
|
2752
|
-
// --- Python-style: for variable in iterable (supports optional 'let') ---
|
|
2753
2853
|
if ((this.current.type === 'LET' && this.peekType() === 'IDENTIFIER' && this.peekType(2) === 'IN') ||
|
|
2754
2854
|
(this.current.type === 'IDENTIFIER' && this.peekType() === 'IN')) {
|
|
2755
2855
|
|
|
@@ -2790,7 +2890,6 @@ forStatement() {
|
|
|
2790
2890
|
};
|
|
2791
2891
|
}
|
|
2792
2892
|
|
|
2793
|
-
// --- C-style: for(init; test; update) ---
|
|
2794
2893
|
let init = null;
|
|
2795
2894
|
let test = null;
|
|
2796
2895
|
let update = null;
|
|
@@ -2798,22 +2897,18 @@ forStatement() {
|
|
|
2798
2897
|
if (this.current.type === 'LPAREN') {
|
|
2799
2898
|
this.eat('LPAREN');
|
|
2800
2899
|
|
|
2801
|
-
// init
|
|
2802
2900
|
if (this.current.type !== 'SEMICOLON') {
|
|
2803
2901
|
init = this.current.type === 'LET' ? this.varDeclaration() : this.expressionStatement();
|
|
2804
2902
|
} else {
|
|
2805
2903
|
this.eat('SEMICOLON');
|
|
2806
2904
|
}
|
|
2807
2905
|
|
|
2808
|
-
// test
|
|
2809
2906
|
if (this.current.type !== 'SEMICOLON') test = this.expression();
|
|
2810
2907
|
this.eat('SEMICOLON');
|
|
2811
2908
|
|
|
2812
|
-
// update
|
|
2813
2909
|
if (this.current.type !== 'RPAREN') update = this.expression();
|
|
2814
2910
|
this.eat('RPAREN');
|
|
2815
2911
|
} else {
|
|
2816
|
-
// fallback: single expression (rare, mostly handled above)
|
|
2817
2912
|
init = this.expression();
|
|
2818
2913
|
if (this.current.type === 'IN') {
|
|
2819
2914
|
this.eat('IN');
|
|
@@ -2836,21 +2931,19 @@ forStatement() {
|
|
|
2836
2931
|
breakStatement() {
|
|
2837
2932
|
const t = this.current; // BREAK token
|
|
2838
2933
|
this.eat('BREAK');
|
|
2839
|
-
// Python-style: no semicolon needed, ignore if present
|
|
2840
2934
|
if (this.current.type === 'SEMICOLON') this.advance();
|
|
2841
2935
|
return { type: 'BreakStatement', line: t.line, column: t.column };
|
|
2842
2936
|
}
|
|
2843
2937
|
|
|
2844
2938
|
continueStatement() {
|
|
2845
|
-
const t = this.current;
|
|
2939
|
+
const t = this.current;
|
|
2846
2940
|
this.eat('CONTINUE');
|
|
2847
|
-
// Python-style: no semicolon needed, ignore if present
|
|
2848
2941
|
if (this.current.type === 'SEMICOLON') this.advance();
|
|
2849
2942
|
return { type: 'ContinueStatement', line: t.line, column: t.column };
|
|
2850
2943
|
}
|
|
2851
2944
|
|
|
2852
2945
|
funcDeclaration() {
|
|
2853
|
-
const t = this.current;
|
|
2946
|
+
const t = this.current;
|
|
2854
2947
|
this.eat('FUNC');
|
|
2855
2948
|
const nameToken = this.current;
|
|
2856
2949
|
const name = nameToken.value;
|
|
@@ -2872,7 +2965,6 @@ funcDeclaration() {
|
|
|
2872
2965
|
}
|
|
2873
2966
|
this.eat('RPAREN');
|
|
2874
2967
|
} else {
|
|
2875
|
-
// Python-style: single param without parentheses
|
|
2876
2968
|
if (this.current.type === 'IDENTIFIER') {
|
|
2877
2969
|
const paramToken = this.current;
|
|
2878
2970
|
params.push({ name: paramToken.value, line: paramToken.line, column: paramToken.column });
|
|
@@ -2893,7 +2985,6 @@ returnStatement() {
|
|
|
2893
2985
|
argument = this.expression();
|
|
2894
2986
|
}
|
|
2895
2987
|
|
|
2896
|
-
// semicolon optional
|
|
2897
2988
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
2898
2989
|
|
|
2899
2990
|
return { type: 'ReturnStatement', argument, line: t.line, column: t.column };
|
|
@@ -2911,9 +3002,8 @@ block() {
|
|
|
2911
3002
|
}
|
|
2912
3003
|
|
|
2913
3004
|
expressionStatement() {
|
|
2914
|
-
const exprToken = this.current;
|
|
3005
|
+
const exprToken = this.current;
|
|
2915
3006
|
const expr = this.expression();
|
|
2916
|
-
// semicolon optional
|
|
2917
3007
|
if (this.current.type === 'SEMICOLON') this.eat('SEMICOLON');
|
|
2918
3008
|
return { type: 'ExpressionStatement', expression: expr, line: exprToken.line, column: exprToken.column };
|
|
2919
3009
|
}
|
|
@@ -3026,7 +3116,6 @@ unary() {
|
|
|
3026
3116
|
};
|
|
3027
3117
|
}
|
|
3028
3118
|
|
|
3029
|
-
// Python-like: ignore ++ and -- if not used
|
|
3030
3119
|
if (t.type === 'PLUSPLUS' || t.type === 'MINUSMINUS') {
|
|
3031
3120
|
const op = t.type;
|
|
3032
3121
|
this.eat(op);
|
|
@@ -3066,7 +3155,7 @@ postfix() {
|
|
|
3066
3155
|
const args = [];
|
|
3067
3156
|
while (this.current.type !== 'RPAREN' && this.current.type !== 'EOF') {
|
|
3068
3157
|
args.push(this.expression());
|
|
3069
|
-
if (this.current.type === 'COMMA') this.eat('COMMA');
|
|
3158
|
+
if (this.current.type === 'COMMA') this.eat('COMMA');
|
|
3070
3159
|
}
|
|
3071
3160
|
if (this.current.type === 'RPAREN') this.eat('RPAREN');
|
|
3072
3161
|
node = { type: 'CallExpression', callee: node, arguments: args, line: startLine, column: startCol };
|
|
@@ -3103,15 +3192,26 @@ postfix() {
|
|
|
3103
3192
|
|
|
3104
3193
|
arrowFunction(params) {
|
|
3105
3194
|
const t = this.current;
|
|
3106
|
-
|
|
3107
|
-
|
|
3195
|
+
this.eat('ARROW');
|
|
3196
|
+
|
|
3197
|
+
let body;
|
|
3198
|
+
let isBlock = false;
|
|
3199
|
+
|
|
3200
|
+
if (this.current.type === 'LBRACE') {
|
|
3201
|
+
body = this.block();
|
|
3202
|
+
isBlock = true;
|
|
3203
|
+
} else {
|
|
3204
|
+
body = this.expression();
|
|
3205
|
+
}
|
|
3206
|
+
|
|
3108
3207
|
const startLine = params.length > 0 ? params[0].line : t.line;
|
|
3109
|
-
const startCol
|
|
3208
|
+
const startCol = params.length > 0 ? params[0].column : t.column;
|
|
3110
3209
|
|
|
3111
3210
|
return {
|
|
3112
3211
|
type: 'ArrowFunctionExpression',
|
|
3113
3212
|
params,
|
|
3114
3213
|
body,
|
|
3214
|
+
isBlock,
|
|
3115
3215
|
line: startLine,
|
|
3116
3216
|
column: startCol
|
|
3117
3217
|
};
|
|
@@ -3243,11 +3343,11 @@ arrowFunction(params) {
|
|
|
3243
3343
|
this.eat('LBRACE');
|
|
3244
3344
|
const props = [];
|
|
3245
3345
|
while (this.current.type !== 'RBRACE') {
|
|
3246
|
-
const key = this.expression();
|
|
3346
|
+
const key = this.expression();
|
|
3247
3347
|
this.eat('COLON');
|
|
3248
3348
|
const value = this.expression();
|
|
3249
3349
|
props.push({ key, value });
|
|
3250
|
-
if (this.current.type === 'COMMA') this.eat('COMMA');
|
|
3350
|
+
if (this.current.type === 'COMMA') this.eat('COMMA');
|
|
3251
3351
|
}
|
|
3252
3352
|
this.eat('RBRACE');
|
|
3253
3353
|
return { type: 'ObjectExpression', props, line: startLine, column: startCol };
|
|
@@ -3440,7 +3540,7 @@ const Lexer = __nccwpck_require__(211);
|
|
|
3440
3540
|
const Parser = __nccwpck_require__(222);
|
|
3441
3541
|
const Evaluator = __nccwpck_require__(112);
|
|
3442
3542
|
|
|
3443
|
-
const VERSION = '1.1.
|
|
3543
|
+
const VERSION = '1.1.4';
|
|
3444
3544
|
|
|
3445
3545
|
const COLOR = {
|
|
3446
3546
|
reset: '\x1b[0m',
|