starlight-cli 1.1.17 → 1.1.18
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 +184 -151
- package/package.json +1 -1
- package/src/evaluator.js +123 -10
- package/src/parser.js +58 -139
- package/src/starlight.js +2 -2
package/dist/index.js
CHANGED
|
@@ -10466,6 +10466,8 @@ this.global.define('toStr', arg => String(arg));
|
|
|
10466
10466
|
this.global.define('isNaN', arg => {
|
|
10467
10467
|
return typeof arg !== 'number' || Number.isNaN(arg);
|
|
10468
10468
|
});
|
|
10469
|
+
|
|
10470
|
+
|
|
10469
10471
|
this.global.define('random', (min, max) => {
|
|
10470
10472
|
if (max === undefined) {
|
|
10471
10473
|
// Only one argument → random between 0 and min
|
|
@@ -10568,6 +10570,9 @@ this.global.define('encodeURLComponent', arg => {
|
|
|
10568
10570
|
|
|
10569
10571
|
return acc;
|
|
10570
10572
|
});
|
|
10573
|
+
this.global.define('hasOwn', (obj, prop) => {
|
|
10574
|
+
return Object.prototype.hasOwnProperty.call(obj, prop)
|
|
10575
|
+
});
|
|
10571
10576
|
|
|
10572
10577
|
this.global.define('keys', arg => arg && typeof arg === 'object' ? Object.keys(arg) : []);
|
|
10573
10578
|
this.global.define('values', arg => arg && typeof arg === 'object' ? Object.values(arg) : []);
|
|
@@ -11122,8 +11127,6 @@ async evalProgram(node, env) {
|
|
|
11122
11127
|
async evalSlice(node, env) {
|
|
11123
11128
|
try {
|
|
11124
11129
|
const arr = await this.evaluate(node.object, env);
|
|
11125
|
-
const start = node.start ? await this.evaluate(node.start, env) : 0;
|
|
11126
|
-
const end = node.end ? await this.evaluate(node.end, env) : arr.length;
|
|
11127
11130
|
|
|
11128
11131
|
if (!Array.isArray(arr)) {
|
|
11129
11132
|
throw new RuntimeError(
|
|
@@ -11134,10 +11137,37 @@ async evalSlice(node, env) {
|
|
|
11134
11137
|
);
|
|
11135
11138
|
}
|
|
11136
11139
|
|
|
11137
|
-
|
|
11138
|
-
|
|
11140
|
+
let start = node.start ? await this.evaluate(node.start, env) : 0;
|
|
11141
|
+
let end = node.end ? await this.evaluate(node.end, env) : arr.length;
|
|
11142
|
+
let step = node.step ? await this.evaluate(node.step, env) : 1;
|
|
11143
|
+
|
|
11144
|
+
if (step === 0) {
|
|
11145
|
+
throw new RuntimeError(
|
|
11146
|
+
'Slice step cannot be zero',
|
|
11147
|
+
node,
|
|
11148
|
+
this.source,
|
|
11149
|
+
env
|
|
11150
|
+
);
|
|
11151
|
+
}
|
|
11152
|
+
start = start < 0 ? arr.length + start : start;
|
|
11153
|
+
end = end < 0 ? arr.length + end : end;
|
|
11154
|
+
|
|
11155
|
+
start = Math.min(Math.max(start, 0), arr.length);
|
|
11156
|
+
end = Math.min(Math.max(end, 0), arr.length);
|
|
11157
|
+
|
|
11158
|
+
const result = [];
|
|
11159
|
+
|
|
11160
|
+
if (step > 0) {
|
|
11161
|
+
for (let i = start; i < end; i += step) {
|
|
11162
|
+
result.push(arr[i]);
|
|
11163
|
+
}
|
|
11164
|
+
} else {
|
|
11165
|
+
for (let i = start; i > end; i += step) {
|
|
11166
|
+
result.push(arr[i]);
|
|
11167
|
+
}
|
|
11168
|
+
}
|
|
11139
11169
|
|
|
11140
|
-
return
|
|
11170
|
+
return result;
|
|
11141
11171
|
|
|
11142
11172
|
} catch (err) {
|
|
11143
11173
|
if (err instanceof RuntimeError) throw err;
|
|
@@ -11150,6 +11180,7 @@ async evalSlice(node, env) {
|
|
|
11150
11180
|
}
|
|
11151
11181
|
}
|
|
11152
11182
|
|
|
11183
|
+
|
|
11153
11184
|
async evalStartStatement(node, env) {
|
|
11154
11185
|
try {
|
|
11155
11186
|
const value = await this.evaluate(node.discriminant, env);
|
|
@@ -11674,12 +11705,49 @@ async evalLogical(node, env) {
|
|
|
11674
11705
|
|
|
11675
11706
|
async evalUnary(node, env) {
|
|
11676
11707
|
try {
|
|
11708
|
+
// Handle prefix update operators
|
|
11709
|
+
if (node.type === 'UpdateExpression' && node.prefix) {
|
|
11710
|
+
if (node.argument.type !== 'Identifier') {
|
|
11711
|
+
throw new RuntimeError(
|
|
11712
|
+
`Invalid operand for update operator: ${node.operator}`,
|
|
11713
|
+
node,
|
|
11714
|
+
this.source,
|
|
11715
|
+
env
|
|
11716
|
+
);
|
|
11717
|
+
}
|
|
11718
|
+
|
|
11719
|
+
const varName = node.argument.name;
|
|
11720
|
+
let currentVal = await env.get(varName); // assuming env.get returns the variable value
|
|
11721
|
+
|
|
11722
|
+
switch (node.operator) {
|
|
11723
|
+
case 'PLUSPLUS':
|
|
11724
|
+
currentVal++;
|
|
11725
|
+
await env.set(varName, currentVal);
|
|
11726
|
+
return currentVal;
|
|
11727
|
+
case 'MINUSMINUS':
|
|
11728
|
+
currentVal--;
|
|
11729
|
+
await env.set(varName, currentVal);
|
|
11730
|
+
return currentVal;
|
|
11731
|
+
default:
|
|
11732
|
+
throw new RuntimeError(
|
|
11733
|
+
`Unknown update operator: ${node.operator}`,
|
|
11734
|
+
node,
|
|
11735
|
+
this.source,
|
|
11736
|
+
env
|
|
11737
|
+
);
|
|
11738
|
+
}
|
|
11739
|
+
}
|
|
11740
|
+
|
|
11741
|
+
// Evaluate the argument first
|
|
11677
11742
|
const val = await this.evaluate(node.argument, env);
|
|
11678
11743
|
|
|
11679
11744
|
switch (node.operator) {
|
|
11680
|
-
case 'NOT':
|
|
11681
|
-
|
|
11682
|
-
case '
|
|
11745
|
+
case 'NOT':
|
|
11746
|
+
return !val;
|
|
11747
|
+
case 'MINUS':
|
|
11748
|
+
return -val;
|
|
11749
|
+
case 'PLUS':
|
|
11750
|
+
return +val;
|
|
11683
11751
|
default:
|
|
11684
11752
|
throw new RuntimeError(
|
|
11685
11753
|
`Unknown unary operator: ${node.operator}`,
|
|
@@ -11688,7 +11756,6 @@ async evalUnary(node, env) {
|
|
|
11688
11756
|
env
|
|
11689
11757
|
);
|
|
11690
11758
|
}
|
|
11691
|
-
|
|
11692
11759
|
} catch (e) {
|
|
11693
11760
|
if (e instanceof RuntimeError) throw e;
|
|
11694
11761
|
throw new RuntimeError(
|
|
@@ -11700,6 +11767,51 @@ async evalUnary(node, env) {
|
|
|
11700
11767
|
}
|
|
11701
11768
|
}
|
|
11702
11769
|
|
|
11770
|
+
// Handle postfix updates (x++ or x--)
|
|
11771
|
+
async evalPostfixUpdate(node, env) {
|
|
11772
|
+
if (node.type !== 'UpdateExpression' || node.prefix) {
|
|
11773
|
+
throw new RuntimeError(
|
|
11774
|
+
`Invalid postfix update node`,
|
|
11775
|
+
node,
|
|
11776
|
+
this.source,
|
|
11777
|
+
env
|
|
11778
|
+
);
|
|
11779
|
+
}
|
|
11780
|
+
|
|
11781
|
+
if (node.argument.type !== 'Identifier') {
|
|
11782
|
+
throw new RuntimeError(
|
|
11783
|
+
`Invalid operand for postfix update operator: ${node.operator}`,
|
|
11784
|
+
node,
|
|
11785
|
+
this.source,
|
|
11786
|
+
env
|
|
11787
|
+
);
|
|
11788
|
+
}
|
|
11789
|
+
|
|
11790
|
+
const varName = node.argument.name;
|
|
11791
|
+
let currentVal = await env.get(varName);
|
|
11792
|
+
|
|
11793
|
+
let returnVal = currentVal;
|
|
11794
|
+
|
|
11795
|
+
switch (node.operator) {
|
|
11796
|
+
case 'PLUSPLUS':
|
|
11797
|
+
currentVal++;
|
|
11798
|
+
await env.set(varName, currentVal);
|
|
11799
|
+
return returnVal; // return original value
|
|
11800
|
+
case 'MINUSMINUS':
|
|
11801
|
+
currentVal--;
|
|
11802
|
+
await env.set(varName, currentVal);
|
|
11803
|
+
return returnVal; // return original value
|
|
11804
|
+
default:
|
|
11805
|
+
throw new RuntimeError(
|
|
11806
|
+
`Unknown postfix operator: ${node.operator}`,
|
|
11807
|
+
node,
|
|
11808
|
+
this.source,
|
|
11809
|
+
env
|
|
11810
|
+
);
|
|
11811
|
+
}
|
|
11812
|
+
}
|
|
11813
|
+
|
|
11814
|
+
|
|
11703
11815
|
async evalIf(node, env) {
|
|
11704
11816
|
let test = await this.evaluate(node.test, env);
|
|
11705
11817
|
test = !!test; // coerce to boolean
|
|
@@ -11978,7 +12090,8 @@ async evalIndex(node, env) {
|
|
|
11978
12090
|
env
|
|
11979
12091
|
);
|
|
11980
12092
|
}
|
|
11981
|
-
}
|
|
12093
|
+
}
|
|
12094
|
+
|
|
11982
12095
|
async evalObject(node, env) {
|
|
11983
12096
|
try {
|
|
11984
12097
|
const out = {};
|
|
@@ -13617,192 +13730,111 @@ unary() {
|
|
|
13617
13730
|
|
|
13618
13731
|
return this.postfix();
|
|
13619
13732
|
}
|
|
13733
|
+
|
|
13620
13734
|
postfix() {
|
|
13621
13735
|
let node = this.primary();
|
|
13622
13736
|
|
|
13623
13737
|
while (true) {
|
|
13624
13738
|
const t = this.current;
|
|
13625
13739
|
|
|
13626
|
-
|
|
13627
|
-
|
|
13628
|
-
|
|
13629
|
-
|
|
13630
|
-
|
|
13631
|
-
let start = null;
|
|
13632
|
-
let end = null;
|
|
13633
|
-
let step = null;
|
|
13634
|
-
|
|
13635
|
-
if (this.current.type === 'COLON') {
|
|
13636
|
-
this.eat('COLON');
|
|
13637
|
-
|
|
13638
|
-
if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
|
|
13639
|
-
end = this.expression();
|
|
13640
|
-
}
|
|
13641
|
-
|
|
13642
|
-
if (this.current.type === 'COLON') {
|
|
13643
|
-
this.eat('COLON');
|
|
13644
|
-
if (this.current.type !== 'RBRACKET') {
|
|
13645
|
-
step = this.expression();
|
|
13646
|
-
}
|
|
13647
|
-
}
|
|
13648
|
-
|
|
13649
|
-
if (this.current.type !== 'RBRACKET') {
|
|
13650
|
-
throw new ParseError(
|
|
13651
|
-
"Expected ']' after slice",
|
|
13652
|
-
this.current,
|
|
13653
|
-
this.source
|
|
13654
|
-
);
|
|
13655
|
-
}
|
|
13656
|
-
|
|
13657
|
-
this.eat('RBRACKET');
|
|
13658
|
-
|
|
13659
|
-
node = {
|
|
13660
|
-
type: 'SliceExpression',
|
|
13661
|
-
object: node,
|
|
13662
|
-
start,
|
|
13663
|
-
end,
|
|
13664
|
-
step,
|
|
13665
|
-
line: startLine,
|
|
13666
|
-
column: startCol
|
|
13667
|
-
};
|
|
13668
|
-
|
|
13669
|
-
} else {
|
|
13670
|
-
start = this.expression();
|
|
13671
|
-
|
|
13672
|
-
if (this.current.type === 'COLON') {
|
|
13673
|
-
this.eat('COLON');
|
|
13740
|
+
// Handle indexing / slicing
|
|
13741
|
+
if (t.type === 'LBRACKET') {
|
|
13742
|
+
const startLine = t.line;
|
|
13743
|
+
const startCol = t.column;
|
|
13744
|
+
this.eat('LBRACKET');
|
|
13674
13745
|
|
|
13675
|
-
|
|
13676
|
-
|
|
13677
|
-
|
|
13746
|
+
let start = null;
|
|
13747
|
+
let end = null;
|
|
13748
|
+
let step = null;
|
|
13678
13749
|
|
|
13750
|
+
// Slice: [:end:step] or [start:end:step]
|
|
13679
13751
|
if (this.current.type === 'COLON') {
|
|
13680
13752
|
this.eat('COLON');
|
|
13753
|
+
if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
|
|
13754
|
+
end = this.expression();
|
|
13755
|
+
}
|
|
13756
|
+
if (this.current.type === 'COLON') {
|
|
13757
|
+
this.eat('COLON');
|
|
13758
|
+
if (this.current.type !== 'RBRACKET') {
|
|
13759
|
+
step = this.expression();
|
|
13760
|
+
}
|
|
13761
|
+
}
|
|
13681
13762
|
if (this.current.type !== 'RBRACKET') {
|
|
13682
|
-
|
|
13763
|
+
throw new ParseError("Expected ']' after slice", this.current, this.source);
|
|
13683
13764
|
}
|
|
13684
|
-
|
|
13685
|
-
|
|
13686
|
-
if (this.current.type !== 'RBRACKET') {
|
|
13687
|
-
throw new ParseError(
|
|
13688
|
-
"Expected ']' after slice",
|
|
13689
|
-
this.current,
|
|
13690
|
-
this.source
|
|
13691
|
-
);
|
|
13692
|
-
}
|
|
13693
|
-
|
|
13694
|
-
this.eat('RBRACKET');
|
|
13765
|
+
this.eat('RBRACKET');
|
|
13695
13766
|
|
|
13696
|
-
|
|
13697
|
-
|
|
13698
|
-
|
|
13699
|
-
start
|
|
13700
|
-
|
|
13701
|
-
|
|
13702
|
-
|
|
13703
|
-
|
|
13704
|
-
|
|
13767
|
+
node = { type: 'SliceExpression', object: node, start, end, step, line: startLine, column: startCol };
|
|
13768
|
+
} else {
|
|
13769
|
+
// Normal index or slice starting with an expression
|
|
13770
|
+
start = this.expression();
|
|
13771
|
+
if (this.current.type === 'COLON') {
|
|
13772
|
+
this.eat('COLON');
|
|
13773
|
+
if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
|
|
13774
|
+
end = this.expression();
|
|
13775
|
+
}
|
|
13776
|
+
if (this.current.type === 'COLON') {
|
|
13777
|
+
this.eat('COLON');
|
|
13778
|
+
if (this.current.type !== 'RBRACKET') {
|
|
13779
|
+
step = this.expression();
|
|
13780
|
+
}
|
|
13781
|
+
}
|
|
13782
|
+
if (this.current.type !== 'RBRACKET') {
|
|
13783
|
+
throw new ParseError("Expected ']' after slice", this.current, this.source);
|
|
13784
|
+
}
|
|
13785
|
+
this.eat('RBRACKET');
|
|
13705
13786
|
|
|
13706
|
-
|
|
13707
|
-
|
|
13708
|
-
|
|
13709
|
-
|
|
13710
|
-
|
|
13711
|
-
|
|
13712
|
-
|
|
13713
|
-
column: startCol
|
|
13714
|
-
};
|
|
13787
|
+
node = { type: 'SliceExpression', object: node, start, end, step, line: startLine, column: startCol };
|
|
13788
|
+
} else {
|
|
13789
|
+
this.eat('RBRACKET');
|
|
13790
|
+
node = { type: 'IndexExpression', object: node, indexer: start, line: startLine, column: startCol };
|
|
13791
|
+
}
|
|
13792
|
+
}
|
|
13793
|
+
continue; // continue to allow . or () after []
|
|
13715
13794
|
}
|
|
13716
|
-
}
|
|
13717
|
-
}
|
|
13718
|
-
|
|
13719
|
-
|
|
13720
13795
|
|
|
13796
|
+
// Handle function call
|
|
13721
13797
|
if (t.type === 'LPAREN') {
|
|
13722
13798
|
const startLine = t.line;
|
|
13723
13799
|
const startCol = t.column;
|
|
13724
13800
|
this.eat('LPAREN');
|
|
13725
13801
|
|
|
13726
13802
|
const args = [];
|
|
13727
|
-
|
|
13728
13803
|
while (this.current.type !== 'RPAREN') {
|
|
13729
13804
|
if (this.current.type === 'EOF') {
|
|
13730
|
-
throw new ParseError(
|
|
13731
|
-
"Unterminated function call",
|
|
13732
|
-
this.current,
|
|
13733
|
-
this.source,
|
|
13734
|
-
"Did you forget to close ')'?"
|
|
13735
|
-
);
|
|
13805
|
+
throw new ParseError("Unterminated function call", this.current, this.source, "Did you forget to close ')'?");
|
|
13736
13806
|
}
|
|
13737
|
-
|
|
13738
13807
|
args.push(this.expression());
|
|
13739
|
-
|
|
13740
|
-
if (this.current.type === 'COMMA') {
|
|
13741
|
-
this.eat('COMMA');
|
|
13742
|
-
} else {
|
|
13743
|
-
break;
|
|
13744
|
-
}
|
|
13745
|
-
}
|
|
13746
|
-
|
|
13747
|
-
if (this.current.type !== 'RPAREN') {
|
|
13748
|
-
throw new ParseError(
|
|
13749
|
-
"Expected ')' after function arguments",
|
|
13750
|
-
this.current,
|
|
13751
|
-
this.source,
|
|
13752
|
-
"Function calls must be closed with ')'"
|
|
13753
|
-
);
|
|
13808
|
+
if (this.current.type === 'COMMA') this.eat('COMMA');
|
|
13754
13809
|
}
|
|
13755
13810
|
|
|
13756
13811
|
this.eat('RPAREN');
|
|
13757
13812
|
|
|
13758
|
-
node = {
|
|
13759
|
-
type: 'CallExpression',
|
|
13760
|
-
callee: node,
|
|
13761
|
-
arguments: args,
|
|
13762
|
-
line: startLine,
|
|
13763
|
-
column: startCol
|
|
13764
|
-
};
|
|
13813
|
+
node = { type: 'CallExpression', callee: node, arguments: args, line: startLine, column: startCol };
|
|
13765
13814
|
continue;
|
|
13766
13815
|
}
|
|
13767
13816
|
|
|
13817
|
+
// Handle member access
|
|
13768
13818
|
if (t.type === 'DOT') {
|
|
13769
13819
|
const startLine = t.line;
|
|
13770
13820
|
const startCol = t.column;
|
|
13771
13821
|
this.eat('DOT');
|
|
13772
13822
|
|
|
13773
13823
|
if (this.current.type !== 'IDENTIFIER') {
|
|
13774
|
-
throw new ParseError(
|
|
13775
|
-
"Expected property name after '.'",
|
|
13776
|
-
this.current,
|
|
13777
|
-
this.source,
|
|
13778
|
-
"Member access requires a property name, e.g. obj.value"
|
|
13779
|
-
);
|
|
13824
|
+
throw new ParseError("Expected property name after '.'", this.current, this.source, "Member access requires a property name, e.g. obj.value");
|
|
13780
13825
|
}
|
|
13781
13826
|
|
|
13782
13827
|
const property = this.current.value;
|
|
13783
13828
|
this.eat('IDENTIFIER');
|
|
13784
13829
|
|
|
13785
|
-
node = {
|
|
13786
|
-
type: 'MemberExpression',
|
|
13787
|
-
object: node,
|
|
13788
|
-
property,
|
|
13789
|
-
line: startLine,
|
|
13790
|
-
column: startCol
|
|
13791
|
-
};
|
|
13830
|
+
node = { type: 'MemberExpression', object: node, property, line: startLine, column: startCol };
|
|
13792
13831
|
continue;
|
|
13793
13832
|
}
|
|
13794
13833
|
|
|
13834
|
+
// Handle postfix update operators
|
|
13795
13835
|
if (t.type === 'PLUSPLUS' || t.type === 'MINUSMINUS') {
|
|
13796
13836
|
this.eat(t.type);
|
|
13797
|
-
|
|
13798
|
-
node = {
|
|
13799
|
-
type: 'UpdateExpression',
|
|
13800
|
-
operator: t.type,
|
|
13801
|
-
argument: node,
|
|
13802
|
-
prefix: false,
|
|
13803
|
-
line: t.line,
|
|
13804
|
-
column: t.column
|
|
13805
|
-
};
|
|
13837
|
+
node = { type: 'UpdateExpression', operator: t.type, argument: node, prefix: false, line: t.line, column: t.column };
|
|
13806
13838
|
continue;
|
|
13807
13839
|
}
|
|
13808
13840
|
|
|
@@ -13812,6 +13844,7 @@ postfix() {
|
|
|
13812
13844
|
return node;
|
|
13813
13845
|
}
|
|
13814
13846
|
|
|
13847
|
+
|
|
13815
13848
|
arrowFunction(params) {
|
|
13816
13849
|
const t = this.current;
|
|
13817
13850
|
this.eat('ARROW');
|
|
@@ -14162,8 +14195,8 @@ if (t.type === 'FUNC') {
|
|
|
14162
14195
|
}
|
|
14163
14196
|
|
|
14164
14197
|
}
|
|
14165
|
-
|
|
14166
|
-
|
|
14198
|
+
module.exports = Parser;
|
|
14199
|
+
|
|
14167
14200
|
|
|
14168
14201
|
/***/ }),
|
|
14169
14202
|
|
|
@@ -14343,7 +14376,7 @@ const Lexer = __nccwpck_require__(211);
|
|
|
14343
14376
|
const Parser = __nccwpck_require__(222);
|
|
14344
14377
|
const Evaluator = __nccwpck_require__(112);
|
|
14345
14378
|
|
|
14346
|
-
const VERSION = '1.1.
|
|
14379
|
+
const VERSION = '1.1.18';
|
|
14347
14380
|
|
|
14348
14381
|
const COLOR = {
|
|
14349
14382
|
reset: '\x1b[0m',
|
|
@@ -14503,7 +14536,7 @@ Usage:
|
|
|
14503
14536
|
|
|
14504
14537
|
if (args[0] === '-v' || args[0] === '--version') {
|
|
14505
14538
|
console.log(`${COLOR.bold}Starlight CLI v${VERSION}${COLOR.reset}`);
|
|
14506
|
-
console.log(`${COLOR.magenta}Developed by Macedon${COLOR.reset}`);
|
|
14539
|
+
console.log(`${COLOR.magenta}Developed by Dominex Macedon${COLOR.reset}`);
|
|
14507
14540
|
process.exit(0);
|
|
14508
14541
|
}
|
|
14509
14542
|
|
package/package.json
CHANGED
package/src/evaluator.js
CHANGED
|
@@ -256,6 +256,8 @@ this.global.define('toStr', arg => String(arg));
|
|
|
256
256
|
this.global.define('isNaN', arg => {
|
|
257
257
|
return typeof arg !== 'number' || Number.isNaN(arg);
|
|
258
258
|
});
|
|
259
|
+
|
|
260
|
+
|
|
259
261
|
this.global.define('random', (min, max) => {
|
|
260
262
|
if (max === undefined) {
|
|
261
263
|
// Only one argument → random between 0 and min
|
|
@@ -358,6 +360,9 @@ this.global.define('encodeURLComponent', arg => {
|
|
|
358
360
|
|
|
359
361
|
return acc;
|
|
360
362
|
});
|
|
363
|
+
this.global.define('hasOwn', (obj, prop) => {
|
|
364
|
+
return Object.prototype.hasOwnProperty.call(obj, prop)
|
|
365
|
+
});
|
|
361
366
|
|
|
362
367
|
this.global.define('keys', arg => arg && typeof arg === 'object' ? Object.keys(arg) : []);
|
|
363
368
|
this.global.define('values', arg => arg && typeof arg === 'object' ? Object.values(arg) : []);
|
|
@@ -912,8 +917,6 @@ async evalProgram(node, env) {
|
|
|
912
917
|
async evalSlice(node, env) {
|
|
913
918
|
try {
|
|
914
919
|
const arr = await this.evaluate(node.object, env);
|
|
915
|
-
const start = node.start ? await this.evaluate(node.start, env) : 0;
|
|
916
|
-
const end = node.end ? await this.evaluate(node.end, env) : arr.length;
|
|
917
920
|
|
|
918
921
|
if (!Array.isArray(arr)) {
|
|
919
922
|
throw new RuntimeError(
|
|
@@ -924,10 +927,37 @@ async evalSlice(node, env) {
|
|
|
924
927
|
);
|
|
925
928
|
}
|
|
926
929
|
|
|
927
|
-
|
|
928
|
-
|
|
930
|
+
let start = node.start ? await this.evaluate(node.start, env) : 0;
|
|
931
|
+
let end = node.end ? await this.evaluate(node.end, env) : arr.length;
|
|
932
|
+
let step = node.step ? await this.evaluate(node.step, env) : 1;
|
|
933
|
+
|
|
934
|
+
if (step === 0) {
|
|
935
|
+
throw new RuntimeError(
|
|
936
|
+
'Slice step cannot be zero',
|
|
937
|
+
node,
|
|
938
|
+
this.source,
|
|
939
|
+
env
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
start = start < 0 ? arr.length + start : start;
|
|
943
|
+
end = end < 0 ? arr.length + end : end;
|
|
944
|
+
|
|
945
|
+
start = Math.min(Math.max(start, 0), arr.length);
|
|
946
|
+
end = Math.min(Math.max(end, 0), arr.length);
|
|
947
|
+
|
|
948
|
+
const result = [];
|
|
949
|
+
|
|
950
|
+
if (step > 0) {
|
|
951
|
+
for (let i = start; i < end; i += step) {
|
|
952
|
+
result.push(arr[i]);
|
|
953
|
+
}
|
|
954
|
+
} else {
|
|
955
|
+
for (let i = start; i > end; i += step) {
|
|
956
|
+
result.push(arr[i]);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
929
959
|
|
|
930
|
-
return
|
|
960
|
+
return result;
|
|
931
961
|
|
|
932
962
|
} catch (err) {
|
|
933
963
|
if (err instanceof RuntimeError) throw err;
|
|
@@ -940,6 +970,7 @@ async evalSlice(node, env) {
|
|
|
940
970
|
}
|
|
941
971
|
}
|
|
942
972
|
|
|
973
|
+
|
|
943
974
|
async evalStartStatement(node, env) {
|
|
944
975
|
try {
|
|
945
976
|
const value = await this.evaluate(node.discriminant, env);
|
|
@@ -1464,12 +1495,49 @@ async evalLogical(node, env) {
|
|
|
1464
1495
|
|
|
1465
1496
|
async evalUnary(node, env) {
|
|
1466
1497
|
try {
|
|
1498
|
+
// Handle prefix update operators
|
|
1499
|
+
if (node.type === 'UpdateExpression' && node.prefix) {
|
|
1500
|
+
if (node.argument.type !== 'Identifier') {
|
|
1501
|
+
throw new RuntimeError(
|
|
1502
|
+
`Invalid operand for update operator: ${node.operator}`,
|
|
1503
|
+
node,
|
|
1504
|
+
this.source,
|
|
1505
|
+
env
|
|
1506
|
+
);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
const varName = node.argument.name;
|
|
1510
|
+
let currentVal = await env.get(varName); // assuming env.get returns the variable value
|
|
1511
|
+
|
|
1512
|
+
switch (node.operator) {
|
|
1513
|
+
case 'PLUSPLUS':
|
|
1514
|
+
currentVal++;
|
|
1515
|
+
await env.set(varName, currentVal);
|
|
1516
|
+
return currentVal;
|
|
1517
|
+
case 'MINUSMINUS':
|
|
1518
|
+
currentVal--;
|
|
1519
|
+
await env.set(varName, currentVal);
|
|
1520
|
+
return currentVal;
|
|
1521
|
+
default:
|
|
1522
|
+
throw new RuntimeError(
|
|
1523
|
+
`Unknown update operator: ${node.operator}`,
|
|
1524
|
+
node,
|
|
1525
|
+
this.source,
|
|
1526
|
+
env
|
|
1527
|
+
);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// Evaluate the argument first
|
|
1467
1532
|
const val = await this.evaluate(node.argument, env);
|
|
1468
1533
|
|
|
1469
1534
|
switch (node.operator) {
|
|
1470
|
-
case 'NOT':
|
|
1471
|
-
|
|
1472
|
-
case '
|
|
1535
|
+
case 'NOT':
|
|
1536
|
+
return !val;
|
|
1537
|
+
case 'MINUS':
|
|
1538
|
+
return -val;
|
|
1539
|
+
case 'PLUS':
|
|
1540
|
+
return +val;
|
|
1473
1541
|
default:
|
|
1474
1542
|
throw new RuntimeError(
|
|
1475
1543
|
`Unknown unary operator: ${node.operator}`,
|
|
@@ -1478,7 +1546,6 @@ async evalUnary(node, env) {
|
|
|
1478
1546
|
env
|
|
1479
1547
|
);
|
|
1480
1548
|
}
|
|
1481
|
-
|
|
1482
1549
|
} catch (e) {
|
|
1483
1550
|
if (e instanceof RuntimeError) throw e;
|
|
1484
1551
|
throw new RuntimeError(
|
|
@@ -1490,6 +1557,51 @@ async evalUnary(node, env) {
|
|
|
1490
1557
|
}
|
|
1491
1558
|
}
|
|
1492
1559
|
|
|
1560
|
+
// Handle postfix updates (x++ or x--)
|
|
1561
|
+
async evalPostfixUpdate(node, env) {
|
|
1562
|
+
if (node.type !== 'UpdateExpression' || node.prefix) {
|
|
1563
|
+
throw new RuntimeError(
|
|
1564
|
+
`Invalid postfix update node`,
|
|
1565
|
+
node,
|
|
1566
|
+
this.source,
|
|
1567
|
+
env
|
|
1568
|
+
);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
if (node.argument.type !== 'Identifier') {
|
|
1572
|
+
throw new RuntimeError(
|
|
1573
|
+
`Invalid operand for postfix update operator: ${node.operator}`,
|
|
1574
|
+
node,
|
|
1575
|
+
this.source,
|
|
1576
|
+
env
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
const varName = node.argument.name;
|
|
1581
|
+
let currentVal = await env.get(varName);
|
|
1582
|
+
|
|
1583
|
+
let returnVal = currentVal;
|
|
1584
|
+
|
|
1585
|
+
switch (node.operator) {
|
|
1586
|
+
case 'PLUSPLUS':
|
|
1587
|
+
currentVal++;
|
|
1588
|
+
await env.set(varName, currentVal);
|
|
1589
|
+
return returnVal; // return original value
|
|
1590
|
+
case 'MINUSMINUS':
|
|
1591
|
+
currentVal--;
|
|
1592
|
+
await env.set(varName, currentVal);
|
|
1593
|
+
return returnVal; // return original value
|
|
1594
|
+
default:
|
|
1595
|
+
throw new RuntimeError(
|
|
1596
|
+
`Unknown postfix operator: ${node.operator}`,
|
|
1597
|
+
node,
|
|
1598
|
+
this.source,
|
|
1599
|
+
env
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
|
|
1493
1605
|
async evalIf(node, env) {
|
|
1494
1606
|
let test = await this.evaluate(node.test, env);
|
|
1495
1607
|
test = !!test; // coerce to boolean
|
|
@@ -1768,7 +1880,8 @@ async evalIndex(node, env) {
|
|
|
1768
1880
|
env
|
|
1769
1881
|
);
|
|
1770
1882
|
}
|
|
1771
|
-
}
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1772
1885
|
async evalObject(node, env) {
|
|
1773
1886
|
try {
|
|
1774
1887
|
const out = {};
|
package/src/parser.js
CHANGED
|
@@ -1286,192 +1286,111 @@ unary() {
|
|
|
1286
1286
|
|
|
1287
1287
|
return this.postfix();
|
|
1288
1288
|
}
|
|
1289
|
+
|
|
1289
1290
|
postfix() {
|
|
1290
1291
|
let node = this.primary();
|
|
1291
1292
|
|
|
1292
1293
|
while (true) {
|
|
1293
1294
|
const t = this.current;
|
|
1294
1295
|
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
let start = null;
|
|
1301
|
-
let end = null;
|
|
1302
|
-
let step = null;
|
|
1303
|
-
|
|
1304
|
-
if (this.current.type === 'COLON') {
|
|
1305
|
-
this.eat('COLON');
|
|
1306
|
-
|
|
1307
|
-
if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
|
|
1308
|
-
end = this.expression();
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
if (this.current.type === 'COLON') {
|
|
1312
|
-
this.eat('COLON');
|
|
1313
|
-
if (this.current.type !== 'RBRACKET') {
|
|
1314
|
-
step = this.expression();
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
if (this.current.type !== 'RBRACKET') {
|
|
1319
|
-
throw new ParseError(
|
|
1320
|
-
"Expected ']' after slice",
|
|
1321
|
-
this.current,
|
|
1322
|
-
this.source
|
|
1323
|
-
);
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
this.eat('RBRACKET');
|
|
1327
|
-
|
|
1328
|
-
node = {
|
|
1329
|
-
type: 'SliceExpression',
|
|
1330
|
-
object: node,
|
|
1331
|
-
start,
|
|
1332
|
-
end,
|
|
1333
|
-
step,
|
|
1334
|
-
line: startLine,
|
|
1335
|
-
column: startCol
|
|
1336
|
-
};
|
|
1337
|
-
|
|
1338
|
-
} else {
|
|
1339
|
-
start = this.expression();
|
|
1340
|
-
|
|
1341
|
-
if (this.current.type === 'COLON') {
|
|
1342
|
-
this.eat('COLON');
|
|
1296
|
+
// Handle indexing / slicing
|
|
1297
|
+
if (t.type === 'LBRACKET') {
|
|
1298
|
+
const startLine = t.line;
|
|
1299
|
+
const startCol = t.column;
|
|
1300
|
+
this.eat('LBRACKET');
|
|
1343
1301
|
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1302
|
+
let start = null;
|
|
1303
|
+
let end = null;
|
|
1304
|
+
let step = null;
|
|
1347
1305
|
|
|
1306
|
+
// Slice: [:end:step] or [start:end:step]
|
|
1348
1307
|
if (this.current.type === 'COLON') {
|
|
1349
1308
|
this.eat('COLON');
|
|
1309
|
+
if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
|
|
1310
|
+
end = this.expression();
|
|
1311
|
+
}
|
|
1312
|
+
if (this.current.type === 'COLON') {
|
|
1313
|
+
this.eat('COLON');
|
|
1314
|
+
if (this.current.type !== 'RBRACKET') {
|
|
1315
|
+
step = this.expression();
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1350
1318
|
if (this.current.type !== 'RBRACKET') {
|
|
1351
|
-
|
|
1319
|
+
throw new ParseError("Expected ']' after slice", this.current, this.source);
|
|
1352
1320
|
}
|
|
1353
|
-
|
|
1321
|
+
this.eat('RBRACKET');
|
|
1322
|
+
|
|
1323
|
+
node = { type: 'SliceExpression', object: node, start, end, step, line: startLine, column: startCol };
|
|
1324
|
+
} else {
|
|
1325
|
+
// Normal index or slice starting with an expression
|
|
1326
|
+
start = this.expression();
|
|
1327
|
+
if (this.current.type === 'COLON') {
|
|
1328
|
+
this.eat('COLON');
|
|
1329
|
+
if (this.current.type !== 'RBRACKET' && this.current.type !== 'COLON') {
|
|
1330
|
+
end = this.expression();
|
|
1331
|
+
}
|
|
1332
|
+
if (this.current.type === 'COLON') {
|
|
1333
|
+
this.eat('COLON');
|
|
1334
|
+
if (this.current.type !== 'RBRACKET') {
|
|
1335
|
+
step = this.expression();
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
if (this.current.type !== 'RBRACKET') {
|
|
1339
|
+
throw new ParseError("Expected ']' after slice", this.current, this.source);
|
|
1340
|
+
}
|
|
1341
|
+
this.eat('RBRACKET');
|
|
1354
1342
|
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
);
|
|
1343
|
+
node = { type: 'SliceExpression', object: node, start, end, step, line: startLine, column: startCol };
|
|
1344
|
+
} else {
|
|
1345
|
+
this.eat('RBRACKET');
|
|
1346
|
+
node = { type: 'IndexExpression', object: node, indexer: start, line: startLine, column: startCol };
|
|
1347
|
+
}
|
|
1361
1348
|
}
|
|
1362
|
-
|
|
1363
|
-
this.eat('RBRACKET');
|
|
1364
|
-
|
|
1365
|
-
node = {
|
|
1366
|
-
type: 'SliceExpression',
|
|
1367
|
-
object: node,
|
|
1368
|
-
start,
|
|
1369
|
-
end,
|
|
1370
|
-
step,
|
|
1371
|
-
line: startLine,
|
|
1372
|
-
column: startCol
|
|
1373
|
-
};
|
|
1374
|
-
|
|
1375
|
-
} else {
|
|
1376
|
-
this.eat('RBRACKET');
|
|
1377
|
-
node = {
|
|
1378
|
-
type: 'IndexExpression',
|
|
1379
|
-
object: node,
|
|
1380
|
-
indexer: start,
|
|
1381
|
-
line: startLine,
|
|
1382
|
-
column: startCol
|
|
1383
|
-
};
|
|
1349
|
+
continue; // continue to allow . or () after []
|
|
1384
1350
|
}
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
1351
|
|
|
1352
|
+
// Handle function call
|
|
1390
1353
|
if (t.type === 'LPAREN') {
|
|
1391
1354
|
const startLine = t.line;
|
|
1392
1355
|
const startCol = t.column;
|
|
1393
1356
|
this.eat('LPAREN');
|
|
1394
1357
|
|
|
1395
1358
|
const args = [];
|
|
1396
|
-
|
|
1397
1359
|
while (this.current.type !== 'RPAREN') {
|
|
1398
1360
|
if (this.current.type === 'EOF') {
|
|
1399
|
-
throw new ParseError(
|
|
1400
|
-
"Unterminated function call",
|
|
1401
|
-
this.current,
|
|
1402
|
-
this.source,
|
|
1403
|
-
"Did you forget to close ')'?"
|
|
1404
|
-
);
|
|
1361
|
+
throw new ParseError("Unterminated function call", this.current, this.source, "Did you forget to close ')'?");
|
|
1405
1362
|
}
|
|
1406
|
-
|
|
1407
1363
|
args.push(this.expression());
|
|
1408
|
-
|
|
1409
|
-
if (this.current.type === 'COMMA') {
|
|
1410
|
-
this.eat('COMMA');
|
|
1411
|
-
} else {
|
|
1412
|
-
break;
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
if (this.current.type !== 'RPAREN') {
|
|
1417
|
-
throw new ParseError(
|
|
1418
|
-
"Expected ')' after function arguments",
|
|
1419
|
-
this.current,
|
|
1420
|
-
this.source,
|
|
1421
|
-
"Function calls must be closed with ')'"
|
|
1422
|
-
);
|
|
1364
|
+
if (this.current.type === 'COMMA') this.eat('COMMA');
|
|
1423
1365
|
}
|
|
1424
1366
|
|
|
1425
1367
|
this.eat('RPAREN');
|
|
1426
1368
|
|
|
1427
|
-
node = {
|
|
1428
|
-
type: 'CallExpression',
|
|
1429
|
-
callee: node,
|
|
1430
|
-
arguments: args,
|
|
1431
|
-
line: startLine,
|
|
1432
|
-
column: startCol
|
|
1433
|
-
};
|
|
1369
|
+
node = { type: 'CallExpression', callee: node, arguments: args, line: startLine, column: startCol };
|
|
1434
1370
|
continue;
|
|
1435
1371
|
}
|
|
1436
1372
|
|
|
1373
|
+
// Handle member access
|
|
1437
1374
|
if (t.type === 'DOT') {
|
|
1438
1375
|
const startLine = t.line;
|
|
1439
1376
|
const startCol = t.column;
|
|
1440
1377
|
this.eat('DOT');
|
|
1441
1378
|
|
|
1442
1379
|
if (this.current.type !== 'IDENTIFIER') {
|
|
1443
|
-
throw new ParseError(
|
|
1444
|
-
"Expected property name after '.'",
|
|
1445
|
-
this.current,
|
|
1446
|
-
this.source,
|
|
1447
|
-
"Member access requires a property name, e.g. obj.value"
|
|
1448
|
-
);
|
|
1380
|
+
throw new ParseError("Expected property name after '.'", this.current, this.source, "Member access requires a property name, e.g. obj.value");
|
|
1449
1381
|
}
|
|
1450
1382
|
|
|
1451
1383
|
const property = this.current.value;
|
|
1452
1384
|
this.eat('IDENTIFIER');
|
|
1453
1385
|
|
|
1454
|
-
node = {
|
|
1455
|
-
type: 'MemberExpression',
|
|
1456
|
-
object: node,
|
|
1457
|
-
property,
|
|
1458
|
-
line: startLine,
|
|
1459
|
-
column: startCol
|
|
1460
|
-
};
|
|
1386
|
+
node = { type: 'MemberExpression', object: node, property, line: startLine, column: startCol };
|
|
1461
1387
|
continue;
|
|
1462
1388
|
}
|
|
1463
1389
|
|
|
1390
|
+
// Handle postfix update operators
|
|
1464
1391
|
if (t.type === 'PLUSPLUS' || t.type === 'MINUSMINUS') {
|
|
1465
1392
|
this.eat(t.type);
|
|
1466
|
-
|
|
1467
|
-
node = {
|
|
1468
|
-
type: 'UpdateExpression',
|
|
1469
|
-
operator: t.type,
|
|
1470
|
-
argument: node,
|
|
1471
|
-
prefix: false,
|
|
1472
|
-
line: t.line,
|
|
1473
|
-
column: t.column
|
|
1474
|
-
};
|
|
1393
|
+
node = { type: 'UpdateExpression', operator: t.type, argument: node, prefix: false, line: t.line, column: t.column };
|
|
1475
1394
|
continue;
|
|
1476
1395
|
}
|
|
1477
1396
|
|
|
@@ -1481,6 +1400,7 @@ postfix() {
|
|
|
1481
1400
|
return node;
|
|
1482
1401
|
}
|
|
1483
1402
|
|
|
1403
|
+
|
|
1484
1404
|
arrowFunction(params) {
|
|
1485
1405
|
const t = this.current;
|
|
1486
1406
|
this.eat('ARROW');
|
|
@@ -1831,5 +1751,4 @@ if (t.type === 'FUNC') {
|
|
|
1831
1751
|
}
|
|
1832
1752
|
|
|
1833
1753
|
}
|
|
1834
|
-
|
|
1835
|
-
module.exports = Parser;
|
|
1754
|
+
module.exports = Parser;
|
package/src/starlight.js
CHANGED
|
@@ -12,7 +12,7 @@ const Lexer = require('./lexer');
|
|
|
12
12
|
const Parser = require('./parser');
|
|
13
13
|
const Evaluator = require('./evaluator');
|
|
14
14
|
|
|
15
|
-
const VERSION = '1.1.
|
|
15
|
+
const VERSION = '1.1.18';
|
|
16
16
|
|
|
17
17
|
const COLOR = {
|
|
18
18
|
reset: '\x1b[0m',
|
|
@@ -172,7 +172,7 @@ Usage:
|
|
|
172
172
|
|
|
173
173
|
if (args[0] === '-v' || args[0] === '--version') {
|
|
174
174
|
console.log(`${COLOR.bold}Starlight CLI v${VERSION}${COLOR.reset}`);
|
|
175
|
-
console.log(`${COLOR.magenta}Developed by Macedon${COLOR.reset}`);
|
|
175
|
+
console.log(`${COLOR.magenta}Developed by Dominex Macedon${COLOR.reset}`);
|
|
176
176
|
process.exit(0);
|
|
177
177
|
}
|
|
178
178
|
|