redscript-mc 3.0.1 → 3.0.2
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/workflows/ci.yml +1 -0
- package/README.md +119 -313
- package/README.zh.md +118 -314
- package/ROADMAP.md +5 -5
- package/dist/data/impl_test/function/counter/get.mcfunction +5 -0
- package/dist/data/impl_test/function/counter/inc.mcfunction +7 -0
- package/dist/data/impl_test/function/counter/new.mcfunction +4 -0
- package/dist/data/impl_test/function/load.mcfunction +1 -0
- package/dist/data/impl_test/function/test_impl.mcfunction +10 -0
- package/dist/data/minecraft/tags/function/load.json +5 -0
- package/dist/data/playground/function/load.mcfunction +1 -0
- package/dist/data/playground/function/start.mcfunction +4 -0
- package/dist/data/playground/function/start__say_macro_t1.mcfunction +1 -0
- package/dist/data/playground/function/stop.mcfunction +5 -0
- package/dist/data/playground/function/stop__say_macro_t0.mcfunction +1 -0
- package/dist/data/stdlib_queue8_test/function/__queue_append_apply.mcfunction +4 -0
- package/dist/data/stdlib_queue8_test/function/__queue_peek_apply.mcfunction +4 -0
- package/dist/data/stdlib_queue8_test/function/__queue_size_raw_apply.mcfunction +4 -0
- package/dist/data/stdlib_queue8_test/function/load.mcfunction +1 -0
- package/dist/data/stdlib_queue8_test/function/queue_clear.mcfunction +6 -0
- package/dist/data/stdlib_queue8_test/function/queue_empty__merge_1.mcfunction +5 -0
- package/dist/data/stdlib_queue8_test/function/queue_empty__then_0.mcfunction +5 -0
- package/dist/data/stdlib_queue8_test/function/queue_peek__merge_1.mcfunction +13 -0
- package/dist/data/stdlib_queue8_test/function/queue_peek__then_0.mcfunction +5 -0
- package/dist/data/stdlib_queue8_test/function/queue_pop__merge_1.mcfunction +15 -0
- package/dist/data/stdlib_queue8_test/function/queue_pop__then_0.mcfunction +5 -0
- package/dist/data/stdlib_queue8_test/function/queue_push__const_11.mcfunction +6 -0
- package/dist/data/stdlib_queue8_test/function/queue_push__const_22.mcfunction +6 -0
- package/dist/data/stdlib_queue8_test/function/queue_size.mcfunction +13 -0
- package/dist/data/stdlib_queue8_test/function/test_queue_push_and_size.mcfunction +13 -0
- package/dist/data/test/function/load.mcfunction +1 -0
- package/dist/data/test/function/say_at.mcfunction +6 -0
- package/dist/data/test/function/test.mcfunction +4 -0
- package/dist/pack.mcmeta +6 -0
- package/dist/package.json +1 -1
- package/dist/src/__tests__/formatter-extra.test.d.ts +7 -0
- package/dist/src/__tests__/formatter-extra.test.js +123 -0
- package/dist/src/__tests__/global-vars.test.d.ts +13 -0
- package/dist/src/__tests__/global-vars.test.js +156 -0
- package/dist/src/__tests__/lint/new-rules.test.d.ts +9 -0
- package/dist/src/__tests__/lint/new-rules.test.js +402 -0
- package/dist/src/__tests__/lsp-rename.test.d.ts +8 -0
- package/dist/src/__tests__/lsp-rename.test.js +157 -0
- package/dist/src/__tests__/mc-integration/say-fstring.test.d.ts +11 -0
- package/dist/src/__tests__/mc-integration/say-fstring.test.js +220 -0
- package/dist/src/__tests__/mc-integration/stdlib-coverage-2.test.js +1 -1
- package/dist/src/__tests__/mc-integration/stdlib-coverage-3.test.js +1 -1
- package/dist/src/__tests__/mc-integration/stdlib-coverage-4.test.js +1 -1
- package/dist/src/__tests__/mc-integration/stdlib-coverage-5.test.js +1 -1
- package/dist/src/__tests__/mc-integration/stdlib-coverage-6.test.js +1 -1
- package/dist/src/__tests__/mc-integration/stdlib-coverage-7.test.js +1 -1
- package/dist/src/__tests__/mc-integration/stdlib-coverage-8.test.js +1 -1
- package/dist/src/__tests__/mc-syntax.test.js +4 -1
- package/dist/src/__tests__/monomorphize-coverage.test.d.ts +9 -0
- package/dist/src/__tests__/monomorphize-coverage.test.js +204 -0
- package/dist/src/__tests__/optimizer-cse.test.d.ts +7 -0
- package/dist/src/__tests__/optimizer-cse.test.js +226 -0
- package/dist/src/__tests__/parser.test.js +4 -13
- package/dist/src/__tests__/repl-server-extra.test.js +6 -7
- package/dist/src/__tests__/repl-server.test.js +5 -7
- package/dist/src/__tests__/stdlib/queue.test.js +6 -6
- package/dist/src/cli.js +0 -0
- package/dist/src/lexer/index.js +2 -1
- package/dist/src/lint/index.d.ts +12 -5
- package/dist/src/lint/index.js +730 -5
- package/dist/src/lsp/main.js +0 -0
- package/dist/src/mc-test/client.d.ts +21 -0
- package/dist/src/mc-test/client.js +34 -0
- package/dist/src/mir/lower.js +108 -6
- package/dist/src/optimizer/interprocedural.js +37 -2
- package/dist/src/parser/decl-parser.d.ts +19 -0
- package/dist/src/parser/decl-parser.js +323 -0
- package/dist/src/parser/expr-parser.d.ts +46 -0
- package/dist/src/parser/expr-parser.js +759 -0
- package/dist/src/parser/index.d.ts +8 -129
- package/dist/src/parser/index.js +13 -2262
- package/dist/src/parser/stmt-parser.d.ts +28 -0
- package/dist/src/parser/stmt-parser.js +577 -0
- package/dist/src/parser/type-parser.d.ts +20 -0
- package/dist/src/parser/type-parser.js +257 -0
- package/dist/src/parser/utils.d.ts +34 -0
- package/dist/src/parser/utils.js +141 -0
- package/docs/dev/README-mc-integration-tests.md +141 -0
- package/docs/lint-rules.md +162 -0
- package/docs/stdlib/bigint.md +2 -0
- package/editors/vscode/README.md +63 -41
- package/editors/vscode/out/extension.js +1881 -1776
- package/editors/vscode/out/lsp-server.js +4257 -3651
- package/editors/vscode/package-lock.json +3 -3
- package/editors/vscode/package.json +1 -1
- package/examples/loops-demo.mcrs +87 -0
- package/package.json +1 -1
- package/redscript-docs/docs/en/stdlib/advanced.md +629 -0
- package/redscript-docs/docs/en/stdlib/bigint.md +316 -0
- package/redscript-docs/docs/en/stdlib/bits.md +292 -0
- package/redscript-docs/docs/en/stdlib/bossbar.md +177 -0
- package/redscript-docs/docs/en/stdlib/calculus.md +289 -0
- package/redscript-docs/docs/en/stdlib/color.md +353 -0
- package/redscript-docs/docs/en/stdlib/combat.md +88 -0
- package/redscript-docs/docs/en/stdlib/cooldown.md +82 -0
- package/redscript-docs/docs/en/stdlib/dialog.md +155 -0
- package/redscript-docs/docs/en/stdlib/easing.md +558 -0
- package/redscript-docs/docs/en/stdlib/ecs.md +475 -0
- package/redscript-docs/docs/en/stdlib/effects.md +324 -0
- package/redscript-docs/docs/en/stdlib/events.md +3 -0
- package/redscript-docs/docs/en/stdlib/expr.md +45 -0
- package/redscript-docs/docs/en/stdlib/fft.md +141 -0
- package/redscript-docs/docs/en/stdlib/geometry.md +430 -0
- package/redscript-docs/docs/en/stdlib/graph.md +259 -0
- package/redscript-docs/docs/en/stdlib/heap.md +185 -0
- package/redscript-docs/docs/en/stdlib/interactions.md +179 -0
- package/redscript-docs/docs/en/stdlib/inventory.md +97 -0
- package/redscript-docs/docs/en/stdlib/linalg.md +557 -0
- package/redscript-docs/docs/en/stdlib/list.md +559 -0
- package/redscript-docs/docs/en/stdlib/map.md +140 -0
- package/redscript-docs/docs/en/stdlib/math.md +193 -0
- package/redscript-docs/docs/en/stdlib/math_hp.md +149 -0
- package/redscript-docs/docs/en/stdlib/matrix.md +403 -0
- package/redscript-docs/docs/en/stdlib/mobs.md +965 -0
- package/redscript-docs/docs/en/stdlib/noise.md +244 -0
- package/redscript-docs/docs/en/stdlib/ode.md +253 -0
- package/redscript-docs/docs/en/stdlib/parabola.md +342 -0
- package/redscript-docs/docs/en/stdlib/particles.md +311 -0
- package/redscript-docs/docs/en/stdlib/pathfind.md +255 -0
- package/redscript-docs/docs/en/stdlib/physics.md +493 -0
- package/redscript-docs/docs/en/stdlib/player.md +78 -0
- package/redscript-docs/docs/en/stdlib/quaternion.md +673 -0
- package/redscript-docs/docs/en/stdlib/queue.md +134 -0
- package/redscript-docs/docs/en/stdlib/random.md +223 -0
- package/redscript-docs/docs/en/stdlib/result.md +143 -0
- package/redscript-docs/docs/en/stdlib/scheduler.md +183 -0
- package/redscript-docs/docs/en/stdlib/set_int.md +190 -0
- package/redscript-docs/docs/en/stdlib/sets.md +101 -0
- package/redscript-docs/docs/en/stdlib/signal.md +400 -0
- package/redscript-docs/docs/en/stdlib/sort.md +104 -0
- package/redscript-docs/docs/en/stdlib/spawn.md +147 -0
- package/redscript-docs/docs/en/stdlib/state.md +142 -0
- package/redscript-docs/docs/en/stdlib/strings.md +154 -0
- package/redscript-docs/docs/en/stdlib/tags.md +3451 -0
- package/redscript-docs/docs/en/stdlib/teams.md +153 -0
- package/redscript-docs/docs/en/stdlib/timer.md +246 -0
- package/redscript-docs/docs/en/stdlib/vec.md +158 -0
- package/redscript-docs/docs/en/stdlib/world.md +298 -0
- package/redscript-docs/docs/zh/stdlib/advanced.md +615 -0
- package/redscript-docs/docs/zh/stdlib/bigint.md +316 -0
- package/redscript-docs/docs/zh/stdlib/bits.md +292 -0
- package/redscript-docs/docs/zh/stdlib/bossbar.md +170 -0
- package/redscript-docs/docs/zh/stdlib/calculus.md +287 -0
- package/redscript-docs/docs/zh/stdlib/color.md +353 -0
- package/redscript-docs/docs/zh/stdlib/combat.md +88 -0
- package/redscript-docs/docs/zh/stdlib/cooldown.md +84 -0
- package/redscript-docs/docs/zh/stdlib/dialog.md +152 -0
- package/redscript-docs/docs/zh/stdlib/easing.md +558 -0
- package/redscript-docs/docs/zh/stdlib/ecs.md +472 -0
- package/redscript-docs/docs/zh/stdlib/effects.md +324 -0
- package/redscript-docs/docs/zh/stdlib/events.md +3 -0
- package/redscript-docs/docs/zh/stdlib/expr.md +37 -0
- package/redscript-docs/docs/zh/stdlib/fft.md +128 -0
- package/redscript-docs/docs/zh/stdlib/geometry.md +430 -0
- package/redscript-docs/docs/zh/stdlib/graph.md +259 -0
- package/redscript-docs/docs/zh/stdlib/heap.md +185 -0
- package/redscript-docs/docs/zh/stdlib/interactions.md +160 -0
- package/redscript-docs/docs/zh/stdlib/inventory.md +94 -0
- package/redscript-docs/docs/zh/stdlib/linalg.md +543 -0
- package/redscript-docs/docs/zh/stdlib/list.md +561 -0
- package/redscript-docs/docs/zh/stdlib/map.md +132 -0
- package/redscript-docs/docs/zh/stdlib/math.md +193 -0
- package/redscript-docs/docs/zh/stdlib/math_hp.md +143 -0
- package/redscript-docs/docs/zh/stdlib/matrix.md +396 -0
- package/redscript-docs/docs/zh/stdlib/mobs.md +965 -0
- package/redscript-docs/docs/zh/stdlib/noise.md +244 -0
- package/redscript-docs/docs/zh/stdlib/ode.md +243 -0
- package/redscript-docs/docs/zh/stdlib/parabola.md +337 -0
- package/redscript-docs/docs/zh/stdlib/particles.md +307 -0
- package/redscript-docs/docs/zh/stdlib/pathfind.md +255 -0
- package/redscript-docs/docs/zh/stdlib/physics.md +493 -0
- package/redscript-docs/docs/zh/stdlib/player.md +78 -0
- package/redscript-docs/docs/zh/stdlib/quaternion.md +669 -0
- package/redscript-docs/docs/zh/stdlib/queue.md +124 -0
- package/redscript-docs/docs/zh/stdlib/random.md +222 -0
- package/redscript-docs/docs/zh/stdlib/result.md +147 -0
- package/redscript-docs/docs/zh/stdlib/scheduler.md +173 -0
- package/redscript-docs/docs/zh/stdlib/set_int.md +180 -0
- package/redscript-docs/docs/zh/stdlib/sets.md +107 -0
- package/redscript-docs/docs/zh/stdlib/signal.md +373 -0
- package/redscript-docs/docs/zh/stdlib/sort.md +104 -0
- package/redscript-docs/docs/zh/stdlib/spawn.md +142 -0
- package/redscript-docs/docs/zh/stdlib/state.md +134 -0
- package/redscript-docs/docs/zh/stdlib/strings.md +107 -0
- package/redscript-docs/docs/zh/stdlib/tags.md +3451 -0
- package/redscript-docs/docs/zh/stdlib/teams.md +150 -0
- package/redscript-docs/docs/zh/stdlib/timer.md +254 -0
- package/redscript-docs/docs/zh/stdlib/vec.md +158 -0
- package/redscript-docs/docs/zh/stdlib/world.md +289 -0
- package/src/__tests__/formatter-extra.test.ts +139 -0
- package/src/__tests__/global-vars.test.ts +171 -0
- package/src/__tests__/lint/new-rules.test.ts +437 -0
- package/src/__tests__/lsp-rename.test.ts +171 -0
- package/src/__tests__/mc-integration/say-fstring.test.ts +211 -0
- package/src/__tests__/mc-integration/stdlib-coverage-2.test.ts +1 -1
- package/src/__tests__/mc-integration/stdlib-coverage-3.test.ts +1 -1
- package/src/__tests__/mc-integration/stdlib-coverage-4.test.ts +1 -1
- package/src/__tests__/mc-integration/stdlib-coverage-5.test.ts +1 -1
- package/src/__tests__/mc-integration/stdlib-coverage-6.test.ts +1 -1
- package/src/__tests__/mc-integration/stdlib-coverage-7.test.ts +1 -1
- package/src/__tests__/mc-integration/stdlib-coverage-8.test.ts +1 -1
- package/src/__tests__/mc-syntax.test.ts +3 -0
- package/src/__tests__/monomorphize-coverage.test.ts +220 -0
- package/src/__tests__/optimizer-cse.test.ts +250 -0
- package/src/__tests__/parser.test.ts +4 -13
- package/src/__tests__/repl-server-extra.test.ts +6 -6
- package/src/__tests__/repl-server.test.ts +5 -6
- package/src/__tests__/stdlib/queue.test.ts +6 -6
- package/src/lexer/index.ts +2 -1
- package/src/lint/index.ts +713 -5
- package/src/mc-test/client.ts +40 -0
- package/src/mir/lower.ts +111 -2
- package/src/optimizer/interprocedural.ts +40 -2
- package/src/parser/decl-parser.ts +349 -0
- package/src/parser/expr-parser.ts +838 -0
- package/src/parser/index.ts +17 -2558
- package/src/parser/stmt-parser.ts +585 -0
- package/src/parser/type-parser.ts +276 -0
- package/src/parser/utils.ts +173 -0
- package/src/stdlib/queue.mcrs +19 -6
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TypeParser — parses type annotations and generic type arguments.
|
|
4
|
+
* Extends ParserBase to gain token navigation.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.TypeParser = void 0;
|
|
8
|
+
const utils_1 = require("./utils");
|
|
9
|
+
class TypeParser extends utils_1.ParserBase {
|
|
10
|
+
// -------------------------------------------------------------------------
|
|
11
|
+
// Type Parsing
|
|
12
|
+
// -------------------------------------------------------------------------
|
|
13
|
+
parseType() {
|
|
14
|
+
const token = this.peek();
|
|
15
|
+
let type;
|
|
16
|
+
if (token.kind === '(') {
|
|
17
|
+
// Disambiguate: tuple type `(T, T)` vs function type `(T) -> R`
|
|
18
|
+
const saved = this.pos;
|
|
19
|
+
this.advance(); // consume '('
|
|
20
|
+
const elements = [];
|
|
21
|
+
if (!this.check(')')) {
|
|
22
|
+
do {
|
|
23
|
+
elements.push(this.parseType());
|
|
24
|
+
} while (this.match(','));
|
|
25
|
+
}
|
|
26
|
+
this.expect(')');
|
|
27
|
+
if (this.check('->')) {
|
|
28
|
+
this.pos = saved;
|
|
29
|
+
return this.parseFunctionType();
|
|
30
|
+
}
|
|
31
|
+
return { kind: 'tuple', elements };
|
|
32
|
+
}
|
|
33
|
+
if (token.kind === 'float') {
|
|
34
|
+
this.advance();
|
|
35
|
+
const filePart = this.filePath ? `${this.filePath}:` : '';
|
|
36
|
+
this.warnings.push(`[DeprecatedType] ${filePart}line ${token.line}, col ${token.col}: 'float' is deprecated, use 'fixed' instead (×10000 fixed-point)`);
|
|
37
|
+
type = { kind: 'named', name: 'float' };
|
|
38
|
+
}
|
|
39
|
+
else if (token.kind === 'int' || token.kind === 'bool' ||
|
|
40
|
+
token.kind === 'fixed' || token.kind === 'string' || token.kind === 'void' ||
|
|
41
|
+
token.kind === 'BlockPos') {
|
|
42
|
+
this.advance();
|
|
43
|
+
type = { kind: 'named', name: token.kind };
|
|
44
|
+
}
|
|
45
|
+
else if (token.kind === 'ident') {
|
|
46
|
+
this.advance();
|
|
47
|
+
if (token.value === 'selector' && this.check('<')) {
|
|
48
|
+
this.advance(); // consume <
|
|
49
|
+
const entityType = this.expect('ident').value;
|
|
50
|
+
this.expect('>');
|
|
51
|
+
type = { kind: 'selector', entityType };
|
|
52
|
+
}
|
|
53
|
+
else if (token.value === 'selector') {
|
|
54
|
+
type = { kind: 'selector' };
|
|
55
|
+
}
|
|
56
|
+
else if (token.value === 'Option' && this.check('<')) {
|
|
57
|
+
this.advance(); // consume <
|
|
58
|
+
const inner = this.parseType();
|
|
59
|
+
this.expect('>');
|
|
60
|
+
type = { kind: 'option', inner };
|
|
61
|
+
}
|
|
62
|
+
else if (token.value === 'double' || token.value === 'byte' ||
|
|
63
|
+
token.value === 'short' || token.value === 'long' ||
|
|
64
|
+
token.value === 'format_string') {
|
|
65
|
+
type = { kind: 'named', name: token.value };
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
type = { kind: 'struct', name: token.value };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.error(`Expected type, got '${token.value || token.kind}'. Valid types: int, float, bool, string, void, or a struct/enum name`);
|
|
73
|
+
}
|
|
74
|
+
while (this.match('[')) {
|
|
75
|
+
this.expect(']');
|
|
76
|
+
type = { kind: 'array', elem: type };
|
|
77
|
+
}
|
|
78
|
+
return type;
|
|
79
|
+
}
|
|
80
|
+
parseFunctionType() {
|
|
81
|
+
this.expect('(');
|
|
82
|
+
const params = [];
|
|
83
|
+
if (!this.check(')')) {
|
|
84
|
+
do {
|
|
85
|
+
params.push(this.parseType());
|
|
86
|
+
} while (this.match(','));
|
|
87
|
+
}
|
|
88
|
+
this.expect(')');
|
|
89
|
+
this.expect('->');
|
|
90
|
+
const returnType = this.parseType();
|
|
91
|
+
return { kind: 'function_type', params, return: returnType };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Try to parse `<Type, ...>` as explicit generic type arguments.
|
|
95
|
+
* Returns the parsed type list if successful, null if this looks like a comparison.
|
|
96
|
+
* Does NOT consume any tokens if it returns null.
|
|
97
|
+
*/
|
|
98
|
+
tryParseTypeArgs() {
|
|
99
|
+
const saved = this.pos;
|
|
100
|
+
this.advance(); // consume '<'
|
|
101
|
+
const typeArgs = [];
|
|
102
|
+
try {
|
|
103
|
+
do {
|
|
104
|
+
typeArgs.push(this.parseType());
|
|
105
|
+
} while (this.match(','));
|
|
106
|
+
if (!this.check('>')) {
|
|
107
|
+
this.pos = saved;
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
this.advance(); // consume '>'
|
|
111
|
+
return typeArgs;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
this.pos = saved;
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// -------------------------------------------------------------------------
|
|
119
|
+
// Lambda lookahead helpers (needed by expr-parser)
|
|
120
|
+
// -------------------------------------------------------------------------
|
|
121
|
+
isLambdaStart() {
|
|
122
|
+
if (!this.check('('))
|
|
123
|
+
return false;
|
|
124
|
+
let offset = 1;
|
|
125
|
+
if (this.peek(offset).kind !== ')') {
|
|
126
|
+
while (true) {
|
|
127
|
+
if (this.peek(offset).kind !== 'ident') {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
offset += 1;
|
|
131
|
+
if (this.peek(offset).kind === ':') {
|
|
132
|
+
offset += 1;
|
|
133
|
+
const consumed = this.typeTokenLength(offset);
|
|
134
|
+
if (consumed === 0) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
offset += consumed;
|
|
138
|
+
}
|
|
139
|
+
if (this.peek(offset).kind === ',') {
|
|
140
|
+
offset += 1;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (this.peek(offset).kind !== ')') {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
offset += 1;
|
|
150
|
+
if (this.peek(offset).kind === '=>') {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
if (this.peek(offset).kind === '->') {
|
|
154
|
+
offset += 1;
|
|
155
|
+
const consumed = this.typeTokenLength(offset);
|
|
156
|
+
if (consumed === 0) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
offset += consumed;
|
|
160
|
+
return this.peek(offset).kind === '=>';
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
typeTokenLength(offset) {
|
|
165
|
+
const token = this.peek(offset);
|
|
166
|
+
if (token.kind === '(') {
|
|
167
|
+
let inner = offset + 1;
|
|
168
|
+
if (this.peek(inner).kind !== ')') {
|
|
169
|
+
while (true) {
|
|
170
|
+
const consumed = this.typeTokenLength(inner);
|
|
171
|
+
if (consumed === 0) {
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
inner += consumed;
|
|
175
|
+
if (this.peek(inner).kind === ',') {
|
|
176
|
+
inner += 1;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (this.peek(inner).kind !== ')') {
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
inner += 1;
|
|
186
|
+
if (this.peek(inner).kind !== '->') {
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
inner += 1;
|
|
190
|
+
const returnLen = this.typeTokenLength(inner);
|
|
191
|
+
return returnLen === 0 ? 0 : inner + returnLen - offset;
|
|
192
|
+
}
|
|
193
|
+
const isNamedType = token.kind === 'int' ||
|
|
194
|
+
token.kind === 'bool' ||
|
|
195
|
+
token.kind === 'float' ||
|
|
196
|
+
token.kind === 'fixed' ||
|
|
197
|
+
token.kind === 'string' ||
|
|
198
|
+
token.kind === 'void' ||
|
|
199
|
+
token.kind === 'BlockPos' ||
|
|
200
|
+
token.kind === 'ident';
|
|
201
|
+
if (!isNamedType) {
|
|
202
|
+
return 0;
|
|
203
|
+
}
|
|
204
|
+
let length = 1;
|
|
205
|
+
while (this.peek(offset + length).kind === '[' && this.peek(offset + length + 1).kind === ']') {
|
|
206
|
+
length += 2;
|
|
207
|
+
}
|
|
208
|
+
return length;
|
|
209
|
+
}
|
|
210
|
+
// -------------------------------------------------------------------------
|
|
211
|
+
// Params parsing (used by decl-parser)
|
|
212
|
+
// -------------------------------------------------------------------------
|
|
213
|
+
parseParams(implTypeName) {
|
|
214
|
+
const params = [];
|
|
215
|
+
if (!this.check(')')) {
|
|
216
|
+
do {
|
|
217
|
+
const paramToken = this.expect('ident');
|
|
218
|
+
const name = paramToken.value;
|
|
219
|
+
let type;
|
|
220
|
+
if (implTypeName && params.length === 0 && name === 'self' && !this.check(':')) {
|
|
221
|
+
type = { kind: 'struct', name: implTypeName };
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
this.expect(':');
|
|
225
|
+
type = this.parseType();
|
|
226
|
+
}
|
|
227
|
+
let defaultValue;
|
|
228
|
+
if (this.match('=')) {
|
|
229
|
+
defaultValue = this.parseExpr();
|
|
230
|
+
}
|
|
231
|
+
params.push(this.withLoc({ name, type, default: defaultValue }, paramToken));
|
|
232
|
+
} while (this.match(','));
|
|
233
|
+
}
|
|
234
|
+
return params;
|
|
235
|
+
}
|
|
236
|
+
parseInterfaceParams() {
|
|
237
|
+
const params = [];
|
|
238
|
+
if (!this.check(')')) {
|
|
239
|
+
do {
|
|
240
|
+
const paramToken = this.expect('ident');
|
|
241
|
+
const paramName = paramToken.value;
|
|
242
|
+
let type;
|
|
243
|
+
if (params.length === 0 && paramName === 'self' && !this.check(':')) {
|
|
244
|
+
type = { kind: 'named', name: 'void' };
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
this.expect(':');
|
|
248
|
+
type = this.parseType();
|
|
249
|
+
}
|
|
250
|
+
params.push(this.withLoc({ name: paramName, type }, paramToken));
|
|
251
|
+
} while (this.match(','));
|
|
252
|
+
}
|
|
253
|
+
return params;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
exports.TypeParser = TypeParser;
|
|
257
|
+
//# sourceMappingURL=type-parser.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser utilities — base class with token navigation, error handling,
|
|
3
|
+
* and shared constants used by all sub-parsers.
|
|
4
|
+
*/
|
|
5
|
+
import { Lexer, type Token, type TokenKind } from '../lexer';
|
|
6
|
+
import { DiagnosticError } from '../diagnostics';
|
|
7
|
+
export declare const PRECEDENCE: Record<string, number>;
|
|
8
|
+
export declare const BINARY_OPS: Set<string>;
|
|
9
|
+
export type { Lexer };
|
|
10
|
+
export declare class ParserBase {
|
|
11
|
+
protected tokens: Token[];
|
|
12
|
+
protected pos: number;
|
|
13
|
+
protected sourceLines: string[];
|
|
14
|
+
protected filePath?: string;
|
|
15
|
+
/** Set to true once `module library;` is seen. */
|
|
16
|
+
protected inLibraryMode: boolean;
|
|
17
|
+
/** Warnings accumulated during parsing (e.g. deprecated keyword usage). */
|
|
18
|
+
readonly warnings: string[];
|
|
19
|
+
/** Parse errors collected during error-recovery mode. */
|
|
20
|
+
readonly parseErrors: DiagnosticError[];
|
|
21
|
+
constructor(tokens: Token[], source?: string, filePath?: string);
|
|
22
|
+
peek(offset?: number): Token;
|
|
23
|
+
advance(): Token;
|
|
24
|
+
check(kind: TokenKind): boolean;
|
|
25
|
+
match(...kinds: TokenKind[]): boolean;
|
|
26
|
+
expect(kind: TokenKind): Token;
|
|
27
|
+
error(message: string): never;
|
|
28
|
+
withLoc<T extends object>(node: T, token: Token): T;
|
|
29
|
+
getLocToken(node: object): Token | null;
|
|
30
|
+
checkIdent(value: string): boolean;
|
|
31
|
+
syncToNextDecl(): void;
|
|
32
|
+
syncToNextStmt(): void;
|
|
33
|
+
protected makeSubParser(source: string): ParserBase;
|
|
34
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser utilities — base class with token navigation, error handling,
|
|
4
|
+
* and shared constants used by all sub-parsers.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ParserBase = exports.BINARY_OPS = exports.PRECEDENCE = void 0;
|
|
8
|
+
const lexer_1 = require("../lexer");
|
|
9
|
+
const diagnostics_1 = require("../diagnostics");
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Operator Precedence (higher = binds tighter)
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
exports.PRECEDENCE = {
|
|
14
|
+
'||': 1,
|
|
15
|
+
'&&': 2,
|
|
16
|
+
'==': 3, '!=': 3,
|
|
17
|
+
'<': 4, '<=': 4, '>': 4, '>=': 4, 'is': 4,
|
|
18
|
+
'+': 5, '-': 5,
|
|
19
|
+
'*': 6, '/': 6, '%': 6,
|
|
20
|
+
};
|
|
21
|
+
exports.BINARY_OPS = new Set(['||', '&&', '==', '!=', '<', '<=', '>', '>=', 'is', '+', '-', '*', '/', '%']);
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// ParserBase — token navigation, error reporting, span attachment
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
class ParserBase {
|
|
26
|
+
constructor(tokens, source, filePath) {
|
|
27
|
+
this.pos = 0;
|
|
28
|
+
/** Set to true once `module library;` is seen. */
|
|
29
|
+
this.inLibraryMode = false;
|
|
30
|
+
/** Warnings accumulated during parsing (e.g. deprecated keyword usage). */
|
|
31
|
+
this.warnings = [];
|
|
32
|
+
/** Parse errors collected during error-recovery mode. */
|
|
33
|
+
this.parseErrors = [];
|
|
34
|
+
this.tokens = tokens;
|
|
35
|
+
this.sourceLines = source?.split('\n') ?? [];
|
|
36
|
+
this.filePath = filePath;
|
|
37
|
+
}
|
|
38
|
+
// -------------------------------------------------------------------------
|
|
39
|
+
// Token navigation
|
|
40
|
+
// -------------------------------------------------------------------------
|
|
41
|
+
peek(offset = 0) {
|
|
42
|
+
const idx = this.pos + offset;
|
|
43
|
+
if (idx >= this.tokens.length) {
|
|
44
|
+
return this.tokens[this.tokens.length - 1]; // eof
|
|
45
|
+
}
|
|
46
|
+
return this.tokens[idx];
|
|
47
|
+
}
|
|
48
|
+
advance() {
|
|
49
|
+
const token = this.tokens[this.pos];
|
|
50
|
+
if (token.kind !== 'eof')
|
|
51
|
+
this.pos++;
|
|
52
|
+
return token;
|
|
53
|
+
}
|
|
54
|
+
check(kind) {
|
|
55
|
+
return this.peek().kind === kind;
|
|
56
|
+
}
|
|
57
|
+
match(...kinds) {
|
|
58
|
+
for (const kind of kinds) {
|
|
59
|
+
if (this.check(kind)) {
|
|
60
|
+
this.advance();
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
expect(kind) {
|
|
67
|
+
const token = this.peek();
|
|
68
|
+
if (token.kind !== kind) {
|
|
69
|
+
throw new diagnostics_1.DiagnosticError('ParseError', `Expected '${kind}' but got '${token.kind}'`, { file: this.filePath, line: token.line, col: token.col }, this.sourceLines);
|
|
70
|
+
}
|
|
71
|
+
return this.advance();
|
|
72
|
+
}
|
|
73
|
+
error(message) {
|
|
74
|
+
const token = this.peek();
|
|
75
|
+
throw new diagnostics_1.DiagnosticError('ParseError', message, { file: this.filePath, line: token.line, col: token.col }, this.sourceLines);
|
|
76
|
+
}
|
|
77
|
+
withLoc(node, token) {
|
|
78
|
+
const span = { line: token.line, col: token.col };
|
|
79
|
+
Object.defineProperty(node, 'span', {
|
|
80
|
+
value: span,
|
|
81
|
+
enumerable: false,
|
|
82
|
+
configurable: true,
|
|
83
|
+
writable: true,
|
|
84
|
+
});
|
|
85
|
+
return node;
|
|
86
|
+
}
|
|
87
|
+
getLocToken(node) {
|
|
88
|
+
const span = node.span;
|
|
89
|
+
if (!span) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return { kind: 'eof', value: '', line: span.line, col: span.col };
|
|
93
|
+
}
|
|
94
|
+
checkIdent(value) {
|
|
95
|
+
return this.check('ident') && this.peek().value === value;
|
|
96
|
+
}
|
|
97
|
+
// -------------------------------------------------------------------------
|
|
98
|
+
// Error Recovery
|
|
99
|
+
// -------------------------------------------------------------------------
|
|
100
|
+
syncToNextDecl() {
|
|
101
|
+
const TOP_LEVEL_KEYWORDS = new Set([
|
|
102
|
+
'fn', 'struct', 'impl', 'enum', 'const', 'let', 'export', 'declare', 'import', 'namespace', 'module'
|
|
103
|
+
]);
|
|
104
|
+
while (!this.check('eof')) {
|
|
105
|
+
const kind = this.peek().kind;
|
|
106
|
+
if (kind === '}') {
|
|
107
|
+
this.advance();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (TOP_LEVEL_KEYWORDS.has(kind)) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (kind === 'ident' && this.peek().value === 'import') {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.advance();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
syncToNextStmt() {
|
|
120
|
+
while (!this.check('eof')) {
|
|
121
|
+
const kind = this.peek().kind;
|
|
122
|
+
if (kind === ';') {
|
|
123
|
+
this.advance();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (kind === '}') {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
this.advance();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// -------------------------------------------------------------------------
|
|
133
|
+
// Sub-parser helper (used by string interpolation)
|
|
134
|
+
// -------------------------------------------------------------------------
|
|
135
|
+
makeSubParser(source) {
|
|
136
|
+
const tokens = new lexer_1.Lexer(source, this.filePath).tokenize();
|
|
137
|
+
return new ParserBase(tokens, source, this.filePath);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.ParserBase = ParserBase;
|
|
141
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# MC Integration Tests — 操作手册
|
|
2
|
+
|
|
3
|
+
RedScript 有两层测试:
|
|
4
|
+
|
|
5
|
+
1. **单元/e2e 测试**:纯编译器,不需要 MC 服务器,直接 `npm test`
|
|
6
|
+
2. **MC 联动测试**:需要 Paper 服务器 + TestHarness 插件,测试生成的 mcfunction 在真实 MC 里是否正确执行
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 快速跑法
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# 1. 确认服务器在跑
|
|
14
|
+
curl http://localhost:25561/status
|
|
15
|
+
|
|
16
|
+
# 2. 跑所有 MC 联动测试
|
|
17
|
+
cd ~/projects/redscript
|
|
18
|
+
MC_SERVER_DIR=~/mc-test-server MC_PORT=25561 npx jest src/__tests__/mc-integration/ --testTimeout=120000 --no-coverage --forceExit
|
|
19
|
+
|
|
20
|
+
# 3. 跑单个测试文件
|
|
21
|
+
MC_SERVER_DIR=~/mc-test-server MC_PORT=25561 npx jest say-fstring --testTimeout=60000 --forceExit
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
服务器不在线时,测试会自动跳过(不报错),只有编译时测试会跑。
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 启动 Paper 服务器
|
|
29
|
+
|
|
30
|
+
Java 装在 Homebrew(不在系统 PATH):
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
export JAVA_HOME="/opt/homebrew/opt/openjdk@21"
|
|
34
|
+
export PATH="$JAVA_HOME/bin:$PATH"
|
|
35
|
+
|
|
36
|
+
cd ~/mc-test-server
|
|
37
|
+
java -Xmx2G -Xms512M -jar paper.jar --nogui
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
服务器起来后 TestHarness 插件自动在 **端口 25561** 上开 HTTP API。
|
|
41
|
+
|
|
42
|
+
验证:
|
|
43
|
+
```bash
|
|
44
|
+
curl http://localhost:25561/status
|
|
45
|
+
# → {"online":true,"tps_1m":20.0,...}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 关键注意事项
|
|
51
|
+
|
|
52
|
+
### 1. 只能有一个 redscript-test datapack
|
|
53
|
+
|
|
54
|
+
`~/mc-test-server/world/datapacks/` 里**只留 `redscript-test/`**,其他旧 datapack 全删掉。
|
|
55
|
+
多个 datapack 有相同 namespace 会导致 MC 用旧版本,测试结果不对。
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# 清理旧 datapack(只留 redscript-test)
|
|
59
|
+
ls ~/mc-test-server/world/datapacks/
|
|
60
|
+
trash ~/mc-test-server/world/datapacks/<旧的>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. Reload 用 TestHarness API
|
|
64
|
+
|
|
65
|
+
不要用 `/reload` 命令(需要 `/reload confirm`,Paper 1.21+ 要求)。
|
|
66
|
+
测试代码里用 `mc.reload()`,它调用的是 `POST /reload`。
|
|
67
|
+
|
|
68
|
+
### 3. Java 路径
|
|
69
|
+
|
|
70
|
+
系统 `java` 命令指向一个壳,实际没装。用:
|
|
71
|
+
```bash
|
|
72
|
+
/opt/homebrew/opt/openjdk@21/bin/java -version
|
|
73
|
+
# openjdk version "21.0.10"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 测试文件结构
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
src/__tests__/mc-integration/
|
|
82
|
+
├── syntax-coverage.test.ts # 语法特性:for-each、match、Option、impl、struct、array
|
|
83
|
+
├── say-fstring.test.ts # say() + f-string macro 编译和运行时测试
|
|
84
|
+
├── stdlib-coverage.test.ts # stdlib 覆盖(数学、物理等)
|
|
85
|
+
├── stdlib-coverage-2~8.test.ts # stdlib 覆盖续集
|
|
86
|
+
└── item-entity-events.test.ts # 实体/物品/事件
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 写新 test case 的模式
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { compile } from '../../compile'
|
|
93
|
+
import { MCTestClient } from '../../mc-test/client'
|
|
94
|
+
|
|
95
|
+
const NS = 'my_test_ns' // 每个文件用唯一 namespace
|
|
96
|
+
let serverOnline = false
|
|
97
|
+
let mc: MCTestClient
|
|
98
|
+
|
|
99
|
+
beforeAll(async () => {
|
|
100
|
+
mc = new MCTestClient(process.env.MC_HOST ?? 'localhost', parseInt(process.env.MC_PORT ?? '25561'))
|
|
101
|
+
try {
|
|
102
|
+
serverOnline = await mc.isOnline()
|
|
103
|
+
} catch { serverOnline = false }
|
|
104
|
+
if (!serverOnline) return
|
|
105
|
+
|
|
106
|
+
// 编译 + 写入 datapack
|
|
107
|
+
writeFixture(`...redscript code...`, NS)
|
|
108
|
+
await mc.reload()
|
|
109
|
+
await mc.ticks(5)
|
|
110
|
+
}, 30_000)
|
|
111
|
+
|
|
112
|
+
test('runtime: xxx', async () => {
|
|
113
|
+
if (!serverOnline) return // 服务器不在线就跳过
|
|
114
|
+
|
|
115
|
+
await mc.command(`/function ${NS}:fn_name`)
|
|
116
|
+
await mc.ticks(5)
|
|
117
|
+
const score = await mc.scoreboard('#result', 'objective')
|
|
118
|
+
expect(score).toBe(42)
|
|
119
|
+
}, 20_000)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 可用的 TestHarness API(via MCTestClient)
|
|
123
|
+
|
|
124
|
+
| 方法 | 说明 |
|
|
125
|
+
|------|------|
|
|
126
|
+
| `mc.isOnline()` | 检查服务器是否在线 |
|
|
127
|
+
| `mc.reload()` | Reload datapacks(正确方式) |
|
|
128
|
+
| `mc.ticks(n)` | 等待 n 个游戏 tick(50ms 各) |
|
|
129
|
+
| `mc.command('/xxx')` | 执行 MC 命令 |
|
|
130
|
+
| `mc.scoreboard(player, obj)` | 读取 scoreboard 值 |
|
|
131
|
+
| `mc.chat(since)` | 读取聊天日志 |
|
|
132
|
+
| `mc.assertChatContains(str)` | 断言聊天包含字符串 |
|
|
133
|
+
| `mc.reset()` | 清空聊天和事件日志 |
|
|
134
|
+
| `mc.fullReset(...)` | 完整重置(清空区域、实体、scoreboard) |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 已知问题
|
|
139
|
+
|
|
140
|
+
- `impl Counter` 测试(syntax-coverage):runtime 失败,`#impl_out` 读到 0 而不是 3。这是预存在的 impl 方法运行时 bug,与 f-string/say macro 无关。
|
|
141
|
+
- `repl-server-extra.test.ts`:端口 3001 占用导致 10 个测试失败,与代码无关。
|