redscript-mc 1.1.0 → 1.2.0
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/CHANGELOG.md +54 -0
- package/dist/__tests__/cli.test.js +138 -0
- package/dist/__tests__/codegen.test.js +25 -0
- package/dist/__tests__/e2e.test.js +190 -12
- package/dist/__tests__/lexer.test.js +12 -2
- package/dist/__tests__/lowering.test.js +164 -9
- package/dist/__tests__/mc-integration.test.js +145 -51
- package/dist/__tests__/optimizer-advanced.test.js +3 -3
- package/dist/__tests__/parser.test.js +80 -0
- package/dist/__tests__/runtime.test.js +8 -8
- package/dist/__tests__/typechecker.test.js +158 -0
- package/dist/ast/types.d.ts +20 -1
- package/dist/codegen/mcfunction/index.js +30 -1
- package/dist/codegen/structure/index.js +25 -0
- package/dist/compile.d.ts +10 -0
- package/dist/compile.js +36 -5
- package/dist/events/types.d.ts +35 -0
- package/dist/events/types.js +59 -0
- package/dist/index.js +3 -2
- package/dist/ir/types.d.ts +4 -0
- package/dist/lexer/index.d.ts +1 -1
- package/dist/lexer/index.js +2 -0
- package/dist/lowering/index.d.ts +32 -1
- package/dist/lowering/index.js +439 -15
- package/dist/parser/index.d.ts +2 -0
- package/dist/parser/index.js +79 -10
- package/dist/typechecker/index.d.ts +17 -0
- package/dist/typechecker/index.js +343 -17
- package/docs/ENTITY_TYPE_SYSTEM.md +242 -0
- package/editors/vscode/CHANGELOG.md +9 -0
- package/editors/vscode/out/extension.js +1144 -72
- package/editors/vscode/package-lock.json +2 -2
- package/editors/vscode/package.json +1 -1
- package/package.json +1 -1
- package/src/__tests__/cli.test.ts +166 -0
- package/src/__tests__/codegen.test.ts +27 -0
- package/src/__tests__/e2e.test.ts +201 -12
- package/src/__tests__/fixtures/event-test.mcrs +13 -0
- package/src/__tests__/fixtures/impl-test.mcrs +46 -0
- package/src/__tests__/fixtures/interval-test.mcrs +11 -0
- package/src/__tests__/fixtures/is-check-test.mcrs +20 -0
- package/src/__tests__/fixtures/timeout-test.mcrs +7 -0
- package/src/__tests__/lexer.test.ts +14 -2
- package/src/__tests__/lowering.test.ts +178 -9
- package/src/__tests__/mc-integration.test.ts +166 -51
- package/src/__tests__/optimizer-advanced.test.ts +3 -3
- package/src/__tests__/parser.test.ts +91 -5
- package/src/__tests__/runtime.test.ts +8 -8
- package/src/__tests__/typechecker.test.ts +171 -0
- package/src/ast/types.ts +25 -1
- package/src/codegen/mcfunction/index.ts +31 -1
- package/src/codegen/structure/index.ts +27 -0
- package/src/compile.ts +54 -6
- package/src/events/types.ts +69 -0
- package/src/index.ts +4 -3
- package/src/ir/types.ts +4 -0
- package/src/lexer/index.ts +3 -1
- package/src/lowering/index.ts +528 -16
- package/src/parser/index.ts +90 -12
- package/src/stdlib/README.md +34 -4
- package/src/stdlib/tags.mcrs +951 -0
- package/src/stdlib/timer.mcrs +54 -33
- package/src/typechecker/index.ts +404 -18
package/dist/parser/index.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export declare class Parser {
|
|
|
23
23
|
parse(defaultNamespace?: string): Program;
|
|
24
24
|
private parseStructDecl;
|
|
25
25
|
private parseEnumDecl;
|
|
26
|
+
private parseImplBlock;
|
|
26
27
|
private parseConstDecl;
|
|
27
28
|
private parseGlobalDecl;
|
|
28
29
|
private parseFnDecl;
|
|
@@ -49,6 +50,7 @@ export declare class Parser {
|
|
|
49
50
|
private parseAssignment;
|
|
50
51
|
private parseBinaryExpr;
|
|
51
52
|
private parseUnaryExpr;
|
|
53
|
+
private parseEntityTypeName;
|
|
52
54
|
private isSubtraction;
|
|
53
55
|
private parsePostfixExpr;
|
|
54
56
|
private parseArgs;
|
package/dist/parser/index.js
CHANGED
|
@@ -16,11 +16,31 @@ const PRECEDENCE = {
|
|
|
16
16
|
'||': 1,
|
|
17
17
|
'&&': 2,
|
|
18
18
|
'==': 3, '!=': 3,
|
|
19
|
-
'<': 4, '<=': 4, '>': 4, '>=': 4,
|
|
19
|
+
'<': 4, '<=': 4, '>': 4, '>=': 4, 'is': 4,
|
|
20
20
|
'+': 5, '-': 5,
|
|
21
21
|
'*': 6, '/': 6, '%': 6,
|
|
22
22
|
};
|
|
23
|
-
const BINARY_OPS = new Set(['||', '&&', '==', '!=', '<', '<=', '>', '>=', '+', '-', '*', '/', '%']);
|
|
23
|
+
const BINARY_OPS = new Set(['||', '&&', '==', '!=', '<', '<=', '>', '>=', 'is', '+', '-', '*', '/', '%']);
|
|
24
|
+
const ENTITY_TYPE_NAMES = new Set([
|
|
25
|
+
'entity',
|
|
26
|
+
'Player',
|
|
27
|
+
'Mob',
|
|
28
|
+
'HostileMob',
|
|
29
|
+
'PassiveMob',
|
|
30
|
+
'Zombie',
|
|
31
|
+
'Skeleton',
|
|
32
|
+
'Creeper',
|
|
33
|
+
'Spider',
|
|
34
|
+
'Enderman',
|
|
35
|
+
'Pig',
|
|
36
|
+
'Cow',
|
|
37
|
+
'Sheep',
|
|
38
|
+
'Chicken',
|
|
39
|
+
'Villager',
|
|
40
|
+
'ArmorStand',
|
|
41
|
+
'Item',
|
|
42
|
+
'Arrow',
|
|
43
|
+
]);
|
|
24
44
|
function computeIsSingle(raw) {
|
|
25
45
|
if (/^@[spr](\[|$)/.test(raw))
|
|
26
46
|
return true;
|
|
@@ -102,6 +122,7 @@ class Parser {
|
|
|
102
122
|
const globals = [];
|
|
103
123
|
const declarations = [];
|
|
104
124
|
const structs = [];
|
|
125
|
+
const implBlocks = [];
|
|
105
126
|
const enums = [];
|
|
106
127
|
const consts = [];
|
|
107
128
|
// Check for namespace declaration
|
|
@@ -119,6 +140,9 @@ class Parser {
|
|
|
119
140
|
else if (this.check('struct')) {
|
|
120
141
|
structs.push(this.parseStructDecl());
|
|
121
142
|
}
|
|
143
|
+
else if (this.check('impl')) {
|
|
144
|
+
implBlocks.push(this.parseImplBlock());
|
|
145
|
+
}
|
|
122
146
|
else if (this.check('enum')) {
|
|
123
147
|
enums.push(this.parseEnumDecl());
|
|
124
148
|
}
|
|
@@ -129,7 +153,7 @@ class Parser {
|
|
|
129
153
|
declarations.push(this.parseFnDecl());
|
|
130
154
|
}
|
|
131
155
|
}
|
|
132
|
-
return { namespace, globals, declarations, structs, enums, consts };
|
|
156
|
+
return { namespace, globals, declarations, structs, implBlocks, enums, consts };
|
|
133
157
|
}
|
|
134
158
|
// -------------------------------------------------------------------------
|
|
135
159
|
// Struct Declaration
|
|
@@ -175,6 +199,17 @@ class Parser {
|
|
|
175
199
|
this.expect('}');
|
|
176
200
|
return this.withLoc({ name, variants }, enumToken);
|
|
177
201
|
}
|
|
202
|
+
parseImplBlock() {
|
|
203
|
+
const implToken = this.expect('impl');
|
|
204
|
+
const typeName = this.expect('ident').value;
|
|
205
|
+
this.expect('{');
|
|
206
|
+
const methods = [];
|
|
207
|
+
while (!this.check('}') && !this.check('eof')) {
|
|
208
|
+
methods.push(this.parseFnDecl(typeName));
|
|
209
|
+
}
|
|
210
|
+
this.expect('}');
|
|
211
|
+
return this.withLoc({ kind: 'impl_block', typeName, methods }, implToken);
|
|
212
|
+
}
|
|
178
213
|
parseConstDecl() {
|
|
179
214
|
const constToken = this.expect('const');
|
|
180
215
|
const name = this.expect('ident').value;
|
|
@@ -198,15 +233,15 @@ class Parser {
|
|
|
198
233
|
// -------------------------------------------------------------------------
|
|
199
234
|
// Function Declaration
|
|
200
235
|
// -------------------------------------------------------------------------
|
|
201
|
-
parseFnDecl() {
|
|
236
|
+
parseFnDecl(implTypeName) {
|
|
202
237
|
const decorators = this.parseDecorators();
|
|
203
238
|
const fnToken = this.expect('fn');
|
|
204
239
|
const name = this.expect('ident').value;
|
|
205
240
|
this.expect('(');
|
|
206
|
-
const params = this.parseParams();
|
|
241
|
+
const params = this.parseParams(implTypeName);
|
|
207
242
|
this.expect(')');
|
|
208
243
|
let returnType = { kind: 'named', name: 'void' };
|
|
209
|
-
if (this.match('->')) {
|
|
244
|
+
if (this.match('->') || this.match(':')) {
|
|
210
245
|
returnType = this.parseType();
|
|
211
246
|
}
|
|
212
247
|
const body = this.parseBlock();
|
|
@@ -222,7 +257,7 @@ class Parser {
|
|
|
222
257
|
return decorators;
|
|
223
258
|
}
|
|
224
259
|
parseDecoratorValue(value) {
|
|
225
|
-
// Parse @tick
|
|
260
|
+
// Parse @tick, @on(PlayerDeath), or @on_trigger("name")
|
|
226
261
|
const match = value.match(/^@(\w+)(?:\(([^)]*)\))?$/);
|
|
227
262
|
if (!match) {
|
|
228
263
|
this.error(`Invalid decorator: ${value}`);
|
|
@@ -233,6 +268,13 @@ class Parser {
|
|
|
233
268
|
return { name };
|
|
234
269
|
}
|
|
235
270
|
const args = {};
|
|
271
|
+
if (name === 'on') {
|
|
272
|
+
const eventTypeMatch = argsStr.match(/^([A-Za-z_][A-Za-z0-9_]*)$/);
|
|
273
|
+
if (eventTypeMatch) {
|
|
274
|
+
args.eventType = eventTypeMatch[1];
|
|
275
|
+
return { name, args };
|
|
276
|
+
}
|
|
277
|
+
}
|
|
236
278
|
// Handle @on_trigger("name"), @on_advancement("id"), @on_craft("item"), @on_join_team("team")
|
|
237
279
|
if (name === 'on_trigger' || name === 'on_advancement' || name === 'on_craft' || name === 'on_join_team') {
|
|
238
280
|
const strMatch = argsStr.match(/^"([^"]*)"$/);
|
|
@@ -273,14 +315,20 @@ class Parser {
|
|
|
273
315
|
}
|
|
274
316
|
return { name, args };
|
|
275
317
|
}
|
|
276
|
-
parseParams() {
|
|
318
|
+
parseParams(implTypeName) {
|
|
277
319
|
const params = [];
|
|
278
320
|
if (!this.check(')')) {
|
|
279
321
|
do {
|
|
280
322
|
const paramToken = this.expect('ident');
|
|
281
323
|
const name = paramToken.value;
|
|
282
|
-
|
|
283
|
-
|
|
324
|
+
let type;
|
|
325
|
+
if (implTypeName && params.length === 0 && name === 'self' && !this.check(':')) {
|
|
326
|
+
type = { kind: 'struct', name: implTypeName };
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
this.expect(':');
|
|
330
|
+
type = this.parseType();
|
|
331
|
+
}
|
|
284
332
|
let defaultValue;
|
|
285
333
|
if (this.match('=')) {
|
|
286
334
|
defaultValue = this.parseExpr();
|
|
@@ -612,6 +660,11 @@ class Parser {
|
|
|
612
660
|
if (prec < minPrec)
|
|
613
661
|
break;
|
|
614
662
|
const opToken = this.advance();
|
|
663
|
+
if (op === 'is') {
|
|
664
|
+
const entityType = this.parseEntityTypeName();
|
|
665
|
+
left = this.withLoc({ kind: 'is_check', expr: left, entityType }, this.getLocToken(left) ?? opToken);
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
615
668
|
const right = this.parseBinaryExpr(prec + 1); // left associative
|
|
616
669
|
left = this.withLoc({ kind: 'binary', op: op, left, right }, this.getLocToken(left) ?? opToken);
|
|
617
670
|
}
|
|
@@ -630,6 +683,13 @@ class Parser {
|
|
|
630
683
|
}
|
|
631
684
|
return this.parsePostfixExpr();
|
|
632
685
|
}
|
|
686
|
+
parseEntityTypeName() {
|
|
687
|
+
const token = this.expect('ident');
|
|
688
|
+
if (ENTITY_TYPE_NAMES.has(token.value)) {
|
|
689
|
+
return token.value;
|
|
690
|
+
}
|
|
691
|
+
this.error(`Unknown entity type '${token.value}'`);
|
|
692
|
+
}
|
|
633
693
|
isSubtraction() {
|
|
634
694
|
// Check if this minus is binary (subtraction) by looking at previous token
|
|
635
695
|
// If previous was a value (literal, ident, ), ]) it's subtraction
|
|
@@ -710,6 +770,15 @@ class Parser {
|
|
|
710
770
|
}
|
|
711
771
|
parsePrimaryExpr() {
|
|
712
772
|
const token = this.peek();
|
|
773
|
+
if (token.kind === 'ident' && this.peek(1).kind === '::') {
|
|
774
|
+
const typeToken = this.advance();
|
|
775
|
+
this.expect('::');
|
|
776
|
+
const methodToken = this.expect('ident');
|
|
777
|
+
this.expect('(');
|
|
778
|
+
const args = this.parseArgs();
|
|
779
|
+
this.expect(')');
|
|
780
|
+
return this.withLoc({ kind: 'static_call', type: typeToken.value, method: methodToken.value, args }, typeToken);
|
|
781
|
+
}
|
|
713
782
|
if (token.kind === 'ident' && this.peek(1).kind === '=>') {
|
|
714
783
|
return this.parseSingleParamLambda();
|
|
715
784
|
}
|
|
@@ -9,12 +9,14 @@ import { DiagnosticError } from '../diagnostics';
|
|
|
9
9
|
export declare class TypeChecker {
|
|
10
10
|
private collector;
|
|
11
11
|
private functions;
|
|
12
|
+
private implMethods;
|
|
12
13
|
private structs;
|
|
13
14
|
private enums;
|
|
14
15
|
private consts;
|
|
15
16
|
private currentFn;
|
|
16
17
|
private currentReturnType;
|
|
17
18
|
private scope;
|
|
19
|
+
private selfTypeStack;
|
|
18
20
|
constructor(source?: string, filePath?: string);
|
|
19
21
|
private getNodeLocation;
|
|
20
22
|
private report;
|
|
@@ -23,6 +25,7 @@ export declare class TypeChecker {
|
|
|
23
25
|
*/
|
|
24
26
|
check(program: Program): DiagnosticError[];
|
|
25
27
|
private checkFunction;
|
|
28
|
+
private checkFunctionDecorators;
|
|
26
29
|
private checkBlock;
|
|
27
30
|
private checkStmt;
|
|
28
31
|
private checkLetStmt;
|
|
@@ -33,9 +36,23 @@ export declare class TypeChecker {
|
|
|
33
36
|
private checkFunctionCallArgs;
|
|
34
37
|
private checkTpCall;
|
|
35
38
|
private checkMemberExpr;
|
|
39
|
+
private checkStaticCallExpr;
|
|
36
40
|
private checkLambdaExpr;
|
|
41
|
+
private checkIfBranches;
|
|
42
|
+
private getThenBranchNarrowing;
|
|
37
43
|
private inferType;
|
|
38
44
|
private inferLambdaType;
|
|
45
|
+
/** Infer entity type from a selector */
|
|
46
|
+
private inferEntityTypeFromSelector;
|
|
47
|
+
private resolveInstanceMethod;
|
|
48
|
+
/** Check if childType is a subtype of parentType */
|
|
49
|
+
private isEntitySubtype;
|
|
50
|
+
/** Push a new self type context */
|
|
51
|
+
private pushSelfType;
|
|
52
|
+
/** Pop self type context */
|
|
53
|
+
private popSelfType;
|
|
54
|
+
/** Get current @s type */
|
|
55
|
+
private getCurrentSelfType;
|
|
39
56
|
private typesMatch;
|
|
40
57
|
private typeToString;
|
|
41
58
|
private normalizeType;
|