starlight-cli 1.1.20 → 1.1.21

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
@@ -10356,9 +10356,42 @@ async callFunction(fn, args, env, node = null) {
10356
10356
 
10357
10357
  throw new RuntimeError('Value is not callable', node, this.source);
10358
10358
  }
10359
+ createBaseProxy(instance, parentEntity) {
10360
+ const evaluator = this;
10361
+
10362
+ return new Proxy({}, {
10363
+ get(_, prop) {
10364
+ const method = parentEntity.methods[prop];
10365
+
10366
+ if (!method) {
10367
+ throw new RuntimeError(
10368
+ `Base method '${prop}' not found`,
10369
+ null,
10370
+ evaluator.source
10371
+ );
10372
+ }
10373
+
10374
+ return async (...args) => {
10375
+ const methodEnv = new Environment(parentEntity.env);
10359
10376
 
10377
+ methodEnv.define('self', instance);
10360
10378
 
10379
+ if (parentEntity.parent) {
10380
+ methodEnv.define(
10381
+ 'base',
10382
+ evaluator.createBaseProxy(instance, parentEntity.parent)
10383
+ );
10384
+ }
10361
10385
 
10386
+ for (let i = 0; i < method.params.length; i++) {
10387
+ methodEnv.define(method.params[i], args[i]);
10388
+ }
10389
+
10390
+ return await evaluator.evaluate(method.body, methodEnv);
10391
+ };
10392
+ }
10393
+ });
10394
+ }
10362
10395
  formatValue(value, seen = new Set()) {
10363
10396
  const color = __nccwpck_require__(55);
10364
10397
 
@@ -11028,6 +11061,19 @@ case 'SliceExpression':
11028
11061
  case 'Identifier': return env.get(node.name, node, this.source);
11029
11062
  case 'IfStatement': return await this.evalIf(node, env);
11030
11063
  case 'WhileStatement': return await this.evalWhile(node, env);
11064
+ case 'EntityDeclaration':
11065
+ return await this.evalEntity(node, env);
11066
+
11067
+ case 'EntityInstantiation':
11068
+ return await this.evalEntityNew(node, env);
11069
+
11070
+ case 'SelfExpression':
11071
+ return env.get('self', node, this.source);
11072
+
11073
+ case 'InitMethod':
11074
+ return node;
11075
+ case 'InheritsClause':
11076
+ return await this.evalInherits(node, env);
11031
11077
  case 'ForStatement':
11032
11078
  case 'ForInStatement':
11033
11079
  return await this.evalFor(node, env);
@@ -11065,6 +11111,70 @@ case 'RaceClause':
11065
11111
  case 'NewExpression': {
11066
11112
  const callee = await this.evaluate(node.callee, env);
11067
11113
 
11114
+ const args = [];
11115
+ for (const a of node.arguments) {
11116
+ args.push(await this.evaluate(a, env));
11117
+ }
11118
+
11119
+ if (callee && callee.methods) {
11120
+ const instance = {};
11121
+
11122
+
11123
+
11124
+ const evaluator = this;
11125
+
11126
+ const entityEnv = new Environment(callee.env);
11127
+ entityEnv.define('self', instance);
11128
+
11129
+ if (callee.parent) {
11130
+ entityEnv.define(
11131
+ 'base',
11132
+ this.createBaseProxy(instance, callee.parent)
11133
+ );
11134
+ }
11135
+
11136
+ if (callee.methods.init) {
11137
+ const init = callee.methods.init;
11138
+
11139
+ for (let i = 0; i < init.params.length; i++) {
11140
+ entityEnv.define(init.params[i], args[i]);
11141
+ }
11142
+
11143
+ await evaluator.evaluate(init.body, entityEnv);
11144
+ }
11145
+
11146
+ for (const key in callee.methods) {
11147
+ if (key === 'init') continue;
11148
+
11149
+ instance[key] = async (...callArgs) => {
11150
+ const methodEnv = new Environment(callee.env);
11151
+
11152
+ methodEnv.define('self', instance);
11153
+
11154
+ if (callee.parent) {
11155
+ methodEnv.define(
11156
+ 'base',
11157
+ evaluator.createBaseProxy(instance, callee.parent)
11158
+ );
11159
+ }
11160
+
11161
+ for (let i = 0; i < callee.methods[key].params.length; i++) {
11162
+ methodEnv.define(
11163
+ callee.methods[key].params[i],
11164
+ callArgs[i]
11165
+ );
11166
+ }
11167
+
11168
+ return await evaluator.evaluate(
11169
+ callee.methods[key].body,
11170
+ methodEnv
11171
+ );
11172
+ };
11173
+ }
11174
+
11175
+ return instance;
11176
+ }
11177
+
11068
11178
  if (typeof callee === 'object' && callee.body) {
11069
11179
  const evaluator = this;
11070
11180
 
@@ -11074,7 +11184,9 @@ case 'RaceClause':
11074
11184
 
11075
11185
  for (let i = 0; i < callee.params.length; i++) {
11076
11186
  const param = callee.params[i];
11077
- const paramName = typeof param === 'string' ? param : param.name;
11187
+ const paramName =
11188
+ typeof param === 'string' ? param : param.name;
11189
+
11078
11190
  newEnv.define(paramName, args[i]);
11079
11191
  }
11080
11192
 
@@ -11084,18 +11196,17 @@ case 'RaceClause':
11084
11196
  })();
11085
11197
  };
