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,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeParser — parses type annotations and generic type arguments.
|
|
3
|
+
* Extends ParserBase to gain token navigation.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { TypeNode, Param } from '../ast/types'
|
|
7
|
+
import { ParserBase } from './utils'
|
|
8
|
+
|
|
9
|
+
export class TypeParser extends ParserBase {
|
|
10
|
+
// -------------------------------------------------------------------------
|
|
11
|
+
// Type Parsing
|
|
12
|
+
// -------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
parseType(): TypeNode {
|
|
15
|
+
const token = this.peek()
|
|
16
|
+
let type: TypeNode
|
|
17
|
+
|
|
18
|
+
if (token.kind === '(') {
|
|
19
|
+
// Disambiguate: tuple type `(T, T)` vs function type `(T) -> R`
|
|
20
|
+
const saved = this.pos
|
|
21
|
+
this.advance() // consume '('
|
|
22
|
+
const elements: TypeNode[] = []
|
|
23
|
+
if (!this.check(')')) {
|
|
24
|
+
do {
|
|
25
|
+
elements.push(this.parseType())
|
|
26
|
+
} while (this.match(','))
|
|
27
|
+
}
|
|
28
|
+
this.expect(')')
|
|
29
|
+
if (this.check('->')) {
|
|
30
|
+
this.pos = saved
|
|
31
|
+
return this.parseFunctionType()
|
|
32
|
+
}
|
|
33
|
+
return { kind: 'tuple', elements }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (token.kind === 'float') {
|
|
37
|
+
this.advance()
|
|
38
|
+
const filePart = this.filePath ? `${this.filePath}:` : ''
|
|
39
|
+
this.warnings.push(
|
|
40
|
+
`[DeprecatedType] ${filePart}line ${token.line}, col ${token.col}: 'float' is deprecated, use 'fixed' instead (×10000 fixed-point)`
|
|
41
|
+
)
|
|
42
|
+
type = { kind: 'named', name: 'float' }
|
|
43
|
+
} else if (token.kind === 'int' || token.kind === 'bool' ||
|
|
44
|
+
token.kind === 'fixed' || token.kind === 'string' || token.kind === 'void' ||
|
|
45
|
+
token.kind === 'BlockPos') {
|
|
46
|
+
this.advance()
|
|
47
|
+
type = { kind: 'named', name: token.kind }
|
|
48
|
+
} else if (token.kind === 'ident') {
|
|
49
|
+
this.advance()
|
|
50
|
+
if (token.value === 'selector' && this.check('<')) {
|
|
51
|
+
this.advance() // consume <
|
|
52
|
+
const entityType = this.expect('ident').value
|
|
53
|
+
this.expect('>')
|
|
54
|
+
type = { kind: 'selector', entityType }
|
|
55
|
+
} else if (token.value === 'selector') {
|
|
56
|
+
type = { kind: 'selector' }
|
|
57
|
+
} else if (token.value === 'Option' && this.check('<')) {
|
|
58
|
+
this.advance() // consume <
|
|
59
|
+
const inner = this.parseType()
|
|
60
|
+
this.expect('>')
|
|
61
|
+
type = { kind: 'option', inner }
|
|
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 as any }
|
|
66
|
+
} else {
|
|
67
|
+
type = { kind: 'struct', name: token.value }
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
this.error(`Expected type, got '${token.value || token.kind}'. Valid types: int, float, bool, string, void, or a struct/enum name`)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
while (this.match('[')) {
|
|
74
|
+
this.expect(']')
|
|
75
|
+
type = { kind: 'array', elem: type }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return type
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
parseFunctionType(): TypeNode {
|
|
82
|
+
this.expect('(')
|
|
83
|
+
const params: TypeNode[] = []
|
|
84
|
+
|
|
85
|
+
if (!this.check(')')) {
|
|
86
|
+
do {
|
|
87
|
+
params.push(this.parseType())
|
|
88
|
+
} while (this.match(','))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.expect(')')
|
|
92
|
+
this.expect('->')
|
|
93
|
+
const returnType = this.parseType()
|
|
94
|
+
return { kind: 'function_type', params, return: returnType }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Try to parse `<Type, ...>` as explicit generic type arguments.
|
|
99
|
+
* Returns the parsed type list if successful, null if this looks like a comparison.
|
|
100
|
+
* Does NOT consume any tokens if it returns null.
|
|
101
|
+
*/
|
|
102
|
+
tryParseTypeArgs(): TypeNode[] | null {
|
|
103
|
+
const saved = this.pos
|
|
104
|
+
this.advance() // consume '<'
|
|
105
|
+
const typeArgs: TypeNode[] = []
|
|
106
|
+
try {
|
|
107
|
+
do {
|
|
108
|
+
typeArgs.push(this.parseType())
|
|
109
|
+
} while (this.match(','))
|
|
110
|
+
if (!this.check('>')) {
|
|
111
|
+
this.pos = saved
|
|
112
|
+
return null
|
|
113
|
+
}
|
|
114
|
+
this.advance() // consume '>'
|
|
115
|
+
return typeArgs
|
|
116
|
+
} catch {
|
|
117
|
+
this.pos = saved
|
|
118
|
+
return null
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// -------------------------------------------------------------------------
|
|
123
|
+
// Lambda lookahead helpers (needed by expr-parser)
|
|
124
|
+
// -------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
isLambdaStart(): boolean {
|
|
127
|
+
if (!this.check('(')) return false
|
|
128
|
+
|
|
129
|
+
let offset = 1
|
|
130
|
+
if (this.peek(offset).kind !== ')') {
|
|
131
|
+
while (true) {
|
|
132
|
+
if (this.peek(offset).kind !== 'ident') {
|
|
133
|
+
return false
|
|
134
|
+
}
|
|
135
|
+
offset += 1
|
|
136
|
+
|
|
137
|
+
if (this.peek(offset).kind === ':') {
|
|
138
|
+
offset += 1
|
|
139
|
+
const consumed = this.typeTokenLength(offset)
|
|
140
|
+
if (consumed === 0) {
|
|
141
|
+
return false
|
|
142
|
+
}
|
|
143
|
+
offset += consumed
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (this.peek(offset).kind === ',') {
|
|
147
|
+
offset += 1
|
|
148
|
+
continue
|
|
149
|
+
}
|
|
150
|
+
break
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (this.peek(offset).kind !== ')') {
|
|
155
|
+
return false
|
|
156
|
+
}
|
|
157
|
+
offset += 1
|
|
158
|
+
|
|
159
|
+
if (this.peek(offset).kind === '=>') {
|
|
160
|
+
return true
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (this.peek(offset).kind === '->') {
|
|
164
|
+
offset += 1
|
|
165
|
+
const consumed = this.typeTokenLength(offset)
|
|
166
|
+
if (consumed === 0) {
|
|
167
|
+
return false
|
|
168
|
+
}
|
|
169
|
+
offset += consumed
|
|
170
|
+
return this.peek(offset).kind === '=>'
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return false
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
typeTokenLength(offset: number): number {
|
|
177
|
+
const token = this.peek(offset)
|
|
178
|
+
|
|
179
|
+
if (token.kind === '(') {
|
|
180
|
+
let inner = offset + 1
|
|
181
|
+
if (this.peek(inner).kind !== ')') {
|
|
182
|
+
while (true) {
|
|
183
|
+
const consumed = this.typeTokenLength(inner)
|
|
184
|
+
if (consumed === 0) {
|
|
185
|
+
return 0
|
|
186
|
+
}
|
|
187
|
+
inner += consumed
|
|
188
|
+
if (this.peek(inner).kind === ',') {
|
|
189
|
+
inner += 1
|
|
190
|
+
continue
|
|
191
|
+
}
|
|
192
|
+
break
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (this.peek(inner).kind !== ')') {
|
|
197
|
+
return 0
|
|
198
|
+
}
|
|
199
|
+
inner += 1
|
|
200
|
+
|
|
201
|
+
if (this.peek(inner).kind !== '->') {
|
|
202
|
+
return 0
|
|
203
|
+
}
|
|
204
|
+
inner += 1
|
|
205
|
+
const returnLen = this.typeTokenLength(inner)
|
|
206
|
+
return returnLen === 0 ? 0 : inner + returnLen - offset
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const isNamedType =
|
|
210
|
+
token.kind === 'int' ||
|
|
211
|
+
token.kind === 'bool' ||
|
|
212
|
+
token.kind === 'float' ||
|
|
213
|
+
token.kind === 'fixed' ||
|
|
214
|
+
token.kind === 'string' ||
|
|
215
|
+
token.kind === 'void' ||
|
|
216
|
+
token.kind === 'BlockPos' ||
|
|
217
|
+
token.kind === 'ident'
|
|
218
|
+
if (!isNamedType) {
|
|
219
|
+
return 0
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let length = 1
|
|
223
|
+
while (this.peek(offset + length).kind === '[' && this.peek(offset + length + 1).kind === ']') {
|
|
224
|
+
length += 2
|
|
225
|
+
}
|
|
226
|
+
return length
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// -------------------------------------------------------------------------
|
|
230
|
+
// Params parsing (used by decl-parser)
|
|
231
|
+
// -------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
parseParams(implTypeName?: string): Param[] {
|
|
234
|
+
const params: Param[] = []
|
|
235
|
+
|
|
236
|
+
if (!this.check(')')) {
|
|
237
|
+
do {
|
|
238
|
+
const paramToken = this.expect('ident')
|
|
239
|
+
const name = paramToken.value
|
|
240
|
+
let type: TypeNode
|
|
241
|
+
if (implTypeName && params.length === 0 && name === 'self' && !this.check(':')) {
|
|
242
|
+
type = { kind: 'struct', name: implTypeName }
|
|
243
|
+
} else {
|
|
244
|
+
this.expect(':')
|
|
245
|
+
type = this.parseType()
|
|
246
|
+
}
|
|
247
|
+
let defaultValue: import('../ast/types').Expr | undefined
|
|
248
|
+
if (this.match('=')) {
|
|
249
|
+
defaultValue = (this as any).parseExpr()
|
|
250
|
+
}
|
|
251
|
+
params.push(this.withLoc({ name, type, default: defaultValue }, paramToken))
|
|
252
|
+
} while (this.match(','))
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return params
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
parseInterfaceParams(): Param[] {
|
|
259
|
+
const params: Param[] = []
|
|
260
|
+
if (!this.check(')')) {
|
|
261
|
+
do {
|
|
262
|
+
const paramToken = this.expect('ident')
|
|
263
|
+
const paramName = paramToken.value
|
|
264
|
+
let type: TypeNode
|
|
265
|
+
if (params.length === 0 && paramName === 'self' && !this.check(':')) {
|
|
266
|
+
type = { kind: 'named', name: 'void' }
|
|
267
|
+
} else {
|
|
268
|
+
this.expect(':')
|
|
269
|
+
type = this.parseType()
|
|
270
|
+
}
|
|
271
|
+
params.push(this.withLoc({ name: paramName, type }, paramToken))
|
|
272
|
+
} while (this.match(','))
|
|
273
|
+
}
|
|
274
|
+
return params
|
|
275
|
+
}
|
|
276
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser utilities — base class with token navigation, error handling,
|
|
3
|
+
* and shared constants used by all sub-parsers.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Lexer, type Token, type TokenKind } from '../lexer'
|
|
7
|
+
import type { Span } from '../ast/types'
|
|
8
|
+
import { DiagnosticError } from '../diagnostics'
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Operator Precedence (higher = binds tighter)
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
export const PRECEDENCE: Record<string, number> = {
|
|
15
|
+
'||': 1,
|
|
16
|
+
'&&': 2,
|
|
17
|
+
'==': 3, '!=': 3,
|
|
18
|
+
'<': 4, '<=': 4, '>': 4, '>=': 4, 'is': 4,
|
|
19
|
+
'+': 5, '-': 5,
|
|
20
|
+
'*': 6, '/': 6, '%': 6,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const BINARY_OPS = new Set(['||', '&&', '==', '!=', '<', '<=', '>', '>=', 'is', '+', '-', '*', '/', '%'])
|
|
24
|
+
|
|
25
|
+
export type { Lexer }
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// ParserBase — token navigation, error reporting, span attachment
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
export class ParserBase {
|
|
32
|
+
protected tokens: Token[]
|
|
33
|
+
protected pos: number = 0
|
|
34
|
+
protected sourceLines: string[]
|
|
35
|
+
protected filePath?: string
|
|
36
|
+
/** Set to true once `module library;` is seen. */
|
|
37
|
+
protected inLibraryMode: boolean = false
|
|
38
|
+
/** Warnings accumulated during parsing (e.g. deprecated keyword usage). */
|
|
39
|
+
readonly warnings: string[] = []
|
|
40
|
+
/** Parse errors collected during error-recovery mode. */
|
|
41
|
+
readonly parseErrors: DiagnosticError[] = []
|
|
42
|
+
|
|
43
|
+
constructor(tokens: Token[], source?: string, filePath?: string) {
|
|
44
|
+
this.tokens = tokens
|
|
45
|
+
this.sourceLines = source?.split('\n') ?? []
|
|
46
|
+
this.filePath = filePath
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// -------------------------------------------------------------------------
|
|
50
|
+
// Token navigation
|
|
51
|
+
// -------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
peek(offset = 0): Token {
|
|
54
|
+
const idx = this.pos + offset
|
|
55
|
+
if (idx >= this.tokens.length) {
|
|
56
|
+
return this.tokens[this.tokens.length - 1] // eof
|
|
57
|
+
}
|
|
58
|
+
return this.tokens[idx]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
advance(): Token {
|
|
62
|
+
const token = this.tokens[this.pos]
|
|
63
|
+
if (token.kind !== 'eof') this.pos++
|
|
64
|
+
return token
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
check(kind: TokenKind): boolean {
|
|
68
|
+
return this.peek().kind === kind
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
match(...kinds: TokenKind[]): boolean {
|
|
72
|
+
for (const kind of kinds) {
|
|
73
|
+
if (this.check(kind)) {
|
|
74
|
+
this.advance()
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
expect(kind: TokenKind): Token {
|
|
82
|
+
const token = this.peek()
|
|
83
|
+
if (token.kind !== kind) {
|
|
84
|
+
throw new DiagnosticError(
|
|
85
|
+
'ParseError',
|
|
86
|
+
`Expected '${kind}' but got '${token.kind}'`,
|
|
87
|
+
{ file: this.filePath, line: token.line, col: token.col },
|
|
88
|
+
this.sourceLines
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
return this.advance()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
error(message: string): never {
|
|
95
|
+
const token = this.peek()
|
|
96
|
+
throw new DiagnosticError(
|
|
97
|
+
'ParseError',
|
|
98
|
+
message,
|
|
99
|
+
{ file: this.filePath, line: token.line, col: token.col },
|
|
100
|
+
this.sourceLines
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
withLoc<T extends object>(node: T, token: Token): T {
|
|
105
|
+
const span: Span = { line: token.line, col: token.col }
|
|
106
|
+
Object.defineProperty(node, 'span', {
|
|
107
|
+
value: span,
|
|
108
|
+
enumerable: false,
|
|
109
|
+
configurable: true,
|
|
110
|
+
writable: true,
|
|
111
|
+
})
|
|
112
|
+
return node
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getLocToken(node: object): Token | null {
|
|
116
|
+
const span = (node as { span?: Span }).span
|
|
117
|
+
if (!span) {
|
|
118
|
+
return null
|
|
119
|
+
}
|
|
120
|
+
return { kind: 'eof', value: '', line: span.line, col: span.col }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
checkIdent(value: string): boolean {
|
|
124
|
+
return this.check('ident') && this.peek().value === value
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// -------------------------------------------------------------------------
|
|
128
|
+
// Error Recovery
|
|
129
|
+
// -------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
syncToNextDecl(): void {
|
|
132
|
+
const TOP_LEVEL_KEYWORDS = new Set([
|
|
133
|
+
'fn', 'struct', 'impl', 'enum', 'const', 'let', 'export', 'declare', 'import', 'namespace', 'module'
|
|
134
|
+
])
|
|
135
|
+
while (!this.check('eof')) {
|
|
136
|
+
const kind = this.peek().kind
|
|
137
|
+
if (kind === '}') {
|
|
138
|
+
this.advance()
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
if (TOP_LEVEL_KEYWORDS.has(kind)) {
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
if (kind === 'ident' && this.peek().value === 'import') {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
this.advance()
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
syncToNextStmt(): void {
|
|
152
|
+
while (!this.check('eof')) {
|
|
153
|
+
const kind = this.peek().kind
|
|
154
|
+
if (kind === ';') {
|
|
155
|
+
this.advance()
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
if (kind === '}') {
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
this.advance()
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// -------------------------------------------------------------------------
|
|
166
|
+
// Sub-parser helper (used by string interpolation)
|
|
167
|
+
// -------------------------------------------------------------------------
|
|
168
|
+
|
|
169
|
+
protected makeSubParser(source: string): ParserBase {
|
|
170
|
+
const tokens = new Lexer(source, this.filePath).tokenize()
|
|
171
|
+
return new ParserBase(tokens, source, this.filePath)
|
|
172
|
+
}
|
|
173
|
+
}
|
package/src/stdlib/queue.mcrs
CHANGED
|
@@ -11,6 +11,19 @@
|
|
|
11
11
|
|
|
12
12
|
module library;
|
|
13
13
|
|
|
14
|
+
// Placeholder conventions used in raw() strings:
|
|
15
|
+
// __OBJ__ → replaced with __<namespace> (per-namespace scoreboard objective)
|
|
16
|
+
// __NS__ → replaced with the current namespace
|
|
17
|
+
// __RS__ → replaced with "rs" (global RedScript scoreboard objective)
|
|
18
|
+
|
|
19
|
+
// ─── Initialisation ──────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
/// Ensure the shared "rs" scoreboard objective exists.
|
|
22
|
+
/// Called automatically on datapack load.
|
|
23
|
+
@load fn __queue_init() {
|
|
24
|
+
raw("scoreboard objectives add rs dummy");
|
|
25
|
+
}
|
|
26
|
+
|
|
14
27
|
// ─── Internal macro helpers ──────────────────────────────────────────────────
|
|
15
28
|
|
|
16
29
|
fn __queue_append_apply() {
|
|
@@ -18,11 +31,11 @@ fn __queue_append_apply() {
|
|
|
18
31
|
}
|
|
19
32
|
|
|
20
33
|
fn __queue_peek_apply() {
|
|
21
|
-
raw("$execute store result score
|
|
34
|
+
raw("$execute store result score rs.peek_tmp __RS__ run data get storage rs:arrays Queue[$(idx)]");
|
|
22
35
|
}
|
|
23
36
|
|
|
24
37
|
fn __queue_size_raw_apply() {
|
|
25
|
-
raw("
|
|
38
|
+
raw("execute store result score $ret __OBJ__ run data get storage rs:arrays Queue");
|
|
26
39
|
}
|
|
27
40
|
|
|
28
41
|
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
@@ -53,10 +66,10 @@ fn queue_pop(): int {
|
|
|
53
66
|
let empty: int = queue_empty();
|
|
54
67
|
if (empty == 1) { return -1; }
|
|
55
68
|
|
|
56
|
-
// Read front element
|
|
57
|
-
raw("scoreboard players set $ret __OBJ__ 0");
|
|
69
|
+
// Read front element via macro (idx → Queue[idx] → rs.peek_tmp)
|
|
58
70
|
raw("execute store result storage rs:macro_args idx int 1 run scoreboard players get rs.q_head __RS__");
|
|
59
71
|
raw("function __NS__:__queue_peek_apply with storage rs:macro_args");
|
|
72
|
+
raw("scoreboard players operation $ret __OBJ__ = rs.peek_tmp __RS__");
|
|
60
73
|
// Advance head pointer
|
|
61
74
|
raw("scoreboard players add rs.q_head __RS__ 1");
|
|
62
75
|
raw("return 1");
|
|
@@ -75,9 +88,9 @@ fn queue_peek(): int {
|
|
|
75
88
|
let empty: int = queue_empty();
|
|
76
89
|
if (empty == 1) { return -1; }
|
|
77
90
|
|
|
78
|
-
raw("scoreboard players set $ret __OBJ__ 0");
|
|
79
91
|
raw("execute store result storage rs:macro_args idx int 1 run scoreboard players get rs.q_head __RS__");
|
|
80
92
|
raw("function __NS__:__queue_peek_apply with storage rs:macro_args");
|
|
93
|
+
raw("scoreboard players operation $ret __OBJ__ = rs.peek_tmp __RS__");
|
|
81
94
|
raw("return 1");
|
|
82
95
|
return 0;
|
|
83
96
|
}
|
|
@@ -92,7 +105,7 @@ fn queue_peek(): int {
|
|
|
92
105
|
/// let n: int = queue_size()
|
|
93
106
|
fn queue_size(): int {
|
|
94
107
|
raw("scoreboard players set $ret __OBJ__ 0");
|
|
95
|
-
raw("function __NS__:__queue_size_raw_apply
|
|
108
|
+
raw("function __NS__:__queue_size_raw_apply");
|
|
96
109
|
raw("scoreboard players operation $ret __OBJ__ -= rs.q_head __RS__");
|
|
97
110
|
raw("return 1");
|
|
98
111
|
return 0;
|