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.
Files changed (63) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/__tests__/cli.test.js +138 -0
  3. package/dist/__tests__/codegen.test.js +25 -0
  4. package/dist/__tests__/e2e.test.js +190 -12
  5. package/dist/__tests__/lexer.test.js +12 -2
  6. package/dist/__tests__/lowering.test.js +164 -9
  7. package/dist/__tests__/mc-integration.test.js +145 -51
  8. package/dist/__tests__/optimizer-advanced.test.js +3 -3
  9. package/dist/__tests__/parser.test.js +80 -0
  10. package/dist/__tests__/runtime.test.js +8 -8
  11. package/dist/__tests__/typechecker.test.js +158 -0
  12. package/dist/ast/types.d.ts +20 -1
  13. package/dist/codegen/mcfunction/index.js +30 -1
  14. package/dist/codegen/structure/index.js +25 -0
  15. package/dist/compile.d.ts +10 -0
  16. package/dist/compile.js +36 -5
  17. package/dist/events/types.d.ts +35 -0
  18. package/dist/events/types.js +59 -0
  19. package/dist/index.js +3 -2
  20. package/dist/ir/types.d.ts +4 -0
  21. package/dist/lexer/index.d.ts +1 -1
  22. package/dist/lexer/index.js +2 -0
  23. package/dist/lowering/index.d.ts +32 -1
  24. package/dist/lowering/index.js +439 -15
  25. package/dist/parser/index.d.ts +2 -0
  26. package/dist/parser/index.js +79 -10
  27. package/dist/typechecker/index.d.ts +17 -0
  28. package/dist/typechecker/index.js +343 -17
  29. package/docs/ENTITY_TYPE_SYSTEM.md +242 -0
  30. package/editors/vscode/CHANGELOG.md +9 -0
  31. package/editors/vscode/out/extension.js +1144 -72
  32. package/editors/vscode/package-lock.json +2 -2
  33. package/editors/vscode/package.json +1 -1
  34. package/package.json +1 -1
  35. package/src/__tests__/cli.test.ts +166 -0
  36. package/src/__tests__/codegen.test.ts +27 -0
  37. package/src/__tests__/e2e.test.ts +201 -12
  38. package/src/__tests__/fixtures/event-test.mcrs +13 -0
  39. package/src/__tests__/fixtures/impl-test.mcrs +46 -0
  40. package/src/__tests__/fixtures/interval-test.mcrs +11 -0
  41. package/src/__tests__/fixtures/is-check-test.mcrs +20 -0
  42. package/src/__tests__/fixtures/timeout-test.mcrs +7 -0
  43. package/src/__tests__/lexer.test.ts +14 -2
  44. package/src/__tests__/lowering.test.ts +178 -9
  45. package/src/__tests__/mc-integration.test.ts +166 -51
  46. package/src/__tests__/optimizer-advanced.test.ts +3 -3
  47. package/src/__tests__/parser.test.ts +91 -5
  48. package/src/__tests__/runtime.test.ts +8 -8
  49. package/src/__tests__/typechecker.test.ts +171 -0
  50. package/src/ast/types.ts +25 -1
  51. package/src/codegen/mcfunction/index.ts +31 -1
  52. package/src/codegen/structure/index.ts +27 -0
  53. package/src/compile.ts +54 -6
  54. package/src/events/types.ts +69 -0
  55. package/src/index.ts +4 -3
  56. package/src/ir/types.ts +4 -0
  57. package/src/lexer/index.ts +3 -1
  58. package/src/lowering/index.ts +528 -16
  59. package/src/parser/index.ts +90 -12
  60. package/src/stdlib/README.md +34 -4
  61. package/src/stdlib/tags.mcrs +951 -0
  62. package/src/stdlib/timer.mcrs +54 -33
  63. package/src/typechecker/index.ts +404 -18
@@ -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;
@@ -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 or @on_trigger("name") or @on_advancement("story/mine_diamond")
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
- this.expect(':');
283
- const type = this.parseType();
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;