starlight-cli 1.1.1 → 1.1.2
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 +142 -61
- package/package.json +3 -2
- package/src/evaluator.js +66 -60
- package/src/starlight.js +1 -1
- package/src/program.sl +0 -2
package/dist/index.js
CHANGED
|
@@ -1454,58 +1454,59 @@ suggest(name, env) {
|
|
|
1454
1454
|
return best;
|
|
1455
1455
|
}
|
|
1456
1456
|
|
|
1457
|
-
|
|
1458
1457
|
formatValue(value, seen = new Set()) {
|
|
1459
|
-
|
|
1458
|
+
const color = __nccwpck_require__(55);
|
|
1459
|
+
|
|
1460
|
+
// Handle circular references
|
|
1460
1461
|
if (typeof value === 'object' && value !== null) {
|
|
1461
|
-
if (seen.has(value)) return '[Circular]';
|
|
1462
|
+
if (seen.has(value)) return color.red('[Circular]');
|
|
1462
1463
|
seen.add(value);
|
|
1463
1464
|
}
|
|
1464
1465
|
|
|
1465
1466
|
// Python-style null / undefined
|
|
1466
|
-
if (value === null) return 'None';
|
|
1467
|
-
if (value === undefined) return 'undefined';
|
|
1467
|
+
if (value === null) return color.yellow('None');
|
|
1468
|
+
if (value === undefined) return color.yellow('undefined');
|
|
1468
1469
|
|
|
1469
1470
|
const t = typeof value;
|
|
1470
1471
|
|
|
1471
1472
|
// Strings (no quotes)
|
|
1472
|
-
if (t === 'string') return value;
|
|
1473
|
+
if (t === 'string') return color.cyan(value);
|
|
1473
1474
|
|
|
1474
1475
|
// Numbers
|
|
1475
|
-
if (t === 'number') return String(value);
|
|
1476
|
+
if (t === 'number') return color.green(String(value));
|
|
1476
1477
|
|
|
1477
|
-
// Booleans
|
|
1478
|
-
if (t === 'boolean') return value ? '
|
|
1478
|
+
// Booleans
|
|
1479
|
+
if (t === 'boolean') return color.yellow(value ? 'true' : 'false');
|
|
1479
1480
|
|
|
1480
1481
|
// Native JS functions
|
|
1481
1482
|
if (t === 'function') {
|
|
1482
|
-
return value.name
|
|
1483
|
-
? `<function ${value.name}>`
|
|
1484
|
-
: '<function>';
|
|
1483
|
+
return color.magenta(value.name ? `<function ${value.name}>` : '<function>');
|
|
1485
1484
|
}
|
|
1486
1485
|
|
|
1487
1486
|
// Arrays
|
|
1488
1487
|
if (Array.isArray(value)) {
|
|
1489
|
-
|
|
1488
|
+
const items = value.map(v => this.formatValue(v, seen));
|
|
1489
|
+
return color.white('[ ' + items.join(', ') + ' ]');
|
|
1490
1490
|
}
|
|
1491
1491
|
|
|
1492
1492
|
// Objects (including user-defined functions)
|
|
1493
1493
|
if (t === 'object') {
|
|
1494
|
-
// Detect user-defined functions (AST-based)
|
|
1495
1494
|
if (value.params && value.body) {
|
|
1496
|
-
return value.name
|
|
1497
|
-
? `<function ${value.name}>`
|
|
1498
|
-
: '<function>';
|
|
1495
|
+
return color.magenta(value.name ? `<function ${value.name}>` : '<function>');
|
|
1499
1496
|
}
|
|
1500
1497
|
|
|
1501
1498
|
const entries = Object.entries(value).map(
|
|
1502
|
-
([k, v]) => `${k}:
|
|
1499
|
+
([k, v]) => color.magenta(`${k}: `) + this.formatValue(v, seen)
|
|
1503
1500
|
);
|
|
1504
|
-
return '{ ' + entries.join(', ') + ' }';
|
|
1501
|
+
return color.magenta('{ ') + entries.join(color.magenta(', ')) + color.magenta(' }');
|
|
1505
1502
|
}
|
|
1506
1503
|
|
|
1507
1504
|
// Fallback
|
|
1508
|
-
|
|
1505
|
+
try {
|
|
1506
|
+
return String(value);
|
|
1507
|
+
} catch {
|
|
1508
|
+
return color.red('[Unprintable]');
|
|
1509
|
+
}
|
|
1509
1510
|
}
|
|
1510
1511
|
|
|
1511
1512
|
|
|
@@ -1836,23 +1837,23 @@ async evalAssignment(node, env) {
|
|
|
1836
1837
|
const left = node.left;
|
|
1837
1838
|
|
|
1838
1839
|
if (left.type === 'Identifier') return env.set(left.name, rightVal);
|
|
1839
|
-
if (left.type === 'MemberExpression') {
|
|
1840
|
-
const obj = await this.evaluate(left.object, env);
|
|
1841
|
-
if (obj == null) throw new RuntimeError('Cannot assign to null or undefined', node, this.source);
|
|
1842
|
-
obj[left.property] = rightVal;
|
|
1843
|
-
return rightVal;
|
|
1844
|
-
}
|
|
1845
|
-
if (left.type === 'IndexExpression') {
|
|
1846
|
-
const obj = await this.evaluate(left.object, env);
|
|
1847
|
-
const idx = await this.evaluate(left.indexer, env);
|
|
1848
|
-
if (obj == null) throw new RuntimeError('Cannot assign to null or undefined', node, this.source);
|
|
1849
|
-
obj[idx] = rightVal;
|
|
1850
|
-
return rightVal;
|
|
1851
|
-
}
|
|
1852
1840
|
|
|
1841
|
+
if (left.type === 'MemberExpression') {
|
|
1842
|
+
const obj = await this.evaluate(left.object, env);
|
|
1843
|
+
if (obj == null) throw new RuntimeError('Cannot assign to null or undefined', node, this.source);
|
|
1844
|
+
obj[left.property] = rightVal; // dynamic creation of new properties allowed
|
|
1845
|
+
return rightVal;
|
|
1846
|
+
}
|
|
1853
1847
|
|
|
1854
|
-
|
|
1848
|
+
if (left.type === 'IndexExpression') {
|
|
1849
|
+
const obj = await this.evaluate(left.object, env);
|
|
1850
|
+
const idx = await this.evaluate(left.indexer, env);
|
|
1851
|
+
if (obj == null) throw new RuntimeError('Cannot assign to null or undefined', node, this.source);
|
|
1852
|
+
obj[idx] = rightVal; // dynamic creation allowed
|
|
1853
|
+
return rightVal;
|
|
1854
|
+
}
|
|
1855
1855
|
|
|
1856
|
+
throw new RuntimeError('Invalid assignment target', node, this.source);
|
|
1856
1857
|
}
|
|
1857
1858
|
|
|
1858
1859
|
async evalCompoundAssignment(node, env) {
|
|
@@ -1860,13 +1861,13 @@ async evalCompoundAssignment(node, env) {
|
|
|
1860
1861
|
let current;
|
|
1861
1862
|
|
|
1862
1863
|
if (left.type === 'Identifier') current = env.get(left.name, left, this.source);
|
|
1863
|
-
else if (left.type === 'MemberExpression') current = await this.evalMember(left, env);
|
|
1864
|
-
else if (left.type === 'IndexExpression') current = await this.evalIndex(left, env);
|
|
1864
|
+
else if (left.type === 'MemberExpression') current = await this.evalMember(left, env) ?? 0;
|
|
1865
|
+
else if (left.type === 'IndexExpression') current = await this.evalIndex(left, env) ?? 0;
|
|
1865
1866
|
else throw new RuntimeError('Invalid compound assignment target', node, this.source);
|
|
1866
1867
|
|
|
1867
|
-
|
|
1868
1868
|
const rhs = await this.evaluate(node.right, env);
|
|
1869
1869
|
let computed;
|
|
1870
|
+
|
|
1870
1871
|
switch (node.operator) {
|
|
1871
1872
|
case 'PLUSEQ': computed = current + rhs; break;
|
|
1872
1873
|
case 'MINUSEQ': computed = current - rhs; break;
|
|
@@ -1874,16 +1875,18 @@ async evalCompoundAssignment(node, env) {
|
|
|
1874
1875
|
case 'SLASHEQ': computed = current / rhs; break;
|
|
1875
1876
|
case 'MODEQ': computed = current % rhs; break;
|
|
1876
1877
|
default: throw new RuntimeError(`Unknown compound operator: ${node.operator}`, node, this.source);
|
|
1877
|
-
|
|
1878
1878
|
}
|
|
1879
1879
|
|
|
1880
1880
|
if (left.type === 'Identifier') env.set(left.name, computed);
|
|
1881
|
-
else if (left.type === 'MemberExpression')
|
|
1882
|
-
|
|
1881
|
+
else if (left.type === 'MemberExpression')
|
|
1882
|
+
await this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
|
|
1883
|
+
else
|
|
1884
|
+
await this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
|
|
1883
1885
|
|
|
1884
1886
|
return computed;
|
|
1885
1887
|
}
|
|
1886
1888
|
|
|
1889
|
+
|
|
1887
1890
|
async evalSldeploy(node, env) {
|
|
1888
1891
|
const val = await this.evaluate(node.expr, env);
|
|
1889
1892
|
console.log(this.formatValue(val));
|
|
@@ -1891,6 +1894,7 @@ async evalSldeploy(node, env) {
|
|
|
1891
1894
|
}
|
|
1892
1895
|
|
|
1893
1896
|
|
|
1897
|
+
|
|
1894
1898
|
async evalAsk(node, env) {
|
|
1895
1899
|
const prompt = await this.evaluate(node.prompt, env);
|
|
1896
1900
|
|
|
@@ -1997,11 +2001,8 @@ async evalWhile(node, env) {
|
|
|
1997
2001
|
}
|
|
1998
2002
|
return null;
|
|
1999
2003
|
}
|
|
2000
|
-
|
|
2001
2004
|
async evalFor(node, env) {
|
|
2002
|
-
// -------------------------------
|
|
2003
2005
|
// Python-style: for x in iterable (with optional 'let')
|
|
2004
|
-
// -------------------------------
|
|
2005
2006
|
if (node.type === 'ForInStatement') {
|
|
2006
2007
|
const iterable = await this.evaluate(node.iterable, env);
|
|
2007
2008
|
|
|
@@ -2009,11 +2010,10 @@ async evalFor(node, env) {
|
|
|
2009
2010
|
throw new RuntimeError('Cannot iterate over non-iterable', node, this.source);
|
|
2010
2011
|
}
|
|
2011
2012
|
|
|
2012
|
-
const loopVar = node.variable; //
|
|
2013
|
-
|
|
2013
|
+
const loopVar = node.variable; // string name of the loop variable
|
|
2014
2014
|
const createLoopEnv = () => node.letKeyword ? new Environment(env) : env;
|
|
2015
2015
|
|
|
2016
|
-
// Arrays
|
|
2016
|
+
// Arrays: iterate over elements
|
|
2017
2017
|
if (Array.isArray(iterable)) {
|
|
2018
2018
|
for (const value of iterable) {
|
|
2019
2019
|
const loopEnv = createLoopEnv();
|
|
@@ -2028,7 +2028,7 @@ async evalFor(node, env) {
|
|
|
2028
2028
|
}
|
|
2029
2029
|
}
|
|
2030
2030
|
}
|
|
2031
|
-
// Objects
|
|
2031
|
+
// Objects: iterate over keys
|
|
2032
2032
|
else {
|
|
2033
2033
|
for (const key of Object.keys(iterable)) {
|
|
2034
2034
|
const loopEnv = createLoopEnv();
|
|
@@ -2047,9 +2047,7 @@ async evalFor(node, env) {
|
|
|
2047
2047
|
return null;
|
|
2048
2048
|
}
|
|
2049
2049
|
|
|
2050
|
-
//
|
|
2051
|
-
// C-style for loop
|
|
2052
|
-
// -------------------------------
|
|
2050
|
+
// C-style for loop (classic JS style)
|
|
2053
2051
|
const local = new Environment(env);
|
|
2054
2052
|
|
|
2055
2053
|
if (node.init) await this.evaluate(node.init, local);
|
|
@@ -2071,6 +2069,7 @@ async evalFor(node, env) {
|
|
|
2071
2069
|
|
|
2072
2070
|
return null;
|
|
2073
2071
|
}
|
|
2072
|
+
|
|
2074
2073
|
evalFunctionDeclaration(node, env) {
|
|
2075
2074
|
if (!node.name || typeof node.name !== 'string') {
|
|
2076
2075
|
throw new RuntimeError('Function declaration requires a valid name', node, this.source);
|
|
@@ -2133,18 +2132,22 @@ async evalIndex(node, env) {
|
|
|
2133
2132
|
const obj = await this.evaluate(node.object, env);
|
|
2134
2133
|
const idx = await this.evaluate(node.indexer, env);
|
|
2135
2134
|
|
|
2136
|
-
if (obj == null) throw new RuntimeError('
|
|
2135
|
+
if (obj == null) throw new RuntimeError('Cannot index null or undefined', node, this.source);
|
|
2137
2136
|
|
|
2138
|
-
|
|
2139
|
-
|
|
2137
|
+
// Array access: return undefined if out of bounds
|
|
2138
|
+
if (Array.isArray(obj)) {
|
|
2139
|
+
if (idx < 0 || idx >= obj.length) return undefined;
|
|
2140
|
+
return obj[idx];
|
|
2140
2141
|
}
|
|
2141
|
-
|
|
2142
|
-
|
|
2142
|
+
|
|
2143
|
+
// Object access: return undefined if property missing
|
|
2144
|
+
if (typeof obj === 'object') {
|
|
2145
|
+
return obj[idx]; // undefined if missing
|
|
2143
2146
|
}
|
|
2144
2147
|
|
|
2145
|
-
|
|
2148
|
+
// Fallback for non-object non-array
|
|
2149
|
+
return undefined;
|
|
2146
2150
|
}
|
|
2147
|
-
|
|
2148
2151
|
async evalObject(node, env) {
|
|
2149
2152
|
const out = {};
|
|
2150
2153
|
for (const p of node.props) {
|
|
@@ -2153,19 +2156,22 @@ async evalObject(node, env) {
|
|
|
2153
2156
|
}
|
|
2154
2157
|
const key = await this.evaluate(p.key, env);
|
|
2155
2158
|
const value = await this.evaluate(p.value, env);
|
|
2156
|
-
out[key] = value;
|
|
2159
|
+
out[key] = value; // dynamic property assignment
|
|
2157
2160
|
}
|
|
2158
2161
|
return out;
|
|
2159
2162
|
}
|
|
2160
2163
|
|
|
2161
2164
|
|
|
2165
|
+
|
|
2162
2166
|
async evalMember(node, env) {
|
|
2163
2167
|
const obj = await this.evaluate(node.object, env);
|
|
2164
|
-
|
|
2165
|
-
if (
|
|
2168
|
+
|
|
2169
|
+
if (obj == null) throw new RuntimeError('Member access of null or undefined', node, this.source);
|
|
2170
|
+
|
|
2166
2171
|
return obj[node.property];
|
|
2167
2172
|
}
|
|
2168
2173
|
|
|
2174
|
+
|
|
2169
2175
|
async evalUpdate(node, env) {
|
|
2170
2176
|
const arg = node.argument;
|
|
2171
2177
|
const getCurrent = async () => {
|
|
@@ -3307,6 +3313,53 @@ module.exports = require("path");
|
|
|
3307
3313
|
"use strict";
|
|
3308
3314
|
module.exports = require("readline");
|
|
3309
3315
|
|
|
3316
|
+
/***/ }),
|
|
3317
|
+
|
|
3318
|
+
/***/ 55:
|
|
3319
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => {
|
|
3320
|
+
|
|
3321
|
+
"use strict";
|
|
3322
|
+
__nccwpck_require__.r(__webpack_exports__);
|
|
3323
|
+
/* harmony export */ __nccwpck_require__.d(__webpack_exports__, {
|
|
3324
|
+
/* harmony export */ blue: () => (/* binding */ blue),
|
|
3325
|
+
/* harmony export */ brightBlue: () => (/* binding */ brightBlue),
|
|
3326
|
+
/* harmony export */ brightCyan: () => (/* binding */ brightCyan),
|
|
3327
|
+
/* harmony export */ brightGreen: () => (/* binding */ brightGreen),
|
|
3328
|
+
/* harmony export */ brightMagenta: () => (/* binding */ brightMagenta),
|
|
3329
|
+
/* harmony export */ brightRed: () => (/* binding */ brightRed),
|
|
3330
|
+
/* harmony export */ brightWhite: () => (/* binding */ brightWhite),
|
|
3331
|
+
/* harmony export */ brightYellow: () => (/* binding */ brightYellow),
|
|
3332
|
+
/* harmony export */ cyan: () => (/* binding */ cyan),
|
|
3333
|
+
/* harmony export */ gray: () => (/* binding */ gray),
|
|
3334
|
+
/* harmony export */ green: () => (/* binding */ green),
|
|
3335
|
+
/* harmony export */ magenta: () => (/* binding */ magenta),
|
|
3336
|
+
/* harmony export */ red: () => (/* binding */ red),
|
|
3337
|
+
/* harmony export */ white: () => (/* binding */ white),
|
|
3338
|
+
/* harmony export */ yellow: () => (/* binding */ yellow)
|
|
3339
|
+
/* harmony export */ });
|
|
3340
|
+
const RESET = "\x1b[0m";
|
|
3341
|
+
|
|
3342
|
+
function wrap(code, text) {
|
|
3343
|
+
return code + String(text) + RESET;
|
|
3344
|
+
}
|
|
3345
|
+
|
|
3346
|
+
const red = text => wrap("\x1b[31m", text);
|
|
3347
|
+
const green = text => wrap("\x1b[32m", text);
|
|
3348
|
+
const yellow = text => wrap("\x1b[33m", text);
|
|
3349
|
+
const blue = text => wrap("\x1b[34m", text);
|
|
3350
|
+
const magenta = text => wrap("\x1b[35m", text);
|
|
3351
|
+
const cyan = text => wrap("\x1b[36m", text);
|
|
3352
|
+
const white = text => wrap("\x1b[37m", text);
|
|
3353
|
+
const gray = text => wrap("\x1b[90m", text);
|
|
3354
|
+
const brightRed = text => wrap("\x1b[91m", text);
|
|
3355
|
+
const brightGreen = text => wrap("\x1b[92m", text);
|
|
3356
|
+
const brightYellow = text => wrap("\x1b[93m", text);
|
|
3357
|
+
const brightBlue = text => wrap("\x1b[94m", text);
|
|
3358
|
+
const brightMagenta = text => wrap("\x1b[95m", text);
|
|
3359
|
+
const brightCyan = text => wrap("\x1b[96m", text);
|
|
3360
|
+
const brightWhite = text => wrap("\x1b[97m", text);
|
|
3361
|
+
|
|
3362
|
+
|
|
3310
3363
|
/***/ })
|
|
3311
3364
|
|
|
3312
3365
|
/******/ });
|
|
@@ -3342,6 +3395,34 @@ module.exports = require("readline");
|
|
|
3342
3395
|
/******/ }
|
|
3343
3396
|
/******/
|
|
3344
3397
|
/************************************************************************/
|
|
3398
|
+
/******/ /* webpack/runtime/define property getters */
|
|
3399
|
+
/******/ (() => {
|
|
3400
|
+
/******/ // define getter functions for harmony exports
|
|
3401
|
+
/******/ __nccwpck_require__.d = (exports, definition) => {
|
|
3402
|
+
/******/ for(var key in definition) {
|
|
3403
|
+
/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) {
|
|
3404
|
+
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
3405
|
+
/******/ }
|
|
3406
|
+
/******/ }
|
|
3407
|
+
/******/ };
|
|
3408
|
+
/******/ })();
|
|
3409
|
+
/******/
|
|
3410
|
+
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
3411
|
+
/******/ (() => {
|
|
3412
|
+
/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
3413
|
+
/******/ })();
|
|
3414
|
+
/******/
|
|
3415
|
+
/******/ /* webpack/runtime/make namespace object */
|
|
3416
|
+
/******/ (() => {
|
|
3417
|
+
/******/ // define __esModule on exports
|
|
3418
|
+
/******/ __nccwpck_require__.r = (exports) => {
|
|
3419
|
+
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
3420
|
+
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
3421
|
+
/******/ }
|
|
3422
|
+
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
3423
|
+
/******/ };
|
|
3424
|
+
/******/ })();
|
|
3425
|
+
/******/
|
|
3345
3426
|
/******/ /* webpack/runtime/compat */
|
|
3346
3427
|
/******/
|
|
3347
3428
|
/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/";
|
|
@@ -3359,7 +3440,7 @@ const Lexer = __nccwpck_require__(211);
|
|
|
3359
3440
|
const Parser = __nccwpck_require__(222);
|
|
3360
3441
|
const Evaluator = __nccwpck_require__(112);
|
|
3361
3442
|
|
|
3362
|
-
const VERSION = '1.1.
|
|
3443
|
+
const VERSION = '1.1.2';
|
|
3363
3444
|
|
|
3364
3445
|
const COLOR = {
|
|
3365
3446
|
reset: '\x1b[0m',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "starlight-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Starlight Programming Language CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"starlight": "index.js"
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"blessed": "^0.1.81",
|
|
16
|
-
"readline-sync": "^1.4.10"
|
|
16
|
+
"readline-sync": "^1.4.10",
|
|
17
|
+
"starlight-color": "^1.0.4"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
19
20
|
"@vercel/ncc": "^0.38.4"
|
package/src/evaluator.js
CHANGED
|
@@ -111,58 +111,59 @@ suggest(name, env) {
|
|
|
111
111
|
return best;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
|
|
115
114
|
formatValue(value, seen = new Set()) {
|
|
116
|
-
|
|
115
|
+
const color = require('starlight-color');
|
|
116
|
+
|
|
117
|
+
// Handle circular references
|
|
117
118
|
if (typeof value === 'object' && value !== null) {
|
|
118
|
-
if (seen.has(value)) return '[Circular]';
|
|
119
|
+
if (seen.has(value)) return color.red('[Circular]');
|
|
119
120
|
seen.add(value);
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
// Python-style null / undefined
|
|
123
|
-
if (value === null) return 'None';
|
|
124
|
-
if (value === undefined) return 'undefined';
|
|
124
|
+
if (value === null) return color.yellow('None');
|
|
125
|
+
if (value === undefined) return color.yellow('undefined');
|
|
125
126
|
|
|
126
127
|
const t = typeof value;
|
|
127
128
|
|
|
128
129
|
// Strings (no quotes)
|
|
129
|
-
if (t === 'string') return value;
|
|
130
|
+
if (t === 'string') return color.cyan(value);
|
|
130
131
|
|
|
131
132
|
// Numbers
|
|
132
|
-
if (t === 'number') return String(value);
|
|
133
|
+
if (t === 'number') return color.green(String(value));
|
|
133
134
|
|
|
134
|
-
// Booleans
|
|
135
|
-
if (t === 'boolean') return value ? '
|
|
135
|
+
// Booleans
|
|
136
|
+
if (t === 'boolean') return color.yellow(value ? 'true' : 'false');
|
|
136
137
|
|
|
137
138
|
// Native JS functions
|
|
138
139
|
if (t === 'function') {
|
|
139
|
-
return value.name
|
|
140
|
-
? `<function ${value.name}>`
|
|
141
|
-
: '<function>';
|
|
140
|
+
return color.magenta(value.name ? `<function ${value.name}>` : '<function>');
|
|
142
141
|
}
|
|
143
142
|
|
|
144
143
|
// Arrays
|
|
145
144
|
if (Array.isArray(value)) {
|
|
146
|
-
|
|
145
|
+
const items = value.map(v => this.formatValue(v, seen));
|
|
146
|
+
return color.white('[ ' + items.join(', ') + ' ]');
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
// Objects (including user-defined functions)
|
|
150
150
|
if (t === 'object') {
|
|
151
|
-
// Detect user-defined functions (AST-based)
|
|
152
151
|
if (value.params && value.body) {
|
|
153
|
-
return value.name
|
|
154
|
-
? `<function ${value.name}>`
|
|
155
|
-
: '<function>';
|
|
152
|
+
return color.magenta(value.name ? `<function ${value.name}>` : '<function>');
|
|
156
153
|
}
|
|
157
154
|
|
|
158
155
|
const entries = Object.entries(value).map(
|
|
159
|
-
([k, v]) => `${k}:
|
|
156
|
+
([k, v]) => color.magenta(`${k}: `) + this.formatValue(v, seen)
|
|
160
157
|
);
|
|
161
|
-
return '{ ' + entries.join(', ') + ' }';
|
|
158
|
+
return color.magenta('{ ') + entries.join(color.magenta(', ')) + color.magenta(' }');
|
|
162
159
|
}
|
|
163
160
|
|
|
164
161
|
// Fallback
|
|
165
|
-
|
|
162
|
+
try {
|
|
163
|
+
return String(value);
|
|
164
|
+
} catch {
|
|
165
|
+
return color.red('[Unprintable]');
|
|
166
|
+
}
|
|
166
167
|
}
|
|
167
168
|
|
|
168
169
|
|
|
@@ -493,23 +494,23 @@ async evalAssignment(node, env) {
|
|
|
493
494
|
const left = node.left;
|
|
494
495
|
|
|
495
496
|
if (left.type === 'Identifier') return env.set(left.name, rightVal);
|
|
496
|
-
if (left.type === 'MemberExpression') {
|
|
497
|
-
const obj = await this.evaluate(left.object, env);
|
|
498
|
-
if (obj == null) throw new RuntimeError('Cannot assign to null or undefined', node, this.source);
|
|
499
|
-
obj[left.property] = rightVal;
|
|
500
|
-
return rightVal;
|
|
501
|
-
}
|
|
502
|
-
if (left.type === 'IndexExpression') {
|
|
503
|
-
const obj = await this.evaluate(left.object, env);
|
|
504
|
-
const idx = await this.evaluate(left.indexer, env);
|
|
505
|
-
if (obj == null) throw new RuntimeError('Cannot assign to null or undefined', node, this.source);
|
|
506
|
-
obj[idx] = rightVal;
|
|
507
|
-
return rightVal;
|
|
508
|
-
}
|
|
509
497
|
|
|
498
|
+
if (left.type === 'MemberExpression') {
|
|
499
|
+
const obj = await this.evaluate(left.object, env);
|
|
500
|
+
if (obj == null) throw new RuntimeError('Cannot assign to null or undefined', node, this.source);
|
|
501
|
+
obj[left.property] = rightVal; // dynamic creation of new properties allowed
|
|
502
|
+
return rightVal;
|
|
503
|
+
}
|
|
510
504
|
|
|
511
|
-
|
|
505
|
+
if (left.type === 'IndexExpression') {
|
|
506
|
+
const obj = await this.evaluate(left.object, env);
|
|
507
|
+
const idx = await this.evaluate(left.indexer, env);
|
|
508
|
+
if (obj == null) throw new RuntimeError('Cannot assign to null or undefined', node, this.source);
|
|
509
|
+
obj[idx] = rightVal; // dynamic creation allowed
|
|
510
|
+
return rightVal;
|
|
511
|
+
}
|
|
512
512
|
|
|
513
|
+
throw new RuntimeError('Invalid assignment target', node, this.source);
|
|
513
514
|
}
|
|
514
515
|
|
|
515
516
|
async evalCompoundAssignment(node, env) {
|
|
@@ -517,13 +518,13 @@ async evalCompoundAssignment(node, env) {
|
|
|
517
518
|
let current;
|
|
518
519
|
|
|
519
520
|
if (left.type === 'Identifier') current = env.get(left.name, left, this.source);
|
|
520
|
-
else if (left.type === 'MemberExpression') current = await this.evalMember(left, env);
|
|
521
|
-
else if (left.type === 'IndexExpression') current = await this.evalIndex(left, env);
|
|
521
|
+
else if (left.type === 'MemberExpression') current = await this.evalMember(left, env) ?? 0;
|
|
522
|
+
else if (left.type === 'IndexExpression') current = await this.evalIndex(left, env) ?? 0;
|
|
522
523
|
else throw new RuntimeError('Invalid compound assignment target', node, this.source);
|
|
523
524
|
|
|
524
|
-
|
|
525
525
|
const rhs = await this.evaluate(node.right, env);
|
|
526
526
|
let computed;
|
|
527
|
+
|
|
527
528
|
switch (node.operator) {
|
|
528
529
|
case 'PLUSEQ': computed = current + rhs; break;
|
|
529
530
|
case 'MINUSEQ': computed = current - rhs; break;
|
|
@@ -531,16 +532,18 @@ async evalCompoundAssignment(node, env) {
|
|
|
531
532
|
case 'SLASHEQ': computed = current / rhs; break;
|
|
532
533
|
case 'MODEQ': computed = current % rhs; break;
|
|
533
534
|
default: throw new RuntimeError(`Unknown compound operator: ${node.operator}`, node, this.source);
|
|
534
|
-
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
if (left.type === 'Identifier') env.set(left.name, computed);
|
|
538
|
-
else if (left.type === 'MemberExpression')
|
|
539
|
-
|
|
538
|
+
else if (left.type === 'MemberExpression')
|
|
539
|
+
await this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
|
|
540
|
+
else
|
|
541
|
+
await this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
|
|
540
542
|
|
|
541
543
|
return computed;
|
|
542
544
|
}
|
|
543
545
|
|
|
546
|
+
|
|
544
547
|
async evalSldeploy(node, env) {
|
|
545
548
|
const val = await this.evaluate(node.expr, env);
|
|
546
549
|
console.log(this.formatValue(val));
|
|
@@ -548,6 +551,7 @@ async evalSldeploy(node, env) {
|
|
|
548
551
|
}
|
|
549
552
|
|
|
550
553
|
|
|
554
|
+
|
|
551
555
|
async evalAsk(node, env) {
|
|
552
556
|
const prompt = await this.evaluate(node.prompt, env);
|
|
553
557
|
|
|
@@ -654,11 +658,8 @@ async evalWhile(node, env) {
|
|
|
654
658
|
}
|
|
655
659
|
return null;
|
|
656
660
|
}
|
|
657
|
-
|
|
658
661
|
async evalFor(node, env) {
|
|
659
|
-
// -------------------------------
|
|
660
662
|
// Python-style: for x in iterable (with optional 'let')
|
|
661
|
-
// -------------------------------
|
|
662
663
|
if (node.type === 'ForInStatement') {
|
|
663
664
|
const iterable = await this.evaluate(node.iterable, env);
|
|
664
665
|
|
|
@@ -666,11 +667,10 @@ async evalFor(node, env) {
|
|
|
666
667
|
throw new RuntimeError('Cannot iterate over non-iterable', node, this.source);
|
|
667
668
|
}
|
|
668
669
|
|
|
669
|
-
const loopVar = node.variable; //
|
|
670
|
-
|
|
670
|
+
const loopVar = node.variable; // string name of the loop variable
|
|
671
671
|
const createLoopEnv = () => node.letKeyword ? new Environment(env) : env;
|
|
672
672
|
|
|
673
|
-
// Arrays
|
|
673
|
+
// Arrays: iterate over elements
|
|
674
674
|
if (Array.isArray(iterable)) {
|
|
675
675
|
for (const value of iterable) {
|
|
676
676
|
const loopEnv = createLoopEnv();
|
|
@@ -685,7 +685,7 @@ async evalFor(node, env) {
|
|
|
685
685
|
}
|
|
686
686
|
}
|
|
687
687
|
}
|
|
688
|
-
// Objects
|
|
688
|
+
// Objects: iterate over keys
|
|
689
689
|
else {
|
|
690
690
|
for (const key of Object.keys(iterable)) {
|
|
691
691
|
const loopEnv = createLoopEnv();
|
|
@@ -704,9 +704,7 @@ async evalFor(node, env) {
|
|
|
704
704
|
return null;
|
|
705
705
|
}
|
|
706
706
|
|
|
707
|
-
//
|
|
708
|
-
// C-style for loop
|
|
709
|
-
// -------------------------------
|
|
707
|
+
// C-style for loop (classic JS style)
|
|
710
708
|
const local = new Environment(env);
|
|
711
709
|
|
|
712
710
|
if (node.init) await this.evaluate(node.init, local);
|
|
@@ -728,6 +726,7 @@ async evalFor(node, env) {
|
|
|
728
726
|
|
|
729
727
|
return null;
|
|
730
728
|
}
|
|
729
|
+
|
|
731
730
|
evalFunctionDeclaration(node, env) {
|
|
732
731
|
if (!node.name || typeof node.name !== 'string') {
|
|
733
732
|
throw new RuntimeError('Function declaration requires a valid name', node, this.source);
|
|
@@ -790,18 +789,22 @@ async evalIndex(node, env) {
|
|
|
790
789
|
const obj = await this.evaluate(node.object, env);
|
|
791
790
|
const idx = await this.evaluate(node.indexer, env);
|
|
792
791
|
|
|
793
|
-
if (obj == null) throw new RuntimeError('
|
|
792
|
+
if (obj == null) throw new RuntimeError('Cannot index null or undefined', node, this.source);
|
|
794
793
|
|
|
795
|
-
|
|
796
|
-
|
|
794
|
+
// Array access: return undefined if out of bounds
|
|
795
|
+
if (Array.isArray(obj)) {
|
|
796
|
+
if (idx < 0 || idx >= obj.length) return undefined;
|
|
797
|
+
return obj[idx];
|
|
797
798
|
}
|
|
798
|
-
|
|
799
|
-
|
|
799
|
+
|
|
800
|
+
// Object access: return undefined if property missing
|
|
801
|
+
if (typeof obj === 'object') {
|
|
802
|
+
return obj[idx]; // undefined if missing
|
|
800
803
|
}
|
|
801
804
|
|
|
802
|
-
|
|
805
|
+
// Fallback for non-object non-array
|
|
806
|
+
return undefined;
|
|
803
807
|
}
|
|
804
|
-
|
|
805
808
|
async evalObject(node, env) {
|
|
806
809
|
const out = {};
|
|
807
810
|
for (const p of node.props) {
|
|
@@ -810,19 +813,22 @@ async evalObject(node, env) {
|
|
|
810
813
|
}
|
|
811
814
|
const key = await this.evaluate(p.key, env);
|
|
812
815
|
const value = await this.evaluate(p.value, env);
|
|
813
|
-
out[key] = value;
|
|
816
|
+
out[key] = value; // dynamic property assignment
|
|
814
817
|
}
|
|
815
818
|
return out;
|
|
816
819
|
}
|
|
817
820
|
|
|
818
821
|
|
|
822
|
+
|
|
819
823
|
async evalMember(node, env) {
|
|
820
824
|
const obj = await this.evaluate(node.object, env);
|
|
821
|
-
|
|
822
|
-
if (
|
|
825
|
+
|
|
826
|
+
if (obj == null) throw new RuntimeError('Member access of null or undefined', node, this.source);
|
|
827
|
+
|
|
823
828
|
return obj[node.property];
|
|
824
829
|
}
|
|
825
830
|
|
|
831
|
+
|
|
826
832
|
async evalUpdate(node, env) {
|
|
827
833
|
const arg = node.argument;
|
|
828
834
|
const getCurrent = async () => {
|
package/src/starlight.js
CHANGED
package/src/program.sl
DELETED