redscript-mc 1.0.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/.github/ISSUE_TEMPLATE/bug_report.yml +72 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +57 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +17 -25
- package/CHANGELOG.md +112 -0
- package/CONTRIBUTING.md +140 -0
- package/README.md +28 -19
- package/README.zh.md +28 -19
- package/dist/__tests__/cli.test.js +148 -10
- package/dist/__tests__/codegen.test.js +26 -1
- package/dist/__tests__/diagnostics.test.js +5 -5
- package/dist/__tests__/e2e.test.js +336 -17
- package/dist/__tests__/formatter.test.d.ts +1 -0
- package/dist/__tests__/formatter.test.js +40 -0
- package/dist/__tests__/lexer.test.js +12 -2
- package/dist/__tests__/lowering.test.js +200 -12
- package/dist/__tests__/mc-integration.test.js +370 -31
- package/dist/__tests__/mc-syntax.test.js +3 -3
- package/dist/__tests__/nbt.test.js +2 -2
- package/dist/__tests__/optimizer-advanced.test.js +5 -5
- package/dist/__tests__/parser.test.js +80 -0
- package/dist/__tests__/runtime.test.js +9 -9
- package/dist/__tests__/typechecker.test.js +158 -0
- package/dist/ast/types.d.ts +40 -3
- package/dist/cli.js +25 -7
- package/dist/codegen/mcfunction/index.d.ts +1 -1
- package/dist/codegen/mcfunction/index.js +38 -3
- package/dist/codegen/structure/index.js +32 -1
- 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/formatter/index.d.ts +1 -0
- package/dist/formatter/index.js +26 -0
- package/dist/index.js +3 -2
- package/dist/ir/builder.d.ts +2 -1
- package/dist/ir/types.d.ts +11 -2
- package/dist/ir/types.js +1 -1
- package/dist/lexer/index.d.ts +1 -1
- package/dist/lexer/index.js +2 -0
- package/dist/lowering/index.d.ts +34 -1
- package/dist/lowering/index.js +622 -23
- package/dist/mc-test/runner.d.ts +2 -2
- package/dist/mc-test/runner.js +3 -3
- package/dist/mc-test/setup.js +2 -2
- package/dist/parser/index.d.ts +4 -0
- package/dist/parser/index.js +153 -16
- package/dist/typechecker/index.d.ts +17 -0
- package/dist/typechecker/index.js +343 -17
- package/docs/COMPILATION_STATS.md +24 -24
- package/docs/ENTITY_TYPE_SYSTEM.md +242 -0
- package/docs/IMPLEMENTATION_GUIDE.md +1 -1
- package/docs/STRUCTURE_TARGET.md +1 -1
- package/editors/vscode/.vscodeignore +1 -0
- package/editors/vscode/CHANGELOG.md +9 -0
- package/editors/vscode/icons/mcrs.svg +7 -0
- package/editors/vscode/icons/redscript-icons.json +10 -0
- package/editors/vscode/out/extension.js +1295 -80
- package/editors/vscode/package-lock.json +2 -2
- package/editors/vscode/package.json +10 -3
- package/editors/vscode/src/hover.ts +55 -2
- package/editors/vscode/src/symbols.ts +42 -0
- package/package.json +1 -1
- package/src/__tests__/cli.test.ts +176 -10
- package/src/__tests__/codegen.test.ts +28 -1
- package/src/__tests__/diagnostics.test.ts +5 -5
- package/src/__tests__/e2e.test.ts +335 -17
- 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 +226 -12
- package/src/__tests__/mc-integration.test.ts +421 -31
- package/src/__tests__/mc-syntax.test.ts +3 -3
- package/src/__tests__/nbt.test.ts +2 -2
- package/src/__tests__/optimizer-advanced.test.ts +5 -5
- package/src/__tests__/parser.test.ts +91 -5
- package/src/__tests__/runtime.test.ts +9 -9
- package/src/__tests__/typechecker.test.ts +171 -0
- package/src/ast/types.ts +44 -3
- package/src/cli.ts +10 -10
- package/src/codegen/mcfunction/index.ts +40 -3
- package/src/codegen/structure/index.ts +35 -1
- package/src/compile.ts +54 -6
- package/src/events/types.ts +69 -0
- package/src/examples/capture_the_flag.mcrs +208 -0
- package/src/examples/{counter.rs → counter.mcrs} +1 -1
- package/src/examples/hunger_games.mcrs +301 -0
- package/src/examples/new_features_demo.mcrs +193 -0
- package/src/examples/parkour_race.mcrs +233 -0
- package/src/examples/rpg.mcrs +13 -0
- package/src/examples/{shop.rs → shop.mcrs} +1 -1
- package/src/examples/{showcase_game.rs → showcase_game.mcrs} +3 -3
- package/src/examples/{turret.rs → turret.mcrs} +1 -1
- package/src/examples/zombie_survival.mcrs +314 -0
- package/src/index.ts +4 -3
- package/src/ir/builder.ts +3 -1
- package/src/ir/types.ts +12 -2
- package/src/lexer/index.ts +3 -1
- package/src/lowering/index.ts +684 -24
- package/src/mc-test/runner.ts +3 -3
- package/src/mc-test/setup.ts +2 -2
- package/src/parser/index.ts +170 -19
- package/src/stdlib/README.md +178 -140
- package/src/stdlib/bossbar.mcrs +68 -0
- package/src/stdlib/{cooldown.rs → cooldown.mcrs} +1 -1
- package/src/stdlib/effects.mcrs +64 -0
- package/src/stdlib/interactions.mcrs +195 -0
- package/src/stdlib/inventory.mcrs +38 -0
- package/src/stdlib/mobs.mcrs +99 -0
- package/src/stdlib/particles.mcrs +52 -0
- package/src/stdlib/sets.mcrs +20 -0
- package/src/stdlib/spawn.mcrs +41 -0
- package/src/stdlib/tags.mcrs +951 -0
- package/src/stdlib/teams.mcrs +68 -0
- package/src/stdlib/timer.mcrs +72 -0
- package/src/stdlib/world.mcrs +92 -0
- package/src/typechecker/index.ts +404 -18
- package/src/examples/rpg.rs +0 -13
- package/src/stdlib/mobs.rs +0 -99
- package/src/stdlib/timer.rs +0 -51
- /package/src/examples/{arena.rs → arena.mcrs} +0 -0
- /package/src/examples/{pvp_arena.rs → pvp_arena.mcrs} +0 -0
- /package/src/examples/{quiz.rs → quiz.mcrs} +0 -0
- /package/src/examples/{stdlib_demo.rs → stdlib_demo.mcrs} +0 -0
- /package/src/examples/{world_manager.rs → world_manager.mcrs} +0 -0
- /package/src/stdlib/{combat.rs → combat.mcrs} +0 -0
- /package/src/stdlib/{math.rs → math.mcrs} +0 -0
- /package/src/stdlib/{player.rs → player.mcrs} +0 -0
- /package/src/stdlib/{strings.rs → strings.mcrs} +0 -0
- /package/src/templates/{combat.rs → combat.mcrs} +0 -0
- /package/src/templates/{economy.rs → economy.mcrs} +0 -0
- /package/src/templates/{mini-game-framework.rs → mini-game-framework.mcrs} +0 -0
- /package/src/templates/{quest.rs → quest.mcrs} +0 -0
- /package/src/test_programs/{zombie_game.rs → zombie_game.mcrs} +0 -0
package/dist/mc-test/runner.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* RedScript MC Integration Test Runner
|
|
3
3
|
*
|
|
4
|
-
* Compiles a .
|
|
4
|
+
* Compiles a .mcrs file, installs it to a running Paper server,
|
|
5
5
|
* runs test scenarios, and reports results.
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
|
-
* npx ts-node src/mc-test/runner.ts src/examples/counter.
|
|
8
|
+
* npx ts-node src/mc-test/runner.ts src/examples/counter.mcrs
|
|
9
9
|
*
|
|
10
10
|
* Requires:
|
|
11
11
|
* - Paper server running with TestHarnessPlugin
|
package/dist/mc-test/runner.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* RedScript MC Integration Test Runner
|
|
4
4
|
*
|
|
5
|
-
* Compiles a .
|
|
5
|
+
* Compiles a .mcrs file, installs it to a running Paper server,
|
|
6
6
|
* runs test scenarios, and reports results.
|
|
7
7
|
*
|
|
8
8
|
* Usage:
|
|
9
|
-
* npx ts-node src/mc-test/runner.ts src/examples/counter.
|
|
9
|
+
* npx ts-node src/mc-test/runner.ts src/examples/counter.mcrs
|
|
10
10
|
*
|
|
11
11
|
* Requires:
|
|
12
12
|
* - Paper server running with TestHarnessPlugin
|
|
@@ -115,7 +115,7 @@ async function runMCTests(sourceFile, tests, options = {}) {
|
|
|
115
115
|
if (require.main === module) {
|
|
116
116
|
const sourceFile = process.argv[2];
|
|
117
117
|
if (!sourceFile) {
|
|
118
|
-
console.error('Usage: ts-node runner.ts <source.
|
|
118
|
+
console.error('Usage: ts-node runner.ts <source.mcrs>');
|
|
119
119
|
process.exit(1);
|
|
120
120
|
}
|
|
121
121
|
// Example test suite (replace with actual tests)
|
package/dist/mc-test/setup.js
CHANGED
|
@@ -66,12 +66,12 @@ function main() {
|
|
|
66
66
|
// Example files
|
|
67
67
|
const exampleNamespaces = ['counter', 'world_manager'];
|
|
68
68
|
for (const ns of exampleNamespaces) {
|
|
69
|
-
const file = path.join(EXAMPLES_DIR, `${ns}.
|
|
69
|
+
const file = path.join(EXAMPLES_DIR, `${ns}.mcrs`);
|
|
70
70
|
if (fs.existsSync(file)) {
|
|
71
71
|
writeFixture(fs.readFileSync(file, 'utf-8'), ns);
|
|
72
72
|
}
|
|
73
73
|
else {
|
|
74
|
-
console.log(` ⚠ ${ns}.
|
|
74
|
+
console.log(` ⚠ ${ns}.mcrs not found, skipping`);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
// Inline test fixtures
|
package/dist/parser/index.d.ts
CHANGED
|
@@ -23,7 +23,9 @@ 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;
|
|
28
|
+
private parseGlobalDecl;
|
|
27
29
|
private parseFnDecl;
|
|
28
30
|
private parseDecorators;
|
|
29
31
|
private parseDecoratorValue;
|
|
@@ -48,6 +50,7 @@ export declare class Parser {
|
|
|
48
50
|
private parseAssignment;
|
|
49
51
|
private parseBinaryExpr;
|
|
50
52
|
private parseUnaryExpr;
|
|
53
|
+
private parseEntityTypeName;
|
|
51
54
|
private isSubtraction;
|
|
52
55
|
private parsePostfixExpr;
|
|
53
56
|
private parseArgs;
|
|
@@ -68,6 +71,7 @@ export declare class Parser {
|
|
|
68
71
|
private parseCoordComponent;
|
|
69
72
|
private parseSignedCoordOffset;
|
|
70
73
|
private parseSelector;
|
|
74
|
+
private parseSelectorOrVarSelector;
|
|
71
75
|
private parseSelectorValue;
|
|
72
76
|
private parseSelectorFilters;
|
|
73
77
|
private splitSelectorParams;
|
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;
|
|
@@ -99,8 +119,10 @@ class Parser {
|
|
|
99
119
|
// -------------------------------------------------------------------------
|
|
100
120
|
parse(defaultNamespace = 'redscript') {
|
|
101
121
|
let namespace = defaultNamespace;
|
|
122
|
+
const globals = [];
|
|
102
123
|
const declarations = [];
|
|
103
124
|
const structs = [];
|
|
125
|
+
const implBlocks = [];
|
|
104
126
|
const enums = [];
|
|
105
127
|
const consts = [];
|
|
106
128
|
// Check for namespace declaration
|
|
@@ -112,9 +134,15 @@ class Parser {
|
|
|
112
134
|
}
|
|
113
135
|
// Parse struct and function declarations
|
|
114
136
|
while (!this.check('eof')) {
|
|
115
|
-
if (this.check('
|
|
137
|
+
if (this.check('let')) {
|
|
138
|
+
globals.push(this.parseGlobalDecl(true));
|
|
139
|
+
}
|
|
140
|
+
else if (this.check('struct')) {
|
|
116
141
|
structs.push(this.parseStructDecl());
|
|
117
142
|
}
|
|
143
|
+
else if (this.check('impl')) {
|
|
144
|
+
implBlocks.push(this.parseImplBlock());
|
|
145
|
+
}
|
|
118
146
|
else if (this.check('enum')) {
|
|
119
147
|
enums.push(this.parseEnumDecl());
|
|
120
148
|
}
|
|
@@ -125,7 +153,7 @@ class Parser {
|
|
|
125
153
|
declarations.push(this.parseFnDecl());
|
|
126
154
|
}
|
|
127
155
|
}
|
|
128
|
-
return { namespace, declarations, structs, enums, consts };
|
|
156
|
+
return { namespace, globals, declarations, structs, implBlocks, enums, consts };
|
|
129
157
|
}
|
|
130
158
|
// -------------------------------------------------------------------------
|
|
131
159
|
// Struct Declaration
|
|
@@ -171,6 +199,17 @@ class Parser {
|
|
|
171
199
|
this.expect('}');
|
|
172
200
|
return this.withLoc({ name, variants }, enumToken);
|
|
173
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
|
+
}
|
|
174
213
|
parseConstDecl() {
|
|
175
214
|
const constToken = this.expect('const');
|
|
176
215
|
const name = this.expect('ident').value;
|
|
@@ -181,18 +220,28 @@ class Parser {
|
|
|
181
220
|
this.match(';');
|
|
182
221
|
return this.withLoc({ name, type, value }, constToken);
|
|
183
222
|
}
|
|
223
|
+
parseGlobalDecl(mutable) {
|
|
224
|
+
const token = this.advance(); // consume 'let'
|
|
225
|
+
const name = this.expect('ident').value;
|
|
226
|
+
this.expect(':');
|
|
227
|
+
const type = this.parseType();
|
|
228
|
+
this.expect('=');
|
|
229
|
+
const init = this.parseExpr();
|
|
230
|
+
this.expect(';');
|
|
231
|
+
return this.withLoc({ kind: 'global', name, type, init, mutable }, token);
|
|
232
|
+
}
|
|
184
233
|
// -------------------------------------------------------------------------
|
|
185
234
|
// Function Declaration
|
|
186
235
|
// -------------------------------------------------------------------------
|
|
187
|
-
parseFnDecl() {
|
|
236
|
+
parseFnDecl(implTypeName) {
|
|
188
237
|
const decorators = this.parseDecorators();
|
|
189
238
|
const fnToken = this.expect('fn');
|
|
190
239
|
const name = this.expect('ident').value;
|
|
191
240
|
this.expect('(');
|
|
192
|
-
const params = this.parseParams();
|
|
241
|
+
const params = this.parseParams(implTypeName);
|
|
193
242
|
this.expect(')');
|
|
194
243
|
let returnType = { kind: 'named', name: 'void' };
|
|
195
|
-
if (this.match('->')) {
|
|
244
|
+
if (this.match('->') || this.match(':')) {
|
|
196
245
|
returnType = this.parseType();
|
|
197
246
|
}
|
|
198
247
|
const body = this.parseBlock();
|
|
@@ -208,7 +257,7 @@ class Parser {
|
|
|
208
257
|
return decorators;
|
|
209
258
|
}
|
|
210
259
|
parseDecoratorValue(value) {
|
|
211
|
-
// Parse @tick
|
|
260
|
+
// Parse @tick, @on(PlayerDeath), or @on_trigger("name")
|
|
212
261
|
const match = value.match(/^@(\w+)(?:\(([^)]*)\))?$/);
|
|
213
262
|
if (!match) {
|
|
214
263
|
this.error(`Invalid decorator: ${value}`);
|
|
@@ -219,6 +268,13 @@ class Parser {
|
|
|
219
268
|
return { name };
|
|
220
269
|
}
|
|
221
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
|
+
}
|
|
222
278
|
// Handle @on_trigger("name"), @on_advancement("id"), @on_craft("item"), @on_join_team("team")
|
|
223
279
|
if (name === 'on_trigger' || name === 'on_advancement' || name === 'on_craft' || name === 'on_join_team') {
|
|
224
280
|
const strMatch = argsStr.match(/^"([^"]*)"$/);
|
|
@@ -259,14 +315,20 @@ class Parser {
|
|
|
259
315
|
}
|
|
260
316
|
return { name, args };
|
|
261
317
|
}
|
|
262
|
-
parseParams() {
|
|
318
|
+
parseParams(implTypeName) {
|
|
263
319
|
const params = [];
|
|
264
320
|
if (!this.check(')')) {
|
|
265
321
|
do {
|
|
266
322
|
const paramToken = this.expect('ident');
|
|
267
323
|
const name = paramToken.value;
|
|
268
|
-
|
|
269
|
-
|
|
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
|
+
}
|
|
270
332
|
let defaultValue;
|
|
271
333
|
if (this.match('=')) {
|
|
272
334
|
defaultValue = this.parseExpr();
|
|
@@ -534,16 +596,16 @@ class Parser {
|
|
|
534
596
|
if (this.peek().kind === 'ident' && this.peek().value === 'entity') {
|
|
535
597
|
this.advance(); // consume 'entity'
|
|
536
598
|
}
|
|
537
|
-
const
|
|
538
|
-
subcommands.push({ kind: 'if_entity',
|
|
599
|
+
const selectorOrVar = this.parseSelectorOrVarSelector();
|
|
600
|
+
subcommands.push({ kind: 'if_entity', ...selectorOrVar });
|
|
539
601
|
}
|
|
540
602
|
else if (this.match('unless')) {
|
|
541
603
|
// Expect 'entity' keyword (as ident) or just parse selector directly
|
|
542
604
|
if (this.peek().kind === 'ident' && this.peek().value === 'entity') {
|
|
543
605
|
this.advance(); // consume 'entity'
|
|
544
606
|
}
|
|
545
|
-
const
|
|
546
|
-
subcommands.push({ kind: 'unless_entity',
|
|
607
|
+
const selectorOrVar = this.parseSelectorOrVarSelector();
|
|
608
|
+
subcommands.push({ kind: 'unless_entity', ...selectorOrVar });
|
|
547
609
|
}
|
|
548
610
|
else if (this.match('in')) {
|
|
549
611
|
const dim = this.expect('ident').value;
|
|
@@ -598,6 +660,11 @@ class Parser {
|
|
|
598
660
|
if (prec < minPrec)
|
|
599
661
|
break;
|
|
600
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
|
+
}
|
|
601
668
|
const right = this.parseBinaryExpr(prec + 1); // left associative
|
|
602
669
|
left = this.withLoc({ kind: 'binary', op: op, left, right }, this.getLocToken(left) ?? opToken);
|
|
603
670
|
}
|
|
@@ -616,6 +683,13 @@ class Parser {
|
|
|
616
683
|
}
|
|
617
684
|
return this.parsePostfixExpr();
|
|
618
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
|
+
}
|
|
619
693
|
isSubtraction() {
|
|
620
694
|
// Check if this minus is binary (subtraction) by looking at previous token
|
|
621
695
|
// If previous was a value (literal, ident, ), ]) it's subtraction
|
|
@@ -645,6 +719,10 @@ class Parser {
|
|
|
645
719
|
'has_tag': '__entity_has_tag',
|
|
646
720
|
'push': '__array_push',
|
|
647
721
|
'pop': '__array_pop',
|
|
722
|
+
'add': 'set_add',
|
|
723
|
+
'contains': 'set_contains',
|
|
724
|
+
'remove': 'set_remove',
|
|
725
|
+
'clear': 'set_clear',
|
|
648
726
|
};
|
|
649
727
|
const internalFn = methodMap[expr.field];
|
|
650
728
|
if (internalFn) {
|
|
@@ -653,7 +731,11 @@ class Parser {
|
|
|
653
731
|
expr = this.withLoc({ kind: 'call', fn: internalFn, args: [expr.obj, ...args] }, this.getLocToken(expr) ?? openParenToken);
|
|
654
732
|
continue;
|
|
655
733
|
}
|
|
656
|
-
|
|
734
|
+
// Generic method sugar: obj.method(args) → method(obj, args)
|
|
735
|
+
const args = this.parseArgs();
|
|
736
|
+
this.expect(')');
|
|
737
|
+
expr = this.withLoc({ kind: 'call', fn: expr.field, args: [expr.obj, ...args] }, this.getLocToken(expr) ?? openParenToken);
|
|
738
|
+
continue;
|
|
657
739
|
}
|
|
658
740
|
const args = this.parseArgs();
|
|
659
741
|
this.expect(')');
|
|
@@ -688,6 +770,15 @@ class Parser {
|
|
|
688
770
|
}
|
|
689
771
|
parsePrimaryExpr() {
|
|
690
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
|
+
}
|
|
691
782
|
if (token.kind === 'ident' && this.peek(1).kind === '=>') {
|
|
692
783
|
return this.parseSingleParamLambda();
|
|
693
784
|
}
|
|
@@ -1077,6 +1168,37 @@ class Parser {
|
|
|
1077
1168
|
const token = this.expect('selector');
|
|
1078
1169
|
return this.parseSelectorValue(token.value);
|
|
1079
1170
|
}
|
|
1171
|
+
// Parse either a selector (@a[...]) or a variable with filters (p[...])
|
|
1172
|
+
// Returns { selector } for selectors or { varName, filters } for variables
|
|
1173
|
+
parseSelectorOrVarSelector() {
|
|
1174
|
+
if (this.check('selector')) {
|
|
1175
|
+
return { selector: this.parseSelector() };
|
|
1176
|
+
}
|
|
1177
|
+
// Must be an identifier (variable) possibly with filters
|
|
1178
|
+
const varToken = this.expect('ident');
|
|
1179
|
+
const varName = varToken.value;
|
|
1180
|
+
// Check for optional filters [...]
|
|
1181
|
+
if (this.check('[')) {
|
|
1182
|
+
this.advance(); // consume '['
|
|
1183
|
+
// Collect everything until ']'
|
|
1184
|
+
let filterStr = '';
|
|
1185
|
+
let depth = 1;
|
|
1186
|
+
while (depth > 0 && !this.check('eof')) {
|
|
1187
|
+
if (this.check('['))
|
|
1188
|
+
depth++;
|
|
1189
|
+
else if (this.check(']'))
|
|
1190
|
+
depth--;
|
|
1191
|
+
if (depth > 0) {
|
|
1192
|
+
filterStr += this.peek().value ?? this.peek().kind;
|
|
1193
|
+
this.advance();
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
this.expect(']');
|
|
1197
|
+
const filters = this.parseSelectorFilters(filterStr);
|
|
1198
|
+
return { varName, filters };
|
|
1199
|
+
}
|
|
1200
|
+
return { varName };
|
|
1201
|
+
}
|
|
1080
1202
|
parseSelectorValue(value) {
|
|
1081
1203
|
// Parse @e[type=zombie, distance=..5]
|
|
1082
1204
|
const bracketIndex = value.indexOf('[');
|
|
@@ -1129,6 +1251,21 @@ class Parser {
|
|
|
1129
1251
|
case 'scores':
|
|
1130
1252
|
filters.scores = this.parseScoresFilter(val);
|
|
1131
1253
|
break;
|
|
1254
|
+
case 'x':
|
|
1255
|
+
filters.x = this.parseRangeValue(val);
|
|
1256
|
+
break;
|
|
1257
|
+
case 'y':
|
|
1258
|
+
filters.y = this.parseRangeValue(val);
|
|
1259
|
+
break;
|
|
1260
|
+
case 'z':
|
|
1261
|
+
filters.z = this.parseRangeValue(val);
|
|
1262
|
+
break;
|
|
1263
|
+
case 'x_rotation':
|
|
1264
|
+
filters.x_rotation = this.parseRangeValue(val);
|
|
1265
|
+
break;
|
|
1266
|
+
case 'y_rotation':
|
|
1267
|
+
filters.y_rotation = this.parseRangeValue(val);
|
|
1268
|
+
break;
|
|
1132
1269
|
}
|
|
1133
1270
|
}
|
|
1134
1271
|
return filters;
|
|
@@ -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;
|