11086
11198
 
11087
- const args = [];
11088
- for (const a of node.arguments) args.push(await this.evaluate(a, env));
11089
11199
  return await new Constructor(...args);
11090
11200
  }
11091
-
11092
- // native JS constructor fallback
11201
+
11093
11202
  if (typeof callee !== 'function') {
11094
- throw new RuntimeError('NewExpression callee is not a function', node, this.source);
11203
+ throw new RuntimeError(
11204
+ 'NewExpression callee is not a function',
11205
+ node,
11206
+ this.source
11207
+ );
11095
11208
  }
11096
11209
 
11097
- const args = [];
11098
- for (const a of node.arguments) args.push(await this.evaluate(a, env));
11099
11210
  return new callee(...args);
11100
11211
  }
11101
11212
 
@@ -11104,7 +11215,20 @@ case 'RaceClause':
11104
11215
 
11105
11216
  }
11106
11217
  }
11218
+ async evalInherits(node, env) {
11219
+ const parent = await this.evaluate(node.parent, env);
11107
11220
 
11221
+ if (!parent || !parent.methods) {
11222
+ throw new RuntimeError(
11223
+ 'Invalid parent entity',
11224
+ node,
11225
+ this.source,
11226
+ env
11227
+ );
11228
+ }
11229
+
11230
+ return parent;
11231
+ }
11108
11232
  async evalProgram(node, env) {
11109
11233
  let result = null;
11110
11234
  for (const stmt of node.body) {
@@ -11126,6 +11250,29 @@ async evalProgram(node, env) {
11126
11250
  }
11127
11251
  return result;
11128
11252
  }
11253
+ async evalEntity(node, env) {
11254
+ const entity = {
11255
+ name: node.name,
11256
+ methods: {},
11257
+ parent: null,
11258
+ env
11259
+ };
11260
+
11261
+ if (node.inherits) {
11262
+ entity.parent = await this.evaluate(node.inherits, env);
11263
+ }
11264
+
11265
+ for (const method of node.body) {
11266
+ entity.methods[method.name] = {
11267
+ params: method.params,
11268
+ body: method.body,
11269
+ env
11270
+ };
11271
+ }
11272
+
11273
+ env.define(node.name, entity);
11274
+ return entity;
11275
+ }
11129
11276
  async evalSlice(node, env) {
11130
11277
  try {
11131
11278
  const arr = await this.evaluate(node.object, env);
@@ -12242,7 +12389,7 @@ class Lexer {
12242
12389
  'break', 'continue', 'func', 'return',
12243
12390
  'true', 'false', 'null',
12244
12391
  'ask', 'define', 'import', 'from', 'as',
12245
- 'async', 'await', 'new', 'in', 'do', 'track', 'start', 'race', 'not', 'and', 'or'
12392
+ 'async', 'await', 'new', 'in', 'do', 'track', 'start', 'race', 'not', 'and', 'or', 'entity', 'self', 'inherits', 'init'
12246
12393
  ];
12247
12394
  }
12248
12395
 
@@ -12536,6 +12683,7 @@ class Parser {
12536
12683
  case 'IF': return this.ifStatement();
12537
12684
  case 'WHILE': return this.whileStatement();
12538
12685
  case 'FOR': return this.forStatement();
12686
+ case 'ENTITY': return this.entityDeclaration();
12539
12687
  case 'DO': return this.doTrackStatement();
12540
12688
  case 'START': return this.startStatement();
12541
12689
  case 'BREAK': return this.breakStatement();
@@ -12603,7 +12751,103 @@ varDeclaration() {
12603
12751
  column: t.column
12604
12752
  };
12605
12753
  }
12754
+ entityDeclaration() {
12755
+ const t = this.current;
12756
+ this.eat('ENTITY');
12757
+
12758
+ if (this.current.type !== 'IDENTIFIER') {
12759
+ throw new ParseError(
12760
+ "Expected entity name",
12761
+ this.current,
12762
+ this.source,
12763
+ "Use: entity Car { ... }"
12764
+ );
12765
+ }
12766
+
12767
+ const name = this.current.value;
12768
+ this.eat('IDENTIFIER');
12769
+
12770
+ let parent = null;
12771
+
12772
+ if (this.current.type === 'INHERITS') {
12773
+ this.eat('INHERITS');
12774
+
12775
+ if (this.current.type !== 'IDENTIFIER') {
12776
+ throw new ParseError(
12777
+ "Expected parent entity name",
12778
+ this.current,
12779
+ this.source
12780
+ );
12781
+ }
12782
+
12783
+ parent = {
12784
+ type: 'Identifier',
12785
+ name: this.current.value
12786
+ };
12787
+
12788
+ this.eat('IDENTIFIER');
12789
+ }
12790
+
12791
+ this.eat('LBRACE');
12792
+
12793
+ const body = [];
12794
+
12795
+ while (this.current.type !== 'RBRACE') {
12796
+ body.push(this.methodDefinition());
12797
+ }
12798
+
12799
+ this.eat('RBRACE');
12800
+
12801
+ return {
12802
+ type: 'EntityDeclaration',
12803
+ name,
12804
+ parent,
12805
+ body,
12806
+ line: t.line,
12807
+ column: t.column
12808
+ };
12809
+ }
12810
+ methodDefinition() {
12811
+ if (this.current.type !== 'IDENTIFIER' && this.current.type !== 'INIT') {
12812
+ throw new ParseError(
12813
+ "Expected method name",
12814
+ this.current,
12815
+ this.source
12816
+ );
12817
+ }
12818
+
12819
+ const isInit = this.current.type === 'INIT';
12820
+ const name = isInit ? 'init' : this.current.value;
12821
+
12822
+ this.eat(this.current.type);
12606
12823
 
12824
+ this.eat('LPAREN');
12825
+
12826
+ const params = [];
12827
+
12828
+ if (this.current.type !== 'RPAREN') {
12829
+ params.push(this.current.value);
12830
+ this.eat('IDENTIFIER');
12831
+
12832
+ while (this.current.type === 'COMMA') {
12833
+ this.eat('COMMA');
12834
+ params.push(this.current.value);
12835
+ this.eat('IDENTIFIER');
12836
+ }
12837
+ }
12838
+
12839
+ this.eat('RPAREN');
12840
+
12841
+ const body = this.block();
12842
+
12843
+ return {
12844
+ type: 'MethodDefinition',
12845
+ name,
12846
+ params,
12847
+ body,
12848
+ isInit
12849
+ };
12850
+ }
12607
12851
  startStatement() {
12608
12852
  const t = this.current;
12609
12853
  this.eat('START');
@@ -13886,7 +14130,15 @@ arrowFunction(params) {
13886
14130
 
13887
14131
  primary() {
13888
14132
  const t = this.current;
14133
+ if (t.type === 'BASE') {
14134
+ this.eat('BASE');
14135
+ return { type: 'Identifier', name: 'base', line: t.line, column: t.column };
14136
+ }
13889
14137
 
14138
+ if (t.type === 'SELF') {
14139
+ this.eat('SELF');
14140
+ return { type: 'Identifier', name: 'self', line: t.line, column: t.column };
14141
+ }
13890
14142
  if (t.type === 'NUMBER') {
13891
14143
  this.eat('NUMBER');
13892
14144
  return { type: 'Literal', value: t.value, line: t.line, column: t.column };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starlight-cli",
3
- "version": "1.1.20",
3
+ "version": "1.1.21",
4
4
  "description": "Starlight Programming Language CLI",
5
5
  "bin": {
6
6
  "starlight": "index.js"
package/src/evaluator.js CHANGED
@@ -146,9 +146,42 @@ async callFunction(fn, args, env, node = null) {
146
146
 
147
147
  throw new RuntimeError('Value is not callable', node, this.source);
148
148
  }
149
+ createBaseProxy(instance, parentEntity) {
150
+ const evaluator = this;
149
151
 
152
+ return new Proxy({}, {
153
+ get(_, prop) {
154
+ const method = parentEntity.methods[prop];
150
155
 
156
+ if (!method) {
157
+ throw new RuntimeError(
158
+ `Base method '${prop}' not found`,
159
+ null,
160
+ evaluator.source
161
+ );
162
+ }
163
+
164
+ return async (...args) => {
165
+ const methodEnv = new Environment(parentEntity.env);
151
166
 
167
+ methodEnv.define('self', instance);
168
+
169
+ if (parentEntity.parent) {
170
+ methodEnv.define(
171
+ 'base',
172
+ evaluator.createBaseProxy(instance, parentEntity.parent)
173
+ );
174
+ }
175
+
176
+ for (let i = 0; i < method.params.length; i++) {
177
+ methodEnv.define(method.params[i], args[i]);
178
+ }
179
+
180
+ return await evaluator.evaluate(method.body, methodEnv);
181
+ };
182
+ }
183
+ });
184
+ }
152
185
  formatValue(value, seen = new Set()) {
153
186
  const color = require('starlight-color');
154
187
 
@@ -818,6 +851,19 @@ case 'SliceExpression':
818
851
  case 'Identifier': return env.get(node.name, node, this.source);
819
852
  case 'IfStatement': return await this.evalIf(node, env);
820
853
  case 'WhileStatement': return await this.evalWhile(node, env);
854
+ case 'EntityDeclaration':
855
+ return await this.evalEntity(node, env);
856
+
857
+ case 'EntityInstantiation':
858
+ return await this.evalEntityNew(node, env);
859
+
860
+ case 'SelfExpression':
861
+ return env.get('self', node, this.source);
862
+
863
+ case 'InitMethod':
864
+ return node;
865
+ case 'InheritsClause':
866
+ return await this.evalInherits(node, env);
821
867
  case 'ForStatement':
822
868
  case 'ForInStatement':
823
869
  return await this.evalFor(node, env);
@@ -855,6 +901,70 @@ case 'RaceClause':
855
901
  case 'NewExpression': {
856
902
  const callee = await this.evaluate(node.callee, env);
857
903
 
904
+ const args = [];
905
+ for (const a of node.arguments) {
906
+ args.push(await this.evaluate(a, env));
907
+ }
908
+
909
+ if (callee && callee.methods) {
910
+ const instance = {};
911
+
912
+
913
+
914
+ const evaluator = this;
915
+
916
+ const entityEnv = new Environment(callee.env);
917
+ entityEnv.define('self', instance);
918
+
919
+ if (callee.parent) {
920
+ entityEnv.define(
921
+ 'base',
922
+ this.createBaseProxy(instance, callee.parent)
923
+ );
924
+ }
925
+
926
+ if (callee.methods.init) {
927
+ const init = callee.methods.init;
928
+
929
+ for (let i = 0; i < init.params.length; i++) {
930
+ entityEnv.define(init.params[i], args[i]);
931
+ }
932
+
933
+ await evaluator.evaluate(init.body, entityEnv);
934
+ }
935
+
936
+ for (const key in callee.methods) {
937
+ if (key === 'init') continue;
938
+
939
+ instance[key] = async (...callArgs) => {
940
+ const methodEnv = new Environment(callee.env);
941
+
942
+ methodEnv.define('self', instance);
943
+
944
+ if (callee.parent) {
945
+ methodEnv.define(
946
+ 'base',
947
+ evaluator.createBaseProxy(instance, callee.parent)
948
+ );
949
+ }
950
+
951
+ for (let i = 0; i < callee.methods[key].params.length; i++) {
952
+ methodEnv.define(
953
+ callee.methods[key].params[i],
954
+ callArgs[i]
955
+ );
956
+ }
957
+
958
+ return await evaluator.evaluate(
959
+ callee.methods[key].body,
960
+ methodEnv
961
+ );
962
+ };
963
+ }
964
+
965
+ return instance;
966
+ }
967
+
858
968
  if (typeof callee === 'object' && callee.body) {
859
969
  const evaluator = this;
860
970
 
@@ -864,7 +974,9 @@ case 'RaceClause':
864
974
 
865
975
  for (let i = 0; i < callee.params.length; i++) {
866
976
  const param = callee.params[i];
867
- const paramName = typeof param === 'string' ? param : param.name;
977
+ const paramName =
978
+ typeof param === 'string' ? param : param.name;
979
+
868
980
  newEnv.define(paramName, args[i]);
869
981
  }
870
982
 
@@ -874,18 +986,17 @@ case 'RaceClause':
874
986
  })();
875
987
  };
876
988
 
877
- const args = [];
878
- for (const a of node.arguments) args.push(await this.evaluate(a, env));
879
989
  return await new Constructor(...args);
880
990
  }
881
-
882
- // native JS constructor fallback
991
+
883
992
  if (typeof callee !== 'function') {
884
- throw new RuntimeError('NewExpression callee is not a function', node, this.source);
993
+ throw new RuntimeError(
994
+ 'NewExpression callee is not a function',
995
+ node,
996
+ this.source
997
+ );
885
998
  }
886
999
 
887
- const args = [];
888
- for (const a of node.arguments) args.push(await this.evaluate(a, env));
889
1000
  return new callee(...args);
890
1001
  }
891
1002
 
@@ -894,7 +1005,20 @@ case 'RaceClause':
894
1005
 
895
1006
  }
896
1007
  }
1008
+ async evalInherits(node, env) {
1009
+ const parent = await this.evaluate(node.parent, env);
897
1010
 
1011
+ if (!parent || !parent.methods) {
1012
+ throw new RuntimeError(
1013
+ 'Invalid parent entity',
1014
+ node,
1015
+ this.source,
1016
+ env
1017
+ );
1018
+ }
1019
+
1020
+ return parent;
1021
+ }
898
1022
  async evalProgram(node, env) {
899
1023
  let result = null;
900
1024
  for (const stmt of node.body) {
@@ -916,6 +1040,29 @@ async evalProgram(node, env) {
916
1040
  }
917
1041
  return result;
918
1042
  }
1043
+ async evalEntity(node, env) {
1044
+ const entity = {
1045
+ name: node.name,
1046
+ methods: {},
1047
+ parent: null,
1048
+ env
1049
+ };
1050
+
1051
+ if (node.inherits) {
1052
+ entity.parent = await this.evaluate(node.inherits, env);
1053
+ }
1054
+
1055
+ for (const method of node.body) {
1056
+ entity.methods[method.name] = {
1057
+ params: method.params,
1058
+ body: method.body,
1059
+ env
1060
+ };
1061
+ }
1062
+
1063
+ env.define(node.name, entity);
1064
+ return entity;
1065
+ }
919
1066
  async evalSlice(node, env) {
920
1067
  try {
921
1068
  const arr = await this.evaluate(node.object, env);
package/src/lexer.js CHANGED
@@ -28,7 +28,7 @@ class Lexer {
28
28
  'break', 'continue', 'func', 'return',
29
29
  'true', 'false', 'null',
30
30
  'ask', 'define', 'import', 'from', 'as',
31
- 'async', 'await', 'new', 'in', 'do', 'track', 'start', 'race', 'not', 'and', 'or'
31
+ 'async', 'await', 'new', 'in', 'do', 'track', 'start', 'race', 'not', 'and', 'or', 'entity', 'self', 'inherits', 'init'
32
32
  ];
33
33
  }
34
34
 
package/src/parser.js CHANGED
@@ -90,6 +90,7 @@ class Parser {
90
90
  case 'IF': return this.ifStatement();
91
91
  case 'WHILE': return this.whileStatement();
92
92
  case 'FOR': return this.forStatement();
93
+ case 'ENTITY': return this.entityDeclaration();
93
94
  case 'DO': return this.doTrackStatement();
94
95
  case 'START': return this.startStatement();
95
96
  case 'BREAK': return this.breakStatement();
@@ -157,7 +158,103 @@ varDeclaration() {
157
158
  column: t.column
158
159
  };
159
160
  }
161
+ entityDeclaration() {
162
+ const t = this.current;
163
+ this.eat('ENTITY');
164
+
165
+ if (this.current.type !== 'IDENTIFIER') {
166
+ throw new ParseError(
167
+ "Expected entity name",
168
+ this.current,
169
+ this.source,
170
+ "Use: entity Car { ... }"
171
+ );
172
+ }
173
+
174
+ const name = this.current.value;
175
+ this.eat('IDENTIFIER');
176
+
177
+ let parent = null;
178
+
179
+ if (this.current.type === 'INHERITS') {
180
+ this.eat('INHERITS');
181
+
182
+ if (this.current.type !== 'IDENTIFIER') {
183
+ throw new ParseError(
184
+ "Expected parent entity name",
185
+ this.current,
186
+ this.source
187
+ );
188
+ }
189
+
190
+ parent = {
191
+ type: 'Identifier',
192
+ name: this.current.value
193
+ };
194
+
195
+ this.eat('IDENTIFIER');
196
+ }
197
+
198
+ this.eat('LBRACE');
199
+
200
+ const body = [];
201
+
202
+ while (this.current.type !== 'RBRACE') {
203
+ body.push(this.methodDefinition());
204
+ }
205
+
206
+ this.eat('RBRACE');
207
+
208
+ return {
209
+ type: 'EntityDeclaration',
210
+ name,
211
+ parent,
212
+ body,
213
+ line: t.line,
214
+ column: t.column
215
+ };
216
+ }
217
+ methodDefinition() {
218
+ if (this.current.type !== 'IDENTIFIER' && this.current.type !== 'INIT') {
219
+ throw new ParseError(
220
+ "Expected method name",
221
+ this.current,
222
+ this.source
223
+ );
224
+ }
225
+
226
+ const isInit = this.current.type === 'INIT';
227
+ const name = isInit ? 'init' : this.current.value;
228
+
229
+ this.eat(this.current.type);
160
230
 
231
+ this.eat('LPAREN');
232
+
233
+ const params = [];
234
+
235
+ if (this.current.type !== 'RPAREN') {
236
+ params.push(this.current.value);
237
+ this.eat('IDENTIFIER');
238
+
239
+ while (this.current.type === 'COMMA') {
240
+ this.eat('COMMA');
241
+ params.push(this.current.value);
242
+ this.eat('IDENTIFIER');
243
+ }
244
+ }
245
+
246
+ this.eat('RPAREN');
247
+
248
+ const body = this.block();
249
+
250
+ return {
251
+ type: 'MethodDefinition',
252
+ name,
253
+ params,
254
+ body,
255
+ isInit
256
+ };
257
+ }
161
258
  startStatement() {
162
259
  const t = this.current;
163
260
  this.eat('START');
@@ -1440,7 +1537,15 @@ arrowFunction(params) {
1440
1537
 
1441
1538
  primary() {
1442
1539
  const t = this.current;
1540
+ if (t.type === 'BASE') {
1541
+ this.eat('BASE');
1542
+ return { type: 'Identifier', name: 'base', line: t.line, column: t.column };
1543
+ }
1443
1544
 
1545
+ if (t.type === 'SELF') {
1546
+ this.eat('SELF');
1547
+ return { type: 'Identifier', name: 'self', line: t.line, column: t.column };
1548
+ }
1444
1549
  if (t.type === 'NUMBER') {
1445
1550
  this.eat('NUMBER');
1446
1551
  return { type: 'Literal', value: t.value, line: t.line, column: t.column };