redscript-mc 1.2.30 → 2.0.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/.claude/commands/build-test.md +10 -0
- package/.claude/commands/deploy-demo.md +12 -0
- package/.claude/commands/stage-status.md +13 -0
- package/.claude/settings.json +12 -0
- package/.github/workflows/ci.yml +1 -0
- package/CLAUDE.md +231 -0
- package/demo.gif +0 -0
- package/dist/cli.js +2 -554
- package/dist/compile.js +2 -266
- package/dist/index.js +2 -159
- package/dist/lowering/index.js +5 -3
- package/dist/src/__tests__/cli.test.d.ts +1 -0
- package/dist/src/__tests__/cli.test.js +104 -0
- package/dist/src/__tests__/codegen.test.d.ts +1 -0
- package/dist/src/__tests__/codegen.test.js +152 -0
- package/dist/src/__tests__/compile-all.test.d.ts +10 -0
- package/dist/src/__tests__/compile-all.test.js +108 -0
- package/dist/src/__tests__/dce.test.d.ts +1 -0
- package/dist/src/__tests__/dce.test.js +102 -0
- package/dist/src/__tests__/diagnostics.test.d.ts +4 -0
- package/dist/src/__tests__/diagnostics.test.js +177 -0
- package/dist/src/__tests__/e2e.test.d.ts +6 -0
- package/dist/src/__tests__/e2e.test.js +1789 -0
- package/dist/src/__tests__/entity-types.test.d.ts +1 -0
- package/dist/src/__tests__/entity-types.test.js +203 -0
- package/dist/src/__tests__/formatter.test.d.ts +1 -0
- package/dist/src/__tests__/formatter.test.js +40 -0
- package/dist/src/__tests__/lexer.test.d.ts +1 -0
- package/dist/src/__tests__/lexer.test.js +343 -0
- package/dist/src/__tests__/lowering.test.d.ts +1 -0
- package/dist/src/__tests__/lowering.test.js +1015 -0
- package/dist/src/__tests__/macro.test.d.ts +8 -0
- package/dist/src/__tests__/macro.test.js +306 -0
- package/dist/src/__tests__/mc-integration.test.d.ts +12 -0
- package/dist/src/__tests__/mc-integration.test.js +817 -0
- package/dist/src/__tests__/mc-syntax.test.d.ts +1 -0
- package/dist/src/__tests__/mc-syntax.test.js +124 -0
- package/dist/src/__tests__/nbt.test.d.ts +1 -0
- package/dist/src/__tests__/nbt.test.js +82 -0
- package/dist/src/__tests__/optimizer-advanced.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer-advanced.test.js +124 -0
- package/dist/src/__tests__/optimizer.test.d.ts +1 -0
- package/dist/src/__tests__/optimizer.test.js +149 -0
- package/dist/src/__tests__/parser.test.d.ts +1 -0
- package/dist/src/__tests__/parser.test.js +807 -0
- package/dist/src/__tests__/repl.test.d.ts +1 -0
- package/dist/src/__tests__/repl.test.js +27 -0
- package/dist/src/__tests__/runtime.test.d.ts +1 -0
- package/dist/src/__tests__/runtime.test.js +289 -0
- package/dist/src/__tests__/stdlib-advanced.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib-advanced.test.js +374 -0
- package/dist/src/__tests__/stdlib-bigint.test.d.ts +7 -0
- package/dist/src/__tests__/stdlib-bigint.test.js +426 -0
- package/dist/src/__tests__/stdlib-math.test.d.ts +7 -0
- package/dist/src/__tests__/stdlib-math.test.js +351 -0
- package/dist/src/__tests__/stdlib-vec.test.d.ts +4 -0
- package/dist/src/__tests__/stdlib-vec.test.js +263 -0
- package/dist/src/__tests__/structure-optimizer.test.d.ts +1 -0
- package/dist/src/__tests__/structure-optimizer.test.js +33 -0
- package/dist/src/__tests__/typechecker.test.d.ts +1 -0
- package/dist/src/__tests__/typechecker.test.js +552 -0
- package/dist/src/__tests__/var-allocator.test.d.ts +1 -0
- package/dist/src/__tests__/var-allocator.test.js +69 -0
- package/dist/src/ast/types.d.ts +515 -0
- package/dist/src/ast/types.js +9 -0
- package/dist/src/builtins/metadata.d.ts +36 -0
- package/dist/src/builtins/metadata.js +1014 -0
- package/dist/src/cli.d.ts +11 -0
- package/dist/src/cli.js +443 -0
- package/dist/src/codegen/cmdblock/index.d.ts +26 -0
- package/dist/src/codegen/cmdblock/index.js +45 -0
- package/dist/src/codegen/mcfunction/index.d.ts +40 -0
- package/dist/src/codegen/mcfunction/index.js +606 -0
- package/dist/src/codegen/structure/index.d.ts +24 -0
- package/dist/src/codegen/structure/index.js +279 -0
- package/dist/src/codegen/var-allocator.d.ts +45 -0
- package/dist/src/codegen/var-allocator.js +104 -0
- package/dist/src/compile.d.ts +37 -0
- package/dist/src/compile.js +165 -0
- package/dist/src/diagnostics/index.d.ts +44 -0
- package/dist/src/diagnostics/index.js +140 -0
- package/dist/src/events/types.d.ts +35 -0
- package/dist/src/events/types.js +59 -0
- package/dist/src/formatter/index.d.ts +1 -0
- package/dist/src/formatter/index.js +26 -0
- package/dist/src/index.d.ts +22 -0
- package/dist/src/index.js +45 -0
- package/dist/src/ir/builder.d.ts +33 -0
- package/dist/src/ir/builder.js +99 -0
- package/dist/src/ir/types.d.ts +132 -0
- package/dist/src/ir/types.js +15 -0
- package/dist/src/lexer/index.d.ts +37 -0
- package/dist/src/lexer/index.js +569 -0
- package/dist/src/lowering/index.d.ts +188 -0
- package/dist/src/lowering/index.js +3405 -0
- package/dist/src/mc-test/client.d.ts +128 -0
- package/dist/src/mc-test/client.js +174 -0
- package/dist/src/mc-test/runner.d.ts +28 -0
- package/dist/src/mc-test/runner.js +151 -0
- package/dist/src/mc-test/setup.d.ts +11 -0
- package/dist/src/mc-test/setup.js +98 -0
- package/dist/src/mc-validator/index.d.ts +17 -0
- package/dist/src/mc-validator/index.js +322 -0
- package/dist/src/nbt/index.d.ts +86 -0
- package/dist/src/nbt/index.js +250 -0
- package/dist/src/optimizer/commands.d.ts +38 -0
- package/dist/src/optimizer/commands.js +451 -0
- package/dist/src/optimizer/dce.d.ts +34 -0
- package/dist/src/optimizer/dce.js +639 -0
- package/dist/src/optimizer/passes.d.ts +34 -0
- package/dist/src/optimizer/passes.js +243 -0
- package/dist/src/optimizer/structure.d.ts +9 -0
- package/dist/src/optimizer/structure.js +356 -0
- package/dist/src/parser/index.d.ts +93 -0
- package/dist/src/parser/index.js +1687 -0
- package/dist/src/repl.d.ts +16 -0
- package/dist/src/repl.js +165 -0
- package/dist/src/runtime/index.d.ts +107 -0
- package/dist/src/runtime/index.js +1409 -0
- package/dist/src/typechecker/index.d.ts +61 -0
- package/dist/src/typechecker/index.js +1034 -0
- package/dist/src/types/entity-hierarchy.d.ts +29 -0
- package/dist/src/types/entity-hierarchy.js +107 -0
- package/dist/src2/__tests__/e2e/basic.test.d.ts +8 -0
- package/dist/src2/__tests__/e2e/basic.test.js +140 -0
- package/dist/src2/__tests__/e2e/macros.test.d.ts +9 -0
- package/dist/src2/__tests__/e2e/macros.test.js +182 -0
- package/dist/src2/__tests__/e2e/migrate.test.d.ts +13 -0
- package/dist/src2/__tests__/e2e/migrate.test.js +2739 -0
- package/dist/src2/__tests__/hir/desugar.test.d.ts +1 -0
- package/dist/src2/__tests__/hir/desugar.test.js +234 -0
- package/dist/src2/__tests__/lir/lower.test.d.ts +1 -0
- package/dist/src2/__tests__/lir/lower.test.js +559 -0
- package/dist/src2/__tests__/lir/types.test.d.ts +1 -0
- package/dist/src2/__tests__/lir/types.test.js +185 -0
- package/dist/src2/__tests__/lir/verify.test.d.ts +1 -0
- package/dist/src2/__tests__/lir/verify.test.js +221 -0
- package/dist/src2/__tests__/mir/arithmetic.test.d.ts +1 -0
- package/dist/src2/__tests__/mir/arithmetic.test.js +130 -0
- package/dist/src2/__tests__/mir/control-flow.test.d.ts +1 -0
- package/dist/src2/__tests__/mir/control-flow.test.js +205 -0
- package/dist/src2/__tests__/mir/verify.test.d.ts +1 -0
- package/dist/src2/__tests__/mir/verify.test.js +223 -0
- package/dist/src2/__tests__/optimizer/block_merge.test.d.ts +1 -0
- package/dist/src2/__tests__/optimizer/block_merge.test.js +78 -0
- package/dist/src2/__tests__/optimizer/branch_simplify.test.d.ts +1 -0
- package/dist/src2/__tests__/optimizer/branch_simplify.test.js +58 -0
- package/dist/src2/__tests__/optimizer/constant_fold.test.d.ts +1 -0
- package/dist/src2/__tests__/optimizer/constant_fold.test.js +131 -0
- package/dist/src2/__tests__/optimizer/copy_prop.test.d.ts +1 -0
- package/dist/src2/__tests__/optimizer/copy_prop.test.js +91 -0
- package/dist/src2/__tests__/optimizer/dce.test.d.ts +1 -0
- package/dist/src2/__tests__/optimizer/dce.test.js +76 -0
- package/dist/src2/__tests__/optimizer/pipeline.test.d.ts +1 -0
- package/dist/src2/__tests__/optimizer/pipeline.test.js +102 -0
- package/dist/src2/emit/compile.d.ts +19 -0
- package/dist/src2/emit/compile.js +80 -0
- package/dist/src2/emit/index.d.ts +17 -0
- package/dist/src2/emit/index.js +172 -0
- package/dist/src2/hir/lower.d.ts +15 -0
- package/dist/src2/hir/lower.js +378 -0
- package/dist/src2/hir/types.d.ts +373 -0
- package/dist/src2/hir/types.js +16 -0
- package/dist/src2/lir/lower.d.ts +15 -0
- package/dist/src2/lir/lower.js +453 -0
- package/dist/src2/lir/types.d.ts +136 -0
- package/dist/src2/lir/types.js +11 -0
- package/dist/src2/lir/verify.d.ts +14 -0
- package/dist/src2/lir/verify.js +113 -0
- package/dist/src2/mir/lower.d.ts +9 -0
- package/dist/src2/mir/lower.js +1030 -0
- package/dist/src2/mir/macro.d.ts +22 -0
- package/dist/src2/mir/macro.js +168 -0
- package/dist/src2/mir/types.d.ts +183 -0
- package/dist/src2/mir/types.js +11 -0
- package/dist/src2/mir/verify.d.ts +16 -0
- package/dist/src2/mir/verify.js +216 -0
- package/dist/src2/optimizer/block_merge.d.ts +12 -0
- package/dist/src2/optimizer/block_merge.js +84 -0
- package/dist/src2/optimizer/branch_simplify.d.ts +9 -0
- package/dist/src2/optimizer/branch_simplify.js +28 -0
- package/dist/src2/optimizer/constant_fold.d.ts +10 -0
- package/dist/src2/optimizer/constant_fold.js +85 -0
- package/dist/src2/optimizer/copy_prop.d.ts +9 -0
- package/dist/src2/optimizer/copy_prop.js +113 -0
- package/dist/src2/optimizer/dce.d.ts +8 -0
- package/dist/src2/optimizer/dce.js +155 -0
- package/dist/src2/optimizer/pipeline.d.ts +10 -0
- package/dist/src2/optimizer/pipeline.js +42 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/docs/compiler-pipeline-redesign.md +2243 -0
- package/docs/optimization-ideas.md +1076 -0
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/jest.config.js +1 -1
- package/package.json +6 -5
- package/scripts/postbuild.js +15 -0
- package/src/__tests__/cli.test.ts +8 -220
- package/src/__tests__/dce.test.ts +11 -56
- package/src/__tests__/diagnostics.test.ts +59 -38
- package/src/__tests__/mc-integration.test.ts +1 -2
- package/src/ast/types.ts +6 -1
- package/src/cli.ts +29 -156
- package/src/compile.ts +6 -162
- package/src/index.ts +14 -178
- package/src/mc-test/runner.ts +4 -3
- package/src/parser/index.ts +1 -1
- package/src/repl.ts +1 -1
- package/src/runtime/index.ts +1 -1
- package/src2/__tests__/e2e/basic.test.ts +154 -0
- package/src2/__tests__/e2e/macros.test.ts +199 -0
- package/src2/__tests__/e2e/migrate.test.ts +3008 -0
- package/src2/__tests__/hir/desugar.test.ts +263 -0
- package/src2/__tests__/lir/lower.test.ts +619 -0
- package/src2/__tests__/lir/types.test.ts +207 -0
- package/src2/__tests__/lir/verify.test.ts +249 -0
- package/src2/__tests__/mir/arithmetic.test.ts +156 -0
- package/src2/__tests__/mir/control-flow.test.ts +242 -0
- package/src2/__tests__/mir/verify.test.ts +254 -0
- package/src2/__tests__/optimizer/block_merge.test.ts +84 -0
- package/src2/__tests__/optimizer/branch_simplify.test.ts +64 -0
- package/src2/__tests__/optimizer/constant_fold.test.ts +145 -0
- package/src2/__tests__/optimizer/copy_prop.test.ts +99 -0
- package/src2/__tests__/optimizer/dce.test.ts +83 -0
- package/src2/__tests__/optimizer/pipeline.test.ts +116 -0
- package/src2/emit/compile.ts +99 -0
- package/src2/emit/index.ts +222 -0
- package/src2/hir/lower.ts +428 -0
- package/src2/hir/types.ts +216 -0
- package/src2/lir/lower.ts +556 -0
- package/src2/lir/types.ts +109 -0
- package/src2/lir/verify.ts +129 -0
- package/src2/mir/lower.ts +1160 -0
- package/src2/mir/macro.ts +167 -0
- package/src2/mir/types.ts +106 -0
- package/src2/mir/verify.ts +218 -0
- package/src2/optimizer/block_merge.ts +93 -0
- package/src2/optimizer/branch_simplify.ts +27 -0
- package/src2/optimizer/constant_fold.ts +88 -0
- package/src2/optimizer/copy_prop.ts +106 -0
- package/src2/optimizer/dce.ts +133 -0
- package/src2/optimizer/pipeline.ts +44 -0
- package/tsconfig.json +2 -2
- package/src/__tests__/codegen.test.ts +0 -161
- package/src/__tests__/e2e.test.ts +0 -2039
- package/src/__tests__/entity-types.test.ts +0 -236
- package/src/__tests__/lowering.test.ts +0 -1185
- package/src/__tests__/macro.test.ts +0 -343
- package/src/__tests__/nbt.test.ts +0 -58
- package/src/__tests__/optimizer-advanced.test.ts +0 -144
- package/src/__tests__/optimizer.test.ts +0 -162
- package/src/__tests__/runtime.test.ts +0 -305
- package/src/__tests__/stdlib-advanced.test.ts +0 -379
- package/src/__tests__/stdlib-bigint.test.ts +0 -427
- package/src/__tests__/stdlib-math.test.ts +0 -374
- package/src/__tests__/stdlib-vec.test.ts +0 -259
- package/src/__tests__/structure-optimizer.test.ts +0 -38
- package/src/__tests__/var-allocator.test.ts +0 -75
- package/src/codegen/cmdblock/index.ts +0 -63
- package/src/codegen/mcfunction/index.ts +0 -662
- package/src/codegen/structure/index.ts +0 -346
- package/src/codegen/var-allocator.ts +0 -104
- package/src/ir/builder.ts +0 -116
- package/src/ir/types.ts +0 -134
- package/src/lowering/index.ts +0 -3876
- package/src/optimizer/commands.ts +0 -534
- package/src/optimizer/dce.ts +0 -679
- package/src/optimizer/passes.ts +0 -250
- package/src/optimizer/structure.ts +0 -450
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const lexer_1 = require("../lexer");
|
|
4
|
+
const parser_1 = require("../parser");
|
|
5
|
+
const typechecker_1 = require("../typechecker");
|
|
6
|
+
function typeCheck(source) {
|
|
7
|
+
const tokens = new lexer_1.Lexer(source).tokenize();
|
|
8
|
+
const ast = new parser_1.Parser(tokens).parse('test');
|
|
9
|
+
const checker = new typechecker_1.TypeChecker(source);
|
|
10
|
+
return checker.check(ast);
|
|
11
|
+
}
|
|
12
|
+
describe('TypeChecker', () => {
|
|
13
|
+
describe('variable declaration', () => {
|
|
14
|
+
it('allows using top-level consts inside functions', () => {
|
|
15
|
+
const errors = typeCheck(`
|
|
16
|
+
const MAX_HP: int = 100
|
|
17
|
+
|
|
18
|
+
fn test() {
|
|
19
|
+
let hp: int = MAX_HP;
|
|
20
|
+
}
|
|
21
|
+
`);
|
|
22
|
+
expect(errors).toHaveLength(0);
|
|
23
|
+
});
|
|
24
|
+
it('allows using declared variables', () => {
|
|
25
|
+
const errors = typeCheck(`
|
|
26
|
+
fn test() {
|
|
27
|
+
let x: int = 5;
|
|
28
|
+
let y: int = x;
|
|
29
|
+
}
|
|
30
|
+
`);
|
|
31
|
+
expect(errors).toHaveLength(0);
|
|
32
|
+
});
|
|
33
|
+
it('detects undeclared variable usage', () => {
|
|
34
|
+
const errors = typeCheck(`
|
|
35
|
+
fn test() {
|
|
36
|
+
let x: int = y;
|
|
37
|
+
}
|
|
38
|
+
`);
|
|
39
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
40
|
+
expect(errors[0].message).toContain("'y' used before declaration");
|
|
41
|
+
expect(errors[0].location.line).toBe(3);
|
|
42
|
+
expect(errors[0].location.col).toBe(18);
|
|
43
|
+
});
|
|
44
|
+
it('detects undeclared variable in expression', () => {
|
|
45
|
+
const errors = typeCheck(`
|
|
46
|
+
fn test() {
|
|
47
|
+
let x: int = 5 + undeclared;
|
|
48
|
+
}
|
|
49
|
+
`);
|
|
50
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
51
|
+
expect(errors[0].message).toContain("'undeclared' used before declaration");
|
|
52
|
+
});
|
|
53
|
+
it('accepts BlockPos literals for BlockPos variables', () => {
|
|
54
|
+
const errors = typeCheck(`
|
|
55
|
+
fn test() {
|
|
56
|
+
let spawn: BlockPos = (~0, 64, ~0);
|
|
57
|
+
}
|
|
58
|
+
`);
|
|
59
|
+
expect(errors).toHaveLength(0);
|
|
60
|
+
});
|
|
61
|
+
it('rejects assigning to const', () => {
|
|
62
|
+
const errors = typeCheck(`
|
|
63
|
+
const MAX_HP: int = 100
|
|
64
|
+
|
|
65
|
+
fn test() {
|
|
66
|
+
MAX_HP = 50;
|
|
67
|
+
}
|
|
68
|
+
`);
|
|
69
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
70
|
+
expect(errors[0].message).toContain("Cannot assign to const 'MAX_HP'");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('function calls', () => {
|
|
74
|
+
it('allows correct number of arguments', () => {
|
|
75
|
+
const errors = typeCheck(`
|
|
76
|
+
fn add(a: int, b: int) -> int {
|
|
77
|
+
return a + b;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn test() {
|
|
81
|
+
let x: int = add(1, 2);
|
|
82
|
+
}
|
|
83
|
+
`);
|
|
84
|
+
expect(errors).toHaveLength(0);
|
|
85
|
+
});
|
|
86
|
+
it('detects wrong number of arguments', () => {
|
|
87
|
+
const errors = typeCheck(`
|
|
88
|
+
fn add(a: int, b: int) -> int {
|
|
89
|
+
return a + b;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fn test() {
|
|
93
|
+
let x: int = add(1);
|
|
94
|
+
}
|
|
95
|
+
`);
|
|
96
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
97
|
+
expect(errors[0].message).toContain("expects 2 arguments, got 1");
|
|
98
|
+
expect(errors[0].location.line).toBe(7);
|
|
99
|
+
});
|
|
100
|
+
it('allows omitting trailing default arguments', () => {
|
|
101
|
+
const errors = typeCheck(`
|
|
102
|
+
fn greet(name: string, formal: bool = false) {
|
|
103
|
+
say(name);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fn test() {
|
|
107
|
+
greet("Player");
|
|
108
|
+
greet("Player", true);
|
|
109
|
+
}
|
|
110
|
+
`);
|
|
111
|
+
expect(errors).toHaveLength(0);
|
|
112
|
+
});
|
|
113
|
+
it('allows f-strings in runtime output builtins', () => {
|
|
114
|
+
const errors = typeCheck(`
|
|
115
|
+
fn test() {
|
|
116
|
+
let score: int = 5;
|
|
117
|
+
say(f"Score: {score}");
|
|
118
|
+
tellraw(@a, f"Score: {score}");
|
|
119
|
+
actionbar(@s, f"Score: {score}");
|
|
120
|
+
title(@s, f"Score: {score}");
|
|
121
|
+
}
|
|
122
|
+
`);
|
|
123
|
+
expect(errors).toHaveLength(0);
|
|
124
|
+
});
|
|
125
|
+
it('rejects f-strings outside runtime output builtins', () => {
|
|
126
|
+
const errors = typeCheck(`
|
|
127
|
+
fn test() {
|
|
128
|
+
let msg: string = f"Score";
|
|
129
|
+
}
|
|
130
|
+
`);
|
|
131
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
132
|
+
expect(errors[0].message).toContain('expected string, got format_string');
|
|
133
|
+
});
|
|
134
|
+
it('rejects unsupported f-string placeholder types', () => {
|
|
135
|
+
const errors = typeCheck(`
|
|
136
|
+
fn test() {
|
|
137
|
+
say(f"Flag: {true}");
|
|
138
|
+
}
|
|
139
|
+
`);
|
|
140
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
141
|
+
expect(errors[0].message).toContain('f-string placeholder must be int or string');
|
|
142
|
+
});
|
|
143
|
+
it('detects too many arguments', () => {
|
|
144
|
+
const errors = typeCheck(`
|
|
145
|
+
fn greet() {
|
|
146
|
+
say("hello");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
fn test() {
|
|
150
|
+
greet(1, 2, 3);
|
|
151
|
+
}
|
|
152
|
+
`);
|
|
153
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
154
|
+
expect(errors[0].message).toContain("expects 0 arguments, got 3");
|
|
155
|
+
});
|
|
156
|
+
it('rejects a required parameter after a default parameter', () => {
|
|
157
|
+
const errors = typeCheck(`
|
|
158
|
+
fn bad(a: int = 1, b: int) {}
|
|
159
|
+
`);
|
|
160
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
161
|
+
expect(errors[0].message).toContain("cannot follow a default parameter");
|
|
162
|
+
});
|
|
163
|
+
it('rejects non-single selector tp destinations', () => {
|
|
164
|
+
const errors = typeCheck(`
|
|
165
|
+
fn test() {
|
|
166
|
+
tp(@s, @a);
|
|
167
|
+
}
|
|
168
|
+
`);
|
|
169
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
170
|
+
expect(errors[0].message).toContain('tp destination must be a single-entity selector');
|
|
171
|
+
});
|
|
172
|
+
it('allows single-selector and BlockPos tp destinations', () => {
|
|
173
|
+
const singleErrors = typeCheck(`
|
|
174
|
+
fn test() {
|
|
175
|
+
tp(@s, @p);
|
|
176
|
+
tp(@s, @e[limit=1, tag=target]);
|
|
177
|
+
}
|
|
178
|
+
`);
|
|
179
|
+
expect(singleErrors).toHaveLength(0);
|
|
180
|
+
const posErrors = typeCheck(`
|
|
181
|
+
fn test() {
|
|
182
|
+
tp(@a, (1, 64, 1));
|
|
183
|
+
tp(@s, (~0, ~10, ~0));
|
|
184
|
+
}
|
|
185
|
+
`);
|
|
186
|
+
expect(posErrors).toHaveLength(0);
|
|
187
|
+
});
|
|
188
|
+
it('treats bossbar_get_value() as int', () => {
|
|
189
|
+
const errors = typeCheck(`
|
|
190
|
+
fn test() {
|
|
191
|
+
let current: int = bossbar_get_value("ns:health");
|
|
192
|
+
}
|
|
193
|
+
`);
|
|
194
|
+
expect(errors).toHaveLength(0);
|
|
195
|
+
});
|
|
196
|
+
it('allows lambda variables to be called via function types', () => {
|
|
197
|
+
const errors = typeCheck(`
|
|
198
|
+
fn test() {
|
|
199
|
+
let double: (int) -> int = (x: int) => x * 2;
|
|
200
|
+
let result: int = double(5);
|
|
201
|
+
}
|
|
202
|
+
`);
|
|
203
|
+
expect(errors).toHaveLength(0);
|
|
204
|
+
});
|
|
205
|
+
it('allows lambdas as callback arguments', () => {
|
|
206
|
+
const errors = typeCheck(`
|
|
207
|
+
fn apply(val: int, cb: (int) -> int) -> int {
|
|
208
|
+
return cb(val);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
fn test() {
|
|
212
|
+
let result: int = apply(5, (x: int) => x * 3);
|
|
213
|
+
}
|
|
214
|
+
`);
|
|
215
|
+
expect(errors).toHaveLength(0);
|
|
216
|
+
});
|
|
217
|
+
it('infers single-parameter lambdas from the expected function type', () => {
|
|
218
|
+
const errors = typeCheck(`
|
|
219
|
+
fn test() {
|
|
220
|
+
let double: (int) -> int = x => x * 2;
|
|
221
|
+
let result: int = double(5);
|
|
222
|
+
}
|
|
223
|
+
`);
|
|
224
|
+
expect(errors).toHaveLength(0);
|
|
225
|
+
});
|
|
226
|
+
it('type checks timer builtins with void callbacks and interval IDs', () => {
|
|
227
|
+
const errors = typeCheck(`
|
|
228
|
+
fn test() {
|
|
229
|
+
setTimeout(100, () => {
|
|
230
|
+
say("later");
|
|
231
|
+
});
|
|
232
|
+
let intervalId: int = setInterval(20, () => {
|
|
233
|
+
say("tick");
|
|
234
|
+
});
|
|
235
|
+
clearInterval(intervalId);
|
|
236
|
+
}
|
|
237
|
+
`);
|
|
238
|
+
expect(errors).toHaveLength(0);
|
|
239
|
+
});
|
|
240
|
+
it('rejects timer callbacks with the wrong return type', () => {
|
|
241
|
+
const errors = typeCheck(`
|
|
242
|
+
fn test() {
|
|
243
|
+
setTimeout(100, () => 1);
|
|
244
|
+
}
|
|
245
|
+
`);
|
|
246
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
247
|
+
expect(errors[0].message).toContain('Return type mismatch: expected void, got int');
|
|
248
|
+
});
|
|
249
|
+
it('allows impl instance methods with inferred self type', () => {
|
|
250
|
+
const errors = typeCheck(`
|
|
251
|
+
struct Timer { duration: int }
|
|
252
|
+
|
|
253
|
+
impl Timer {
|
|
254
|
+
fn elapsed(self) -> int {
|
|
255
|
+
return self.duration;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
fn test() {
|
|
260
|
+
let timer: Timer = { duration: 10 };
|
|
261
|
+
let value: int = timer.elapsed();
|
|
262
|
+
}
|
|
263
|
+
`);
|
|
264
|
+
expect(errors).toHaveLength(0);
|
|
265
|
+
});
|
|
266
|
+
it('records then-branch entity narrowing for is-checks', () => {
|
|
267
|
+
const source = `
|
|
268
|
+
fn test() {
|
|
269
|
+
foreach (e in @e) {
|
|
270
|
+
if (e is Player) {
|
|
271
|
+
kill(e);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
`;
|
|
276
|
+
const tokens = new lexer_1.Lexer(source).tokenize();
|
|
277
|
+
const ast = new parser_1.Parser(tokens).parse('test');
|
|
278
|
+
const checker = new typechecker_1.TypeChecker(source);
|
|
279
|
+
checker.check(ast);
|
|
280
|
+
const foreachStmt = ast.declarations[0].body[0];
|
|
281
|
+
const ifStmt = foreachStmt.body[0];
|
|
282
|
+
expect(checker.getThenBranchNarrowing(ifStmt.cond)).toEqual({
|
|
283
|
+
name: 'e',
|
|
284
|
+
type: { kind: 'entity', entityType: 'Player' },
|
|
285
|
+
mutable: false,
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
it('allows static impl method calls', () => {
|
|
289
|
+
const errors = typeCheck(`
|
|
290
|
+
struct Timer { duration: int }
|
|
291
|
+
|
|
292
|
+
impl Timer {
|
|
293
|
+
fn new(duration: int) -> Timer {
|
|
294
|
+
return { duration: duration };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
fn test() {
|
|
299
|
+
let timer: Timer = Timer::new(10);
|
|
300
|
+
}
|
|
301
|
+
`);
|
|
302
|
+
expect(errors).toHaveLength(0);
|
|
303
|
+
});
|
|
304
|
+
it('rejects using is-checks on non-entity values', () => {
|
|
305
|
+
const errors = typeCheck(`
|
|
306
|
+
fn test() {
|
|
307
|
+
let x: int = 1;
|
|
308
|
+
if (x is Player) {
|
|
309
|
+
say("nope");
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
`);
|
|
313
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
314
|
+
expect(errors[0].message).toContain("'is' checks require an entity expression, got int");
|
|
315
|
+
});
|
|
316
|
+
it('rejects calling instance impl methods as static methods', () => {
|
|
317
|
+
const errors = typeCheck(`
|
|
318
|
+
struct Point { x: int, y: int }
|
|
319
|
+
|
|
320
|
+
impl Point {
|
|
321
|
+
fn distance(self) -> int {
|
|
322
|
+
return self.x + self.y;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
fn test() {
|
|
327
|
+
let total: int = Point::distance();
|
|
328
|
+
}
|
|
329
|
+
`);
|
|
330
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
331
|
+
expect(errors[0].message).toContain("Method 'Point::distance' is an instance method");
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
describe('entity is-check narrowing', () => {
|
|
335
|
+
it('allows entity type checks on foreach bindings', () => {
|
|
336
|
+
const errors = typeCheck(`
|
|
337
|
+
fn test() {
|
|
338
|
+
foreach (e in @e) {
|
|
339
|
+
if (e is Player) {
|
|
340
|
+
kill(@s);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
`);
|
|
345
|
+
expect(errors).toHaveLength(0);
|
|
346
|
+
});
|
|
347
|
+
it('rejects is-checks on non-entity expressions', () => {
|
|
348
|
+
const errors = typeCheck(`
|
|
349
|
+
fn test() {
|
|
350
|
+
let x: int = 1;
|
|
351
|
+
if (x is Player) {}
|
|
352
|
+
}
|
|
353
|
+
`);
|
|
354
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
355
|
+
expect(errors[0].message).toContain("'is' checks require an entity expression");
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
describe('return type checking', () => {
|
|
359
|
+
it('allows matching return type', () => {
|
|
360
|
+
const errors = typeCheck(`
|
|
361
|
+
fn get_five() -> int {
|
|
362
|
+
return 5;
|
|
363
|
+
}
|
|
364
|
+
`);
|
|
365
|
+
expect(errors).toHaveLength(0);
|
|
366
|
+
});
|
|
367
|
+
it('detects return type mismatch', () => {
|
|
368
|
+
const errors = typeCheck(`
|
|
369
|
+
fn get_bool() -> bool {
|
|
370
|
+
return 5;
|
|
371
|
+
}
|
|
372
|
+
`);
|
|
373
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
374
|
+
expect(errors[0].message).toContain("Return type mismatch");
|
|
375
|
+
});
|
|
376
|
+
it('detects missing return value', () => {
|
|
377
|
+
const errors = typeCheck(`
|
|
378
|
+
fn get_int() -> int {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
`);
|
|
382
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
383
|
+
expect(errors[0].message).toContain("Missing return value");
|
|
384
|
+
});
|
|
385
|
+
it('allows void return with no value', () => {
|
|
386
|
+
const errors = typeCheck(`
|
|
387
|
+
fn do_nothing() {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
`);
|
|
391
|
+
expect(errors).toHaveLength(0);
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
describe('member access', () => {
|
|
395
|
+
it('allows struct field access', () => {
|
|
396
|
+
const errors = typeCheck(`
|
|
397
|
+
struct Point { x: int, y: int }
|
|
398
|
+
|
|
399
|
+
fn test() {
|
|
400
|
+
let p: Point = { x: 10, y: 20 };
|
|
401
|
+
let val: int = p.x;
|
|
402
|
+
}
|
|
403
|
+
`);
|
|
404
|
+
expect(errors).toHaveLength(0);
|
|
405
|
+
});
|
|
406
|
+
it('detects invalid struct field', () => {
|
|
407
|
+
const errors = typeCheck(`
|
|
408
|
+
struct Point { x: int, y: int }
|
|
409
|
+
|
|
410
|
+
fn test() {
|
|
411
|
+
let p: Point = { x: 10, y: 20 };
|
|
412
|
+
let val: int = p.z;
|
|
413
|
+
}
|
|
414
|
+
`);
|
|
415
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
416
|
+
expect(errors[0].message).toContain("has no field 'z'");
|
|
417
|
+
expect(errors[0].location.line).toBe(6);
|
|
418
|
+
});
|
|
419
|
+
it('allows array.len access', () => {
|
|
420
|
+
const errors = typeCheck(`
|
|
421
|
+
fn test() {
|
|
422
|
+
let arr: int[] = [1, 2, 3];
|
|
423
|
+
let len: int = arr.len;
|
|
424
|
+
}
|
|
425
|
+
`);
|
|
426
|
+
expect(errors).toHaveLength(0);
|
|
427
|
+
});
|
|
428
|
+
it('detects member access on primitive', () => {
|
|
429
|
+
const errors = typeCheck(`
|
|
430
|
+
fn test() {
|
|
431
|
+
let x: int = 5;
|
|
432
|
+
let y: int = x.value;
|
|
433
|
+
}
|
|
434
|
+
`);
|
|
435
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
436
|
+
expect(errors[0].message).toContain("Cannot access member");
|
|
437
|
+
});
|
|
438
|
+
it('allows enum variants and enum-typed variables', () => {
|
|
439
|
+
const errors = typeCheck(`
|
|
440
|
+
enum Direction { North, South, East, West }
|
|
441
|
+
|
|
442
|
+
fn test() {
|
|
443
|
+
let dir: Direction = Direction.North;
|
|
444
|
+
if (dir == Direction.South) {
|
|
445
|
+
say("south");
|
|
446
|
+
}
|
|
447
|
+
match (dir) {
|
|
448
|
+
Direction.East => { say("east"); }
|
|
449
|
+
_ => { say("other"); }
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
`);
|
|
453
|
+
expect(errors).toHaveLength(0);
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
describe('control flow', () => {
|
|
457
|
+
it('checks conditions in if statements', () => {
|
|
458
|
+
const errors = typeCheck(`
|
|
459
|
+
fn test() {
|
|
460
|
+
if (undeclared > 0) {
|
|
461
|
+
say("yes");
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
`);
|
|
465
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
466
|
+
expect(errors[0].message).toContain("'undeclared' used before declaration");
|
|
467
|
+
});
|
|
468
|
+
it('checks conditions in while loops', () => {
|
|
469
|
+
const errors = typeCheck(`
|
|
470
|
+
fn test() {
|
|
471
|
+
while (missing) {
|
|
472
|
+
say("loop");
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
`);
|
|
476
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
477
|
+
expect(errors[0].message).toContain("'missing' used before declaration");
|
|
478
|
+
});
|
|
479
|
+
it('checks for loop parts', () => {
|
|
480
|
+
const errors = typeCheck(`
|
|
481
|
+
fn test() {
|
|
482
|
+
for (let i: int = 0; i < count; i = i + 1) {
|
|
483
|
+
say("loop");
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
`);
|
|
487
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
488
|
+
expect(errors[0].message).toContain("'count' used before declaration");
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
describe('complex programs', () => {
|
|
492
|
+
it('handles valid complex program', () => {
|
|
493
|
+
const errors = typeCheck(`
|
|
494
|
+
struct Stats { health: int, mana: int }
|
|
495
|
+
|
|
496
|
+
fn heal(amount: int) -> int {
|
|
497
|
+
let bonus: int = amount * 2;
|
|
498
|
+
return bonus;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
@tick
|
|
502
|
+
fn game_loop() {
|
|
503
|
+
let stats: Stats = { health: 100, mana: 50 };
|
|
504
|
+
let healed: int = heal(10);
|
|
505
|
+
stats.health = stats.health + healed;
|
|
506
|
+
}
|
|
507
|
+
`);
|
|
508
|
+
expect(errors).toHaveLength(0);
|
|
509
|
+
});
|
|
510
|
+
it('collects multiple errors', () => {
|
|
511
|
+
const errors = typeCheck(`
|
|
512
|
+
fn broken() -> int {
|
|
513
|
+
let x: int = undefined_var;
|
|
514
|
+
let y: int = another_undefined;
|
|
515
|
+
missing_func();
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
`);
|
|
519
|
+
// Should have multiple errors: 2 undefined vars, return type mismatch
|
|
520
|
+
// (missing_func is not checked since it's not defined as a user function)
|
|
521
|
+
expect(errors.length).toBeGreaterThanOrEqual(3);
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
describe('event handlers', () => {
|
|
525
|
+
it('accepts matching @on event signatures', () => {
|
|
526
|
+
const errors = typeCheck(`
|
|
527
|
+
@on(PlayerDeath)
|
|
528
|
+
fn handle_death(player: Player) {
|
|
529
|
+
tp(player, @p);
|
|
530
|
+
}
|
|
531
|
+
`);
|
|
532
|
+
expect(errors).toHaveLength(0);
|
|
533
|
+
});
|
|
534
|
+
it('rejects unknown event types', () => {
|
|
535
|
+
const errors = typeCheck(`
|
|
536
|
+
@on(NotARealEvent)
|
|
537
|
+
fn handle(player: Player) {}
|
|
538
|
+
`);
|
|
539
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
540
|
+
expect(errors[0].message).toContain("Unknown event type 'NotARealEvent'");
|
|
541
|
+
});
|
|
542
|
+
it('rejects mismatched event signatures', () => {
|
|
543
|
+
const errors = typeCheck(`
|
|
544
|
+
@on(BlockBreak)
|
|
545
|
+
fn handle_break(player: Player) {}
|
|
546
|
+
`);
|
|
547
|
+
expect(errors.length).toBeGreaterThan(0);
|
|
548
|
+
expect(errors[0].message).toContain('must declare 2 parameter(s)');
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
//# sourceMappingURL=typechecker.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const var_allocator_1 = require("../codegen/var-allocator");
|
|
4
|
+
describe('VarAllocator', () => {
|
|
5
|
+
describe('mangle mode (default)', () => {
|
|
6
|
+
it('generates sequential names: a, b, ..., z, aa, ab', () => {
|
|
7
|
+
const alloc = new var_allocator_1.VarAllocator(true);
|
|
8
|
+
const names = [];
|
|
9
|
+
for (let i = 0; i < 28; i++) {
|
|
10
|
+
names.push(alloc.alloc(`var${i}`));
|
|
11
|
+
}
|
|
12
|
+
expect(names[0]).toBe('$a');
|
|
13
|
+
expect(names[1]).toBe('$b');
|
|
14
|
+
expect(names[25]).toBe('$z');
|
|
15
|
+
expect(names[26]).toBe('$aa');
|
|
16
|
+
expect(names[27]).toBe('$ab');
|
|
17
|
+
});
|
|
18
|
+
it('caches: same name returns same result', () => {
|
|
19
|
+
const alloc = new var_allocator_1.VarAllocator(true);
|
|
20
|
+
const first = alloc.alloc('x');
|
|
21
|
+
const second = alloc.alloc('x');
|
|
22
|
+
expect(first).toBe(second);
|
|
23
|
+
});
|
|
24
|
+
it('constant() is content-addressed: same value returns same result', () => {
|
|
25
|
+
const alloc = new var_allocator_1.VarAllocator(true);
|
|
26
|
+
const first = alloc.constant(42);
|
|
27
|
+
const second = alloc.constant(42);
|
|
28
|
+
expect(first).toBe(second);
|
|
29
|
+
});
|
|
30
|
+
it('different variables get different names', () => {
|
|
31
|
+
const alloc = new var_allocator_1.VarAllocator(true);
|
|
32
|
+
const a = alloc.alloc('foo');
|
|
33
|
+
const b = alloc.alloc('bar');
|
|
34
|
+
expect(a).not.toBe(b);
|
|
35
|
+
});
|
|
36
|
+
it('alloc, constant, and internal share the same sequential pool', () => {
|
|
37
|
+
const alloc = new var_allocator_1.VarAllocator(true);
|
|
38
|
+
const v = alloc.alloc('x'); // $a
|
|
39
|
+
const c = alloc.constant(1); // $b
|
|
40
|
+
const i = alloc.internal('ret'); // $c
|
|
41
|
+
expect(v).toBe('$a');
|
|
42
|
+
expect(c).toBe('$b');
|
|
43
|
+
expect(i).toBe('$c');
|
|
44
|
+
});
|
|
45
|
+
it('strips $ prefix from variable names', () => {
|
|
46
|
+
const alloc = new var_allocator_1.VarAllocator(true);
|
|
47
|
+
const a = alloc.alloc('$foo');
|
|
48
|
+
const b = alloc.alloc('foo');
|
|
49
|
+
expect(a).toBe(b); // same underlying name
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('no-mangle mode', () => {
|
|
53
|
+
it('uses $<name> for user vars', () => {
|
|
54
|
+
const alloc = new var_allocator_1.VarAllocator(false);
|
|
55
|
+
expect(alloc.alloc('counter')).toBe('$counter');
|
|
56
|
+
});
|
|
57
|
+
it('uses $const_<value> for constants', () => {
|
|
58
|
+
const alloc = new var_allocator_1.VarAllocator(false);
|
|
59
|
+
expect(alloc.constant(10)).toBe('$const_10');
|
|
60
|
+
expect(alloc.constant(-3)).toBe('$const_-3');
|
|
61
|
+
});
|
|
62
|
+
it('uses $<suffix> for internals', () => {
|
|
63
|
+
const alloc = new var_allocator_1.VarAllocator(false);
|
|
64
|
+
expect(alloc.internal('ret')).toBe('$ret');
|
|
65
|
+
expect(alloc.internal('p0')).toBe('$p0');
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=var-allocator.test.js.map
|