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 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
- // Circular reference handling
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 (Python-style)
1478
- if (t === 'boolean') return value ? 'True' : 'False';
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
- return '[' + value.map(v => this.formatValue(v, seen)).join(', ') + ']';
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}: ${this.formatValue(v, seen)}`
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
- return String(value);
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
- throw new RuntimeError('Invalid assignment target', node, this.source);
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') await this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
1882
- else await this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
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; // STRING from parser
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 (keys)
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('Indexing null or undefined', node, this.source);
2135
+ if (obj == null) throw new RuntimeError('Cannot index null or undefined', node, this.source);
2137
2136
 
2138
- if (Array.isArray(obj) && (idx < 0 || idx >= obj.length)) {
2139
- throw new RuntimeError('Array index out of bounds', node, this.source);
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
- if (typeof obj === 'object' && !(idx in obj)) {
2142
- throw new RuntimeError(`Property '${idx}' does not exist`, node, this.source);
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
- return obj[idx];
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
- if (obj == null) throw new RuntimeError('Member access of null or undefined', node, this.source);
2165
- if (!(node.property in obj)) throw new RuntimeError(`Property '${node.property}' does not exist`, node, this.source);
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.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.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
- // Circular reference handling
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 (Python-style)
135
- if (t === 'boolean') return value ? 'True' : 'False';
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
- return '[' + value.map(v => this.formatValue(v, seen)).join(', ') + ']';
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}: ${this.formatValue(v, seen)}`
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
- return String(value);
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
- throw new RuntimeError('Invalid assignment target', node, this.source);
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') await this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
539
- else await this.evalAssignment({ left, right: { type: 'Literal', value: computed }, type: 'AssignmentExpression' }, env);
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; // STRING from parser
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 (keys)
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('Indexing null or undefined', node, this.source);
792
+ if (obj == null) throw new RuntimeError('Cannot index null or undefined', node, this.source);
794
793
 
795
- if (Array.isArray(obj) && (idx < 0 || idx >= obj.length)) {
796
- throw new RuntimeError('Array index out of bounds', node, this.source);
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
- if (typeof obj === 'object' && !(idx in obj)) {
799
- throw new RuntimeError(`Property '${idx}' does not exist`, node, this.source);
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
- return obj[idx];
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
- if (obj == null) throw new RuntimeError('Member access of null or undefined', node, this.source);
822
- if (!(node.property in obj)) throw new RuntimeError(`Property '${node.property}' does not exist`, node, this.source);
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
@@ -9,7 +9,7 @@ const Lexer = require('./lexer');
9
9
  const Parser = require('./parser');
10
10
  const Evaluator = require('./evaluator');
11
11
 
12
- const VERSION = '1.1.1';
12
+ const VERSION = '1.1.2';
13
13
 
14
14
  const COLOR = {
15
15
  reset: '\x1b[0m',
package/src/program.sl DELETED
@@ -1,2 +0,0 @@
1
- import color from "starlight-color"
2
- sldeploy color.red("Hello")