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 +261 -9
- package/package.json +1 -1
- package/src/evaluator.js +155 -8
- package/src/lexer.js +1 -1
- package/src/parser.js +105 -0
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 =
|
|
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(
|
|
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
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 =
|
|
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(
|
|
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 };
|